Skip to content

Proposal: i18n via per-request language negotiation#42

Open
SamMorrowDrums wants to merge 7 commits into
modelcontextprotocol:mainfrom
SamMorrowDrums:sammorrowdrums/i18n-language-negotiation
Open

Proposal: i18n via per-request language negotiation#42
SamMorrowDrums wants to merge 7 commits into
modelcontextprotocol:mainfrom
SamMorrowDrums:sammorrowdrums/i18n-language-negotiation

Conversation

@SamMorrowDrums

@SamMorrowDrums SamMorrowDrums commented Jun 17, 2026

Copy link
Copy Markdown

Bringing this to the Transports WG for review and comment before bringing to the core maintainers.

Upstream SEP: modelcontextprotocol/modelcontextprotocol#2792 (now draft, pending WG input).

Reference implementation: modelcontextprotocol/typescript-sdk#2158 — full client + server, stdio and Streamable HTTP, byte-equality enforcement, 11-case integration matrix.

What this proposes

A single, transport-agnostic way for clients to express language preference per request, and for servers to indicate the language used per response:

  • params._meta['io.modelcontextprotocol/acceptLanguage'] on requests
  • result._meta['io.modelcontextprotocol/contentLanguage'] (and error.data._meta[...]) on responses
  • Values are RFC 9110 Accept-Language / Content-Language strings (BCP 47 language-range lists with quality values), so no new grammar.

Why this is a transports concern

For Streamable HTTP, the proposal mirrors _meta into the standard Accept-Language / Content-Language HTTP headers:

  • Following the precedent set by SEP-2243 for Mcp-Method / Mcp-Name (RPC content surfaced as headers for infra observability and routing).
  • With strict byte-equality between header and _meta when both are present, rejected via a new HeaderMismatch JSON-RPC error (provisional -32005, HTTP 400). Header absence is tolerated (CDNs strip Accept-Language by default); _meta is the authoritative carrier.
  • Vary: Accept-Language is MUST on cacheable responses whose body depends on negotiated language.
  • SSE responses MAY omit the response header (headers flushed before body is known); _meta is still authoritative.

For stdio: same _meta fields, no transport changes.

Why per-request, not session

Aligns with the recent stateless-by-default direction: language can change mid-conversation (user retries in another language, agent serves multiple end-users) without renegotiation. There is no initialize-time language state.

Mirrors SEP-2792 (modelcontextprotocol#2792) into the Transports WG
proposals/ directory for review of the transport-touching aspects:
Streamable HTTP Accept-Language / Content-Language mirroring, byte-equality
enforcement, Vary semantics, and per-request statelessness.

Co-authored-by: Copilot <[email protected]>
Comment thread proposals/XXXX-i18n-language-negotiation.md Outdated
Comment thread proposals/XXXX-i18n-language-negotiation.md Outdated
Copilot AI added 3 commits June 25, 2026 18:46
Mirrors changes from modelcontextprotocol#2792. Addresses @kurtisvg
review comments on this PR:

- Schema-property descriptions: explicit MAY with a caveat that
  translation alters what the model sees, with a concrete Codex
  reference instead of handwaving.
- Open-ended "any other displayed field" bullet replaced with an
  enumerated MUST/MAY/MUST NOT list anchored to the spec's own
  classification of each field.

Co-authored-by: Copilot <[email protected]>
- Resolve duplicate MUST-NOT-translate list (Scope is canonical).
- Fix broken Scope anchor.
- Resolve notifications open question into the Specification.
- Make error.message localization choice explicit.
- Update SEP-2243/2575 link targets to merged seps/ files.
- Trim Reference Implementation paragraph.

Co-authored-by: Copilot <[email protected]>
Drop the parallel localizedMessage field in favor of translating
error.message directly. error.code remains the machine-interpreted
identifier; error.message is the display string.

Co-authored-by: Copilot <[email protected]>

@kurtisvg kurtisvg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Overall, supportive of voting on this to bring to the CMs.

Comment thread proposals/XXXX-i18n-language-negotiation.md Outdated
Comment thread proposals/XXXX-i18n-language-negotiation.md Outdated
Comment thread proposals/XXXX-i18n-language-negotiation.md Outdated
Comment thread proposals/XXXX-i18n-language-negotiation.md Outdated
SamMorrowDrums and others added 3 commits July 21, 2026 13:27
Add concrete public-sector and mixed-language examples, make UI localization a SHOULD, adopt HeaderMismatch -32020, and narrow contentLanguage requirements to negotiated responses. Tighten RFC semantics, header matching, and citations.

Co-authored-by: Copilot App <[email protected]>
Copilot-Session: b3246f61-3161-4e03-88b5-1137fcbd59e9
Co-authored-by: Copilot App <[email protected]>
Copilot-Session: b3246f61-3161-4e03-88b5-1137fcbd59e9
Specify comparison over decoded JSON and parsed HTTP field values, including repeated fields, OWS, normalization, and cache behavior.

Co-authored-by: Copilot App <[email protected]>
Copilot-Session: b3246f61-3161-4e03-88b5-1137fcbd59e9
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.

3 participants