Skip to content

Commit 952a29c

Browse files
committed
refactor: merge request_handler.py and notification_handler.py into handler.py
Create Handler base class with RequestHandler and NotificationHandler subclasses in a single handler.py file. This prepares for future middleware support. Also: - Update Server.__init__ to use Sequence[Handler] = () instead of list[RequestHandler | NotificationHandler] | None - Drop explicit [Any, Any] generic params where defaults suffice - Export Handler from lowlevel __init__
1 parent d5c3b5f commit 952a29c

File tree

7 files changed

+214
-225
lines changed

7 files changed

+214
-225
lines changed
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from .notification_handler import NotificationHandler
2-
from .request_handler import RequestHandler
1+
from .handler import Handler, NotificationHandler, RequestHandler
32
from .server import NotificationOptions, Server
43

5-
__all__ = ["NotificationHandler", "NotificationOptions", "RequestHandler", "Server"]
4+
__all__ = ["Handler", "NotificationHandler", "NotificationOptions", "RequestHandler", "Server"]

src/mcp/server/lowlevel/experimental.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
from typing import Any
1111

1212
from mcp.server.experimental.task_support import TaskSupport
13-
from mcp.server.lowlevel.notification_handler import NotificationHandler
14-
from mcp.server.lowlevel.request_handler import RequestHandler
13+
from mcp.server.lowlevel.handler import Handler, RequestHandler
1514
from mcp.shared.context import RequestContext
1615
from mcp.shared.exceptions import MCPError
1716
from mcp.shared.experimental.tasks.helpers import cancel_task
@@ -49,7 +48,7 @@ class ExperimentalHandlers:
4948

5049
def __init__(
5150
self,
52-
add_handler: Callable[[RequestHandler[Any, Any] | NotificationHandler[Any, Any]], None],
51+
add_handler: Callable[[Handler], None],
5352
has_handler: Callable[[str], bool],
5453
) -> None:
5554
self._add_handler = add_handler

src/mcp/server/lowlevel/handler.py

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
"""Request and notification handlers for the low-level MCP server."""
2+
3+
from collections.abc import Awaitable, Callable
4+
from typing import Any, Generic, Literal, overload
5+
6+
from typing_extensions import TypeVar
7+
8+
from mcp.server.session import ServerSession
9+
from mcp.shared.context import RequestContext
10+
from mcp.types import (
11+
CallToolRequestParams,
12+
CallToolResult,
13+
CancelledNotificationParams,
14+
CompleteRequestParams,
15+
CompleteResult,
16+
EmptyResult,
17+
GetPromptRequestParams,
18+
GetPromptResult,
19+
ListPromptsResult,
20+
ListResourcesResult,
21+
ListResourceTemplatesResult,
22+
ListToolsResult,
23+
NotificationParams,
24+
PaginatedRequestParams,
25+
ProgressNotificationParams,
26+
ReadResourceRequestParams,
27+
ReadResourceResult,
28+
RequestParams,
29+
SetLevelRequestParams,
30+
SubscribeRequestParams,
31+
UnsubscribeRequestParams,
32+
)
33+
34+
LifespanResultT = TypeVar("LifespanResultT", default=Any)
35+
RequestT = TypeVar("RequestT", default=Any)
36+
37+
Ctx = RequestContext[ServerSession, LifespanResultT, RequestT]
38+
39+
40+
class Handler(Generic[LifespanResultT, RequestT]):
41+
"""Base class for MCP handlers."""
42+
43+
method: str
44+
45+
async def handle(self, ctx: Ctx, params: Any) -> Any:
46+
raise NotImplementedError
47+
48+
49+
class RequestHandler(Handler[LifespanResultT, RequestT]):
50+
"""Handler for MCP request methods.
51+
52+
Each handler is associated with a method string (e.g. "tools/call") and
53+
an async endpoint function that receives a RequestContext and the request params.
54+
"""
55+
56+
@overload
57+
def __init__(
58+
self,
59+
method: Literal["ping"],
60+
handler: Callable[[Ctx, RequestParams | None], Awaitable[EmptyResult]],
61+
) -> None: ...
62+
63+
@overload
64+
def __init__(
65+
self,
66+
method: Literal["prompts/list"],
67+
handler: Callable[[Ctx, PaginatedRequestParams | None], Awaitable[ListPromptsResult]],
68+
) -> None: ...
69+
70+
@overload
71+
def __init__(
72+
self,
73+
method: Literal["prompts/get"],
74+
handler: Callable[[Ctx, GetPromptRequestParams], Awaitable[GetPromptResult]],
75+
) -> None: ...
76+
77+
@overload
78+
def __init__(
79+
self,
80+
method: Literal["resources/list"],
81+
handler: Callable[[Ctx, PaginatedRequestParams | None], Awaitable[ListResourcesResult]],
82+
) -> None: ...
83+
84+
@overload
85+
def __init__(
86+
self,
87+
method: Literal["resources/templates/list"],
88+
handler: Callable[[Ctx, PaginatedRequestParams | None], Awaitable[ListResourceTemplatesResult]],
89+
) -> None: ...
90+
91+
@overload
92+
def __init__(
93+
self,
94+
method: Literal["resources/read"],
95+
handler: Callable[[Ctx, ReadResourceRequestParams], Awaitable[ReadResourceResult]],
96+
) -> None: ...
97+
98+
@overload
99+
def __init__(
100+
self,
101+
method: Literal["resources/subscribe"],
102+
handler: Callable[[Ctx, SubscribeRequestParams], Awaitable[EmptyResult]],
103+
) -> None: ...
104+
105+
@overload
106+
def __init__(
107+
self,
108+
method: Literal["resources/unsubscribe"],
109+
handler: Callable[[Ctx, UnsubscribeRequestParams], Awaitable[EmptyResult]],
110+
) -> None: ...
111+
112+
@overload
113+
def __init__(
114+
self,
115+
method: Literal["logging/setLevel"],
116+
handler: Callable[[Ctx, SetLevelRequestParams], Awaitable[EmptyResult]],
117+
) -> None: ...
118+
119+
@overload
120+
def __init__(
121+
self,
122+
method: Literal["tools/list"],
123+
handler: Callable[[Ctx, PaginatedRequestParams | None], Awaitable[ListToolsResult]],
124+
) -> None: ...
125+
126+
@overload
127+
def __init__(
128+
self,
129+
method: Literal["tools/call"],
130+
handler: Callable[[Ctx, CallToolRequestParams], Awaitable[CallToolResult]],
131+
) -> None: ...
132+
133+
@overload
134+
def __init__(
135+
self,
136+
method: Literal["completion/complete"],
137+
handler: Callable[[Ctx, CompleteRequestParams], Awaitable[CompleteResult]],
138+
) -> None: ...
139+
140+
@overload
141+
def __init__(
142+
self,
143+
method: str,
144+
handler: Callable[[Ctx, Any], Awaitable[Any]],
145+
) -> None: ...
146+
147+
def __init__(self, method: str, handler: Callable[[Ctx, Any], Awaitable[Any]]) -> None:
148+
self.method = method
149+
self.handler = handler
150+
151+
async def handle(self, ctx: Ctx, params: Any) -> Any:
152+
return await self.handler(ctx, params)
153+
154+
155+
class NotificationHandler(Handler[LifespanResultT, RequestT]):
156+
"""Handler for MCP notification methods.
157+
158+
Each handler is associated with a method string (e.g. "notifications/progress") and
159+
an async endpoint function that receives a RequestContext and the notification params.
160+
"""
161+
162+
@overload
163+
def __init__(
164+
self,
165+
method: Literal["notifications/initialized"],
166+
handler: Callable[[Ctx, NotificationParams | None], Awaitable[None]],
167+
) -> None: ...
168+
169+
@overload
170+
def __init__(
171+
self,
172+
method: Literal["notifications/cancelled"],
173+
handler: Callable[[Ctx, CancelledNotificationParams], Awaitable[None]],
174+
) -> None: ...
175+
176+
@overload
177+
def __init__(
178+
self,
179+
method: Literal["notifications/progress"],
180+
handler: Callable[[Ctx, ProgressNotificationParams], Awaitable[None]],
181+
) -> None: ...
182+
183+
@overload
184+
def __init__(
185+
self,
186+
method: Literal["notifications/roots/list_changed"],
187+
handler: Callable[[Ctx, NotificationParams | None], Awaitable[None]],
188+
) -> None: ...
189+
190+
@overload
191+
def __init__(
192+
self,
193+
method: str,
194+
handler: Callable[[Ctx, Any], Awaitable[None]],
195+
) -> None: ...
196+
197+
def __init__(self, method: str, handler: Callable[[Ctx, Any], Awaitable[None]]) -> None:
198+
self.method = method
199+
self.handler = handler
200+
201+
async def handle(self, ctx: Ctx, params: Any) -> None:
202+
await self.handler(ctx, params)

src/mcp/server/lowlevel/notification_handler.py

Lines changed: 0 additions & 69 deletions
This file was deleted.

0 commit comments

Comments
 (0)