Skip to content

fix(server): stamp resultType on the hand-written stateless tool results - #563

Open
lnuxe wants to merge 2 commits into
modelcontextprotocol:mainfrom
lnuxe:fix/streamed-stateless-result-type
Open

lnuxe wants to merge 2 commits into
modelcontextprotocol:mainfrom
lnuxe:fix/streamed-stateless-result-type

Conversation

@lnuxe

@lnuxe lnuxe commented Oct 16, 2026

Copy link
Copy Markdown

Fixes #562.

Problem

test_logging_tool and test_streaming_elicitation are the two stateless
tools/call branches that write their response by hand: each emits a
notification line first, so it cannot use res.json(). Writing directly with
res.end(JSON.stringify(...)) also bypassed normalizeStatelessResponse, so
their results came back without resultType:

{ "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "Logging evaluated" } ] } }

resultType is a MUST on results on the 2026-07-28 wire, so the reference
fixture was failing the rule the suite grades, and the dedicated
test_logging_tool branch stayed outside the normalization that #462 applied to
the streamed tool responses.

Change

Both branches now build their payload and pass it through
normalizeStatelessResponse(method, ...) - the same helper the neighbouring
sendStatelessJson dispatch already uses. The notification frames are
untouched.

Evidence

New src/scenarios/server/draft-result-fields.test.ts, the server-direction
counterpart of src/scenarios/client/draft-result-fields.test.ts, before the
fixture fix:

FAIL  src/scenarios/server/draft-result-fields.test.ts > everything-server stateless tools/call results (2026-07-28) > test_logging_tool result carries the draft-required resultType
AssertionError: expected { content: [ { type: 'text', ...(1) } ] } to deeply equal { resultType: 'complete', ...(1) }
-   "resultType": "complete",

Both cases failed the same way; after the fix:

? src/scenarios/server/draft-result-fields.test.ts (2 tests) 1096ms

The test also pins the notification frame of each branch: tools/call with no
logLevel _meta must not log, and the elicitation branch writes its
notifications/progress line unconditionally.

Verification

  • npm test - 49 files, 643 tests passed
  • npm run lint (eslint + prettier --check .) - clean
  • npm run typecheck - clean

I reproduced it against the fixture directly (npx tsx examples/servers/typescript/everything-server.ts, then POST tools/call with
MCP-Protocol-Version: 2026-07-28); no real-SDK run was needed because the
change is confined to the fixture's own response shape, which no check
inspects.

Per AGENTS.md this was prepared with an AI coding agent pointed at this open
issue as the concrete target; I reviewed every change and ran the commands
above myself.

lnuxe added 2 commits October 16, 2026 23:34
The stateless tools/call branches for test_logging_tool and
test_streaming_elicitation write their response by hand: they emit a
notification line before the result, so they cannot go through res.json().
That bypassed normalizeStatelessResponse, so their results omitted
resultType -- a MUST for results on the 2026-07-28 wire. The reference
fixture was therefore failing the rule the suite grades, and any scenario
that validates the fixture's frames against the schema saw a violation.

Route both payloads through the same normalization the neighbouring
sendStatelessJson dispatch already uses.
Adds a server-direction counterpart to
src/scenarios/client/draft-result-fields.test.ts: it pins that the two
hand-written stateless tools/call branches return
resultType: "complete" on the 2026-07-28 wire.

Both cases fail against the unpatched fixture with the missing member and
pass after the previous commit.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test_logging_tool response omits resultType on the 2026-07-28 wire

1 participant