diff --git a/examples/custom-server/README.md b/examples/custom-server/README.md new file mode 100644 index 0000000..5e8546d --- /dev/null +++ b/examples/custom-server/README.md @@ -0,0 +1,88 @@ +# Example - custom MCP server on Databricks Apps + +This example shows how to create and launch a custom agent on Databricks Apps. +Please note that this example doesn't use any Databricks SDK, and is independent of the `mcp` package in the root dir of this repo. + +## Prerequisites + +- Databricks CLI installed and configured +- `uv` + +## Local development + +- run `uv` sync: + +```bash +uv sync +``` + +- start the server locally. Changes will trigger a reload: + +```bash +uvicorn custom-server.app:mcp --reload +``` + +## Deploying a custom MCP server on Databricks Apps + +There are two ways to deploy the server on Databricks Apps: using the `databricks apps` CLI or using the `databricks bundle` CLI. Depending on your preference, you can choose either method. + +Both approaches require first configuring Databricks authentication: +```bash +export DATABRICKS_CONFIG_PROFILE= # e.g. custom-mcp-server +databricks auth login --profile "$DATABRICKS_CONFIG_PROFILE" +``` + +### Using `databricks apps` CLI + +To deploy the server using the `databricks apps` CLI, follow these steps: + +Create a Databricks app to host your MCP server: +```bash +databricks apps create mcp-custom-server +``` + +Upload the source code to Databricks and deploy the app: + +```bash +DATABRICKS_USERNAME=$(databricks current-user me | jq -r .userName) +databricks sync . "/Users/$DATABRICKS_USERNAME/my-mcp-server" +databricks apps deploy mcp-custom-server --source-code-path "/Workspace/Users/$DATABRICKS_USERNAME/my-mcp-server" +``` + +### Using `databricks bundle` CLI + +To deploy the server using the `databricks bundle` CLI, follow these steps + +[//]: # (TODO: would be nice to also be able to use the same uv command to auto-install dependencies and run the app) +Update the `app.yaml` file in this directory to use the following command: +```yaml +command: ["uvicorn", "custom_server.app:app"] +``` + +- In this directory, run the following command to deploy and run the MCP server on Databricks Apps: + +```bash +uv build --wheel +databricks bundle deploy +databricks bundle run custom-mcp-server +``` + +## Connecting to the MCP server + +[//]: # (TODO: once official Databricks docs for using MCP servers in agents are live, replace this with a link) +[//]: # (to that section) + +To connect to the MCP server, use the `Streamable HTTP` transport with the following URL: + +``` +https://your-app-url.usually.ends.with.databricksapps.com/mcp/ +``` + +For authentication, you can use the `Bearer` token from your Databricks profile. +You can get the token by running the following command: + +```bash +databricks auth token -p +``` + +Please note that the URL should end with `/mcp/` (including the trailing slash), as this is required for the server to work correctly. diff --git a/examples/custom-server/app.yaml b/examples/custom-server/app.yaml new file mode 100644 index 0000000..edbc253 --- /dev/null +++ b/examples/custom-server/app.yaml @@ -0,0 +1 @@ +command: ["uv", "run", "custom-server"] diff --git a/examples/custom-server/databricks.yml b/examples/custom-server/databricks.yml index ecb7831..ec33490 100644 --- a/examples/custom-server/databricks.yml +++ b/examples/custom-server/databricks.yml @@ -14,11 +14,9 @@ artifacts: resources: apps: custom-mcp-server: - name: "custom-mcp-server" + name: "mcp-custom-server-bundles" description: "Custom MCP Server on Databricks Apps" source_code_path: ./.build - config: - command: ["uvicorn", "custom_server.app:app"] targets: dev: diff --git a/examples/custom-server/pyproject.toml b/examples/custom-server/pyproject.toml index 09ff7b9..050cbdc 100644 --- a/examples/custom-server/pyproject.toml +++ b/examples/custom-server/pyproject.toml @@ -25,3 +25,6 @@ dev = [ [tool.hatch.build.hooks.custom] path = "hooks/apps_build.py" + +[project.scripts] +custom-server = "custom_server.main:main" diff --git a/examples/custom-server/requirements.txt b/examples/custom-server/requirements.txt new file mode 100644 index 0000000..e1bbc23 --- /dev/null +++ b/examples/custom-server/requirements.txt @@ -0,0 +1,2 @@ +# We only need `uv` in our requirements if deploying via apps CLI +uv diff --git a/examples/custom-server/src/custom_server/app.py b/examples/custom-server/src/custom_server/app.py index 5c6ab29..8965ffe 100644 --- a/examples/custom-server/src/custom_server/app.py +++ b/examples/custom-server/src/custom_server/app.py @@ -1,7 +1,9 @@ from pathlib import Path -from fastapi.staticfiles import StaticFiles from mcp.server.fastmcp import FastMCP from fastapi import FastAPI +from fastapi.responses import FileResponse + +STATIC_DIR = Path(__file__).parent / "static" # Create an MCP server mcp = FastMCP("Custom MCP Server on Databricks Apps") @@ -22,14 +24,16 @@ def get_greeting(name: str) -> str: mcp_app = mcp.streamable_http_app() -static = StaticFiles(directory=Path(__file__).parent / "static", html=True) app = FastAPI( lifespan=lambda _: mcp.session_manager.run(), ) -# note the order of mounting here, -# and don't change it unless you know what you're doing -app.mount("/api", mcp_app) -app.mount("/", static) + +@app.get("/", include_in_schema=False) +async def serve_index(): + return FileResponse(STATIC_DIR / "index.html") + + +app.mount("/", mcp_app) diff --git a/examples/custom-server/src/custom_server/main.py b/examples/custom-server/src/custom_server/main.py new file mode 100644 index 0000000..b5b9c50 --- /dev/null +++ b/examples/custom-server/src/custom_server/main.py @@ -0,0 +1,10 @@ +import uvicorn + + +def main(): + uvicorn.run( + "custom_server.app:app", # import path to your `app` + host="0.0.0.0", + port=8000, + reload=True, # optional + ) diff --git a/examples/custom-server/src/custom_server/static/index.html b/examples/custom-server/src/custom_server/static/index.html index b1dfe94..d3c28e2 100644 --- a/examples/custom-server/src/custom_server/static/index.html +++ b/examples/custom-server/src/custom_server/static/index.html @@ -88,6 +88,7 @@

Connection Details

 
 from databricks.sdk import WorkspaceClient
+from databricks_mcp import DatabricksOAuthClientProvider
 from mcp.client.streamable_http import streamablehttp_client as connect
 from mcp import ClientSession
 
@@ -96,9 +97,8 @@ 

Connection Details

async def main(): # Connect to a streamable HTTP server - headers = client.config.authenticate() - app_url = "https://your.app.url.databricksapps.com/api/mcp" - async with connect(app_url, headers=headers) as ( + app_url = "https://your.app.url.databricksapps.com/mcp/" + async with connect(app_url, auth=DatabricksOAuthClientProvider(client)) as ( read_stream, write_stream, _, @@ -118,7 +118,7 @@

Connection Details

Or use your favourite tool
- You can also connect using tools like Cloude, MCP + You can also connect using tools like Claude, MCP Inspector, or any other compatible client.