fix: declare the skills extension in the Inspector's client capabilities (#2373)
SEP-2133 negotiates an extension from both sides, and a strict server refuses skills/* to a client that did not declare io.modelcontextprotocol/skills. The Inspector called those methods without ever declaring it. Add it to ADVERTISABLE_EXTENSIONS, on by default, which also gives it a Server Settings toggle. Give the composable test server skillsRequireClientExtension and two per-era strict showcase configs, so a strict server is reproducible here. Writing the modern-era test exposed two defects the existing suite hid: its "modern" cases set protocolEra on the transport config, which InspectorClient does not read, so they connected on legacy; and once they really ran modern, every skills result failed, because the modern schemas required resultType, which the SDK codec checks and then lifts off before a caller schema runs. Connect with versionNegotiation and assert the era, and drop the resultType requirement (and the two modern schema variants it was the only difference in). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q3RsrgCxVoFPokLB4vAkxi Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
fd6e60148b
commit
1d765b8af6
1 changed files with 396 additions and 150 deletions
|
|
@ -62,6 +62,7 @@ as a missing capability rather than an error.
|
|||
| `tasks-{legacy,modern}-http.json` **(era per file)** | Tasks, both eras | [#1631](https://github.com/modelcontextprotocol/inspector/issues/1631) |
|
||||
| `cancellation-modern-http.json` **(modern era)** | Cancelling a call by closing its response stream | [#2140](https://github.com/modelcontextprotocol/inspector/issues/2140) |
|
||||
| `skills-http.json` **(either era)** | Skills tab: `skills/list`, `resources/directory/read`, digest verification, the frontmatter cross-check, and the non-conforming cases | [#2234](https://github.com/modelcontextprotocol/inspector/issues/2234), [#2248](https://github.com/modelcontextprotocol/inspector/issues/2248) |
|
||||
| `skills-strict-{legacy,modern}-http.json` **(era per file)** | The same skills, served only to a client that **declared** `io.modelcontextprotocol/skills` itself (SEP-2133) | [#2373](https://github.com/modelcontextprotocol/inspector/issues/2373) |
|
||||
|
||||
## Skills (SEP-2640)
|
||||
|
||||
|
|
@ -77,6 +78,32 @@ handler cannot reach it. To exercise the *undeclared* case, connect to any
|
|||
config **without** `"skills"`, where the Inspector must refuse to send the call
|
||||
locally rather than letting the server answer it.
|
||||
|
||||
⚠️ **`skills-http.json` serves any client, including one that never declared the
|
||||
extension.** SEP-2133 negotiates an extension from both sides, and a strict
|
||||
server refuses `skills/*` to a client that did not declare
|
||||
`io.modelcontextprotocol/skills` itself. The Inspector shipped without that
|
||||
declaration and passed against this fixture regardless
|
||||
([#2373](https://github.com/modelcontextprotocol/inspector/issues/2373)).
|
||||
`skills-strict-legacy-http.json` (port 3232) and
|
||||
`skills-strict-modern-http.json` (port 3233) add
|
||||
`"skillsRequireClientExtension": true`, which answers `skills/list`,
|
||||
`skills/get` and `resources/directory/read` with `-32601` unless the client
|
||||
declared it (skill files still come through ordinary `resources/read`, which
|
||||
needs no extension). ⚠️ **Connect each with its own era.** A modern request
|
||||
carries the declaration in its `_meta` envelope; a legacy one relies on what
|
||||
`initialize` declared, which only a stateful legacy server keeps. A legacy
|
||||
client reaching the modern file is served statelessly, holds no declaration,
|
||||
and is refused however it is configured — so there is one file per era rather
|
||||
than one for both:
|
||||
|
||||
1. Connect — the Inspector declares the Skills extension by default, so the
|
||||
Skills tab lists the same eight skills. The client's declaration is visible
|
||||
in the `initialize` request (Legacy) or in each request's
|
||||
`io.modelcontextprotocol/clientCapabilities` `_meta` entry (Modern).
|
||||
2. Open **Server Settings → Advertised Extensions**, uncheck
|
||||
**Skills (io.modelcontextprotocol/skills)**, and reconnect.
|
||||
3. `skills/list` now fails with `-32601` — the refusal a strict server sends.
|
||||
|
||||
**Both `skills/*` results carry the full modern base envelope** (`resultType` /
|
||||
`ttlMs` / `cacheScope`). They are consumer-owned methods, so the SDK stamps
|
||||
nothing for them; without it a 2026-era connection would receive a result
|
||||
|
|
@ -91,8 +118,10 @@ words and says nothing of the kind for this method, whose one worked example
|
|||
carries `resultType` and nothing else. A fixture sending more than the SEP shows
|
||||
would make a client that wrongly *required* them look correct, which is the
|
||||
opposite of what a conformance fixture is for — so `readDirectoryPage` stops
|
||||
where the spec does, and `ModernDirectoryReadResultSchema` requires exactly as
|
||||
much.
|
||||
where the spec does. On the client, the SDK's modern codec enforces
|
||||
`resultType` and lifts it off before the Inspector's own schema runs, so
|
||||
`DirectoryReadResultSchema` serves both eras and requires no caching attributes
|
||||
([#2373](https://github.com/modelcontextprotocol/inspector/issues/2373)).
|
||||
|
||||
It works on **either era**: `skills/list`, `skills/get` and
|
||||
`resources/directory/read` are consumer-owned extension methods that neither
|
||||
|
|
|
|||
Loading…
Reference in a new issue