refactor: replace lowlevel Server decorators with on_* constructor kwargs - #1985
Conversation
acea3d7 to
8e1d947
Compare
7221cca to
7e9e53f
Compare
…args Replace the decorator-based handler registration on the lowlevel Server with direct on_* keyword arguments on the constructor. Handlers are raw callables with a uniform (ctx, params) -> result signature. - Server constructor takes on_list_tools, on_call_tool, etc. - String-keyed dispatch instead of type-keyed - Remove RequestT generic from Server (transport-specific, not bound at construction) - Delete handler.py and func_inspection.py (no longer needed) - Update ExperimentalHandlers to use callback-based registration - Update MCPServer to pass on_* kwargs via _create_handler_kwargs() - Update migration docs and docstrings
- Fix all migration.md examples to use ServerRequestContext instead of RequestContext - Fix all imports to use 'from mcp.server import Server, ServerRequestContext' - Apply ruff formatting to code examples - Add Server constructor calls to examples that were missing them - Re-export ServerRequestContext from mcp.server.__init__ - Add type hints to TaskResultHandler docstring example
…erver Move handler functions from a dict-returning method to private methods on MCPServer, passed directly to the Server constructor by name. This eliminates the **kwargs unpacking pattern and makes the handler registration explicit.
Allow users to override default task handlers by passing on_get_task, on_task_result, on_list_tasks, and on_cancel_task to enable_tasks(). User-provided handlers are registered first; defaults fill in the rest.
The subscribe capability in ResourcesCapability was hardcoded to False, even when on_subscribe_resource handler was provided. Now dynamically checks whether a subscribe handler is registered. Also applies ruff formatting to experimental.py.
e9df629 to
ca8fd2e
Compare
Migrate test files from the old decorator-based handler registration to the new on_* constructor kwargs pattern. Key changes: - Replace @server.list_tools(), @server.call_tool(), etc. decorators with on_list_tools, on_call_tool, etc. kwargs on Server() - Replace server.request_context access with ctx parameter (first argument to all handlers) - Handlers now receive (ctx, params) and return full result types (e.g. ListToolsResult instead of list[Tool]) - Convert experimental task decorators to enable_tasks() kwargs - Add LifespanContextT default to ServerRequestContext - Widen on_call_tool return type to include CreateTaskResult - Delete redundant tests/shared/test_memory.py - Simplify tests to use Client where possible
Convert the 3 remaining experimental task server test files: - test_integration.py: proper lifespan + Client pattern - test_run_task_flow.py: constructor kwargs + Client pattern - test_server.py: enable_tasks() kwargs + Client pattern All tests use proper typing with ServerRequestContext and constructor kwargs instead of decorators.
- Delete test_func_inspection.py (tested removed create_call_wrapper) - Delete test_lowlevel_input_validation.py and test_lowlevel_output_validation.py (tested jsonschema validation removed from low-level server) - Migrate test_read_resource.py to E2E with Client - Migrate test_129_resource_templates.py to E2E with Client - Migrate test_output_schema_validation.py to constructor kwargs (removed bypass_server_output_validation hack, no longer needed) - Migrate test_ws.py, test_sse.py, test_streamable_http.py from Server subclasses with decorators to standalone handlers with constructor kwargs - Replace self.request_context contextvar with ctx parameter - Replace global _server_lock with typed ServerState lifespan context - Fix import paths for TextContent, ClientRegistrationOptions, RevocationOptions, StreamableHTTPServerTransport
enable_tasks registers default handlers for all task methods, so partial capabilities aren't currently possible. Skipped tests with TODO(maxisbey) to revisit when low-level API supports selectively enabling/disabling individual task capabilities.
…attern - Convert all low-level Server examples from decorator-based handlers to constructor on_* kwargs pattern - Replace server.request_context with ctx parameter passed to handlers - Replace auto-wrapped return values (dict, str, bytes) with explicit result type construction (CallToolResult, ReadResourceResult, etc.) - Update everything-server to use _add_request_handler for subscribe/ unsubscribe/logging handlers (MCPServer doesn't expose these yet) - Update migration docs with detailed section on removed auto-wrapping - Update README snippets
Remove references to automatic content/structured conversion and the four return type options that no longer exist. Low-level handlers now always return CallToolResult directly.
- Remove unused _add_notification_handler from Server - Add pragma no cover for dead dict code path in MCPServer._handle_call_tool - Add E2E test for MCPServer.completion() decorator - Replace unused handle_list_tools stubs with raise NotImplementedError - Add pragma no cover for skipped tests in test_server and test_spec_compliance - Flatten nested completion handler logic to eliminate untaken branches - Fix test_88_random_error to use assert + shared return path - Clean up unused imports (Tool, ToolExecution, TASK_REQUIRED)
|
Although CI is all green, I still want to manually test all the examples that were modified. |
Kludex
left a comment
There was a problem hiding this comment.
Please check if copilot's comments make sense. I don't think it's reasonable for me to review at this point - but if there's anything specific that you want me to look at, happy to.
There was a problem hiding this comment.
Pull request overview
This PR completes the refactor of the low-level Server API by removing decorator-based handler registration and moving to constructor on_* keyword arguments with uniform (ctx, params) -> result handler signatures. It updates the test suite, examples, and supporting server/context utilities to align with the new handler model and method-string dispatch.
Changes:
- Migrates tests and examples from
@server.*()decorators toServer(..., on_*=...)handlers returning concrete MCP result models. - Updates server/context plumbing to support the new request context flow (context passed into handlers; request context fields now optional for notifications).
- Refreshes documentation/migration guidance to describe the new low-level
ServerAPI and handler contracts.
Reviewed changes
Copilot reviewed 71 out of 71 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/shared/test_ws.py | Updates WS test server to use on_* constructor handlers and typed params/results. |
| tests/shared/test_sse.py | Updates SSE test server(s) to on_* handlers; shifts request-context usage to ctx. |
| tests/shared/test_session.py | Updates cancellation test to new ServerRequestContext + CallToolRequestParams signature. |
| tests/shared/test_progress_notifications.py | Updates progress notification handlers and tool calls to new meta/params model. |
| tests/shared/test_memory.py | Removes legacy memory-stream example test that depended on decorator API. |
| tests/server/test_streamable_http_manager.py | Updates streamable-http tests to new low-level Server imports and on_list_tools. |
| tests/server/test_session.py | Updates capability tests to rely on on_* presence instead of decorators. |
| tests/server/test_read_resource.py | Reworks read-resource tests to use Client(server) and explicit ReadResourceResult contents. |
| tests/server/test_lowlevel_tool_annotations.py | Simplifies tool-annotation test using Client(server) and on_list_tools. |
| tests/server/test_lowlevel_output_validation.py | Removes legacy low-level output validation tests (old decorator/validation behavior). |
| tests/server/test_lowlevel_input_validation.py | Removes legacy low-level input validation tests (old decorator/validation behavior). |
| tests/server/test_lifespan.py | Updates lifespan/tool handler to new (ctx, params) signature and result type. |
| tests/server/test_completion_with_context.py | Updates completion tests to use on_completion and CompleteRequestParams/CompleteResult. |
| tests/server/test_cancel_handling.py | Updates cancel-handling test to use on_list_tools/on_call_tool and CallToolResult. |
| tests/server/mcpserver/test_server.py | Adds/updates MCPServer completion decorator test coverage. |
| tests/server/mcpserver/prompts/test_manager.py | Fixes imports to use TextContent from mcp.types. |
| tests/server/mcpserver/prompts/test_base.py | Fixes imports to use TextContent from mcp.types. |
| tests/server/mcpserver/auth/test_auth_integration.py | Updates auth settings imports split between routes/settings modules. |
| tests/server/lowlevel/test_server_pagination.py | Updates pagination tests to Client(server) flow and (ctx, params) handlers. |
| tests/server/lowlevel/test_server_listing.py | Updates list tests to Client(server) and explicit result models. |
| tests/server/lowlevel/test_func_inspection.py | Removes tests for old signature-inspection wrapper (decorator era). |
| tests/issues/test_88_random_error.py | Updates reproduction test to new low-level server handler signatures/results. |
| tests/issues/test_342_base64_encoding.py | Updates base64 regression test to use MCPServer + Client read_resource path. |
| tests/issues/test_1574_resource_uri_validation.py | Updates URI roundtrip tests to new low-level handler signatures/results. |
| tests/issues/test_152_resource_mime_type.py | Updates mime-type test to explicit TextResourceContents/BlobResourceContents results. |
| tests/issues/test_129_resource_templates.py | Updates resource-template listing test to use Client(mcp) rather than internal handler access. |
| tests/experimental/tasks/test_spec_compliance.py | Migrates to experimental.enable_tasks(on_*=...) and skips partial-capability tests for now. |
| tests/experimental/tasks/test_elicitation_scenarios.py | Migrates scenario servers to on_* handlers and uses ctx.session APIs. |
| tests/experimental/tasks/server/test_integration.py | Refactors integration tests to use Client(server) and lifespan context patterns. |
| tests/client/transports/test_memory.py | Updates in-memory transport test server to on_list_resources and ListResourcesResult. |
| tests/client/test_output_schema_validation.py | Refactors malicious-server simulation to just return invalid structured content from low-level server. |
| tests/client/test_list_methods_cursor.py | Updates low-level server cursor echo test to (ctx, params) list-tools handler. |
| tests/client/test_http_unicode.py | Migrates unicode server example in tests to on_* handlers and typed params/results. |
| tests/client/test_client.py | Updates client fixture server to provide required capabilities via on_* handlers returning EmptyResult/CompleteResult. |
| src/mcp/shared/message.py | Types request_context as Any to keep transport-agnostic metadata while allowing None. |
| src/mcp/shared/experimental/tasks/helpers.py | Updates docs/example snippets to new (ctx, params) task handlers. |
| src/mcp/shared/_context.py | Makes request_id optional so one context type can support both requests and notifications. |
| src/mcp/server/streamable_http_manager.py | Updates Server generic usage after type parameter simplification. |
| src/mcp/server/session.py | Updates module docs/example to the new handler API and explicit result types. |
| src/mcp/server/mcpserver/server.py | Wires core MCP protocol handlers via low-level Server(..., on_*=...) and updates context access. |
| src/mcp/server/lowlevel/func_inspection.py | Removes obsolete decorator-era call wrapper utility. |
| src/mcp/server/lowlevel/experimental.py | Replaces decorator registration with enable_tasks(on_*=...) and method-string handler wiring. |
| src/mcp/server/lowlevel/init.py | Minor __all__ ordering update. |
| src/mcp/server/experimental/task_result_handler.py | Updates usage docs to reflect enable_tasks(on_task_result=...). |
| src/mcp/server/experimental/request_context.py | Updates example snippet to the new handler signature. |
| src/mcp/server/context.py | Sets default typevar for lifespan context and keeps request type default as Any. |
| src/mcp/server/init.py | Exports ServerRequestContext at mcp.server top-level. |
| examples/snippets/servers/pagination_example.py | Migrates snippet to on_list_resources and (ctx, params) pagination. |
| examples/snippets/servers/lowlevel/structured_output.py | Migrates snippet to explicit CallToolResult (content + structured_content) and create_initialization_options(). |
| examples/snippets/servers/lowlevel/lifespan.py | Migrates snippet to on_* handlers and typed lifespan context. |
| examples/snippets/servers/lowlevel/direct_call_tool_result.py | Migrates snippet to on_* handlers returning CallToolResult directly. |
| examples/snippets/servers/lowlevel/basic.py | Migrates snippet to on_list_prompts/on_get_prompt and explicit result types. |
| examples/servers/structured-output-lowlevel/mcp_structured_output_lowlevel/main.py | Migrates example to explicit CallToolResult and create_initialization_options(). |
| examples/servers/sse-polling-demo/mcp_sse_polling_demo/server.py | Migrates example handlers to on_* and uses ctx.session for logs/stream control. |
| examples/servers/simple-tool/mcp_simple_tool/server.py | Migrates to on_list_tools/on_call_tool and returns CallToolResult. |
| examples/servers/simple-task/mcp_simple_task/server.py | Migrates to on_* handlers; task support enabled via experimental.enable_tasks(). |
| examples/servers/simple-task-interactive/mcp_simple_task_interactive/server.py | Migrates to on_* handlers and passes ctx through to task helpers. |
| examples/servers/simple-streamablehttp/mcp_simple_streamablehttp/server.py | Migrates to on_* handlers and uses StreamableHTTPSessionManager with new server type. |
| examples/servers/simple-streamablehttp-stateless/mcp_simple_streamablehttp_stateless/server.py | Migrates stateless streamable-http example to on_* handlers. |
| examples/servers/simple-resource/mcp_simple_resource/server.py | Migrates resources example to on_* and explicit ReadResourceResult. |
| examples/servers/simple-prompt/mcp_simple_prompt/server.py | Migrates prompts example to on_* and typed params/results. |
| examples/servers/simple-pagination/mcp_simple_pagination/server.py | Refactors pagination example to shared helper + on_* handlers for all list/read/call methods. |
| examples/servers/everything-server/mcp_everything_server/server.py | Updates low-level handler registration via _add_request_handler and new request param types. |
| docs/migration.md | Adds migration guidance for on_* handlers, kw-only constructor params, and removed auto-wrapping. |
| docs/experimental/index.md | Updates tasks docs to emphasize experimental.enable_tasks() rather than decorators. |
| README.v2.md | Updates embedded snippets to match new low-level handler API and init options helper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…g changes - Document Server type parameter reduction (Generic[L, R] -> Generic[L]) - Document request_handlers/notification_handlers removal - Document subscribe capability bug fix - Fix RequestContext -> ServerRequestContext in prose - Clarify request_ctx as internal implementation detail - Remove task_store from enable_tasks examples to focus on handler changes - Fix Request import to Any in type parameter example - Add missing typing import
Merge upstream/main into fix/1671-serversession-progress-callback. Resolved import conflict in mcpserver/server.py by keeping both the new ProgressFnT import and the expanded mcp.types imports from the lowlevel Server decorator refactor (modelcontextprotocol#1985).
Merge upstream/main which included major refactors: - Context class moved from server.py to mcpserver/context.py (modelcontextprotocol#2203) - Lowlevel Server decorators replaced with on_* kwargs (modelcontextprotocol#1985) - Docstring formatting standardized with periods (modelcontextprotocol#2095) - mcp.shared.progress module removed (modelcontextprotocol#2080) Resolved conflicts by: - Taking upstream server.py (Context class removed) - Adding progress_callback to new context.py - Keeping progress_callback docstrings with upstream period style - Restoring RequestContext import for new tests - Updating tests to use ctx: Context injection pattern

















Replace the decorator-based handler registration on the lowlevel
Serverwith directon_*keyword arguments on the constructor. Handlers are now raw callables with a uniform(ctx, params) -> resultsignature, dispatched by method string.Supersedes #1968.