|
6 | 6 | Surface maps key `(method, version)` to per-version wire types (key absence is |
7 | 7 | the version gate; shape validation is per schema era, i.e. 2025-11-25 for every |
8 | 8 | pre-2026 version and 2026-07-28 for 2026). Monolith maps key `method` to the |
9 | | -version-free `mcp_types` models user code receives.""" |
| 9 | +version-free `mcp_types` models user code receives. |
| 10 | +`SERVER_CAPABILITY_REQUIREMENTS` keys `method` to the `ServerCapabilities` |
| 11 | +attribute path it requires (version-invariant); `missing_server_capability` |
| 12 | +evaluates it.""" |
10 | 13 |
|
11 | 14 | from __future__ import annotations |
12 | 15 |
|
|
29 | 32 | "MONOLITH_NOTIFICATIONS", |
30 | 33 | "MONOLITH_REQUESTS", |
31 | 34 | "MONOLITH_RESULTS", |
| 35 | + "SERVER_CAPABILITY_REQUIREMENTS", |
32 | 36 | "SERVER_NOTIFICATIONS", |
33 | 37 | "SERVER_REQUESTS", |
34 | 38 | "SERVER_RESULTS", |
35 | 39 | "SPEC_CLIENT_METHODS", |
36 | 40 | "SPEC_CLIENT_NOTIFICATION_METHODS", |
| 41 | + "missing_server_capability", |
37 | 42 | "parse_client_notification", |
38 | 43 | "parse_client_request", |
39 | 44 | "parse_client_result", |
|
404 | 409 | """Monolith result model (or two-arm union) per request method.""" |
405 | 410 |
|
406 | 411 |
|
| 412 | +# --- Server capability requirements --- |
| 413 | + |
| 414 | +SERVER_CAPABILITY_REQUIREMENTS: Final[Mapping[str, tuple[str, ...]]] = MappingProxyType( |
| 415 | + { |
| 416 | + "completion/complete": ("completions",), |
| 417 | + "logging/setLevel": ("logging",), |
| 418 | + "prompts/get": ("prompts",), |
| 419 | + "prompts/list": ("prompts",), |
| 420 | + "resources/list": ("resources",), |
| 421 | + "resources/read": ("resources",), |
| 422 | + "resources/subscribe": ("resources", "subscribe"), |
| 423 | + "resources/templates/list": ("resources",), |
| 424 | + "resources/unsubscribe": ("resources",), |
| 425 | + "tools/call": ("tools",), |
| 426 | + "tools/list": ("tools",), |
| 427 | + } |
| 428 | +) |
| 429 | +"""The server capability each client request method requires, as an attribute path into |
| 430 | +`ServerCapabilities`. Methods with no entry (`ping`, `initialize`, `server/discover`, |
| 431 | +`subscriptions/listen`) require no server capability. `subscriptions/listen` stays ungated on |
| 432 | +purpose: at 2026-07-28 the `resources.subscribe` capability licenses that request's |
| 433 | +`resourceSubscriptions` FIELD, a params-level fact a method-keyed table cannot express, and |
| 434 | +no spec MUST ties the method itself to a capability -- gating it here would wrongly reject a |
| 435 | +listen that asks only for `toolsListChanged`. The relationship is the same at every protocol |
| 436 | +version, so the map is keyed by method alone.""" |
| 437 | + |
| 438 | + |
| 439 | +def missing_server_capability(method: str, capabilities: types.ServerCapabilities | None) -> str | None: |
| 440 | + """The server capability `method` requires but `capabilities` does not advertise. |
| 441 | +
|
| 442 | + Returns the dotted name of the first unadvertised step on the required capability path -- |
| 443 | + `resources` when no resources capability is advertised at all, `resources.subscribe` when |
| 444 | + `resources` is advertised but `subscribe` is not -- or None when `method` requires no |
| 445 | + server capability or the required one is advertised. Naming the first missing step rather |
| 446 | + than the whole path matches the TypeScript SDK's assertCapabilityForMethod and is the |
| 447 | + actionable thing to tell the caller. `capabilities=None` (nothing negotiated yet) |
| 448 | + advertises nothing. |
| 449 | + """ |
| 450 | + path = SERVER_CAPABILITY_REQUIREMENTS.get(method) |
| 451 | + if path is None: |
| 452 | + return None |
| 453 | + node: Any = capabilities |
| 454 | + for index, attr in enumerate(path): |
| 455 | + node = node and getattr(node, attr) |
| 456 | + if not node: |
| 457 | + return ".".join(path[: index + 1]) |
| 458 | + return None |
| 459 | + |
| 460 | + |
407 | 461 | # --- Parse functions --- |
408 | 462 |
|
409 | 463 | # Envelope stubs merged into bodies for surface validation (surface classes are full frames). |
|
0 commit comments