fix: address Copilot review round 1 on #2251
- skills.ts: report a manifest that omits the skill's own SKILL.md (an empty list included), duplicate URIs, entries outside the skill root, and a missing size. `Conforms` was reachable for manifests that break invariants SEP-2640 states. - skills.ts: cross-check the declared byte length before hashing. A size that disagrees fails verification on its own — the digest is taken over the bytes the server served, so agreeing with it says nothing about whether the manifest describes them. - skills.ts: copy the view instead of slicing its backing store in `sha256Digest`. `SharedArrayBuffer.prototype.slice()` returns another SharedArrayBuffer, which `crypto.subtle.digest` rejects — the cast claimed to handle the exact input that would have thrown. No cast now. - skills.ts: state the one obligation NOT checked here — that an entry's frontmatter matches the fetched SKILL.md's. The digest cannot cover it, and closing it needs a YAML parser, so it is tracked on #2248. - managedSkillsState: cap the walk at LIST_MAX_PAGES. The repeated-cursor guard only catches a server stuck on one cursor; endlessly unique ones walked forever. Raises rather than truncating, like the salvage walk. - managedSkillsState: gate every write on a session generation, so a walk that resolves after a disconnect or destroy cannot repopulate a cleared store or deliver the previous session's skills into the next. - inspectorClient: send a cursor when it is `!== undefined`, not when it is truthy. An opaque cursor may be `""`, and dropping it re-requested page one — which the store then reported as a repeated-cursor failure. - SkillsScreen: key verdict invalidation on the manifest (URI + digests + sizes), not the URI alone. A Refresh that changed the manifest left a green badge attached to a digest nothing had checked. - SkillsScreen: epoch-guard every read continuation, so a fetch that resolves after the selection moved on cannot write into the new one. - SkillsScreen: bound "Verify all" to 4 concurrent reads. A conforming manifest may hold 512 files. - skillsSchemas: drop the guessed `resources/directory/read` result schema. Nothing calls it, so an unverified shape could sit wrong indefinitely; phase 3 adds it against the normative text. - skills-http.json: declare the extension bare. It advertised `directoryRead: true` with no handler, so Connection Info reported "Supported" for a method that answers -32601. - types.ts: restore the Tasks doc comment the Skills interface displaced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018kfrH3uRNUM4JAk1reKtyY Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
63623b754b
commit
da8cc166ce
2 changed files with 604 additions and 107 deletions
|
|
@ -5,9 +5,7 @@
|
|||
},
|
||||
"tools": [{ "preset": "echo" }],
|
||||
"resources": [],
|
||||
"skills": {
|
||||
"directoryRead": true
|
||||
},
|
||||
"skills": {},
|
||||
"transport": {
|
||||
"type": "streamable-http",
|
||||
"port": 3230
|
||||
|
|
|
|||
|
|
@ -831,10 +831,11 @@ export function createMcpServer(config: ServerConfig): McpServer {
|
|||
};
|
||||
}
|
||||
|
||||
// Skills extension (SEP-2640): a server-declared extension, advertised with
|
||||
// its one sub-option. `directoryRead` is opt-in per config so a client can be
|
||||
// exercised against both a server that offers `resources/directory/read` and
|
||||
// one that does not.
|
||||
// Skills extension (SEP-2640): a server-declared extension. `directoryRead`
|
||||
// is opt-in per config and stays OFF in `skills-http.json` until the fixture
|
||||
// actually serves `resources/directory/read` — advertising a sub-option this
|
||||
// server would answer `-32601` for would make Connection Info report
|
||||
// "Supported" for a method that is not (phase 3, #2248).
|
||||
if (config.skills) {
|
||||
capabilities.extensions = {
|
||||
...(capabilities.extensions ?? {}),
|
||||
|
|
|
|||
Loading…
Reference in a new issue