fix(mcp_cloud): migrate to MCP Python SDK 2.0 - #865
Merged
Conversation
The mcp 1.28.1 -> 2.0.0 bump (#845/#851) removed mcp.server.fastmcp, so mcp_cloud crashed on import and the container sat in a restart loop. Four separate API changes had to be handled. FastMCP -> MCPServer, imported from mcp.server.mcpserver. Transport settings (streamable_http_path, json_response, stateless_http) moved off the constructor onto streamable_http_app(). SERVER_VERSION is now passed to the server, so initialize reports serverInfo.version instead of an empty string. streamable_http_app() auto-enables DNS rebinding protection when host is a loopback address, restricting Host and Origin to localhost. That would reject the LAN clients the README documents and the Railway proxy in production, so transport_security is set explicitly to keep the 1.28.1 behaviour. Access stays controlled by the API-key middleware and the configured CORS origins. The lowlevel Server in db_setup dropped its list_tools()/call_tool() decorators. Nothing served through that instance -- the HTTP path goes through MCPServer and the REST routes -- so it is removed; handle_list_tools and handle_call_tool stay as plain functions, which is how the tests and the REST bridge already used them. The stdio entry point in app.main() now builds an MCPServer and calls run_stdio_async(). mcp.types models are snake_case in 2.0 with camelCase aliases, so attribute reads become input_schema / output_schema / is_error / structured_content. Construction by alias still works, and the REST /mcp/tools payload keeps its camelCase wire keys. Verified against the running docker-compose stack: container healthy, initialize/tools/list/prompts/list/prompts/get/tools/call all succeed over Streamable HTTP, and the REST endpoints still advertise the canonical outputSchema. The mcp_cloud suite was run against a live Postgres on this branch and on main with mcp 1.28.1: both give the same 5 failures and 2 errors, all pre-existing and unrelated to MCP.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
mcp1.28.1 → 2.0.0 bump (#845 / #851) removedmcp.server.fastmcp, somcp_cloudcrashed on import and the container sat in a restart loop:Four separate API changes had to be handled.
1.
FastMCP→MCPServerNow imported from
mcp.server.mcpserver. Transport settings (streamable_http_path,json_response,stateless_http) moved off the constructor ontostreamable_http_app().SERVER_VERSIONis now passed to the server, soinitializereportsserverInfo.version: "1.0.1"instead of an empty string.2. DNS rebinding protection — behaviour change worth a look
streamable_http_app()auto-enables DNS rebinding protection whenhostis a loopback address, restrictingHost/Originto localhost.PLANEXE_MCP_HTTP_HOSTdefaults to127.0.0.1, so accepting the new default would reject the LAN clientsmcp_cloud/README.mddocuments (http://192.168.1.10:8001/mcp/).This PR sets
transport_securityexplicitly to preserve the 1.28.1 behaviour. Access remains controlled by the API-key middleware and the configured CORS origins. If you would rather opt into the protection, that is a one-line change plus an allowed-hosts list — say the word and I will do it.3. Vestigial lowlevel
ServerremovedThe lowlevel
Serverindb_setup.pylost itslist_tools()/call_tool()decorators in 2.0. Nothing ever served through that instance — the HTTP path goes throughMCPServerplus the REST routes — so it is removed.handle_list_tools/handle_call_toolstay as plain functions, which is how the tests and the REST bridge already used them. The stdio entry point inapp.main()now builds anMCPServerand callsrun_stdio_async()rather than being left broken.4. snake_case model fields
mcp.typesmodels are snake_case in 2.0 with camelCase aliases. Attribute reads becomeinput_schema/output_schema/is_error/structured_content(construction by alias still works). The REST/mcp/toolspayload keeps its camelCase wire keys, so that contract is unchanged.Verification
Against the running docker-compose stack — container healthy, and over Streamable HTTP:
initialize2025-06-18,{'name': 'planexe-mcp-server', 'version': '1.0.1'}tools/listprompts/list/prompts/getgetting_started,plan_a_project— both rendertools/callmodel_profiles,example_promptsreturn real data,isError: false/mcp/tools,/mcp/tools/callplan_status+plan_file_infostill correctly omit theoneOfoutputSchemaThe mcp_cloud suite was run against a live Postgres on this branch and on
mainwith mcp 1.28.1. Both give an identical 5 failures + 2 errors — same test names, all pre-existing and unrelated to MCP (description-text drift, an annotation mismatch, SSE timing, auth-header parsing). No regressions.Note: CI did not catch this
test.pyends withrunner.run(tests)and never inspects the result, so it always exits 0. The tests job on the merge of #845/#851 printedFAILED (errors=25, skipped=13)and was still reported green. Not fixed here — flipping the exit code alone turns CI red, because the job has no Postgres service and 19 mcp_cloud tests error on connection. Filed separately.