Skip to content

feat(schema): add optional serverInfo response metadata and make clientInfo optional#3002

Merged
kurtisvg merged 2 commits into
mainfrom
response-serverinfo
Jul 16, 2026
Merged

feat(schema): add optional serverInfo response metadata and make clientInfo optional#3002
kurtisvg merged 2 commits into
mainfrom
response-serverinfo

Conversation

@kurtisvg

@kurtisvg kurtisvg commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Add a ResultMetaObject _meta type with an optional io.modelcontextprotocol/serverInfo field so servers can identify themselves on responses, restoring the server identity that was previously exchanged in the initialize handshake removed by SEP-2575.

Make io.modelcontextprotocol/clientInfo optional on requests to match; protocolVersion and clientCapabilities remain required. Document in the schema and spec docs that both fields are self-reported and unverified, are intended for display, logging, and debugging, and SHOULD NOT be used to change client or server behavior nor be relied on for security decisions.

@kurtisvg
kurtisvg requested a review from a team as a code owner July 2, 2026 17:45
@mintlify

mintlify Bot commented Jul 2, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
mcp-staging 🟢 Ready View Preview Jul 2, 2026, 5:54 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@mintlify

mintlify Bot commented Jul 2, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
mcp 🟢 Ready View Preview Jul 2, 2026, 5:55 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@Angelomirabella

Angelomirabella commented Jul 7, 2026

Copy link
Copy Markdown

Cross-posting from Discord my concern for visibility. I worry that MAY doesn't give MCP server implementers much incentive to actually report their server info.

To recap the parallel drawn in the Discord thread: this field is effectively a user-agent equivalent, and the HTTP RFC uses considerably stronger language for User-Agent (SHOULD), meaning the User-Agent should be sent by default unless explicitly configured otherwise. I think this model could work as well, even though the preferred choice would be to make the serverInfo mandatory as is the clientInfo in the current draft spec. This would allow enforcing visibility much easier on the network side.

@olaservo, @ostefano I would be curious to hear your thoughts on this since you participated on the Discord thread.

Also worth noticing that this PR actually reduces visibility by making clientInfo optional as well, while it was previously required.

@kurtisvg

kurtisvg commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

There are 3 open decisions on this:

1. MAY vs SHOULD vs MUST

In a recent CM meeting we discussed making this info available on every request but optional (similar to how user agents are handled in HTTP today). For context:
prior spec versions required both clientInfo and serverInfo once, at initialize, and the current draft requires clientInfo on every request.

  • MAY — purely informational; implementations opt in
  • SHOULD — recommended default; well-behaved implementations send it, omitting is still conformant
  • MUST — every message carries it; guaranteed identity on every reply (for better or worse)

For comparison, RFC 7231 says:

A user agent SHOULD send a User-Agent header field in each request unless specifically configured not to do so.

From the SDK perspective it looks like it's currently required in most if not all -- so users more or less have to specify it today.

2. Schema requiredness of _meta.io.modelcontextprotocol/serverInfo

JSON Schema required is hard validation, so this is coupled to (1):

  • Optional — the only coherent choice for MAY or SHOULD, but a deviation from prior spec versions, where the field was required
  • Required — only coherent with MUST; also means all schema examples need serverInfo and validators reject responses without it

3. server/discover duplication

  • Duplicate content — serverInfo appears twice (required in the body, optionally in _meta); closest to the shape SEP-2575 merged, at the cost of redundancy
  • Move to _meta-only — drop the body field and add a DiscoverResultMeta that requires serverInfo in _meta (the SubscriptionsListenResultMeta pattern); one canonical location, no duplication

@kurtisvg

kurtisvg commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

The discussion w/ @pja-ant and @nickcoai this morning our preference is the following:

  1. The language should be "SHOULD" -- we can't actually force clients/servers to populate these fields, and this is aligned with the how user-agents work in HTTP today. Most SDKs have this as required input today, so there's still encouragement for it to be included.
  2. Optional in the schema -- if the language is "SHOULD", the schema should reflect that.
  3. Remove duplication -- having the field in two places introduces concerns with it drifting. We will just return it in _meta.

@Angelomirabella

Copy link
Copy Markdown

The discussion w/ @pja-ant and @nickcoai this morning our preference is the following:

  1. The language should be "SHOULD" -- we can't actually force clients/servers to populate these fields, and this is aligned with the how user-agents work in HTTP today. Most SDKs have this as required input today, so there's still encouragement for it to be included.
  2. Optional in the schema -- if the language is "SHOULD", the schema should reflect that.
  3. Remove duplication -- having the field in two places introduces concerns with it drifting. We will just return it in _meta.

I agree with this outcome.
Thank you for taking the time to address this!

@localden localden added the rc-high-priority Related to an upcoming specification release and needs to be addressed with a high priority. label Jul 10, 2026
@kurtisvg
kurtisvg force-pushed the response-serverinfo branch from 9af8954 to 9db5542 Compare July 10, 2026 15:59
@SamMorrowDrums

Copy link
Copy Markdown
Contributor

I am a little dubious, some people do put garbage in initialize, but GitHub get's tremendous value out of the client field, to the extent we literally implemented persistent sessions in Redis with zero SDK support explicitly to persist the initialize payload across associated requests.

There are other things we can correlate (like the App identity of the client from auth perspective), but the app is often not the actual name of the client.

Having read all the above I still don't know that making client info optional is directionally right. I understand the arguments, it's just a user provided string, it's not a strong or reliable identity, should never be used for auth etc. but if this change would lead to clients not providing the values, that would be damaging to the protocol from my perspective.

I guess I am personally only happy to give up sessions with the guarantee that the the data that was required to create a session is provided statelessly.

This sounds like a walkback on that, now I might lose something I actual find useful. 😅

@SamMorrowDrums

Copy link
Copy Markdown
Contributor

Most SDKs have this as required input today, so there's still encouragement for it to be included.

If this goes ahead, I really hope this remains accurate and in practice it's only a technical change and the reality is that everyone continues to provide this (which I will concede is the case with user agents, with should language).

kurtisvg added 2 commits July 13, 2026 11:39
…ntInfo optional

Add a ResultMetaObject _meta type with an optional
io.modelcontextprotocol/serverInfo field so servers can identify
themselves on responses, restoring the server identity that was
previously exchanged in the initialize handshake removed by SEP-2575.

Make io.modelcontextprotocol/clientInfo optional on requests to match;
protocolVersion and clientCapabilities remain required. Document in the
schema and spec docs that both fields are self-reported and unverified,
are intended for display, logging, and debugging, and SHOULD NOT be
used to change client or server behavior nor be relied on for security
decisions.
…cate discover identity

Strengthen the optional identity fields from MAY to SHOULD: clients
SHOULD send io.modelcontextprotocol/clientInfo on every request and
servers SHOULD send io.modelcontextprotocol/serverInfo on every
response, unless specifically configured not to do so (mirroring
User-Agent handling in RFC 9110). Both fields stay optional in the
schema so omitting them remains valid.

Remove the serverInfo body field from DiscoverResult so the server
identity lives in exactly one place, the result _meta, eliminating
the risk of the two copies drifting.
@kurtisvg

Copy link
Copy Markdown
Contributor Author

Because this ended up being a change to the schema, it went up for a vote to the Core Maintainers today. It passed with 7/7 voters approving.

@kurtisvg
kurtisvg merged commit 71e3069 into main Jul 16, 2026
9 checks passed
@kurtisvg
kurtisvg deleted the response-serverinfo branch July 16, 2026 02:16
@SamMorrowDrums

Copy link
Copy Markdown
Contributor

I appreciate moving to SHOULD for the client info.

guglielmo-san added a commit to modelcontextprotocol/go-sdk that referenced this pull request Jul 16, 2026
Yusufihsangorgel added a commit to Yusufihsangorgel/ai that referenced this pull request Jul 17, 2026
Part of dart-lang#162.

Adds `handleRequestScopedMessage` and `MCPServerFactory`: each decoded
JSON-RPC message is served on a fresh `MCPServer` instance (following
dart-lang#524 (comment)), which gets
`initialize(MCPServerInitialization)` with the per-request context and a
completed `initialized` before the message is delivered. The shape follows
the sketch in
dart-lang#162 (comment): a server
factory plus a handle function, no new transport abstraction.

Each message round-trips through an in-memory string channel, so the exchange
runs through the exact same `Peer` validation and dispatch path as a wire
connection; a message-typed channel could drop the extra encode/decode later.
Notifications emitted while handling are surfaced through a callback so a
transport can decide how to deliver them, and server-to-client requests fail
with an error inside their handler instead of deadlocking the exchange; real
routing for those comes with the transport work.

Also makes `MCPServerInitialization.clientInfo` (and `MCPServer.clientInfo`)
nullable, since
modelcontextprotocol/modelcontextprotocol#3002 made
the per-request client info optional, and records the server implementation
on responses under the reserved `io.modelcontextprotocol/serverInfo` result
metadata key which the same spec change added. `RootsTrackingSupport` now
tolerates the connection closing while `listRoots` is in flight, which the
per-request teardown made easy to hit.

Tests:

- `dart_mcp`: analyze clean; format clean (dev SDK); 828/828 tests across
  chrome/vm and dart2wasm/dart2js/kernel/exe. 22 new request-scoped tests
  cover fresh-instance isolation, per-request capabilities, absent client
  info, server info stamping (including malformed metadata), notification
  passthrough, server-to-client request failure, malformed, legacy, and
  response-shaped message rejection, early shutdown, and initialization
  failure, plus a legacy handshake regression test for the nullable client
  info.
- `dart_mcp_examples` (path-overrides the local `dart_mcp`): analyze clean.

This PR does not add Streamable HTTP or any transport; wire formats, envelope
validation, and `server/discover` land separately.
@ostefano

Copy link
Copy Markdown

@kurtisvg thank you and the core team for your patience while discussing this change over the course of the past few weeks, and for driving this to a conclusion 👍

jakemac53 pushed a commit to dart-lang/ai that referenced this pull request Jul 17, 2026
Part of #162.

Adds `handleRequestScopedMessage` and `MCPServerFactory`: each decoded
JSON-RPC message is served on a fresh `MCPServer` instance (following
#524 (comment)), which gets
`initialize(MCPServerInitialization)` with the per-request context and a
completed `initialized` before the message is delivered. The shape follows
the sketch in
#162 (comment): a server
factory plus a handle function, no new transport abstraction.

Each message round-trips through an in-memory string channel, so the exchange
runs through the exact same `Peer` validation and dispatch path as a wire
connection; a message-typed channel could drop the extra encode/decode later.
Notifications emitted while handling are surfaced through a callback so a
transport can decide how to deliver them, and server-to-client requests fail
with an error inside their handler instead of deadlocking the exchange; real
routing for those comes with the transport work.

Also makes `MCPServerInitialization.clientInfo` (and `MCPServer.clientInfo`)
nullable, since
modelcontextprotocol/modelcontextprotocol#3002 made
the per-request client info optional, and records the server implementation
on responses under the reserved `io.modelcontextprotocol/serverInfo` result
metadata key which the same spec change added. `RootsTrackingSupport` now
tolerates the connection closing while `listRoots` is in flight, which the
per-request teardown made easy to hit.

Tests:

- `dart_mcp`: analyze clean; format clean (dev SDK); 828/828 tests across
  chrome/vm and dart2wasm/dart2js/kernel/exe. 22 new request-scoped tests
  cover fresh-instance isolation, per-request capabilities, absent client
  info, server info stamping (including malformed metadata), notification
  passthrough, server-to-client request failure, malformed, legacy, and
  response-shaped message rejection, early shutdown, and initialization
  failure, plus a legacy handshake regression test for the nullable client
  info.
- `dart_mcp_examples` (path-overrides the local `dart_mcp`): analyze clean.

This PR does not add Streamable HTTP or any transport; wire formats, envelope
validation, and `server/discover` land separately.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rc-high-priority Related to an upcoming specification release and needs to be addressed with a high priority.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants