MCPcopy Index your code
hub / github.com/modelcontextprotocol/python-sdk / _inner

Method _inner

src/mcp/server/runner.py:228–264  ·  view source on GitHub ↗
(ctx: ServerRequestContext[LifespanT, Any])

Source from the content-addressed store, hash-verified

226 is_spec_method = method in _methods.SPEC_CLIENT_METHODS
227
228 async def _inner(ctx: ServerRequestContext[LifespanT, Any]) -> HandlerResult:
229 # Read method/params off `ctx` so a middleware that rewrote them via
230 # `call_next(replace(ctx, ...))` reaches lookup and the handler.
231 method, params = ctx.method, ctx.params
232 # Pinned compat: spec methods are surface-validated before lookup,
233 # so malformed params are INVALID_PARAMS even with no handler
234 # registered. Custom methods miss the monolith map and fall through
235 # to `entry.params_type` exactly as before.
236 if method in _methods.SPEC_CLIENT_METHODS:
237 try:
238 _methods.validate_client_request(method, version, params)
239 except KeyError:
240 raise MCPError(code=METHOD_NOT_FOUND, message="Method not found", data=method) from None
241 # TODO(L29): the 2026-07-28 spec drops the handshake; this branch and
242 # the gate become a per-version legacy path then. Initialize runs inline
243 # (read loop parked), so awaiting the peer anywhere on this path deadlocks.
244 if method == "initialize":
245 return self._handle_initialize(params)
246 # Methods without a handler are METHOD_NOT_FOUND regardless of
247 # initialization state: JSON-RPC 2.0 reserves -32601 for "not
248 # available on this server", and clients probing a server before
249 # the handshake key off that code. The init gate below therefore
250 # only ever applies to methods the server actually serves.
251 entry = self.server.get_request_handler(method)
252 if entry is None:
253 raise MCPError(code=METHOD_NOT_FOUND, message="Method not found", data=method)
254 if not self.connection.initialize_accepted and method not in _INIT_EXEMPT:
255 # Pinned compat: the same error shape the union validation produced.
256 raise MCPError(code=INVALID_PARAMS, message="Invalid request parameters", data="")
257 # Absent params validate as {} (required fields still reject), so
258 # the handler receives the model with its defaults, never None.
259 typed_params = entry.params_type.model_validate({} if params is None else params, by_name=False)
260 result = await entry.handler(ctx, typed_params)
261 if isinstance(result, ErrorData):
262 # Raise inside the chain so middleware observes the failure.
263 raise MCPError.from_error_data(result)
264 return result
265
266 call = self._compose_server_middleware(_inner)
267 result = _dump_result(await call(ctx))

Callers

nothing calls this directly

Calls 8

_handle_initializeMethod · 0.95
MCPErrorClass · 0.90
get_request_handlerMethod · 0.80
handlerMethod · 0.80
from_error_dataMethod · 0.80
debugMethod · 0.80
warningMethod · 0.80

Tested by

no test coverage detected