Skip to content

mcp: reject unencoded non-ASCII characters in Mcp-Param headers - #1521

Closed
luiacuaniello wants to merge 1 commit into
modelcontextprotocol:mainfrom
luiacuaniello:fix-mcp-param-invalid-chars
Closed

luiacuaniello wants to merge 1 commit into
modelcontextprotocol:mainfrom
luiacuaniello:fix-mcp-param-invalid-chars

Conversation

@luiacuaniello

Copy link
Copy Markdown
Contributor

Fixes #1520.

validateParamHeaders decodes an Mcp-Param-{Name} value when it contains the =?base64?...?= wrapper, and otherwise compares it directly with the body value. Therefore, with protocol version 2026-07-28, a value with characters outside visible ASCII, sent unencoded, is accepted if it matches the body argument: Mcp-Param-Region: café with "region": "café" runs the tool. The specification requires that such values be Base64-encoded, and that servers reject a recognized Mcp-Param-{Name} header containing invalid characters (Server Behavior for Custom Headers).

The value is now rejected before decoding if it contains any byte other than visible ASCII, space or horizontal tab (RFC 9110, section 5.5), with the same 400 status and -32020 (HeaderMismatch) error as the other header checks. A tab within the value is still accepted: it is a valid character in a field value, and the TypeScript SDK sends it unencoded. Base64-encoded values behave as before.

The conformance check for this requirement, sep-2243-server-reject-invalid-param-chars, only sends invalid Base64, which the server already rejects; this is why it passes on main.

Tests:

  • TestValidateParamHeaders_InvalidCharacters: unencoded non-ASCII characters and an unencoded control character are rejected; a non-ASCII value encoded in Base64 and a value containing a space and a tab are accepted. Without the change, both rejection cases fail.

go test ./..., go test -race on mcp and auth, go vet ./..., staticcheck v0.6.1 and the server conformance suite (0.2.0-alpha.11, both modes as in CI, with the baseline) pass, with the same conformance results as on main.

AI disclosure: I found this and prepared the fix with Claude Code; I reviewed the change and can explain it.

validateParamHeaders compared an Mcp-Param-{Name} value without the
=?base64?...?= wrapper directly with the body value, so a value with
characters outside visible ASCII would pass whenever it matched the
body argument.

The 2026-07-28 specification requires such values to be Base64-encoded,
and servers to reject a recognized Mcp-Param header that contains
invalid characters.

Reject a value containing any byte other than visible ASCII, space or
horizontal tab before decoding it, with the usual header mismatch
error -32020.

Fixes modelcontextprotocol#1520
@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Nov 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and you aren't currently assigned to #1520.

You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned. There's no need to open a new PR either way.

Maintainers: reopen, remove missing-issue-link, or add bypass-issue-check to override.

@github-actions github-actions Bot closed this Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mcp: streamable server accepts unencoded non-ASCII in Mcp-Param headers

1 participant