Skip to content

Commit a81db71

Browse files
committed
refactor: lowlevel Server
1 parent 2182205 commit a81db71

35 files changed

+3185
-1522
lines changed

REFACTORING_PLAN.md

Lines changed: 660 additions & 0 deletions
Large diffs are not rendered by default.

docs/migration.md

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -426,9 +426,95 @@ await client.read_resource("test://resource")
426426
await client.read_resource(str(my_any_url))
427427
```
428428

429-
## Deprecations
429+
### Low-Level Server Decorator-Based API Removed
430430

431-
<!-- Add deprecations below -->
431+
The decorator-based API for registering handlers on the low-level `Server` class has been removed. Use the constructor-based handler registration instead, which provides better type safety and clearer dependencies.
432+
433+
**Before (v1):**
434+
435+
```python
436+
from mcp.server.lowlevel import Server
437+
438+
server = Server("my-server")
439+
440+
@server.list_tools()
441+
async def list_tools():
442+
return [types.Tool(name="tool", description="...")]
443+
444+
@server.call_tool()
445+
async def call_tool(name: str, arguments: dict):
446+
return {"result": "..."}
447+
```
448+
449+
**After (v2):**
450+
451+
```python
452+
from mcp.server.lowlevel import Server
453+
from mcp.server.session import ServerSession
454+
from mcp.shared.context import RequestContext
455+
import mcp.types as types
456+
from typing import Any
457+
458+
async def list_tools(
459+
ctx: RequestContext[ServerSession, Any, Any],
460+
params: types.PaginatedRequestParams | None,
461+
) -> types.ListToolsResult:
462+
return types.ListToolsResult(tools=[
463+
types.Tool(name="tool", description="...", input_schema={"type": "object"})
464+
])
465+
466+
async def call_tool(
467+
ctx: RequestContext[ServerSession, Any, Any],
468+
params: types.CallToolRequestParams,
469+
) -> types.CallToolResult:
470+
return types.CallToolResult(
471+
content=[types.TextContent(type="text", text="result")]
472+
)
473+
474+
server = Server(
475+
"my-server",
476+
on_list_tools=list_tools,
477+
on_call_tool=call_tool,
478+
)
479+
```
480+
481+
**Key differences:**
482+
483+
1. Handlers receive `(context, params)` instead of extracted arguments
484+
2. Handlers return proper result types (`ListToolsResult`, `CallToolResult`, etc.)
485+
3. Context provides access to session, lifespan data, and request metadata
486+
4. Handlers are passed to the constructor using `on_*` parameters
487+
488+
**Migration steps:**
489+
490+
1. Update handler signatures to accept `(ctx, params)`
491+
2. Update return types to use proper result classes
492+
3. Pass handlers to the Server constructor using `on_*` parameters
493+
4. Remove decorator calls
494+
495+
**Available constructor parameters:**
496+
497+
| Constructor Parameter | Params Type | Return Type |
498+
|-----------------------|-------------|-------------|
499+
| `on_list_prompts` | `PaginatedRequestParams \| None` | `ListPromptsResult` |
500+
| `on_get_prompt` | `GetPromptRequestParams` | `GetPromptResult` |
501+
| `on_list_resources` | `PaginatedRequestParams \| None` | `ListResourcesResult` |
502+
| `on_list_resource_templates` | `PaginatedRequestParams \| None` | `ListResourceTemplatesResult` |
503+
| `on_read_resource` | `ReadResourceRequestParams` | `ReadResourceResult` |
504+
| `on_subscribe_resource` | `SubscribeRequestParams` | `EmptyResult` |
505+
| `on_unsubscribe_resource` | `UnsubscribeRequestParams` | `EmptyResult` |
506+
| `on_list_tools` | `PaginatedRequestParams \| None` | `ListToolsResult` |
507+
| `on_call_tool` | `CallToolRequestParams` | `CallToolResult` |
508+
| `on_set_logging_level` | `SetLevelRequestParams` | `EmptyResult` |
509+
| `on_completion` | `CompleteRequestParams` | `CompleteResult` |
510+
| `on_progress_notification` | `ProgressNotificationParams` | `None` |
511+
512+
**Benefits:**
513+
514+
- Context available in all handlers (session, lifespan data, request metadata)
515+
- Type-safe params and return types
516+
- Clearer dependencies at construction time
517+
- Better testability (handlers can be mocked/replaced)
432518

433519
## Bug Fixes
434520

@@ -462,13 +548,20 @@ The `streamable_http_app()` method is now available directly on the lowlevel `Se
462548

463549
```python
464550
from mcp.server.lowlevel.server import Server
465-
466-
server = Server("my-server")
467-
468-
# Register handlers...
469-
@server.list_tools()
470-
async def list_tools():
471-
return [...]
551+
from mcp.server.session import ServerSession
552+
from mcp.shared.context import RequestContext
553+
import mcp.types as types
554+
from typing import Any
555+
556+
async def list_tools(
557+
ctx: RequestContext[ServerSession, Any, Any],
558+
params: types.PaginatedRequestParams | None,
559+
) -> types.ListToolsResult:
560+
return types.ListToolsResult(tools=[
561+
types.Tool(name="my_tool", description="...", inputSchema={"type": "object"})
562+
])
563+
564+
server = Server("my-server", on_list_tools=list_tools)
472565

473566
# Create a Starlette app for streamable HTTP
474567
app = server.streamable_http_app(

0 commit comments

Comments
 (0)