Skip to content

Modern Skills extension responses are rejected after the SDK consumes resultType #2330

Description

@turadg

Which version line?

v2 — current (@modelcontextprotocol/inspector@latest)

Which client?

All / shared core

Inspector version

2.6.0

Node version

v24.21.0

Operating system (and browser, for the web client)

macOS 26.6.2, Chrome 153.0.8010.37

Transport

Streamable HTTP

MCP server under inspection

Server: authenticated production MCP server using the SEP-2640 Skills extension
Protocol era: Modern (2026-07-28)
OAuth: yes

The server declares io.modelcontextprotocol/skills and implements:

  • skills/list
  • skills/get
  • resources/directory/read

Steps to reproduce

  1. Run Inspector 2.6.0.
  2. Configure a Streamable HTTP server that implements protocol version 2026-07-28.
  3. Have the server declare the io.modelcontextprotocol/skills extension.
  4. Return a valid Modern skills/list response containing:
    • resultType: "complete"
    • ttlMs
    • cacheScope
    • skills
  5. Connect with Protocol Era → Modern.
  6. Open the Skills tab.

The same problem can be reproduced with Inspector's own Skills integration fixture after correcting its Modern connection setup:

versionNegotiation: eraToVersionNegotiation(
  modern ? "modern" : "legacy",
)

The existing test instead sets protocolEra on MCPServerConfig. That field does not configure InspectorClient's SDK negotiation, so the test's purported Modern leg actually negotiates Legacy.

Expected behavior

The SDK should validate the raw Modern resultType, and Inspector should validate the decoded extension result.

A conforming response should populate the Skills tab.

Actual behavior

Inspector rejects the response:

Invalid result for skills/list: resultType: Invalid input: expected "complete"

Equivalent failures occur for the other Skills extension methods:

Invalid result for skills/get: resultType: Invalid input: expected "complete"

Invalid result for resources/directory/read: resultType: Invalid input: expected "complete"

Logs, errors, or screenshots

Diagnosis

The server's raw JSON-RPC response does contain the required discriminator:

{
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "resultType": "complete",
    "ttlMs": 0,
    "cacheScope": "private",
    "skills": [],
    "nextCursor": "..."
  }
}

The TypeScript SDK 2.0.0 Modern codec:

  1. Validates resultType on the raw response.
  2. Rejects a missing or unsupported discriminator.
  3. Copies the result and deletes resultType.
  4. Passes that decoded result to Inspector's supplied schema.

Inspector's extension schemas then require the already-consumed field again:

export const ModernListSkillsResultSchema =
  ListSkillsResultSchema.extend({
    resultType: z.literal("complete"),
    ttlMs: z.int().min(0),
    cacheScope: z.enum(["public", "private"]),
  });

Consequently, no valid Modern response can satisfy both layers: omitting resultType fails SDK decoding, while including it succeeds in SDK decoding and then fails Inspector's post-decoding schema.

Existing-test gap

inspectorClient-skills.test.ts intends to exercise both eras, but its connection helper puts protocolEra in the server configuration instead of passing versionNegotiation to the InspectorClient options.

After fixing that setup without changing the production schemas, the real-transport suite produces eight Modern failures covering:

  • skills/list
  • managed Skills pagination
  • skills/get
  • resources/directory/read
  • whole-catalog verification

All fail with the corresponding resultType validation error.

Already prototyped a fix?

Suggested fix

Keep resultType mandatory on the wire, with validation owned by the SDK's Modern codec.

Do not require the wire-only discriminator again in Inspector's schemas, because those schemas receive the decoded representation:

export const ModernListSkillsResultSchema =
  ListSkillsResultSchema.extend({
    ttlMs: z.int().min(0),
    cacheScope: z.enum(["public", "private"]),
  });

export const ModernGetSkillEnvelopeSchema =
  GetSkillEnvelopeSchema;

export const ModernDirectoryReadResultSchema =
  DirectoryReadResultSchema;

This preserves the intentional behavior:

  • The SDK still strictly requires resultType: "complete" on Modern wire responses.
  • Inspector still requires ttlMs and cacheScope for Modern skills/list.
  • skills/get and resources/directory/read retain their SEP-2640 caching semantics.

The integration test should also pass the intended negotiation mode explicitly and assert the negotiated era:

const connected = new InspectorClient(
  { type: "streamable-http", url },
  {
    environment: { transport: createTransportNode },
    versionNegotiation: eraToVersionNegotiation(
      modern ? "modern" : "legacy",
    ),
  },
);

expect(connected.getProtocolEra()).toBe(era);

Prototype validation

Codex produced and reviewed a local prototype at my request. Nothing was pushed or submitted upstream.

Observed validation:

  • Unmodified schemas plus corrected Modern test setup:
    • 8 failures
    • 18 passes
  • Proposed schema fix plus corrected test setup:
    • 26 passes
  • Focused unit tests:
    • 58 passes
  • Full web validation:
    • 344 test files passed
    • 6,459 tests passed
    • formatting, lint, TypeScript build, and Vite build passed

Prompt used for the prototype

Investigate why Inspector 2.6.0 rejects valid Modern SEP-2640 responses with Invalid result for skills/list: resultType: Invalid input. Preserve the intentional requirement that Modern wire responses contain resultType: "complete". Trace the TypeScript SDK 2.0.0 decoding pipeline before changing the Inspector schemas. Verify whether the SDK validates and consumes resultType before applying caller-provided schemas. Audit skills/list, skills/get, and resources/directory/read for the same layering problem. Correct the Skills real-transport integration test so it genuinely negotiates both Legacy and Modern, assert the negotiated era, reproduce the failure on unmodified production schemas, implement the smallest fix at the correct layer, and run the focused and full web validation suites. Do not push or open a pull request.

Before you submit

  • I searched existing issues and this is not a duplicate.
  • This is not a security vulnerability report (those go through the private advisory process).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions