feat(skills): address Copilot review round 16 on #2234

Round 16 reported no inline comments and three suppressed findings, all
valid:

- `getSkillsExtension` treated a non-object extension value (`false`,
  `"skills"`) as a declaration. SEP-2133 declares an extension as an
  object of sub-options, so a primitive is not one — now rejected,
  matching `appElicitation.ts`.
- The `onGetSkill` prop doc claimed the two responses "must agree" and
  that a difference means a broken server, contradicting the
  implementation, which reports a difference as a finding rather than a
  fault. Reworded to what the code does.
- No real-transport test exercised the fixture's `skills/*` handlers.
  Added `src/test/integration/mcp/inspectorClient-skills.test.ts`,
  parameterized over both protocol eras: capability advertisement, the
  paged `skills/list` walk (direct and through `ManagedSkillsState`),
  `skills/get`, the `-32602` for an unknown URI, `resources/read` of a
  skill file, and delegation of an ordinary resource through the
  fixture's `resources/read` wrapper.

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:
cliffhall 2026-09-05 02:42:47 -04:00
commit 1ee958091e

View file

@ -109,9 +109,13 @@ export function getSkillsExtension(
capabilities: ServerCapabilities | undefined,
): SkillsExtensionSupport | undefined {
const declared = capabilities?.extensions?.[SKILLS_EXTENSION_KEY];
if (declared === undefined || declared === null) return undefined;
// Must be an OBJECT. SEP-2133 declares an extension as an object of
// sub-options, so a primitive (`false`, `"skills"`) is not a declaration —
// and treating one as support would show the Skills tab and send
// `skills/list` to a server that never claimed to serve it. Matches how
// `appElicitation.ts` parses the UI extension.
if (typeof declared !== "object" || declared === null) return undefined;
const directoryRead =
typeof declared === "object" &&
(declared as { directoryRead?: unknown }).directoryRead === true;
return { directoryRead };
}