Client drops `_meta` from `input_required` results when `allowInputRequired: true` (2026-07-28)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 90/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- api
Research direction
Read decodeResult in core-internal/src/wire/rev2026-07-28/codec.ts and manualInputRequiredValue in core-internal/src/shared/inputRequiredEngine.ts, starting with the input_required branches. Preserve the server's result-level _meta through both transformations, then verify the provided real-HTTP reproduction returns the metadata from client.callTool.
Written by the indexing model from the issue text.
Description
Describe the bug
With protocol revision 2026-07-28, a client that calls a tool with { allowInputRequired: true } gets an InputRequiredResult that has no _meta, even when the server's result includes _meta.
InputRequiredResult is a Result, so _meta is valid on it, and the SDK's own InputRequiredResultSchema (wireResult(...)) includes _meta. But the manual input_required path rebuilds the result from only inputRequests and requestState, so the field is lost. A server cannot send result-level metadata (for example display hints for the inputRequests) to a client that fulfils rounds by itself.
Where it happens
The field is dropped in two places:
core-internal/src/wire/rev2026-07-28/codec.ts, indecodeResult, in theinput_requiredbranch:return { kind: 'input_required', inputRequests, ...(typeof requestState === 'string' && { requestState }) };core-internal/src/shared/inputRequiredEngine.ts, inmanualInputRequiredValue:return { resultType: 'input_required', inputRequests: decoded.inputRequests, ...(decoded.requestState !== undefined && { requestState: decoded.requestState }) };
The transport's JSONRPCMessageSchema.parse keeps result._meta. The field is lost only in these two steps.
To reproduce
Server: return this from a tools/call handler:
{
"resultType": "input_required",
"requestState": "state-1",
"inputRequests": {
"approval": {
"method": "elicitation/create",
"params": { "mode": "form", "message": "Allow?", "requestedSchema": { "type": "object", "properties": {} } }
}
},
"_meta": { "example/hint": { "tools": ["a"] } }
}
Client:
const client = new Client(
{ name: "repro", version: "0.0.0" },
{
capabilities: { elicitation: { form: {} } },
supportedProtocolVersions: ["2026-07-28"],
versionNegotiation: { mode: { pin: "2026-07-28" } },
inputRequired: { autoFulfill: false },
},
);
await client.connect(new StreamableHTTPClientTransport(new URL(url)));
const result = await client.callTool({ name: "tool", arguments: {} }, { allowInputRequired: true });
console.log(result._meta); // undefined
Expected behavior
result._meta equals the _meta that the server sent, the same as for a complete result.
Actual behavior
result._meta is undefined.
Suggested fix
Carry _meta through both steps:
// codec.ts, input_required branch
const meta = raw['_meta'];
return {
kind: 'input_required',
inputRequests,
...(typeof requestState === 'string' && { requestState }),
...(isPlainObject(meta) && { _meta: meta })
};
// inputRequiredEngine.ts
return {
resultType: 'input_required',
inputRequests: decoded.inputRequests as InputRequiredResult['inputRequests'],
...(decoded.requestState !== undefined && { requestState: decoded.requestState }),
...(decoded._meta !== undefined && { _meta: decoded._meta })
};
We use this change as a local pnpm patch on @modelcontextprotocol/[email protected], and a real-HTTP test confirms that _meta reaches the caller.
Versions
@modelcontextprotocol/client2.0.0 (the same code is in 2.1.0)- Protocol revision
2026-07-28 - Node.js 22.19.0
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 5d 16h
- Merged PRs (30d)
- 5
Getting set up
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from modelcontextprotocol/typescript-sdk
-
v1 v2
Difficulty 1/5 Under an hour Newbie friendliness 90/100
modelcontextprotocol/typescript-sdk#2867 ·
Maintainers usually reply within 1 day
-
v1 v2
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
modelcontextprotocol/typescript-sdk#2854 · 1 comment ·
Maintainers usually reply within 1 day
-
v1 v2
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
modelcontextprotocol/typescript-sdk#2843 · 1 comment ·
Maintainers usually reply within 1 day
-
Auth metadata discovery: fallback URL built on resource host instead of authorization-server hostOpenv1 v2
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
modelcontextprotocol/typescript-sdk#2784 ·
Maintainers usually reply within 1 day
-
v1 v2
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
modelcontextprotocol/typescript-sdk#2783 · 1 comment ·
Maintainers usually reply within 1 day
All issues in modelcontextprotocol/typescript-sdk
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
diegosouzapw/OmniRoute#14869 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 94/100
Maintainers usually reply within 1 day
-
status: waiting triage
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
freeCodeCamp/freeCodeCamp#70412 ·
Maintainers usually reply within 1 day
-
Mend: dependency security vulnerability untriaged
Difficulty 1/5 Under an hour Newbie friendliness 88/100
opensearch-project/OpenSearch-Dashboards#12816 ·
Maintainers usually reply within 1 day