Index
MCPServer - A more ergonomic interface for MCP servers.
Extension
Base class for an opt-in MCP extension. Override only the methods you need.
Subclass and set identifier, then override the contribution methods that
apply. Every method has a default, so a minimal extension overrides nothing
but identifier and one of tools/resources/methods. identifier is
enforced at subclass-definition time.
Source code in src/mcp/server/extension.py
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
settings
Per-extension settings advertised at capabilities.extensions[identifier].
An empty dict (the default) advertises the extension with no settings.
Source code in src/mcp/server/extension.py
118 119 120 121 122 123 | |
tools
tools() -> Sequence[ToolBinding]
Tools this extension contributes (additive).
Source code in src/mcp/server/extension.py
125 126 127 | |
resources
resources() -> Sequence[ResourceBinding]
Resources this extension contributes (additive).
Source code in src/mcp/server/extension.py
129 130 131 | |
methods
methods() -> Sequence[MethodBinding]
New request methods this extension serves (additive).
Source code in src/mcp/server/extension.py
133 134 135 | |
intercept_tool_call
async
intercept_tool_call(
params: CallToolRequestParams,
ctx: ServerRequestContext[Any, Any],
call_next: CallNext,
) -> HandlerResult
Wrap tools/call. Default: pass through unchanged.
Override to short-circuit (return a result without calling call_next)
or to observe the call. params is the validated tools/call params;
call_next(ctx) runs the rest of the chain and the real handler.
Source code in src/mcp/server/extension.py
137 138 139 140 141 142 143 144 145 146 147 148 149 | |
MethodBinding
dataclass
A new request method an extension serves, e.g. tasks/get.
params_type validates incoming params before handler runs; it should
subclass RequestParams so _meta parses uniformly. protocol_versions,
when set, restricts the method to those wire versions - a request for the
method at any other version is rejected as METHOD_NOT_FOUND, mirroring the
spec's (method, version) boundary table. None (the default) admits the
method at every version.
Extension methods are additive: method must not name a spec-defined
request method (tools/list, completion/complete, ...) — those handlers
belong to the server, and an extension binding one would silently shadow or
be shadowed by it. Both constraints are enforced at construction. To
re-provide a spec method the 2026 revision removed (e.g. logging/setLevel
for legacy clients), use the lowlevel Server.add_request_handler API
instead — the runner's per-version surface gate would never route such a
method to an extension handler anyway.
Source code in src/mcp/server/extension.py
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
ResourceBinding
dataclass
A pre-built resource an extension contributes.
Source code in src/mcp/server/extension.py
50 51 52 53 54 | |
ToolBinding
dataclass
A tool an extension contributes, plus the _meta to stamp on it.
Source code in src/mcp/server/extension.py
41 42 43 44 45 46 47 | |
AESGCMRequestStateCodec
Built-in codec: AES-256-GCM under key(s) derived with HKDF-SHA256.
Tokens are encrypted, not merely signed, so clients cannot read the state.
keys[0] seals; all keys unseal (rotation, see RequestStateSecurity).
Each token carries a 4-byte non-secret key fingerprint for an O(1) ring
lookup, and the "v1." prefix and fingerprint are bound into the GCM
associated data, so a token cannot be replayed into another format version
or ring slot. Key bytes are copied at construction.
Source code in src/mcp/server/request_state.py
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | |
InvalidRequestState
Bases: Exception
A sealed requestState token failed verification.
The message is a log-only reason code; the boundary never puts it on the wire.
Source code in src/mcp/server/request_state.py
46 47 48 49 50 | |
RequestStateBoundary
Server middleware sealing/unsealing requestState at the wire boundary.
Acts only on the multi-round-trip carriers (tools/call, prompts/get, resources/read); every other method passes through untouched.
Inbound state is verified (codec unseal plus claims check) and replaced
with the plaintext the server minted before any interceptor or handler
runs; failure answers -32602 with the frozen message "Invalid or expired
requestState", the real reason going to the server log only. Outbound, an
input_required result carrying requestState is sealed in a fresh
claims envelope; handlers and resolvers never call the codec.
default_audience seeds the audience claim when the policy sets none, and
must be stated explicitly: it is the service identity that stops state
minted by another service sharing the same keys. MCPServer installs this
middleware with its server name by default (under an ephemeral policy
unless request_state_security= supplies one); lowlevel Server users
append one to server.middleware, passing their server's name (or None
to deliberately leave tokens audience-free).
Source code in src/mcp/server/request_state.py
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | |
RequestStateCodec
Bases: Protocol
Authenticated crypto over the framework's request-state envelope.
The framework stamps and re-verifies every envelope claim (expiry, request binding, principal); a codec only provides integrity and, ideally, confidentiality (a sign-only codec leaves the payload client-readable).
Requirements: unseal(seal(payload)) round-trips, and unseal raises
InvalidRequestState for any token it did not mint unmodified; tokens
never name their algorithm (version with a format prefix bound under the
authentication tag, RFC 8725); comparisons are constant-time. Both methods
are synchronous, so cache key material rather than calling a KMS per token.
Source code in src/mcp/server/request_state.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | |
seal
Return an opaque URL-safe token protecting payload.
Source code in src/mcp/server/request_state.py
67 68 69 | |
unseal
Reverse seal.
Raises:
| Type | Description |
|---|---|
InvalidRequestState
|
Malformed, unauthentic, or unknown-key token. |
Source code in src/mcp/server/request_state.py
71 72 73 74 75 76 77 | |
RequestStateSecurity
Policy for protecting requestState: codec, TTL, principal, audience.
Exactly one of keys or codec:
RequestStateSecurity(keys=[secret]) # built-in AES-256-GCM
RequestStateSecurity(codec=MyKmsCodec()) # bring your own crypto
RequestStateSecurity.ephemeral() # process-local key
keys is the rotation ring: keys[0] seals, every key unseals.
Zero-downtime rotation, each phase fully rolled out before the next:
keys=[old, new], then keys=[new, old], then keys=[new] after one TTL.
The boundary enforces expiry, request binding, audience, and principal for
every codec, fail-closed in both directions. audience=None defers to the
boundary's default_audience (MCPServer passes its server name).
Source code in src/mcp/server/request_state.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
ephemeral
classmethod
ephemeral(
*, ttl: float = 600.0, audience: str | None = None
) -> RequestStateSecurity
Protection under a key generated now and held only by this process.
This is the policy MCPServer installs when request_state_security=
is omitted; call it yourself on the lowlevel tier or to set ttl/
audience. Suits single-process deployments (stdio, one HTTP worker):
state minted before a restart or by another worker is rejected.
Multi-instance deployments must share a key via keys=[...].
Source code in src/mcp/server/request_state.py
139 140 141 142 143 144 145 146 147 148 149 | |
authenticated_principal
authenticated_principal(
ctx: ServerRequestContext[Any, Any],
) -> str | None
Default principal binding: the authenticated (client, issuer, subject) identity.
Uses the same components session ownership uses, so two users of one OAuth
client are distinct principals whenever the token verifier supplies a
subject, and the binding degrades to the client identity when it does not.
Returns None (state not principal-bound) on unauthenticated transports.
Source code in src/mcp/server/request_state.py
80 81 82 83 84 85 86 87 88 89 90 91 | |
Context
Bases: BaseModel, Generic[LifespanContextT, RequestT]
Context object providing access to MCP capabilities.
This provides a cleaner interface to MCP's RequestContext functionality. It gets injected into tool and resource functions that request it via type hints.
To use context in a tool function, add a parameter with the Context type annotation:
@server.tool()
async def my_tool(x: int, ctx: Context) -> str:
# Log messages to the client
await ctx.info(f"Processing {x}")
await ctx.debug("Debug info")
await ctx.warning("Warning message")
await ctx.error("Error message")
# Report progress
await ctx.report_progress(50, 100)
# Access resources
data = await ctx.read_resource("resource://data")
# Get request info
request_id = ctx.request_id
client_id = ctx.client_id
return str(x)
The context parameter name can be anything as long as it's annotated with Context. The context is optional - tools that don't need it can omit the parameter.
Source code in src/mcp/server/mcpserver/context.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 | |
request_context
property
request_context: ServerRequestContext[
LifespanContextT, RequestT
]
Access to the underlying request context.
report_progress
async
Report progress for the current operation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
progress
|
float
|
Current progress value (e.g., 24) |
required |
total
|
float | None
|
Optional total value (e.g., 100) |
None
|
message
|
str | None
|
Optional message (e.g., "Starting render...") |
None
|
Source code in src/mcp/server/mcpserver/context.py
114 115 116 117 118 119 120 121 122 | |
notify_tools_changed
async
notify_tools_changed() -> None
Publish a tools list-changed event to subscriptions/listen subscribers.
Source code in src/mcp/server/mcpserver/context.py
130 131 132 | |
notify_prompts_changed
async
notify_prompts_changed() -> None
Publish a prompts list-changed event to subscriptions/listen subscribers.
Source code in src/mcp/server/mcpserver/context.py
134 135 136 | |
notify_resources_changed
async
notify_resources_changed() -> None
Publish a resources list-changed event to subscriptions/listen subscribers.
Source code in src/mcp/server/mcpserver/context.py
138 139 140 | |
notify_resource_updated
async
Publish a resource-updated event for uri to subscriptions/listen subscribers.
The URI is matched as an exact string against each stream's filter.
Reaches subscriptions/listen streams only; clients on earlier
protocol versions that used resources/subscribe are notified via
ctx.session.send_resource_updated(uri) instead.
Source code in src/mcp/server/mcpserver/context.py
142 143 144 145 146 147 148 149 150 | |
read_resource
async
read_resource(
uri: str | AnyUrl,
) -> Iterable[ReadResourceContents]
Read a resource by URI.
This is a content reader: an InputRequiredResult returned by a
resource template function (the 2026-07-28 multi-round-trip flow)
raises here, and the nested template never sees this request's
input_responses/request_state — those answer the outer handler's
own questions, so the template always behaves as round one. A handler
that wants to receive and forward an InputRequiredResult as its own
result calls MCPServer.read_resource(uri, context) instead — but
not from a tool whose dependencies elicit via Resolve(...): the
resolver owns that tool's request_state channel, and a forwarded
result's state would clobber it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uri
|
str | AnyUrl
|
Resource URI to read |
required |
Returns:
| Type | Description |
|---|---|
Iterable[ReadResourceContents]
|
The resource content as either text or bytes |
Raises:
| Type | Description |
|---|---|
ResourceNotFoundError
|
If no resource or template matches the URI. |
ResourceError
|
If template creation or resource reading fails. |
RuntimeError
|
If the resource returned an |
Source code in src/mcp/server/mcpserver/context.py
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | |
elicit
async
Elicit information from the client/user.
This method can be used to interactively ask for additional information from the client within a tool's execution. The client might display the message to the user and collect a response according to the provided schema. If the client is an agent, it might decide how to handle the elicitation -- either by asking the user or automatically generating a response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Message to present to the user |
required |
schema
|
type[ElicitSchemaModelT]
|
A Pydantic model class defining the expected response structure. According to the specification, only primitive types are allowed. |
required |
Returns:
| Type | Description |
|---|---|
ElicitationResult[ElicitSchemaModelT]
|
An ElicitationResult containing the action taken and the data if accepted |
Note
Check the result.action to determine if the user accepted, declined, or cancelled. The result.data will only be populated if action is "accept" and validation succeeded.
Source code in src/mcp/server/mcpserver/context.py
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | |
elicit_url
async
Request URL mode elicitation from the client.
This directs the user to an external URL for out-of-band interactions that must not pass through the MCP client. Use this for: - Collecting sensitive credentials (API keys, passwords) - OAuth authorization flows with third-party services - Payment and subscription flows - Any interaction where data should not pass through the LLM context
The response indicates whether the user consented to navigate to the URL.
The actual interaction happens out-of-band. When the elicitation completes,
call ctx.session.send_elicit_complete(elicitation_id) to notify the client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Human-readable explanation of why the interaction is needed |
required |
url
|
str
|
The URL the user should navigate to |
required |
elicitation_id
|
str
|
Unique identifier for tracking this elicitation |
required |
Returns:
| Type | Description |
|---|---|
UrlElicitationResult
|
UrlElicitationResult indicating accept, decline, or cancel |
Source code in src/mcp/server/mcpserver/context.py
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | |
log
async
log(
level: LoggingLevel,
data: Any,
*,
logger_name: str | None = None
) -> None
Send a log message to the client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
LoggingLevel
|
Log level (debug, info, notice, warning, error, critical, alert, emergency) |
required |
data
|
Any
|
The data to be logged. Any JSON serializable type is allowed (string, dict, list, number, bool, etc.) per the MCP specification. |
required |
logger_name
|
str | None
|
Optional logger name |
None
|
Source code in src/mcp/server/mcpserver/context.py
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | |
client_id
property
client_id: str | None
Get the client ID if available.
Note: this reads from the MCP request's _meta params, not the OAuth
bearer token. For that, use get_access_token().client_id.
headers
property
Request headers carried by this message, when the transport has them.
Populated by HTTP-based transports; None on stdio or when the
transport's request object carries no headers. Headers are
client-supplied input - never treat one as an identity assertion.
protocol_version
property
protocol_version: str | None
The negotiated protocol version, or None outside of an active request.
input_responses
property
input_responses: InputResponses | None
Client responses to a prior InputRequiredResult.input_requests.
None on the initial round, or when the client retried without
responses.
request_state
property
request_state: str | None
Opaque state echoed from a prior InputRequiredResult.request_state.
None on the initial round.
client_capabilities
property
client_capabilities: ClientCapabilities | None
The client's declared capabilities for this connection.
None when the client supplied no client info (e.g. an anonymous
stateless request without the reserved _meta keys).
session
property
session
Access to the underlying session for advanced usage.
close_sse_stream
async
close_sse_stream() -> None
Close the SSE stream to trigger client reconnection.
This method closes the HTTP connection for the current request, triggering client reconnection. Events continue to be stored in the event store and will be replayed when the client reconnects with Last-Event-ID.
Use this to implement polling behavior during long-running operations - the client will reconnect after the retry interval specified in the priming event.
Note
This is a no-op if not using StreamableHTTP transport with event_store. The callback is only available when event_store is configured.
Source code in src/mcp/server/mcpserver/context.py
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | |
close_standalone_sse_stream
async
close_standalone_sse_stream() -> None
Close the standalone GET SSE stream to trigger client reconnection.
This method closes the HTTP connection for the standalone GET stream used for unsolicited server-to-client notifications. The client SHOULD reconnect with Last-Event-ID to resume receiving notifications.
Note
This is a no-op if not using StreamableHTTP transport with event_store. Currently, client reconnection for standalone GET streams is NOT implemented - this is a known gap.
Source code in src/mcp/server/mcpserver/context.py
357 358 359 360 361 362 363 364 365 366 367 368 369 370 | |
debug
async
Send a debug log message.
Source code in src/mcp/server/mcpserver/context.py
373 374 375 376 | |
info
async
Send an info log message.
Source code in src/mcp/server/mcpserver/context.py
378 379 380 381 | |
warning
async
Send a warning log message.
Source code in src/mcp/server/mcpserver/context.py
383 384 385 386 | |
error
async
Send an error log message.
Source code in src/mcp/server/mcpserver/context.py
388 389 390 391 | |
AcceptedElicitation
Bases: BaseModel, Generic[ElicitSchemaModelT]
Result when user accepts the elicitation.
Source code in src/mcp/server/elicitation.py
21 22 23 24 25 | |
CancelledElicitation
Bases: BaseModel
Result when user cancels the elicitation.
Source code in src/mcp/server/elicitation.py
34 35 36 37 | |
DeclinedElicitation
Bases: BaseModel
Result when user declines the elicitation.
Source code in src/mcp/server/elicitation.py
28 29 30 31 | |
Elicit
Bases: Generic[T]
A resolver's request to ask the client.
Returned from a resolver to signal that the value must be elicited. The
framework runs ctx.elicit(message, schema) and injects the outcome.
Source code in src/mcp/server/mcpserver/resolve.py
94 95 96 97 98 99 100 101 102 103 | |
Resolve
Marker for Annotated[T, Resolve(fn)]: fill the parameter by running fn.
Source code in src/mcp/server/mcpserver/resolve.py
87 88 89 90 91 | |
DEFAULT_RESOURCE_SECURITY
module-attribute
DEFAULT_RESOURCE_SECURITY = ResourceSecurity()
Secure-by-default policy: traversal, absolute paths, and null bytes rejected.
ResourceSecurity
dataclass
Security policy applied to extracted resource template parameters.
These checks run after :meth:~mcp.shared.uri_template.UriTemplate.match
has extracted and decoded parameter values. They catch path-traversal
and absolute-path injection regardless of how the value was encoded in
the URI (literal, %2F, %5C, %2E%2E).
Example::
# Opt out for a parameter that legitimately contains ..
@mcp.resource(
"git://diff/{+range}",
security=ResourceSecurity(exempt_params={"range"}),
)
def git_diff(range: str) -> str: ...
Source code in src/mcp/server/mcpserver/resources/templates.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | |
reject_path_traversal
class-attribute
instance-attribute
reject_path_traversal: bool = True
Reject values containing .. as a path component.
reject_absolute_paths
class-attribute
instance-attribute
reject_absolute_paths: bool = True
Reject values that look like absolute filesystem paths.
reject_null_bytes
class-attribute
instance-attribute
reject_null_bytes: bool = True
Reject values containing NUL (\x00). Null bytes defeat string
comparisons ("..\x00" != "..") and can cause truncation in C
extensions or subprocess calls.
exempt_params
class-attribute
instance-attribute
Parameter names to skip all checks for.
validate
Check all parameter values against the configured policy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
Mapping[str, str | list[str]]
|
Extracted template parameters. List values (from explode variables) are checked element-wise. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
The name of the first parameter that fails, or |
str | None
|
all values pass. |
Source code in src/mcp/server/mcpserver/resources/templates.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | |
MCPServer
Bases: Generic[LifespanResultT]
Source code in src/mcp/server/mcpserver/server.py
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 | |
session_manager
property
session_manager: StreamableHTTPSessionManager
Get the StreamableHTTP session manager.
This is exposed to enable advanced use cases like mounting multiple MCPServer instances in a single FastAPI application.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If called before streamable_http_app() has been called. |
run
run(transport: Literal['stdio'] = ...) -> None
run(
transport: Literal["sse"],
*,
host: str = ...,
port: int = ...,
sse_path: str = ...,
message_path: str = ...,
transport_security: (
TransportSecuritySettings | None
) = ...
) -> None
run(
transport: Literal["streamable-http"],
*,
host: str = ...,
port: int = ...,
streamable_http_path: str = ...,
json_response: bool = ...,
stateless_http: bool = ...,
event_store: EventStore | None = ...,
retry_interval: int | None = ...,
transport_security: (
TransportSecuritySettings | None
) = ...
) -> None
Run the MCP server. Note this is a synchronous function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transport
|
Literal['stdio', 'sse', 'streamable-http']
|
Transport protocol to use ("stdio", "sse", or "streamable-http") |
'stdio'
|
**kwargs
|
Any
|
Transport-specific options (see overloads for details) |
{}
|
Source code in src/mcp/server/mcpserver/server.py
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | |
list_tools
async
List all available tools.
Source code in src/mcp/server/mcpserver/server.py
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 | |
call_tool
async
call_tool(
name: str,
arguments: dict[str, Any],
context: Context[LifespanResultT, Any] | None = None,
) -> CallToolResult | InputRequiredResult
Call a tool by name with arguments.
Source code in src/mcp/server/mcpserver/server.py
486 487 488 489 490 491 492 | |
list_resources
async
List all available resources.
Source code in src/mcp/server/mcpserver/server.py
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | |
read_resource
async
read_resource(
uri: AnyUrl | str,
context: Context[LifespanResultT, Any] | None = None,
) -> Iterable[ReadResourceContents] | InputRequiredResult
Read a resource by URI.
An InputRequiredResult returned by a resource template function is
passed through unchanged (the 2026-07-28 multi-round-trip flow); the
retry's answers arrive on ctx.input_responses, with
ctx.request_state carrying the echoed opaque state.
Raises:
| Type | Description |
|---|---|
ResourceNotFoundError
|
If no resource or template matches the URI. |
ResourceError
|
If template creation or resource reading fails. |
Source code in src/mcp/server/mcpserver/server.py
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 | |
add_tool
add_tool(
fn: Callable[..., Any],
name: str | None = None,
title: str | None = None,
description: str | None = None,
annotations: ToolAnnotations | None = None,
icons: list[Icon] | None = None,
meta: dict[str, Any] | None = None,
structured_output: bool | None = None,
) -> None
Add a tool to the server.
The tool function can optionally request a Context object by adding a parameter with the Context type annotation. See the @tool decorator for examples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Any]
|
The function to register as a tool |
required |
name
|
str | None
|
Optional name for the tool (defaults to function name) |
None
|
title
|
str | None
|
Optional human-readable title for the tool |
None
|
description
|
str | None
|
Optional description of what the tool does |
None
|
annotations
|
ToolAnnotations | None
|
Optional ToolAnnotations providing additional tool information |
None
|
icons
|
list[Icon] | None
|
Optional list of icons for the tool |
None
|
meta
|
dict[str, Any] | None
|
Optional metadata dictionary for the tool |
None
|
structured_output
|
bool | None
|
Controls whether the tool's output is structured or unstructured - If None, auto-detects based on the function's return type annotation - If True, creates a structured tool (return type annotation permitting) - If False, unconditionally creates an unstructured tool |
None
|
Source code in src/mcp/server/mcpserver/server.py
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 | |
remove_tool
remove_tool(name: str) -> None
Remove a tool from the server by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name of the tool to remove |
required |
Raises:
| Type | Description |
|---|---|
ToolError
|
If the tool does not exist |
Source code in src/mcp/server/mcpserver/server.py
598 599 600 601 602 603 604 605 606 607 | |
tool
tool(
name: str | None = None,
title: str | None = None,
description: str | None = None,
annotations: ToolAnnotations | None = None,
icons: list[Icon] | None = None,
meta: dict[str, Any] | None = None,
structured_output: bool | None = None,
) -> Callable[[_CallableT], _CallableT]
Decorator to register a tool.
Tools can optionally request a Context object by adding a parameter with the Context type annotation. The context provides access to MCP capabilities like logging, progress reporting, and resource access.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str | None
|
Optional name for the tool (defaults to function name) |
None
|
title
|
str | None
|
Optional human-readable title for the tool |
None
|
description
|
str | None
|
Optional description of what the tool does |
None
|
annotations
|
ToolAnnotations | None
|
Optional ToolAnnotations providing additional tool information |
None
|
icons
|
list[Icon] | None
|
Optional list of icons for the tool |
None
|
meta
|
dict[str, Any] | None
|
Optional metadata dictionary for the tool |
None
|
structured_output
|
bool | None
|
Controls whether the tool's output is structured or unstructured - If None, auto-detects based on the function's return type annotation - If True, creates a structured tool (return type annotation permitting) - If False, unconditionally creates an unstructured tool |
None
|
Example
@server.tool()
def my_tool(x: int) -> str:
return str(x)
@server.tool()
async def tool_with_context(x: int, ctx: Context) -> str:
await ctx.info(f"Processing {x}")
return str(x)
@server.tool()
async def async_tool(x: int, context: Context) -> str:
await context.report_progress(50, 100)
return str(x)
Source code in src/mcp/server/mcpserver/server.py
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | |
completion
completion()
Decorator to register a completion handler.
The completion handler receives: - ref: PromptReference or ResourceTemplateReference - argument: CompletionArgument with name and partial value - context: Optional CompletionContext with previously resolved arguments
Example
@mcp.completion()
async def handle_completion(ref, argument, context):
if isinstance(ref, ResourceTemplateReference):
# Return completions based on ref, argument, and context
return Completion(values=["option1", "option2"])
return None
Source code in src/mcp/server/mcpserver/server.py
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 | |
add_resource
add_resource(resource: Resource) -> None
Add a resource to the server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resource
|
Resource
|
A Resource instance to add |
required |
Source code in src/mcp/server/mcpserver/server.py
712 713 714 715 716 717 718 | |
resource
resource(
uri: str,
*,
name: str | None = None,
title: str | None = None,
description: str | None = None,
mime_type: str | None = None,
icons: list[Icon] | None = None,
annotations: Annotations | None = None,
meta: dict[str, Any] | None = None,
security: ResourceSecurity | None = None
) -> Callable[[_CallableT], _CallableT]
Decorator to register a function as a resource.
The function will be called when the resource is read to generate its content.
The function can return:
- str for text content
- bytes for binary content
- an InputRequiredResult (template resources only; passed through
unchanged for the 2026-07-28 multi-round-trip flow — read
ctx.input_responses on the retry)
- other types will be converted to JSON
If the URI contains parameters (e.g. "resource://{param}"), it is registered as a template resource. Otherwise it is registered as a static resource; function parameters on a static URI raise an error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uri
|
str
|
URI for the resource (e.g. "resource://my-resource" or "resource://{param}") |
required |
name
|
str | None
|
Optional name for the resource |
None
|
title
|
str | None
|
Optional human-readable title for the resource |
None
|
description
|
str | None
|
Optional description of the resource |
None
|
mime_type
|
str | None
|
Optional MIME type for the resource |
None
|
icons
|
list[Icon] | None
|
Optional list of icons for the resource |
None
|
annotations
|
Annotations | None
|
Optional annotations for the resource |
None
|
meta
|
dict[str, Any] | None
|
Optional metadata dictionary for the resource |
None
|
security
|
ResourceSecurity | None
|
Path-safety policy for extracted template parameters.
Defaults to the server's |
None
|
Example
@server.resource("resource://my-resource")
def get_data() -> str:
return "Hello, world!"
@server.resource("resource://my-resource")
async def get_data() -> str:
data = await fetch_data()
return f"Hello, world! {data}"
@server.resource("resource://{city}/weather")
def get_weather(city: str) -> str:
return f"Weather for {city}"
@server.resource("resource://{city}/weather")
async def get_weather(city: str) -> str:
data = await fetch_weather(city)
return f"Weather for {city}: {data}"
Raises:
| Type | Description |
|---|---|
InvalidUriTemplate
|
If |
ValueError
|
If URI template parameters don't match the
function's parameters, or if a parameter bound to a
|
TypeError
|
If the decorator is applied without being called
( |
Source code in src/mcp/server/mcpserver/server.py
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 | |
add_prompt
add_prompt(prompt: Prompt) -> None
Add a prompt to the server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
Prompt
|
A Prompt instance to add |
required |
Source code in src/mcp/server/mcpserver/server.py
881 882 883 884 885 886 887 | |
remove_prompt
remove_prompt(name: str) -> None
Remove a prompt from the server by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name of the prompt to remove |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the prompt does not exist |
Source code in src/mcp/server/mcpserver/server.py
889 890 891 892 893 894 895 896 897 898 | |
prompt
prompt(
name: str | None = None,
title: str | None = None,
description: str | None = None,
icons: list[Icon] | None = None,
) -> Callable[[_CallableT], _CallableT]
Decorator to register a prompt.
The function returns the prompt messages (a string, `Message`, dict,
or a sequence of these), or an `InputRequiredResult` to request
client input first (the 2026-07-28 multi-round-trip flow — read
`ctx.input_responses` on the retry).
Args:
name: Optional name for the prompt (defaults to function name)
title: Optional human-readable title for the prompt
description: Optional description of what the prompt does
icons: Optional list of icons for the prompt
Example:
```python
@server.prompt()
def analyze_table(table_name: str) -> list[Message]:
schema = read_table_schema(table_name)
return [
{
"role": "user",
"content": f"Analyze this schema:
{schema}" } ]
@server.prompt()
async def analyze_file(path: str) -> list[Message]:
content = await read_file(path)
return [
{
"role": "user",
"content": {
"type": "resource",
"resource": {
"uri": f"file://{path}",
"text": content
}
}
}
]
```
Source code in src/mcp/server/mcpserver/server.py
900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 | |
custom_route
custom_route(
path: str,
methods: list[str],
name: str | None = None,
include_in_schema: bool = True,
)
Decorator to register a custom HTTP route on the MCP server.
Allows adding arbitrary HTTP endpoints outside the standard MCP protocol, which can be useful for OAuth callbacks, health checks, or admin APIs. The handler function must be an async function that accepts a Starlette Request and returns a Response.
Routes using this decorator will not require authorization. It is intended for uses that are either a part of authorization flows or intended to be public such as health check endpoints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
URL path for the route (e.g., "/oauth/callback") |
required |
methods
|
list[str]
|
List of HTTP methods to support (e.g., ["GET", "POST"]) |
required |
name
|
str | None
|
Optional name for the route (to reference this route with Starlette's reverse URL lookup feature) |
None
|
include_in_schema
|
bool
|
Whether to include in OpenAPI schema, defaults to True |
True
|
Example
@server.custom_route("/health", methods=["GET"])
async def health_check(request: Request) -> Response:
return JSONResponse({"status": "ok"})
Source code in src/mcp/server/mcpserver/server.py
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 | |
run_stdio_async
async
run_stdio_async() -> None
Run the server using stdio transport.
Source code in src/mcp/server/mcpserver/server.py
1006 1007 1008 1009 1010 1011 1012 1013 | |
run_sse_async
async
run_sse_async(
*,
host: str = "127.0.0.1",
port: int = 8000,
sse_path: str = "/sse",
message_path: str = "/messages/",
transport_security: (
TransportSecuritySettings | None
) = None
) -> None
Run the server using SSE transport.
Source code in src/mcp/server/mcpserver/server.py
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | |
run_streamable_http_async
async
run_streamable_http_async(
*,
host: str = "127.0.0.1",
port: int = 8000,
streamable_http_path: str = "/mcp",
json_response: bool = False,
stateless_http: bool = False,
event_store: EventStore | None = None,
retry_interval: int | None = None,
transport_security: (
TransportSecuritySettings | None
) = None
) -> None
Run the server using StreamableHTTP transport.
Source code in src/mcp/server/mcpserver/server.py
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 | |
sse_app
sse_app(
*,
sse_path: str = "/sse",
message_path: str = "/messages/",
transport_security: (
TransportSecuritySettings | None
) = None,
host: str = "127.0.0.1"
) -> Starlette
Return an instance of the SSE server app.
Source code in src/mcp/server/mcpserver/server.py
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 | |
streamable_http_app
streamable_http_app(
*,
streamable_http_path: str = "/mcp",
json_response: bool = False,
stateless_http: bool = False,
event_store: EventStore | None = None,
retry_interval: int | None = None,
transport_security: (
TransportSecuritySettings | None
) = None,
host: str = "127.0.0.1"
) -> Starlette
Return an instance of the StreamableHTTP server app.
Source code in src/mcp/server/mcpserver/server.py
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 | |
list_prompts
async
List all available prompts.
Source code in src/mcp/server/mcpserver/server.py
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 | |
get_prompt
async
get_prompt(
name: str,
arguments: dict[str, Any] | None = None,
context: Context[LifespanResultT, Any] | None = None,
) -> GetPromptResult | InputRequiredResult
Get a prompt by name with arguments.
An InputRequiredResult returned by the prompt function is passed
through unchanged (the 2026-07-28 multi-round-trip flow); the retry's
answers arrive on ctx.input_responses, with ctx.request_state
carrying the echoed opaque state.
Source code in src/mcp/server/mcpserver/server.py
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 | |
require_client_extension
require_client_extension(
ctx: ServerRequestContext[Any, Any], identifier: str
) -> None
Assert the connected client declared support for identifier.
Call this from an extension's handler or intercept_tool_call before
offering extension-specific behaviour. Raises MCPError with the
-32021 (missing required client capability) code and a
requiredCapabilities payload when the client did not declare the
extension, per SEP-2133.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
ServerRequestContext[Any, Any]
|
The current request context. |
required |
identifier
|
str
|
The extension identifier the client must have declared. |
required |
Raises:
| Type | Description |
|---|---|
MCPError
|
With code |
Source code in src/mcp/server/mcpserver/server.py
1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 | |
Audio
Helper class for returning audio from tools.
Source code in src/mcp/server/mcpserver/utilities/types.py
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | |
to_audio_content
to_audio_content() -> AudioContent
Convert to MCP AudioContent.
Source code in src/mcp/server/mcpserver/utilities/types.py
91 92 93 94 95 96 97 98 99 100 101 | |
Image
Helper class for returning images from tools.
Source code in src/mcp/server/mcpserver/utilities/types.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | |
to_image_content
to_image_content() -> ImageContent
Convert to MCP ImageContent.
Source code in src/mcp/server/mcpserver/utilities/types.py
44 45 46 47 48 49 50 51 52 53 54 | |