Skip to content

SEP: Internationalization via Per-Request Language Negotiation#2792

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

SEP: Internationalization via Per-Request Language Negotiation#2792
SamMorrowDrums wants to merge 7 commits into
modelcontextprotocol:mainfrom
SamMorrowDrums:sammorrowdrums/sep-i18n-language-negotiation

Conversation

@SamMorrowDrums
Copy link
Copy Markdown
Contributor

Summary

Draft SEP proposing a transport-agnostic i18n mechanism for MCP:

  • _meta['io.modelcontextprotocol/acceptLanguage'] on every request — value matches the HTTP Accept-Language field syntax verbatim (BCP 47 ranges + quality values).
  • _meta['io.modelcontextprotocol/contentLanguage'] on every response — server echoes the language actually used.
  • On Streamable HTTP, both fields are mirrored into the standard Accept-Language / Content-Language HTTP headers, with the same strict mismatch rule established by SEP-2243.
  • Per-request scope (not handshake-bound) so users can switch language mid-conversation — aligned with SEP-2575 (stateless-by-default).

Motivation

This SEP converts the docs-only proposal in #2355 into a cross-transport SEP, directly addressing the reviewer feedback there:

  • @pja-ant: a header-only solution leaves stdio without an i18n story → resolved by defining the field in _meta first, with HTTP headers as a mirror.
  • @kurtisvg: "minimize the gaps between transports … put it in _meta and mirror to the header like we're doing for some other values" → exactly the model adopted here.

Motivating precedents

  • SEP-2243 (HTTP Header Standardization) — established the pattern of mirroring JSON-RPC payload fields into HTTP headers with a strict consistency requirement. This SEP applies the same pattern to language negotiation.
  • SEP-2575 (Make MCP Stateless) — mandates transport consistency between stdio and HTTP and removes the initialize handshake as a place to negotiate persistent state. Per-request language preference is the natural fit.
  • SEP-414 (request.params._meta) — establishes _meta as the conventional carrier for per-request metadata.
  • SEP-2133 (Extensions) — provides the io.modelcontextprotocol/ vendor prefix used for the field names.

Relationship to SEP-1809

SEP-1809 (SCCP) proposes a clientContext object on tools/call that includes locale. This SEP proposes to subsume the language aspect of SEP-1809 in favor of the cross-cutting acceptLanguage defined here, leaving timezone, currentTimestamp, and userLocation to SEP-1809. Will coordinate with the author.

Supersedes

This SEP supersedes (and proposes closing) #2355.

Status

  • Type: Standards Track
  • Status: Draft (seeking sponsor — tagging @kurtisvg and @pja-ant given the prior thread)
  • File is currently named seps/0000-i18n-language-negotiation.md; will be renamed to use the PR number and rendered docs regenerated (npm run prep) once the number is known.

Checklist

  • SEP file added to seps/
  • Rename file to {PR-number}-i18n-language-negotiation.md and regenerate docs after PR number is assigned
  • Reference implementation (planned: TypeScript SDK server + stdio integration test)
  • Sponsor assigned

AI Disclosure

This PR was authored with assistance from GitHub Copilot CLI.

SamMorrowDrums and others added 2 commits May 26, 2026 22:31
Adds a draft SEP proposing a transport-agnostic i18n mechanism using
_meta['io.modelcontextprotocol/acceptLanguage'] on every request and
_meta['io.modelcontextprotocol/contentLanguage'] on every response, mirrored
into HTTP Accept-Language / Content-Language headers on the Streamable HTTP
transport.

Converts the docs-only approach from modelcontextprotocol#2355 into a cross-transport SEP,
addressing reviewer feedback (@pja-ant, @kurtisvg) and citing SEP-2243
(header/payload mirroring) and SEP-2575 (stateless-by-default) as
motivating precedents. Proposes subsuming the locale aspect of SEP-1809.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SamMorrowDrums and others added 5 commits May 26, 2026 22:38
…ing scope

- Expand the motivation to explicitly enumerate the existing standards,
  libraries, infrastructure, and translation tooling MCP gets for free by
  adopting Accept-Language verbatim. 'No reinventing the wheel' is now
  the lead message of the SEP.
- Add an explicit Scope subsection clarifying that only user-facing
  content is expected to be translated (title, description, text content,
  human-readable error messages, log notifications), with a normative
  rule that servers MAY translate any/all content in the selected locale
  but MUST NOT translate identifiers, tool names, URIs, schema field
  names, enum tokens, or any value whose semantics depend on the literal
  string.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…f scope

The bulk of payload content from tools/call, resources/read, and prompts/get
is consumed by the model rather than rendered directly to the user. Whether
a server localizes it is a product decision the protocol does not speak to.
The SEP now scopes negotiation to genuinely user-facing fields (titles,
descriptions, UI-bound errors and notifications) and stays silent on
model-facing payload translation rather than blessing it as a MAY.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Servers SHOULD translate user-facing strings (titles, descriptions, UI
errors, notifications) and MAY also translate body content returned from
tools/call, resources/read, and prompts/get. The latter is primarily
model-facing but is often valuable to localize (verbatim quoting, legal
text, dates, currency, units); the choice is the server's.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Lead the Specification with an explicit note that clients MAY send the
field and servers MAY ignore it entirely, no capability advertisement
required. Soften the Scope language so participating servers are not
implicitly required to localize any specific set of fields; the SEP just
defines the negotiation primitive and what is in scope IF a server opts in.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

1 participant