Skip to content

feat(core-internal): expose raw request params through opt-in context - #3038

Open
kesslerio wants to merge 6 commits into
modelcontextprotocol:mainfrom
kesslerio:fm/raw-request-params
Open

kesslerio wants to merge 6 commits into
modelcontextprotocol:mainfrom
kesslerio:fm/raw-request-params

Conversation

@kesslerio

@kesslerio kesslerio commented Oct 15, 2026 •

Copy link
Copy Markdown

Related: #3008

TL;DR

Add includeRawRequestParams, an opt-in context field for applications that need unknown request-param siblings before handler schema parsing strips them. Default behavior stays unchanged.

What Problem This Solves

In both supported protocol eras, loose tools/call fields such as resource_type and record_id disappear before an application's compatibility normalizer can read them.

Why This Change Was Made

ctx.mcpReq.rawParams exposes params after transport parsing and before handler parsing. Applications can validate the extension fields they need while using the parsed request for known fields.

User Impact

The option defaults to false. Raw values remain untrusted and must not be merged over validated fields or treated as CRM attributes.

This is deliberately a post-transport boundary. For example, stdio parsing removes extension from _meta['io.modelcontextprotocol/related-task'] = { taskId: 't', extension: 'sentinel' } before capture. Unknown sibling params survive; nested schema-parsed metadata is not guaranteed unchanged. Broader pre-transport preservation would need a separate design decision.

Evidence

  • From packages/server: ../../node_modules/.bin/vitest run test/server/rawRequestParams.test.ts --reporter=verbose — 22 cases passed across 2025-11-25 and 2026-07-28, including real stdio metadata normalization.
  • Built-SDK stdio verification passed seven scenarios covering default stripping, opt-in siblings, malformed known fields, application validation, task/retry handling, field collisions, and required modern metadata.
  • I separately tested the Attio CLI with a temporary SDK override and explicit consumer adapter: baseline loose calls made zero synthetic upstream attempts; patched calls made exactly one and returned nonretryable uncertainty in both eras. This was offline proof, not repeated by the SDK validation run; no live CRM writes occurred.
  • All 14 GitHub checks passed on c91c25189996e9fa3b19a2e3017f1b2c2d6b7736, including the corrected generated documentation snippet.

@kesslerio
kesslerio requested a review from a team as a code owner October 15, 2026 22:52
@changeset-bot

changeset-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c91c251

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/server Minor
@modelcontextprotocol/client Minor
@modelcontextprotocol/codemod Minor
@modelcontextprotocol/core Minor
@modelcontextprotocol/server-legacy Minor
@modelcontextprotocol/core-internal Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@3038

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@3038

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@3038

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@3038

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@3038

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@3038

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@3038

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@3038

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@3038

commit: c91c251

…r.md using the repository generator. Reproduced the failure before fixing; repository-wide snippet verification and git diff --check now pass. Full check:all was not run because this worktree lacks project dependencies, including tsx
@kesslerio kesslerio changed the title feat: expose original request params through an opt-in context field feat(core-internal): expose raw request params through opt-in context Oct 15, 2026

@koriyoshi2041 koriyoshi2041 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head c91c2518 across both supported protocol revisions. The focused raw-params suite passes 22/22, and core-internal/server typechecks, formatting, and diff hygiene are clean.

The coverage exercises the important opt-in boundary: default/explicit-false omit rawParams, unknown top-level siblings survive separately, malformed known fields still fail identically, stdio metadata normalization is not overstated as raw-wire preservation, and task/input-response lifting remains independent. I found no validation bypass or default-behavior regression in the changed path.

@claude claude Bot added the v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes label Oct 16, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR. Before we add API for this: does the schema form of setRequestHandler cover your case? It parses params with your own schema, so a loose schema keeps the extra members:

server.setRequestHandler(
    'tools/call',
    { params: z.looseObject({ name: z.string(), arguments: z.record(z.string(), z.unknown()).optional() }) },
    async params => {
        // params.resource_type and params.record_id are here
    }
);

On 2.3.1 over stdio, on 2025-11-25 and on 2026-07-28, the handler gets name, arguments, resource_type and record_id, and a call with no name is still rejected with -32602. The docs describe this form for custom methods only. If it works for you, we can document it for spec methods in place of a new option.


Generated by Claude Code

Copy link
Copy Markdown

I rechecked the v2.3.1 source (tag fcef852) against Felix's schema-registration suggestion. It appears to solve the original sibling-field loss, but the 3-argument overload is not validation-equivalent to the 2-argument spec handler:

  • setRequestHandler: the 2-arg path calls the era codec's validateRequest; the 3-arg path instead calls validateStandardSchema(schemas.params, {...request.params}). It replaces method-specific parsing, rather than augmenting it. The callback also receives params, not the parsed Request.
  • Legacy schema / tools/call schema: on 2025-11-25, task: {ttl: 'invalid'} should fail default spec validation (task.ttl must be a number), yet passes the suggested z.looseObject({name, arguments}) and can reach the handler. On 2026-07-28, the default spec method drops the non-era task sibling, whereas the loose schema retains it. Known-field collision/version drift is therefore an application concern. The protocol's era/envelope checks and transport parsing still run before either handler; this is not evidence of an authentication bypass.

I ran a local, source-faithful model for both eras (16/16 checks; mutation of legacy task validation triggered the expected failure), but could not install/run the published SDK here because the isolated environment cannot reach npm. Thus the differential above is source-supported, not independently SDK-runtime-confirmed.

Smallest next improvement: extend merged docs #3087 to show the 3-arg overload for spec methods with a warning that it replaces spec-method validation, and add one SDK-native 2-era regression covering unknown siblings, malformed name/arguments, legacy task.ttl, metadata normalization, and the modern required envelope. If consumers must retain SDK-owned era-specific validation without duplicating it, #3038 still offers a distinct guarantee (validated spec request plus separately untrusted raw params). No CRM calls or external-system tests were made.

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

v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants