-
Notifications
You must be signed in to change notification settings - Fork 3
feat: migrate from FastMCP v2 to v3 #239
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 all commits
6f0260a
702912c
7bc0b7a
e980490
d614875
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 |
|---|---|---|
|
|
@@ -17,7 +17,8 @@ | |
|
|
||
| from fastmcp import FastMCP | ||
| from fastmcp.client.transports import StreamableHttpTransport | ||
| from fastmcp.server.proxy import FastMCPProxy, ProxyClient | ||
| from fastmcp.server import create_proxy | ||
| from fastmcp.server.providers.proxy import FastMCPProxy, ProxyClient | ||
| from fastmcp.tools import Tool | ||
| from lateimport import lateimport | ||
|
|
||
|
|
@@ -300,6 +301,10 @@ def _setup_server[TransportT: Literal["stdio", "streamable-http"]]( | |
| run_args = mutable_args.pop("run_args", {}) | ||
| # Remove transport from args - it's not a FastMCP constructor parameter | ||
| mutable_args.pop("transport", None) | ||
| # FastMCP v3: include_tags/exclude_tags are no longer constructor parameters. | ||
| # Extract them here and apply via app.enable()/app.disable() after construction. | ||
| include_tags: set[str] | None = mutable_args.pop("include_tags", None) | ||
| exclude_tags: set[str] | None = mutable_args.pop("exclude_tags", None) | ||
|
|
||
| # Always use default middleware classes for this transport | ||
| from codeweaver.server.mcp.middleware import default_middleware_for_transport | ||
|
|
@@ -314,6 +319,11 @@ def _setup_server[TransportT: Literal["stdio", "streamable-http"]]( | |
| ) | ||
| app = register_tools(app) | ||
| app = register_middleware(app, cast(list[type[McpMiddleware]], middleware), middleware_opts) | ||
| # FastMCP v3: Apply tag-based visibility filtering using the new enable()/disable() API | ||
| if exclude_tags: | ||
| app.disable(tags=exclude_tags) | ||
| if include_tags: | ||
| app.enable(tags=include_tags, only=True) | ||
| if is_http: | ||
| from codeweaver.server.mcp.state import CwMcpHttpState | ||
|
|
||
|
|
@@ -349,16 +359,23 @@ async def create_stdio_server( | |
| stdio_settings = DictView(FastMcpServerSettingsDict(**cast(dict, stdio_settings))) | ||
| else: | ||
| stdio_settings = _get_fastmcp_settings_map(http=False) | ||
| app = _setup_server(stdio_settings, transport="stdio") | ||
| # Derive the stdio server name directly from settings instead of constructing | ||
| # a full FastMCP app instance just to access `app.name`. | ||
| stdio_name = cast(str | None, stdio_settings.get("name")) | ||
|
Comment on lines
360
to
+364
|
||
| if settings and (http_settings := settings.get("mcp_server")) is not UNSET: | ||
|
Comment on lines
361
to
365
|
||
| http_settings = DictView(FastMcpServerSettingsDict(**cast(dict, http_settings))) | ||
| else: | ||
| http_settings = _get_fastmcp_settings_map(http=True) | ||
| run_args = http_settings.get("run_args", {}) | ||
| run_args = http_settings.get("run_args", {}) | ||
| resolved_host = host or run_args.get("host", LOCALHOST) | ||
| resolved_port = port or run_args.get("port", DEFAULT_MCP_PORT) | ||
| url = f"http://{resolved_host}:{resolved_port}{http_settings.get('path', MCP_ENDPOINT)}" | ||
| return app.as_proxy(backend=ProxyClient(transport=StreamableHttpTransport(url=url))) | ||
| # FastMCP v3: Replace app.as_proxy() with create_proxy() | ||
| # create_proxy() takes a target (URL, transport, client, etc.) and settings | ||
| return create_proxy( | ||
| target=ProxyClient(transport=StreamableHttpTransport(url=url)), | ||
| name=stdio_name or cast(str, http_settings.get("name", "codeweaver-mcp")), | ||
|
|
||
| ) | ||
|
bashandbone marked this conversation as resolved.
Comment on lines
+362
to
+378
|
||
|
|
||
|
|
||
| async def create_http_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.
The PR metadata/uv.lock pins FastMCP to 3.1.1, but this constraint allows 3.0.0. If the intent is to target 3.1.1 specifically (as described), consider bumping the minimum to 3.1.1 or updating the PR description to match the supported minimum.