From 5118cd0991e3e18e1aa0dd261cfbc8304998349c Mon Sep 17 00:00:00 2001 From: Sid Murching Date: Sun, 8 Jun 2025 10:23:23 -0700 Subject: [PATCH 1/5] WIP adding back README, server doesn' deploy yet Signed-off-by: Sid Murching --- examples/custom-server/README.md | 89 +++++++++++++++++++ examples/custom-server/app.yaml | 1 + examples/custom-server/pyproject.toml | 4 + examples/custom-server/requirements.txt | 2 + .../custom-server/src/custom_server/app.py | 2 +- 5 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 examples/custom-server/README.md create mode 100644 examples/custom-server/app.yaml create mode 100644 examples/custom-server/requirements.txt diff --git a/examples/custom-server/README.md b/examples/custom-server/README.md new file mode 100644 index 0000000..6b35876 --- /dev/null +++ b/examples/custom-server/README.md @@ -0,0 +1,89 @@ +# 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. + +### Using `databricks apps` CLI + +To deploy the server using the `databricks apps` CLI, follow these steps: + +Ensure Databricks authentication is configured: +```bash +databricks auth login # Skip specifying a profile name when prompted +``` + +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 + +- In this directory, run the following command to deploy and run the MCP server on Databricks Apps: + +```bash +uv build --wheel +databricks bundle deploy -p +databricks bundle run custom-server -p +``` + + +3. Deploy the app using the `databricks apps` CLI: +```bash +databricks sync ./build -p /Workspace/Users/my-email@org.com/my-app +databricks apps deploy my-app-name -p --source-code-path /Workspace/Users/my-email@org.com/my-app +databricks apps start my-app-name -p +``` + +## 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/pyproject.toml b/examples/custom-server/pyproject.toml index 09ff7b9..192488e 100644 --- a/examples/custom-server/pyproject.toml +++ b/examples/custom-server/pyproject.toml @@ -25,3 +25,7 @@ dev = [ [tool.hatch.build.hooks.custom] path = "hooks/apps_build.py" + +[project.scripts] +#custom-server = "databricks.labs.mcp.servers.unity_catalog:main" +custom-server = "custom_server.app:app" 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..6688f3b 100644 --- a/examples/custom-server/src/custom_server/app.py +++ b/examples/custom-server/src/custom_server/app.py @@ -31,5 +31,5 @@ def get_greeting(name: str) -> str: # 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("/", mcp_app) app.mount("/", static) From cd4a8265521f394fd9ab5e11c1976f8b4064260f Mon Sep 17 00:00:00 2001 From: Sid Murching Date: Sun, 8 Jun 2025 10:57:12 -0700 Subject: [PATCH 2/5] Update custom server instructions to match Databricks docs Signed-off-by: Sid Murching --- examples/custom-server/README.md | 6 ++++-- examples/custom-server/databricks.yml | 2 +- examples/custom-server/pyproject.toml | 2 +- examples/custom-server/src/custom_server/app.py | 12 ++++++++---- examples/custom-server/src/custom_server/main.py | 9 +++++++++ .../src/custom_server/static/index.html | 8 ++++---- 6 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 examples/custom-server/src/custom_server/main.py diff --git a/examples/custom-server/README.md b/examples/custom-server/README.md index 6b35876..a25b1b9 100644 --- a/examples/custom-server/README.md +++ b/examples/custom-server/README.md @@ -32,7 +32,9 @@ To deploy the server using the `databricks apps` CLI, follow these steps: Ensure Databricks authentication is configured: ```bash -databricks auth login # Skip specifying a profile name when prompted +# Skip specifying a profile name when prompted, or add --profile +# to subsequent commands with the name of the profile you specify here +databricks auth login ``` Create a Databricks app to host your MCP server: @@ -57,7 +59,7 @@ To deploy the server using the `databricks bundle` CLI, follow these steps ```bash uv build --wheel databricks bundle deploy -p -databricks bundle run custom-server -p +databricks bundle run custom-mcp-server -p ``` diff --git a/examples/custom-server/databricks.yml b/examples/custom-server/databricks.yml index ecb7831..9038e13 100644 --- a/examples/custom-server/databricks.yml +++ b/examples/custom-server/databricks.yml @@ -14,7 +14,7 @@ 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: diff --git a/examples/custom-server/pyproject.toml b/examples/custom-server/pyproject.toml index 192488e..cc267a2 100644 --- a/examples/custom-server/pyproject.toml +++ b/examples/custom-server/pyproject.toml @@ -28,4 +28,4 @@ path = "hooks/apps_build.py" [project.scripts] #custom-server = "databricks.labs.mcp.servers.unity_catalog:main" -custom-server = "custom_server.app:app" +custom-server = "custom_server.main:main" diff --git a/examples/custom-server/src/custom_server/app.py b/examples/custom-server/src/custom_server/app.py index 6688f3b..1b42340 100644 --- a/examples/custom-server/src/custom_server/app.py +++ b/examples/custom-server/src/custom_server/app.py @@ -2,6 +2,9 @@ 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 +25,15 @@ 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.get("/", include_in_schema=False) +async def serve_index(): + return FileResponse(STATIC_DIR / "index.html") + app.mount("/", mcp_app) -app.mount("/", static) 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..f6937cb --- /dev/null +++ b/examples/custom-server/src/custom_server/main.py @@ -0,0 +1,9 @@ +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 + ) \ No newline at end of file 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.
From 99932cac5bf72c1f1e4d56364203a6b2531b675c Mon Sep 17 00:00:00 2001 From: Sid Murching Date: Sun, 8 Jun 2025 10:58:45 -0700 Subject: [PATCH 3/5] Lint Signed-off-by: Sid Murching --- examples/custom-server/src/custom_server/app.py | 2 +- examples/custom-server/src/custom_server/main.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/custom-server/src/custom_server/app.py b/examples/custom-server/src/custom_server/app.py index 1b42340..8965ffe 100644 --- a/examples/custom-server/src/custom_server/app.py +++ b/examples/custom-server/src/custom_server/app.py @@ -1,5 +1,4 @@ from pathlib import Path -from fastapi.staticfiles import StaticFiles from mcp.server.fastmcp import FastMCP from fastapi import FastAPI from fastapi.responses import FileResponse @@ -36,4 +35,5 @@ def get_greeting(name: str) -> str: 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 index f6937cb..b5b9c50 100644 --- a/examples/custom-server/src/custom_server/main.py +++ b/examples/custom-server/src/custom_server/main.py @@ -1,9 +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 - ) \ No newline at end of file + reload=True, # optional + ) From 99b9e67b33bd8646e8edadcb9767e89e7cc163ce Mon Sep 17 00:00:00 2001 From: Sid Murching Date: Sun, 8 Jun 2025 11:17:49 -0700 Subject: [PATCH 4/5] Update README Signed-off-by: Sid Murching --- examples/custom-server/README.md | 31 ++++++++++++--------------- examples/custom-server/app.yaml | 2 +- examples/custom-server/databricks.yml | 2 -- examples/custom-server/pyproject.toml | 1 - 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/examples/custom-server/README.md b/examples/custom-server/README.md index a25b1b9..5e8546d 100644 --- a/examples/custom-server/README.md +++ b/examples/custom-server/README.md @@ -26,17 +26,16 @@ uvicorn custom-server.app:mcp --reload 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: -Ensure Databricks authentication is configured: -```bash -# Skip specifying a profile name when prompted, or add --profile -# to subsequent commands with the name of the profile you specify here -databricks auth login -``` - Create a Databricks app to host your MCP server: ```bash databricks apps create mcp-custom-server @@ -54,20 +53,18 @@ databricks apps deploy mcp-custom-server --source-code-path "/Workspace/Users/$D To deploy the server using the `databricks bundle` CLI, follow these steps -- In this directory, run the following command to deploy and run the MCP server on Databricks Apps: - -```bash -uv build --wheel -databricks bundle deploy -p -databricks bundle run custom-mcp-server -p +[//]: # (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: -3. Deploy the app using the `databricks apps` CLI: ```bash -databricks sync ./build -p /Workspace/Users/my-email@org.com/my-app -databricks apps deploy my-app-name -p --source-code-path /Workspace/Users/my-email@org.com/my-app -databricks apps start my-app-name -p +uv build --wheel +databricks bundle deploy +databricks bundle run custom-mcp-server ``` ## Connecting to the MCP server diff --git a/examples/custom-server/app.yaml b/examples/custom-server/app.yaml index edbc253..84bf4e2 100644 --- a/examples/custom-server/app.yaml +++ b/examples/custom-server/app.yaml @@ -1 +1 @@ -command: ["uv", "run", "custom-server"] +command: ["uvicorn", "custom_server.app:app"] diff --git a/examples/custom-server/databricks.yml b/examples/custom-server/databricks.yml index 9038e13..ec33490 100644 --- a/examples/custom-server/databricks.yml +++ b/examples/custom-server/databricks.yml @@ -17,8 +17,6 @@ resources: 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 cc267a2..050cbdc 100644 --- a/examples/custom-server/pyproject.toml +++ b/examples/custom-server/pyproject.toml @@ -27,5 +27,4 @@ dev = [ path = "hooks/apps_build.py" [project.scripts] -#custom-server = "databricks.labs.mcp.servers.unity_catalog:main" custom-server = "custom_server.main:main" From ec6701848007587dfadd9f859c2da1a28b60bf1c Mon Sep 17 00:00:00 2001 From: Sid Murching Date: Sun, 8 Jun 2025 11:25:44 -0700 Subject: [PATCH 5/5] Switch back to uv run for normal apps CLI CUJ Signed-off-by: Sid Murching --- examples/custom-server/app.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/custom-server/app.yaml b/examples/custom-server/app.yaml index 84bf4e2..edbc253 100644 --- a/examples/custom-server/app.yaml +++ b/examples/custom-server/app.yaml @@ -1 +1 @@ -command: ["uvicorn", "custom_server.app:app"] +command: ["uv", "run", "custom-server"]