Skip to content

Commit 7e9e53f

Browse files
committed
refactor: replace _create_handler_kwargs with private methods on MCPServer
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.
1 parent 67172b6 commit 7e9e53f

File tree

1 file changed

+65
-74
lines changed

1 file changed

+65
-74
lines changed

src/mcp/server/mcpserver/server.py

Lines changed: 65 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,13 @@ def __init__(
163163
website_url=website_url,
164164
icons=icons,
165165
version=version,
166-
**self._create_handler_kwargs(),
166+
on_list_tools=self._handle_list_tools,
167+
on_call_tool=self._handle_call_tool,
168+
on_list_resources=self._handle_list_resources,
169+
on_read_resource=self._handle_read_resource,
170+
on_list_resource_templates=self._handle_list_resource_templates,
171+
on_list_prompts=self._handle_list_prompts,
172+
on_get_prompt=self._handle_get_prompt,
167173
# TODO(Marcelo): It seems there's a type mismatch between the lifespan type from an MCPServer and Server.
168174
# We need to create a Lifespan type that is a generic on the server type, like Starlette does.
169175
lifespan=(lifespan_wrapper(self, self.settings.lifespan) if self.settings.lifespan else default_lifespan), # type: ignore
@@ -281,81 +287,66 @@ def run(
281287
case "streamable-http": # pragma: no cover
282288
anyio.run(lambda: self.run_streamable_http_async(**kwargs))
283289

284-
def _create_handler_kwargs(
285-
self,
286-
) -> dict[str, Callable[[ServerRequestContext[Any, Any], Any], Awaitable[Any]]]:
287-
"""Create on_* kwargs for the lowlevel Server constructor."""
288-
289-
async def handle_list_tools(ctx: Any, params: Any) -> ListToolsResult:
290-
return ListToolsResult(tools=await self.list_tools())
291-
292-
async def handle_call_tool(ctx: Any, params: Any) -> CallToolResult:
293-
try:
294-
result = await self.call_tool(params.name, params.arguments or {})
295-
except MCPError:
296-
raise
297-
except Exception as e:
298-
return CallToolResult(content=[TextContent(type="text", text=str(e))], is_error=True)
299-
if isinstance(result, CallToolResult):
300-
return result
301-
if isinstance(result, tuple) and len(result) == 2:
302-
unstructured_content, structured_content = result
303-
return CallToolResult(
304-
content=list(unstructured_content), # type: ignore[arg-type]
305-
structured_content=structured_content, # type: ignore[arg-type]
306-
)
307-
if isinstance(result, dict):
308-
return CallToolResult(
309-
content=[TextContent(type="text", text=json.dumps(result, indent=2))],
310-
structured_content=result,
311-
)
312-
return CallToolResult(content=list(result))
313-
314-
async def handle_list_resources(ctx: Any, params: Any) -> ListResourcesResult:
315-
return ListResourcesResult(resources=await self.list_resources())
316-
317-
async def handle_read_resource(ctx: Any, params: Any) -> ReadResourceResult:
318-
results = await self.read_resource(params.uri)
319-
contents: list[TextResourceContents | BlobResourceContents] = []
320-
for item in results:
321-
if isinstance(item.content, bytes):
322-
contents.append(
323-
BlobResourceContents(
324-
uri=params.uri,
325-
blob=base64.b64encode(item.content).decode(),
326-
mime_type=item.mime_type or "application/octet-stream",
327-
_meta=item.meta,
328-
)
290+
async def _handle_list_tools(self, ctx: Any, params: Any) -> ListToolsResult:
291+
return ListToolsResult(tools=await self.list_tools())
292+
293+
async def _handle_call_tool(self, ctx: Any, params: Any) -> CallToolResult:
294+
try:
295+
result = await self.call_tool(params.name, params.arguments or {})
296+
except MCPError:
297+
raise
298+
except Exception as e:
299+
return CallToolResult(content=[TextContent(type="text", text=str(e))], is_error=True)
300+
if isinstance(result, CallToolResult):
301+
return result
302+
if isinstance(result, tuple) and len(result) == 2:
303+
unstructured_content, structured_content = result
304+
return CallToolResult(
305+
content=list(unstructured_content), # type: ignore[arg-type]
306+
structured_content=structured_content, # type: ignore[arg-type]
307+
)
308+
if isinstance(result, dict):
309+
return CallToolResult(
310+
content=[TextContent(type="text", text=json.dumps(result, indent=2))],
311+
structured_content=result,
312+
)
313+
return CallToolResult(content=list(result))
314+
315+
async def _handle_list_resources(self, ctx: Any, params: Any) -> ListResourcesResult:
316+
return ListResourcesResult(resources=await self.list_resources())
317+
318+
async def _handle_read_resource(self, ctx: Any, params: Any) -> ReadResourceResult:
319+
results = await self.read_resource(params.uri)
320+
contents: list[TextResourceContents | BlobResourceContents] = []
321+
for item in results:
322+
if isinstance(item.content, bytes):
323+
contents.append(
324+
BlobResourceContents(
325+
uri=params.uri,
326+
blob=base64.b64encode(item.content).decode(),
327+
mime_type=item.mime_type or "application/octet-stream",
328+
_meta=item.meta,
329329
)
330-
else:
331-
contents.append(
332-
TextResourceContents(
333-
uri=params.uri,
334-
text=item.content,
335-
mime_type=item.mime_type or "text/plain",
336-
_meta=item.meta,
337-
)
330+
)
331+
else:
332+
contents.append(
333+
TextResourceContents(
334+
uri=params.uri,
335+
text=item.content,
336+
mime_type=item.mime_type or "text/plain",
337+
_meta=item.meta,
338338
)
339-
return ReadResourceResult(contents=contents)
340-
341-
async def handle_list_resource_templates(ctx: Any, params: Any) -> ListResourceTemplatesResult:
342-
return ListResourceTemplatesResult(resource_templates=await self.list_resource_templates())
343-
344-
async def handle_list_prompts(ctx: Any, params: Any) -> ListPromptsResult:
345-
return ListPromptsResult(prompts=await self.list_prompts())
346-
347-
async def handle_get_prompt(ctx: Any, params: Any) -> GetPromptResult:
348-
return await self.get_prompt(params.name, params.arguments)
349-
350-
return {
351-
"on_list_tools": handle_list_tools,
352-
"on_call_tool": handle_call_tool,
353-
"on_list_resources": handle_list_resources,
354-
"on_read_resource": handle_read_resource,
355-
"on_list_resource_templates": handle_list_resource_templates,
356-
"on_list_prompts": handle_list_prompts,
357-
"on_get_prompt": handle_get_prompt,
358-
}
339+
)
340+
return ReadResourceResult(contents=contents)
341+
342+
async def _handle_list_resource_templates(self, ctx: Any, params: Any) -> ListResourceTemplatesResult:
343+
return ListResourceTemplatesResult(resource_templates=await self.list_resource_templates())
344+
345+
async def _handle_list_prompts(self, ctx: Any, params: Any) -> ListPromptsResult:
346+
return ListPromptsResult(prompts=await self.list_prompts())
347+
348+
async def _handle_get_prompt(self, ctx: Any, params: Any) -> GetPromptResult:
349+
return await self.get_prompt(params.name, params.arguments)
359350

360351
async def list_tools(self) -> list[MCPTool]:
361352
"""List all available tools."""

0 commit comments

Comments
 (0)