[GH-ISSUE #1167] [Bug] binary_session reports null client identity and no client features #411

Closed
opened 2026-10-15 18:24:50 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 9, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1167

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

MCP or agent integration

Observed behavior

After a normal stdio initialize handshake in which the client sends clientInfo, protocol version 2025-11-25, and roots, sampling, and elicitation capabilities, binary_session returns server_identity.client: null, server_identity.negotiated_protocol_version: null, and every client_features flag false. The server's own initialize response negotiated 2025-11-25, so the session report contradicts the handshake it just completed. Agents cannot use these documented fields to learn which client features are available.

The initialize response is {"protocolVersion":"2025-11-25","serverInfo":{"name":"rea","version":"6.1.0"}, ...}, but binary_session returns:

{"client":null,"negotiated_protocol_version":null,"client_features":{"elicitation_form":false,"elicitation_url":false,"roots":false,"sampling":false}}

Reproduced twice with the SDK client and once with wire logging on the npm release, and on the main build with both the SDK client and the raw JSON-RPC client. Because the raw client uses no MCP SDK, the client-side SDK is not the cause. No current tool was observed to fail because of the wrong flags; the observed defect is the incorrect session report.

Reproduction

With the SDK client:

mkdir rea-identity && cd rea-identity
npm install @modelcontextprotocol/client@2.3.1
cat > identity.mjs <<'EOF'
import {Client} from '@modelcontextprotocol/client';
import {StdioClientTransport} from '@modelcontextprotocol/client/stdio';
const c=new Client({name:'identity-demo',version:'1.0'},{capabilities:{roots:{listChanged:true},sampling:{},elicitation:{form:{},url:{}}}});
await c.connect(new StdioClientTransport({command:'npx',args:['-y','rea-agents@6.1.0','mcp'],env:{...process.env}}));
const s=(await c.callTool({name:'binary_session',arguments:{}})).structuredContent.result;
console.log(JSON.stringify({client:s.server_identity.client,negotiated_protocol_version:s.server_identity.negotiated_protocol_version,client_features:s.client_features},null,2));
await c.close();
EOF
node identity.mjs

Without an SDK, send these three newline-delimited frames to npx -y rea-agents@6.1.0 mcp on stdin and read stdout:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{"roots":{"listChanged":true},"sampling":{},"elicitation":{"form":{},"url":{}}},"clientInfo":{"name":"raw-demo","version":"1.0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"binary_session","arguments":{}}}

Expected behavior

docs/mcp-contracts.md: "binary_session reports the active package, server, SDK, and negotiated protocol details", and tool availability is scoped to the "negotiated client capabilities". The tool's output schema exposes server_identity.client, server_identity.negotiated_protocol_version, and client_features. For the handshake above, client should identify identity-demo 1.0 (or raw-demo 1.0), negotiated_protocol_version should be 2025-11-25, and elicitation_form, elicitation_url, roots, and sampling should be true.

Impact and scope

A documented session-diagnostic surface returns false information about the negotiated client and its features on every stdio session tested; no tool execution failure caused by it was established.

Workaround: No REA tool or field returns the negotiated client identity or features correctly; the client only knows what it sent itself, not what REA recognized.

REA version

rea-agents@6.1.0 from npm, and main at 089bbf58f3 built locally with npm ci && npm run build:cached, started as rea mcp over stdio; the server reports MCP server SDK 2.3.1.

Runtime and platform

Node v24.21.0, macOS 27.0.1 (26A434), arm64.; Clients: @modelcontextprotocol/client 2.3.1, and a hand-written newline-delimited JSON-RPC client with no MCP SDK.

Target and artifact context

Not applicable (session metadata)

Interface

MCP

Earliest failure stage

Transport or result projection

Relevant output or evidence

  • Expected source: docs/mcp-contracts.md, the binary_session identity and discovery paragraphs (unchanged on the main build), and the tool's output schema.
  • Failure source: the initialize response and binary_session result quoted under Observed behavior.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Typed outcome or recovery expected

Failing now: after the handshake above, binary_session returns client: null, negotiated_protocol_version: null, and four false feature flags. Restored: the same handshake yields the client's name and version, 2025-11-25, and true for each advertised capability. Control: a client that initializes with empty capabilities still gets all four flags false, so the fix must reflect the handshake rather than report every feature as present.

Analysis or suspected cause

Source-derived, not verified by a patch: src/server/mcpClientMetadata.ts reads identity, protocol, and features from the current request envelope (context.mcpReq.envelope); the installed server SDK 2.3.1 keeps 2025-era initialize values in separate initialize-scoped state. This is a localization lead from reading the code, not a verified cause.

Additional context

Found while exercising typical REA workflows on macOS with rea-agents@6.1.0; each case was reproduced on the npm release and on a local build of main, and re-checked on main at 6a1e558db3 just before filing.

Originally created by @coygeek on GitHub (Oct 9, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1167 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area MCP or agent integration ### Observed behavior After a normal stdio `initialize` handshake in which the client sends `clientInfo`, protocol version `2025-11-25`, and roots, sampling, and elicitation capabilities, `binary_session` returns `server_identity.client: null`, `server_identity.negotiated_protocol_version: null`, and every `client_features` flag `false`. The server's own initialize response negotiated `2025-11-25`, so the session report contradicts the handshake it just completed. Agents cannot use these documented fields to learn which client features are available. The initialize response is `{"protocolVersion":"2025-11-25","serverInfo":{"name":"rea","version":"6.1.0"}, ...}`, but `binary_session` returns: ```json {"client":null,"negotiated_protocol_version":null,"client_features":{"elicitation_form":false,"elicitation_url":false,"roots":false,"sampling":false}} ``` Reproduced twice with the SDK client and once with wire logging on the npm release, and on the `main` build with both the SDK client and the raw JSON-RPC client. Because the raw client uses no MCP SDK, the client-side SDK is not the cause. No current tool was observed to fail because of the wrong flags; the observed defect is the incorrect session report. ### Reproduction With the SDK client: ```sh mkdir rea-identity && cd rea-identity npm install @modelcontextprotocol/client@2.3.1 cat > identity.mjs <<'EOF' import {Client} from '@modelcontextprotocol/client'; import {StdioClientTransport} from '@modelcontextprotocol/client/stdio'; const c=new Client({name:'identity-demo',version:'1.0'},{capabilities:{roots:{listChanged:true},sampling:{},elicitation:{form:{},url:{}}}}); await c.connect(new StdioClientTransport({command:'npx',args:['-y','rea-agents@6.1.0','mcp'],env:{...process.env}})); const s=(await c.callTool({name:'binary_session',arguments:{}})).structuredContent.result; console.log(JSON.stringify({client:s.server_identity.client,negotiated_protocol_version:s.server_identity.negotiated_protocol_version,client_features:s.client_features},null,2)); await c.close(); EOF node identity.mjs ``` Without an SDK, send these three newline-delimited frames to `npx -y rea-agents@6.1.0 mcp` on stdin and read stdout: ```json {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{"roots":{"listChanged":true},"sampling":{},"elicitation":{"form":{},"url":{}}},"clientInfo":{"name":"raw-demo","version":"1.0"}}} {"jsonrpc":"2.0","method":"notifications/initialized"} {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"binary_session","arguments":{}}} ``` ### Expected behavior `docs/mcp-contracts.md`: "`binary_session` reports the active package, server, SDK, and negotiated protocol details", and tool availability is scoped to the "negotiated client capabilities". The tool's output schema exposes `server_identity.client`, `server_identity.negotiated_protocol_version`, and `client_features`. For the handshake above, `client` should identify `identity-demo` 1.0 (or `raw-demo` 1.0), `negotiated_protocol_version` should be `2025-11-25`, and `elicitation_form`, `elicitation_url`, `roots`, and `sampling` should be `true`. ### Impact and scope A documented session-diagnostic surface returns false information about the negotiated client and its features on every stdio session tested; no tool execution failure caused by it was established. Workaround: No REA tool or field returns the negotiated client identity or features correctly; the client only knows what it sent itself, not what REA recognized. ### REA version `rea-agents@6.1.0` from npm, and `main` at 089bbf58f3561e3f465c544bbcf8c307287b1256 built locally with `npm ci && npm run build:cached`, started as `rea mcp` over stdio; the server reports MCP server SDK 2.3.1. ### Runtime and platform Node v24.21.0, macOS 27.0.1 (26A434), arm64.; Clients: `@modelcontextprotocol/client` 2.3.1, and a hand-written newline-delimited JSON-RPC client with no MCP SDK. ### Target and artifact context Not applicable (session metadata) ### Interface MCP ### Earliest failure stage Transport or result projection ### Relevant output or evidence - Expected source: `docs/mcp-contracts.md`, the `binary_session` identity and discovery paragraphs (unchanged on the `main` build), and the tool's output schema. - Failure source: the initialize response and `binary_session` result quoted under Observed behavior. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete ### Typed outcome or recovery expected Failing now: after the handshake above, `binary_session` returns `client: null`, `negotiated_protocol_version: null`, and four `false` feature flags. Restored: the same handshake yields the client's name and version, `2025-11-25`, and `true` for each advertised capability. Control: a client that initializes with empty `capabilities` still gets all four flags `false`, so the fix must reflect the handshake rather than report every feature as present. ### Analysis or suspected cause Source-derived, not verified by a patch: `src/server/mcpClientMetadata.ts` reads identity, protocol, and features from the current request envelope (`context.mcpReq.envelope`); the installed server SDK 2.3.1 keeps 2025-era initialize values in separate initialize-scoped state. This is a localization lead from reading the code, not a verified cause. ### Additional context Found while exercising typical REA workflows on macOS with `rea-agents@6.1.0`; each case was reproduced on the npm release and on a local build of `main`, and re-checked on `main` at 6a1e558db3ec2b69561e91ea817f1c2065dfc676 just before filing.
bsakaguchi 2026-10-15 18:24:50 +00:00
Author
Owner

@coygeek commented on GitHub (Oct 9, 2026):

Follow-up: the suspected cause in the report is now runtime-confirmed.

Tested on main at b34bdcdb28 with @modelcontextprotocol/server 2.3.1 (Node v24.21.0, macOS arm64), using a raw newline-delimited JSON-RPC stdio client with no MCP SDK. The client sends initialize with protocol 2025-11-25, clientInfo {"name":"raw-demo","version":"1.0"}, and roots/sampling/elicitation capabilities, then notifications/initialized, then tools/call binary_session.

  • Unpatched: client: null, negotiated_protocol_version: null, all four client_features false.
  • With a minimal local experiment (not proposed as the fix) that makes mcpClientMetadata fall back to the SDK's public getClientVersion(), getNegotiatedProtocolVersion(), and getClientCapabilities() only when context.mcpReq.envelope is absent: client is raw-demo 1.0, negotiated_protocol_version is 2025-11-25, and all four flags are true.
  • Control: the same client initializing with empty capabilities still gets four false flags with the experiment.
  • 2026-era path: server/discover plus tools/call carrying the per-request _meta keys for protocol 2026-07-28 returned correct fields both before and after the experiment, and an envelope-precedence probe kept the request's client info over the initialize values.
-export const mcpClientMetadata = (context: {
-  readonly mcpReq: Pick<ServerContext["mcpReq"], "envelope">;
-}) => {
-  const envelope = context.mcpReq.envelope;
+export const mcpClientMetadata = (
+  context: { readonly mcpReq: Pick<ServerContext["mcpReq"], "envelope"> },
+  server: McpServer["server"],
+) => {
+  const envelope = context.mcpReq.envelope ?? {
+    [CLIENT_INFO_META_KEY]: server.getClientVersion(),
+    [PROTOCOL_VERSION_META_KEY]: server.getNegotiatedProtocolVersion(),
+    [CLIENT_CAPABILITIES_META_KEY]: server.getClientCapabilities(),
+  };

(plus passing server.server from registerSessionStatusTool.ts).

The SDK keeps these initialize-scoped values for 2025-era connections; its 2.3.1 server changelog says "Behavior on 2025-era connections is unchanged; the accessors remain functional." No matching SDK defect was found in its issues, PRs, or changelog, so the defect appears to be REA's envelope-only lookup for 2025-era clients rather than lost metadata in the SDK.

Focused tests (tests/boundary/mcp/clientCapabilities.test.ts, serverIdentity.test.ts, sessionMcp.test.ts, tests/composition/analysis-sessions/binarySession.test.ts): 44/44 pass on unpatched main; with the experiment 43/44 pass, the one failure being the assertion in serverIdentity.test.ts ("exposes live identity, a stable catalog, and changing availability") that expects client: null, which encodes the behavior reported here.

<!-- gh-comment-id:6073221832 --> @coygeek commented on GitHub (Oct 9, 2026): Follow-up: the suspected cause in the report is now runtime-confirmed. Tested on `main` at b34bdcdb2877be394c59c9cbf70269dc1841ff78 with `@modelcontextprotocol/server` 2.3.1 (Node v24.21.0, macOS arm64), using a raw newline-delimited JSON-RPC stdio client with no MCP SDK. The client sends `initialize` with protocol `2025-11-25`, `clientInfo` `{"name":"raw-demo","version":"1.0"}`, and roots/sampling/elicitation capabilities, then `notifications/initialized`, then `tools/call binary_session`. - Unpatched: `client: null`, `negotiated_protocol_version: null`, all four `client_features` false. - With a minimal local experiment (not proposed as the fix) that makes `mcpClientMetadata` fall back to the SDK's public `getClientVersion()`, `getNegotiatedProtocolVersion()`, and `getClientCapabilities()` only when `context.mcpReq.envelope` is absent: `client` is `raw-demo` 1.0, `negotiated_protocol_version` is `2025-11-25`, and all four flags are true. - Control: the same client initializing with empty `capabilities` still gets four false flags with the experiment. - 2026-era path: `server/discover` plus `tools/call` carrying the per-request `_meta` keys for protocol `2026-07-28` returned correct fields both before and after the experiment, and an envelope-precedence probe kept the request's client info over the initialize values. ```diff -export const mcpClientMetadata = (context: { - readonly mcpReq: Pick<ServerContext["mcpReq"], "envelope">; -}) => { - const envelope = context.mcpReq.envelope; +export const mcpClientMetadata = ( + context: { readonly mcpReq: Pick<ServerContext["mcpReq"], "envelope"> }, + server: McpServer["server"], +) => { + const envelope = context.mcpReq.envelope ?? { + [CLIENT_INFO_META_KEY]: server.getClientVersion(), + [PROTOCOL_VERSION_META_KEY]: server.getNegotiatedProtocolVersion(), + [CLIENT_CAPABILITIES_META_KEY]: server.getClientCapabilities(), + }; ``` (plus passing `server.server` from `registerSessionStatusTool.ts`). The SDK keeps these initialize-scoped values for 2025-era connections; its 2.3.1 server changelog says "Behavior on 2025-era connections is unchanged; the accessors remain functional." No matching SDK defect was found in its issues, PRs, or changelog, so the defect appears to be REA's envelope-only lookup for 2025-era clients rather than lost metadata in the SDK. Focused tests (`tests/boundary/mcp/clientCapabilities.test.ts`, `serverIdentity.test.ts`, `sessionMcp.test.ts`, `tests/composition/analysis-sessions/binarySession.test.ts`): 44/44 pass on unpatched `main`; with the experiment 43/44 pass, the one failure being the assertion in `serverIdentity.test.ts` ("exposes live identity, a stable catalog, and changing availability") that expects `client: null`, which encodes the behavior reported here.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#411
No description provided.