-
Notifications
You must be signed in to change notification settings - Fork 3.1k
refactor: replace lowlevel Server decorators with on_* constructor kwargs #1985
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
Conversation
acea3d7 to
8e1d947
Compare
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
examples/servers/simple-streamablehttp/mcp_simple_streamablehttp/server.py
Outdated
Show resolved
Hide resolved
…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

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.