-
Notifications
You must be signed in to change notification settings - Fork 31
✨ run MCP on Databricks Apps #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
66695be
d0a1bbb
139732e
2b4965e
afd6218
b50f331
a4ca53f
7adf49f
be75e90
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
bundle: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should move this into the UC server directory, to enable separately deploying individual servers, I can help with that |
||
name: mcp-on-apps | ||
|
||
sync: | ||
include: | ||
- .build | ||
|
||
artifacts: | ||
default: | ||
type: whl | ||
path: . | ||
build: uv build --wheel | ||
|
||
resources: | ||
apps: | ||
mcp-on-apps: | ||
name: "mcp-on-apps" | ||
description: "MCP Server on Databricks Apps" | ||
source_code_path: ./.build | ||
config: | ||
command: ["unitycatalog-mcp-app"] | ||
|
||
targets: | ||
dev: | ||
mode: development | ||
default: true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
from typing import Any | ||
from hatchling.builders.hooks.plugin.interface import BuildHookInterface | ||
from pathlib import Path | ||
import shutil | ||
|
||
|
||
class AppsBuildHook(BuildHookInterface): | ||
"""Hook to create a Databricks Apps-compatible build. | ||
|
||
This hook is used to create a Databricks Apps-compatible build of the project. | ||
|
||
The following steps are performed: | ||
- Remove the ./.build folder if it exists. | ||
- Copy the artifact_path to the ./.build folder. | ||
- Write the name of the artifact to a requirements.txt file in the ./.build folder. | ||
- The resulting build directory is printed to the console. | ||
|
||
""" | ||
|
||
def finalize( | ||
self, version: str, build_data: dict[str, Any], artifact_path: str | ||
) -> None: | ||
self.app.display_info( | ||
f"Running Databricks Apps build hook for project {self.metadata.name} in directory {Path.cwd()}" | ||
) | ||
# remove the ./.build folder if it exists | ||
build_dir = Path(".build") | ||
self.app.display_info(f"Resulting build directory: {build_dir.absolute()}") | ||
|
||
if build_dir.exists(): | ||
self.app.display_info(f"Removing {build_dir}") | ||
shutil.rmtree(build_dir) | ||
self.app.display_info(f"Removed {build_dir}") | ||
else: | ||
self.app.display_info(f"{build_dir} does not exist, skipping removal") | ||
|
||
# copy the artifact_path to the ./.build folder | ||
build_dir.mkdir(exist_ok=True) | ||
self.app.display_info(f"Copying {artifact_path} to {build_dir}") | ||
shutil.copy(artifact_path, build_dir) | ||
|
||
# write the name of the artifact to a requirements.txt file in the ./.build folder | ||
requirements_file = build_dir / "requirements.txt" | ||
|
||
requirements_file.write_text(Path(artifact_path).name, encoding="utf-8") | ||
|
||
self.app.display_info( | ||
f"Apps-compatible build written to {build_dir.absolute()}" | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from mcp.server import NotificationOptions, Server | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, any reason not to just replace the existing stdio-based server implementation with this one? We can do it in a follow-up PR, but just curious There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eventually seems like we'd just have one streamable-HTTP-based implementation (once more clients support streamable HTTP) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, depending on how much work it is, it's actually now possible to use streamable HTTP to write the server, as of four days ago! https://github.com/modelcontextprotocol/python-sdk/releases/tag/v1.8.0 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll update my version then, should work easily. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, updated! |
||
from mcp.types import Tool as ToolSpec | ||
from mcp.server.sse import SseServerTransport | ||
import uvicorn | ||
from databricks.labs.mcp.servers.unity_catalog.tools import ( | ||
Content, | ||
) | ||
from starlette.applications import Starlette | ||
from starlette.routing import Mount, Route | ||
from databricks.labs.mcp.servers.unity_catalog.cli import get_settings | ||
|
||
from databricks.labs.mcp._version import __version__ as VERSION | ||
from databricks.labs.mcp.servers.unity_catalog.server import get_tools_dict | ||
|
||
|
||
server = Server(name="mcp-unitycatalog", version=VERSION) | ||
tools_dict = get_tools_dict(settings=get_settings()) | ||
|
||
|
||
@server.list_tools() | ||
async def list_tools() -> list[ToolSpec]: | ||
return [tool.tool_spec for tool in tools_dict.values()] | ||
|
||
|
||
@server.call_tool() | ||
async def call_tool(name: str, arguments: dict) -> list[Content]: | ||
tool = tools_dict[name] | ||
return tool.execute(**arguments) | ||
|
||
|
||
sse = SseServerTransport("/messages/") | ||
|
||
|
||
# Define handler functions | ||
async def handle_sse(request): | ||
async with sse.connect_sse( | ||
request.scope, request.receive, request._send | ||
) as streams: | ||
await server.run( | ||
streams[0], | ||
streams[1], | ||
server.create_initialization_options( | ||
notification_options=NotificationOptions( | ||
resources_changed=True, tools_changed=True | ||
) | ||
), | ||
) | ||
|
||
|
||
# Create Starlette routes for SSE and message handling | ||
routes = [ | ||
Route("/sse", endpoint=handle_sse), | ||
Mount("/messages/", app=sse.handle_post_message), | ||
] | ||
|
||
# Create and run Starlette app | ||
app = Starlette(routes=routes) | ||
|
||
|
||
def start_app(): | ||
uvicorn.run(app, host="0.0.0.0", port=8000) | ||
|
||
|
||
if __name__ == "__main__": | ||
start_app() |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@renardeinside sorry I totally missed this, thanks for doing this! cc @aravind-segu. Would you mind moving this up into the Unity Catalog MCP server README section? Since the devtools server doesn't include any code just yet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep everything in one readme. I'll later refactor it into proper docs via docusaurus, same as for DQX. Having separate files is quite inconvenient.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I meant just move this section up - currently it looks like it's under or associated with
But it only works for the UC server
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely +1 to a single README
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can help make that edit