Skip to content

Conversation

@maxisbey
Copy link
Contributor

@maxisbey maxisbey commented Feb 2, 2026

Replace the decorator-based handler registration on the lowlevel Server with direct on_* keyword arguments on the constructor. Handlers are now raw callables with a uniform (ctx, params) -> result signature, dispatched by method string.

Supersedes #1968.

@maxisbey maxisbey changed the base branch from sketch/lowlevel-server-v2 to main February 3, 2026 14:06
@maxisbey maxisbey force-pushed the sketch/lowlevel-server-v2-kwargs branch from acea3d7 to 8e1d947 Compare February 3, 2026 15:27
@maxisbey maxisbey changed the title refactor: replace Handler objects with on_* constructor kwargs refactor: replace lowlevel Server decorators with on_* constructor kwargs Feb 3, 2026
@claude
Copy link

claude bot commented Feb 3, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@maxisbey maxisbey marked this pull request as draft February 3, 2026 15:41
@maxisbey maxisbey force-pushed the sketch/lowlevel-server-v2-kwargs branch 2 times, most recently from 7221cca to 7e9e53f Compare February 9, 2026 13:38
…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.
@maxisbey maxisbey force-pushed the sketch/lowlevel-server-v2-kwargs branch from e9df629 to ca8fd2e Compare February 11, 2026 15:16
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)
@maxisbey maxisbey marked this pull request as ready for review February 11, 2026 22:55
@maxisbey
Copy link
Contributor Author

Although CI is all green, I still want to manually test all the examples that were modified.

Kludex
Kludex previously approved these changes Feb 12, 2026
Copy link
Member

@Kludex Kludex left a 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.

Copy link

Copilot AI left a 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 to Server(..., 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 Server API 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.

@maxisbey
Copy link
Contributor Author

Although CI is all green, I still want to manually test all the examples that were modified.

CleanShot 2026-02-12 at 12 03 31@2x

According to Claude the examples pass, will try a few myself as well

…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
@maxisbey maxisbey requested a review from Kludex February 12, 2026 15:50
@maxisbey maxisbey merged commit 0a22a9d into main Feb 12, 2026
52 of 53 checks passed
@maxisbey maxisbey deleted the sketch/lowlevel-server-v2-kwargs branch February 12, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants