fix: remove server_info from DiscoverResult - #1065

Merged
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info
Jul 28, 2026
Merged

fix: remove server_info from DiscoverResult#1065
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info

Conversation

@howardjohn

Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes#1064

Replaces #1044

This removes the server_info field which is not part of the spec:
https://modelcontextprotocol.io/specification/draft/schema#discoverresult.

1044 add a synthetic server_info field; this PR removes it. This aligns with other types in this repo (using _meta as the source of truth, not a projection) and with other SDKS (Go and TypeScript do not have a server_info) field. Instead, users can use the helper accessors.

How Has This Been Tested?

Tested proxying to copilot MCP

Breaking Changes

Breaking from beta3 - the server_info field is removed

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@howardjohn
howardjohn requested a review from a team as a code ownerJuly 27, 2026 23:15
@github-actionsgithub-actionsBot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-service Service layer changes labels Jul 27, 2026

@tsarlandie-oaitsarlandie-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moving serverInfo into _meta for serialization makes sense, but clients must remain able to consume legacy top-level serverInfo, and must not fail when server identity is absent: the specification makes this metadata optional. The current MissingServerInfo path already causes 15 client conformance failures and bypasses the conservative fallback in #1045. We should preserve the legacy deserialization test, emit only the modern representation, and accept both representations on input.


#[test]
fn discover_result_prefers_top_level_server_info_over_namespaced_metadata() {
fn discover_result_ignores_legacy_top_level_server_info() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should not change this test. I think it's a useful test to make sure we remain compatible with legacy servers.

@DaleSeo

Copy link
Copy Markdown
Member

@howardjohn Can you take a look at the failing checks?

@github-actionsgithub-actionsBot added T-CI Changes to CI/CD workflows and configuration T-model Model/data structure changes labels Jul 28, 2026
@howardjohn
howardjohnforce-pushed the modern/server-info branch 2 times, most recently from 18d2c58 to 9e1c5e2CompareJuly 28, 2026 16:47
@howardjohn

Copy link
Copy Markdown
ContributorAuthor

@howardjohn Can you take a look at the failing checks?

sorry should be good to go now!

Comment threadcrates/rmcp/src/service/client.rs Outdated
Comment on lines +849 to +857
if let Some(server_info) = result.server_info() {
peer.set_peer_info(ServerInfo {
protocol_version: selected.clone(),
capabilities: result.capabilities,
server_info,
instructions: result.instructions,
meta: result.meta,
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since serverInfo is optional, this branch leaves peer_info unset even for a valid discovery response. That means the version, capabilities, and instructions were negotiated, but RoleClient::enforce_peer_request_association still treats the connection as legacy and skips the 2026-07-28 guard against unsolicited sampling, roots, or elicitation requests.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good call.. working on it

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we have 3 options:

  1. Make PeerInfo a new type with server_info optional (breaking type change)
  2. Make an Implementation with just version, a empty/fake name, and the rest None. No breaking change but pretty wonky
  3. Just pass the version through for enforce_peer_request_association but do not give any peer info (they lose instructions etc)

(1) seems the best option but wanted to check we are ok with the break?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@howardjohn I agree that 1 is the cleanest option, and I'm okay with the break since this is already a breaking change.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

👍 sent a change with (1)

@DaleSeo

Copy link
Copy Markdown
Member

For context, I introduced the top-level field in #973 following the accepted SEP-2575 schema. About 15 hours before it merged, modelcontextprotocol/modelcontextprotocol/pull/3002 moved server identity into the optional result _meta field. 😅
I opened modelcontextprotocol/modelcontextprotocol/pull/3159 to make that post-Final change clear to future implementers.

@github-actionsgithub-actionsBot added the T-examples Example code changes label Jul 28, 2026
@alexhancock
alexhancock self-requested a review July 28, 2026 19:35
@alexhancock
alexhancock merged commit 9a41811 into modelcontextprotocol:mainJul 28, 2026
22 checks passed
gocamille added a commit to gocamille/rust-sdk that referenced this pull request Jul 29, 2026
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (modelcontextprotocol#1065)
and its constructor takes the protocol version directly.
DaleSeo added a commit that referenced this pull request Jul 29, 2026
…t association (#1055)
* refactor(service): express SEP-2260 receive-side association as an enum
Replaces the has_pending_outbound_request bool on
enforce_peer_request_association with PeerRequestAssociation, so a
stream-separating transport can report per-request association (#1033).
Behavior-preserving: the event loop still passes the coarse signal as
Unknown.
* feat(transport): record inbound stream origin on streamable HTTP client (#1033)
The worker attaches an InboundStreamOrigin extension to each inbound
server request: Unassociated for the standalone GET stream,
OutboundRequest(id) for a POST's SSE stream. Mirror of the
OriginatingRequestId marker used by the server side.
* feat(service): enforce SEP-2260 client receive-side check per stream origin (#1033)
The event loop maps InboundStreamOrigin plus the in-flight responder
pool to PeerRequestAssociation: restricted requests arriving on the
standalone GET stream are now rejected with -32602 even while unrelated
outbound requests are in flight.
* test: end-to-end SEP-2260 stream-based enforcement over streamable HTTP (#1033)
* docs: cross-link SEP-2260 stream markers
* test: origin marker survives SSE resumption per SEP-2260 (#1033)
A POST SSE stream resumed via GET + Last-Event-ID (SEP-1699) reconnects
beneath execute_sse_stream, so requests replayed after a resume keep
their OutboundRequest origin. Pins the layering invariant: hoisting
reconnection above the marker attach point would wrongly reject
associated requests with -32602.
* docs: trim SEP-2260 comments to spec rationale
* docs: align SEP-2577 expect reason with sibling suppressions
* test: harden SEP-2260 stream-enforcement e2e (#1033)
Detect handler invocation via a channel asserted empty instead of a
panic in a spawned task (swallowed, cannot fail the test); surface
scripted-server misuse as transport errors rather than panics in the
transport task; bound the tail awaits with 5s timeouts. Correct the
header comment: a 2026-07-28 server minting a session id is not
spec-legal (SEP-2567 removes sessions and the GET endpoint) — the
scripted server is deliberately non-conforming, which is the point of
receive-side enforcement.
* test: adapt SEP-2260 association tests to ServerPeerInfo
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (#1065)
and its constructor takes the protocol version directly.
* chore: add 'Copy' macro to new PeerRequestAssociation
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
---------
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-CIChanges to CI/CD workflows and configurationT-configConfiguration file changesT-coreCore library changesT-examplesExample code changesT-modelModel/data structure changesT-serviceService layer changesT-testTesting related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DiscoverResult contains invalid server_info field

4 participants

@howardjohn@DaleSeo@alexhancock@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: remove server_info from DiscoverResult - #1065

Merged
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info
Jul 28, 2026
Merged

fix: remove server_info from DiscoverResult#1065
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info

Conversation

@howardjohn

Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes#1064

Replaces #1044

This removes the server_info field which is not part of the spec:
https://modelcontextprotocol.io/specification/draft/schema#discoverresult.

1044 add a synthetic server_info field; this PR removes it. This aligns with other types in this repo (using _meta as the source of truth, not a projection) and with other SDKS (Go and TypeScript do not have a server_info) field. Instead, users can use the helper accessors.

How Has This Been Tested?

Tested proxying to copilot MCP

Breaking Changes

Breaking from beta3 - the server_info field is removed

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@howardjohn
howardjohn requested a review from a team as a code ownerJuly 27, 2026 23:15
@github-actionsgithub-actionsBot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-service Service layer changes labels Jul 27, 2026

@tsarlandie-oaitsarlandie-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moving serverInfo into _meta for serialization makes sense, but clients must remain able to consume legacy top-level serverInfo, and must not fail when server identity is absent: the specification makes this metadata optional. The current MissingServerInfo path already causes 15 client conformance failures and bypasses the conservative fallback in #1045. We should preserve the legacy deserialization test, emit only the modern representation, and accept both representations on input.


#[test]
fn discover_result_prefers_top_level_server_info_over_namespaced_metadata() {
fn discover_result_ignores_legacy_top_level_server_info() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should not change this test. I think it's a useful test to make sure we remain compatible with legacy servers.

@DaleSeo

Copy link
Copy Markdown
Member

@howardjohn Can you take a look at the failing checks?

@github-actionsgithub-actionsBot added T-CI Changes to CI/CD workflows and configuration T-model Model/data structure changes labels Jul 28, 2026
@howardjohn
howardjohnforce-pushed the modern/server-info branch 2 times, most recently from 18d2c58 to 9e1c5e2CompareJuly 28, 2026 16:47
@howardjohn

Copy link
Copy Markdown
ContributorAuthor

@howardjohn Can you take a look at the failing checks?

sorry should be good to go now!

Comment threadcrates/rmcp/src/service/client.rs Outdated
Comment on lines +849 to +857
if let Some(server_info) = result.server_info() {
peer.set_peer_info(ServerInfo {
protocol_version: selected.clone(),
capabilities: result.capabilities,
server_info,
instructions: result.instructions,
meta: result.meta,
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since serverInfo is optional, this branch leaves peer_info unset even for a valid discovery response. That means the version, capabilities, and instructions were negotiated, but RoleClient::enforce_peer_request_association still treats the connection as legacy and skips the 2026-07-28 guard against unsolicited sampling, roots, or elicitation requests.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good call.. working on it

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we have 3 options:

  1. Make PeerInfo a new type with server_info optional (breaking type change)
  2. Make an Implementation with just version, a empty/fake name, and the rest None. No breaking change but pretty wonky
  3. Just pass the version through for enforce_peer_request_association but do not give any peer info (they lose instructions etc)

(1) seems the best option but wanted to check we are ok with the break?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@howardjohn I agree that 1 is the cleanest option, and I'm okay with the break since this is already a breaking change.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

👍 sent a change with (1)

@DaleSeo

Copy link
Copy Markdown
Member

For context, I introduced the top-level field in #973 following the accepted SEP-2575 schema. About 15 hours before it merged, modelcontextprotocol/modelcontextprotocol/pull/3002 moved server identity into the optional result _meta field. 😅
I opened modelcontextprotocol/modelcontextprotocol/pull/3159 to make that post-Final change clear to future implementers.

@github-actionsgithub-actionsBot added the T-examples Example code changes label Jul 28, 2026
@alexhancock
alexhancock self-requested a review July 28, 2026 19:35
@alexhancock
alexhancock merged commit 9a41811 into modelcontextprotocol:mainJul 28, 2026
22 checks passed
gocamille added a commit to gocamille/rust-sdk that referenced this pull request Jul 29, 2026
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (modelcontextprotocol#1065)
and its constructor takes the protocol version directly.
DaleSeo added a commit that referenced this pull request Jul 29, 2026
…t association (#1055)
* refactor(service): express SEP-2260 receive-side association as an enum
Replaces the has_pending_outbound_request bool on
enforce_peer_request_association with PeerRequestAssociation, so a
stream-separating transport can report per-request association (#1033).
Behavior-preserving: the event loop still passes the coarse signal as
Unknown.
* feat(transport): record inbound stream origin on streamable HTTP client (#1033)
The worker attaches an InboundStreamOrigin extension to each inbound
server request: Unassociated for the standalone GET stream,
OutboundRequest(id) for a POST's SSE stream. Mirror of the
OriginatingRequestId marker used by the server side.
* feat(service): enforce SEP-2260 client receive-side check per stream origin (#1033)
The event loop maps InboundStreamOrigin plus the in-flight responder
pool to PeerRequestAssociation: restricted requests arriving on the
standalone GET stream are now rejected with -32602 even while unrelated
outbound requests are in flight.
* test: end-to-end SEP-2260 stream-based enforcement over streamable HTTP (#1033)
* docs: cross-link SEP-2260 stream markers
* test: origin marker survives SSE resumption per SEP-2260 (#1033)
A POST SSE stream resumed via GET + Last-Event-ID (SEP-1699) reconnects
beneath execute_sse_stream, so requests replayed after a resume keep
their OutboundRequest origin. Pins the layering invariant: hoisting
reconnection above the marker attach point would wrongly reject
associated requests with -32602.
* docs: trim SEP-2260 comments to spec rationale
* docs: align SEP-2577 expect reason with sibling suppressions
* test: harden SEP-2260 stream-enforcement e2e (#1033)
Detect handler invocation via a channel asserted empty instead of a
panic in a spawned task (swallowed, cannot fail the test); surface
scripted-server misuse as transport errors rather than panics in the
transport task; bound the tail awaits with 5s timeouts. Correct the
header comment: a 2026-07-28 server minting a session id is not
spec-legal (SEP-2567 removes sessions and the GET endpoint) — the
scripted server is deliberately non-conforming, which is the point of
receive-side enforcement.
* test: adapt SEP-2260 association tests to ServerPeerInfo
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (#1065)
and its constructor takes the protocol version directly.
* chore: add 'Copy' macro to new PeerRequestAssociation
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
---------
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-CIChanges to CI/CD workflows and configurationT-configConfiguration file changesT-coreCore library changesT-examplesExample code changesT-modelModel/data structure changesT-serviceService layer changesT-testTesting related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DiscoverResult contains invalid server_info field

4 participants

@howardjohn@DaleSeo@alexhancock@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: remove server_info from DiscoverResult - #1065

Merged
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info
Jul 28, 2026
Merged

fix: remove server_info from DiscoverResult#1065
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info

Conversation

@howardjohn

Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes#1064

Replaces #1044

This removes the server_info field which is not part of the spec:
https://modelcontextprotocol.io/specification/draft/schema#discoverresult.

1044 add a synthetic server_info field; this PR removes it. This aligns with other types in this repo (using _meta as the source of truth, not a projection) and with other SDKS (Go and TypeScript do not have a server_info) field. Instead, users can use the helper accessors.

How Has This Been Tested?

Tested proxying to copilot MCP

Breaking Changes

Breaking from beta3 - the server_info field is removed

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@howardjohn
howardjohn requested a review from a team as a code ownerJuly 27, 2026 23:15
@github-actionsgithub-actionsBot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-service Service layer changes labels Jul 27, 2026

@tsarlandie-oaitsarlandie-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moving serverInfo into _meta for serialization makes sense, but clients must remain able to consume legacy top-level serverInfo, and must not fail when server identity is absent: the specification makes this metadata optional. The current MissingServerInfo path already causes 15 client conformance failures and bypasses the conservative fallback in #1045. We should preserve the legacy deserialization test, emit only the modern representation, and accept both representations on input.


#[test]
fn discover_result_prefers_top_level_server_info_over_namespaced_metadata() {
fn discover_result_ignores_legacy_top_level_server_info() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should not change this test. I think it's a useful test to make sure we remain compatible with legacy servers.

@DaleSeo

Copy link
Copy Markdown
Member

@howardjohn Can you take a look at the failing checks?

@github-actionsgithub-actionsBot added T-CI Changes to CI/CD workflows and configuration T-model Model/data structure changes labels Jul 28, 2026
@howardjohn
howardjohnforce-pushed the modern/server-info branch 2 times, most recently from 18d2c58 to 9e1c5e2CompareJuly 28, 2026 16:47
@howardjohn

Copy link
Copy Markdown
ContributorAuthor

@howardjohn Can you take a look at the failing checks?

sorry should be good to go now!

Comment threadcrates/rmcp/src/service/client.rs Outdated
Comment on lines +849 to +857
if let Some(server_info) = result.server_info() {
peer.set_peer_info(ServerInfo {
protocol_version: selected.clone(),
capabilities: result.capabilities,
server_info,
instructions: result.instructions,
meta: result.meta,
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since serverInfo is optional, this branch leaves peer_info unset even for a valid discovery response. That means the version, capabilities, and instructions were negotiated, but RoleClient::enforce_peer_request_association still treats the connection as legacy and skips the 2026-07-28 guard against unsolicited sampling, roots, or elicitation requests.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good call.. working on it

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we have 3 options:

  1. Make PeerInfo a new type with server_info optional (breaking type change)
  2. Make an Implementation with just version, a empty/fake name, and the rest None. No breaking change but pretty wonky
  3. Just pass the version through for enforce_peer_request_association but do not give any peer info (they lose instructions etc)

(1) seems the best option but wanted to check we are ok with the break?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@howardjohn I agree that 1 is the cleanest option, and I'm okay with the break since this is already a breaking change.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

👍 sent a change with (1)

@DaleSeo

Copy link
Copy Markdown
Member

For context, I introduced the top-level field in #973 following the accepted SEP-2575 schema. About 15 hours before it merged, modelcontextprotocol/modelcontextprotocol/pull/3002 moved server identity into the optional result _meta field. 😅
I opened modelcontextprotocol/modelcontextprotocol/pull/3159 to make that post-Final change clear to future implementers.

@github-actionsgithub-actionsBot added the T-examples Example code changes label Jul 28, 2026
@alexhancock
alexhancock self-requested a review July 28, 2026 19:35
@alexhancock
alexhancock merged commit 9a41811 into modelcontextprotocol:mainJul 28, 2026
22 checks passed
gocamille added a commit to gocamille/rust-sdk that referenced this pull request Jul 29, 2026
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (modelcontextprotocol#1065)
and its constructor takes the protocol version directly.
DaleSeo added a commit that referenced this pull request Jul 29, 2026
…t association (#1055)
* refactor(service): express SEP-2260 receive-side association as an enum
Replaces the has_pending_outbound_request bool on
enforce_peer_request_association with PeerRequestAssociation, so a
stream-separating transport can report per-request association (#1033).
Behavior-preserving: the event loop still passes the coarse signal as
Unknown.
* feat(transport): record inbound stream origin on streamable HTTP client (#1033)
The worker attaches an InboundStreamOrigin extension to each inbound
server request: Unassociated for the standalone GET stream,
OutboundRequest(id) for a POST's SSE stream. Mirror of the
OriginatingRequestId marker used by the server side.
* feat(service): enforce SEP-2260 client receive-side check per stream origin (#1033)
The event loop maps InboundStreamOrigin plus the in-flight responder
pool to PeerRequestAssociation: restricted requests arriving on the
standalone GET stream are now rejected with -32602 even while unrelated
outbound requests are in flight.
* test: end-to-end SEP-2260 stream-based enforcement over streamable HTTP (#1033)
* docs: cross-link SEP-2260 stream markers
* test: origin marker survives SSE resumption per SEP-2260 (#1033)
A POST SSE stream resumed via GET + Last-Event-ID (SEP-1699) reconnects
beneath execute_sse_stream, so requests replayed after a resume keep
their OutboundRequest origin. Pins the layering invariant: hoisting
reconnection above the marker attach point would wrongly reject
associated requests with -32602.
* docs: trim SEP-2260 comments to spec rationale
* docs: align SEP-2577 expect reason with sibling suppressions
* test: harden SEP-2260 stream-enforcement e2e (#1033)
Detect handler invocation via a channel asserted empty instead of a
panic in a spawned task (swallowed, cannot fail the test); surface
scripted-server misuse as transport errors rather than panics in the
transport task; bound the tail awaits with 5s timeouts. Correct the
header comment: a 2026-07-28 server minting a session id is not
spec-legal (SEP-2567 removes sessions and the GET endpoint) — the
scripted server is deliberately non-conforming, which is the point of
receive-side enforcement.
* test: adapt SEP-2260 association tests to ServerPeerInfo
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (#1065)
and its constructor takes the protocol version directly.
* chore: add 'Copy' macro to new PeerRequestAssociation
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
---------
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-CIChanges to CI/CD workflows and configurationT-configConfiguration file changesT-coreCore library changesT-examplesExample code changesT-modelModel/data structure changesT-serviceService layer changesT-testTesting related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DiscoverResult contains invalid server_info field

4 participants

@howardjohn@DaleSeo@alexhancock@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: remove server_info from DiscoverResult - #1065

Merged
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info
Jul 28, 2026
Merged

fix: remove server_info from DiscoverResult#1065
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info

Conversation

@howardjohn

Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes#1064

Replaces #1044

This removes the server_info field which is not part of the spec:
https://modelcontextprotocol.io/specification/draft/schema#discoverresult.

1044 add a synthetic server_info field; this PR removes it. This aligns with other types in this repo (using _meta as the source of truth, not a projection) and with other SDKS (Go and TypeScript do not have a server_info) field. Instead, users can use the helper accessors.

How Has This Been Tested?

Tested proxying to copilot MCP

Breaking Changes

Breaking from beta3 - the server_info field is removed

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@howardjohn
howardjohn requested a review from a team as a code ownerJuly 27, 2026 23:15
@github-actionsgithub-actionsBot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-service Service layer changes labels Jul 27, 2026

@tsarlandie-oaitsarlandie-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moving serverInfo into _meta for serialization makes sense, but clients must remain able to consume legacy top-level serverInfo, and must not fail when server identity is absent: the specification makes this metadata optional. The current MissingServerInfo path already causes 15 client conformance failures and bypasses the conservative fallback in #1045. We should preserve the legacy deserialization test, emit only the modern representation, and accept both representations on input.


#[test]
fn discover_result_prefers_top_level_server_info_over_namespaced_metadata() {
fn discover_result_ignores_legacy_top_level_server_info() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should not change this test. I think it's a useful test to make sure we remain compatible with legacy servers.

@DaleSeo

Copy link
Copy Markdown
Member

@howardjohn Can you take a look at the failing checks?

@github-actionsgithub-actionsBot added T-CI Changes to CI/CD workflows and configuration T-model Model/data structure changes labels Jul 28, 2026
@howardjohn
howardjohnforce-pushed the modern/server-info branch 2 times, most recently from 18d2c58 to 9e1c5e2CompareJuly 28, 2026 16:47
@howardjohn

Copy link
Copy Markdown
ContributorAuthor

@howardjohn Can you take a look at the failing checks?

sorry should be good to go now!

Comment threadcrates/rmcp/src/service/client.rs Outdated
Comment on lines +849 to +857
if let Some(server_info) = result.server_info() {
peer.set_peer_info(ServerInfo {
protocol_version: selected.clone(),
capabilities: result.capabilities,
server_info,
instructions: result.instructions,
meta: result.meta,
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since serverInfo is optional, this branch leaves peer_info unset even for a valid discovery response. That means the version, capabilities, and instructions were negotiated, but RoleClient::enforce_peer_request_association still treats the connection as legacy and skips the 2026-07-28 guard against unsolicited sampling, roots, or elicitation requests.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good call.. working on it

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we have 3 options:

  1. Make PeerInfo a new type with server_info optional (breaking type change)
  2. Make an Implementation with just version, a empty/fake name, and the rest None. No breaking change but pretty wonky
  3. Just pass the version through for enforce_peer_request_association but do not give any peer info (they lose instructions etc)

(1) seems the best option but wanted to check we are ok with the break?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@howardjohn I agree that 1 is the cleanest option, and I'm okay with the break since this is already a breaking change.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

👍 sent a change with (1)

@DaleSeo

Copy link
Copy Markdown
Member

For context, I introduced the top-level field in #973 following the accepted SEP-2575 schema. About 15 hours before it merged, modelcontextprotocol/modelcontextprotocol/pull/3002 moved server identity into the optional result _meta field. 😅
I opened modelcontextprotocol/modelcontextprotocol/pull/3159 to make that post-Final change clear to future implementers.

@github-actionsgithub-actionsBot added the T-examples Example code changes label Jul 28, 2026
@alexhancock
alexhancock self-requested a review July 28, 2026 19:35
@alexhancock
alexhancock merged commit 9a41811 into modelcontextprotocol:mainJul 28, 2026
22 checks passed
gocamille added a commit to gocamille/rust-sdk that referenced this pull request Jul 29, 2026
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (modelcontextprotocol#1065)
and its constructor takes the protocol version directly.
DaleSeo added a commit that referenced this pull request Jul 29, 2026
…t association (#1055)
* refactor(service): express SEP-2260 receive-side association as an enum
Replaces the has_pending_outbound_request bool on
enforce_peer_request_association with PeerRequestAssociation, so a
stream-separating transport can report per-request association (#1033).
Behavior-preserving: the event loop still passes the coarse signal as
Unknown.
* feat(transport): record inbound stream origin on streamable HTTP client (#1033)
The worker attaches an InboundStreamOrigin extension to each inbound
server request: Unassociated for the standalone GET stream,
OutboundRequest(id) for a POST's SSE stream. Mirror of the
OriginatingRequestId marker used by the server side.
* feat(service): enforce SEP-2260 client receive-side check per stream origin (#1033)
The event loop maps InboundStreamOrigin plus the in-flight responder
pool to PeerRequestAssociation: restricted requests arriving on the
standalone GET stream are now rejected with -32602 even while unrelated
outbound requests are in flight.
* test: end-to-end SEP-2260 stream-based enforcement over streamable HTTP (#1033)
* docs: cross-link SEP-2260 stream markers
* test: origin marker survives SSE resumption per SEP-2260 (#1033)
A POST SSE stream resumed via GET + Last-Event-ID (SEP-1699) reconnects
beneath execute_sse_stream, so requests replayed after a resume keep
their OutboundRequest origin. Pins the layering invariant: hoisting
reconnection above the marker attach point would wrongly reject
associated requests with -32602.
* docs: trim SEP-2260 comments to spec rationale
* docs: align SEP-2577 expect reason with sibling suppressions
* test: harden SEP-2260 stream-enforcement e2e (#1033)
Detect handler invocation via a channel asserted empty instead of a
panic in a spawned task (swallowed, cannot fail the test); surface
scripted-server misuse as transport errors rather than panics in the
transport task; bound the tail awaits with 5s timeouts. Correct the
header comment: a 2026-07-28 server minting a session id is not
spec-legal (SEP-2567 removes sessions and the GET endpoint) — the
scripted server is deliberately non-conforming, which is the point of
receive-side enforcement.
* test: adapt SEP-2260 association tests to ServerPeerInfo
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (#1065)
and its constructor takes the protocol version directly.
* chore: add 'Copy' macro to new PeerRequestAssociation
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
---------
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-CIChanges to CI/CD workflows and configurationT-configConfiguration file changesT-coreCore library changesT-examplesExample code changesT-modelModel/data structure changesT-serviceService layer changesT-testTesting related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DiscoverResult contains invalid server_info field

4 participants

@howardjohn@DaleSeo@alexhancock@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: remove server_info from DiscoverResult - #1065

Merged
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info
Jul 28, 2026
Merged

fix: remove server_info from DiscoverResult#1065
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info

Conversation

@howardjohn

Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes#1064

Replaces #1044

This removes the server_info field which is not part of the spec:
https://modelcontextprotocol.io/specification/draft/schema#discoverresult.

1044 add a synthetic server_info field; this PR removes it. This aligns with other types in this repo (using _meta as the source of truth, not a projection) and with other SDKS (Go and TypeScript do not have a server_info) field. Instead, users can use the helper accessors.

How Has This Been Tested?

Tested proxying to copilot MCP

Breaking Changes

Breaking from beta3 - the server_info field is removed

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@howardjohn
howardjohn requested a review from a team as a code ownerJuly 27, 2026 23:15
@github-actionsgithub-actionsBot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-service Service layer changes labels Jul 27, 2026

@tsarlandie-oaitsarlandie-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moving serverInfo into _meta for serialization makes sense, but clients must remain able to consume legacy top-level serverInfo, and must not fail when server identity is absent: the specification makes this metadata optional. The current MissingServerInfo path already causes 15 client conformance failures and bypasses the conservative fallback in #1045. We should preserve the legacy deserialization test, emit only the modern representation, and accept both representations on input.


#[test]
fn discover_result_prefers_top_level_server_info_over_namespaced_metadata() {
fn discover_result_ignores_legacy_top_level_server_info() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should not change this test. I think it's a useful test to make sure we remain compatible with legacy servers.

@DaleSeo

Copy link
Copy Markdown
Member

@howardjohn Can you take a look at the failing checks?

@github-actionsgithub-actionsBot added T-CI Changes to CI/CD workflows and configuration T-model Model/data structure changes labels Jul 28, 2026
@howardjohn
howardjohnforce-pushed the modern/server-info branch 2 times, most recently from 18d2c58 to 9e1c5e2CompareJuly 28, 2026 16:47
@howardjohn

Copy link
Copy Markdown
ContributorAuthor

@howardjohn Can you take a look at the failing checks?

sorry should be good to go now!

Comment threadcrates/rmcp/src/service/client.rs Outdated
Comment on lines +849 to +857
if let Some(server_info) = result.server_info() {
peer.set_peer_info(ServerInfo {
protocol_version: selected.clone(),
capabilities: result.capabilities,
server_info,
instructions: result.instructions,
meta: result.meta,
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since serverInfo is optional, this branch leaves peer_info unset even for a valid discovery response. That means the version, capabilities, and instructions were negotiated, but RoleClient::enforce_peer_request_association still treats the connection as legacy and skips the 2026-07-28 guard against unsolicited sampling, roots, or elicitation requests.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good call.. working on it

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we have 3 options:

  1. Make PeerInfo a new type with server_info optional (breaking type change)
  2. Make an Implementation with just version, a empty/fake name, and the rest None. No breaking change but pretty wonky
  3. Just pass the version through for enforce_peer_request_association but do not give any peer info (they lose instructions etc)

(1) seems the best option but wanted to check we are ok with the break?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@howardjohn I agree that 1 is the cleanest option, and I'm okay with the break since this is already a breaking change.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

👍 sent a change with (1)

@DaleSeo

Copy link
Copy Markdown
Member

For context, I introduced the top-level field in #973 following the accepted SEP-2575 schema. About 15 hours before it merged, modelcontextprotocol/modelcontextprotocol/pull/3002 moved server identity into the optional result _meta field. 😅
I opened modelcontextprotocol/modelcontextprotocol/pull/3159 to make that post-Final change clear to future implementers.

@github-actionsgithub-actionsBot added the T-examples Example code changes label Jul 28, 2026
@alexhancock
alexhancock self-requested a review July 28, 2026 19:35
@alexhancock
alexhancock merged commit 9a41811 into modelcontextprotocol:mainJul 28, 2026
22 checks passed
gocamille added a commit to gocamille/rust-sdk that referenced this pull request Jul 29, 2026
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (modelcontextprotocol#1065)
and its constructor takes the protocol version directly.
DaleSeo added a commit that referenced this pull request Jul 29, 2026
…t association (#1055)
* refactor(service): express SEP-2260 receive-side association as an enum
Replaces the has_pending_outbound_request bool on
enforce_peer_request_association with PeerRequestAssociation, so a
stream-separating transport can report per-request association (#1033).
Behavior-preserving: the event loop still passes the coarse signal as
Unknown.
* feat(transport): record inbound stream origin on streamable HTTP client (#1033)
The worker attaches an InboundStreamOrigin extension to each inbound
server request: Unassociated for the standalone GET stream,
OutboundRequest(id) for a POST's SSE stream. Mirror of the
OriginatingRequestId marker used by the server side.
* feat(service): enforce SEP-2260 client receive-side check per stream origin (#1033)
The event loop maps InboundStreamOrigin plus the in-flight responder
pool to PeerRequestAssociation: restricted requests arriving on the
standalone GET stream are now rejected with -32602 even while unrelated
outbound requests are in flight.
* test: end-to-end SEP-2260 stream-based enforcement over streamable HTTP (#1033)
* docs: cross-link SEP-2260 stream markers
* test: origin marker survives SSE resumption per SEP-2260 (#1033)
A POST SSE stream resumed via GET + Last-Event-ID (SEP-1699) reconnects
beneath execute_sse_stream, so requests replayed after a resume keep
their OutboundRequest origin. Pins the layering invariant: hoisting
reconnection above the marker attach point would wrongly reject
associated requests with -32602.
* docs: trim SEP-2260 comments to spec rationale
* docs: align SEP-2577 expect reason with sibling suppressions
* test: harden SEP-2260 stream-enforcement e2e (#1033)
Detect handler invocation via a channel asserted empty instead of a
panic in a spawned task (swallowed, cannot fail the test); surface
scripted-server misuse as transport errors rather than panics in the
transport task; bound the tail awaits with 5s timeouts. Correct the
header comment: a 2026-07-28 server minting a session id is not
spec-legal (SEP-2567 removes sessions and the GET endpoint) — the
scripted server is deliberately non-conforming, which is the point of
receive-side enforcement.
* test: adapt SEP-2260 association tests to ServerPeerInfo
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (#1065)
and its constructor takes the protocol version directly.
* chore: add 'Copy' macro to new PeerRequestAssociation
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
---------
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-CIChanges to CI/CD workflows and configurationT-configConfiguration file changesT-coreCore library changesT-examplesExample code changesT-modelModel/data structure changesT-serviceService layer changesT-testTesting related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DiscoverResult contains invalid server_info field

4 participants

@howardjohn@DaleSeo@alexhancock@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: remove server_info from DiscoverResult - #1065

Merged
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info
Jul 28, 2026
Merged

fix: remove server_info from DiscoverResult#1065
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info

Conversation

@howardjohn

Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes#1064

Replaces #1044

This removes the server_info field which is not part of the spec:
https://modelcontextprotocol.io/specification/draft/schema#discoverresult.

1044 add a synthetic server_info field; this PR removes it. This aligns with other types in this repo (using _meta as the source of truth, not a projection) and with other SDKS (Go and TypeScript do not have a server_info) field. Instead, users can use the helper accessors.

How Has This Been Tested?

Tested proxying to copilot MCP

Breaking Changes

Breaking from beta3 - the server_info field is removed

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@howardjohn
howardjohn requested a review from a team as a code ownerJuly 27, 2026 23:15
@github-actionsgithub-actionsBot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-service Service layer changes labels Jul 27, 2026

@tsarlandie-oaitsarlandie-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moving serverInfo into _meta for serialization makes sense, but clients must remain able to consume legacy top-level serverInfo, and must not fail when server identity is absent: the specification makes this metadata optional. The current MissingServerInfo path already causes 15 client conformance failures and bypasses the conservative fallback in #1045. We should preserve the legacy deserialization test, emit only the modern representation, and accept both representations on input.


#[test]
fn discover_result_prefers_top_level_server_info_over_namespaced_metadata() {
fn discover_result_ignores_legacy_top_level_server_info() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should not change this test. I think it's a useful test to make sure we remain compatible with legacy servers.

@DaleSeo

Copy link
Copy Markdown
Member

@howardjohn Can you take a look at the failing checks?

@github-actionsgithub-actionsBot added T-CI Changes to CI/CD workflows and configuration T-model Model/data structure changes labels Jul 28, 2026
@howardjohn
howardjohnforce-pushed the modern/server-info branch 2 times, most recently from 18d2c58 to 9e1c5e2CompareJuly 28, 2026 16:47
@howardjohn

Copy link
Copy Markdown
ContributorAuthor

@howardjohn Can you take a look at the failing checks?

sorry should be good to go now!

Comment threadcrates/rmcp/src/service/client.rs Outdated
Comment on lines +849 to +857
if let Some(server_info) = result.server_info() {
peer.set_peer_info(ServerInfo {
protocol_version: selected.clone(),
capabilities: result.capabilities,
server_info,
instructions: result.instructions,
meta: result.meta,
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since serverInfo is optional, this branch leaves peer_info unset even for a valid discovery response. That means the version, capabilities, and instructions were negotiated, but RoleClient::enforce_peer_request_association still treats the connection as legacy and skips the 2026-07-28 guard against unsolicited sampling, roots, or elicitation requests.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good call.. working on it

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we have 3 options:

  1. Make PeerInfo a new type with server_info optional (breaking type change)
  2. Make an Implementation with just version, a empty/fake name, and the rest None. No breaking change but pretty wonky
  3. Just pass the version through for enforce_peer_request_association but do not give any peer info (they lose instructions etc)

(1) seems the best option but wanted to check we are ok with the break?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@howardjohn I agree that 1 is the cleanest option, and I'm okay with the break since this is already a breaking change.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

👍 sent a change with (1)

@DaleSeo

Copy link
Copy Markdown
Member

For context, I introduced the top-level field in #973 following the accepted SEP-2575 schema. About 15 hours before it merged, modelcontextprotocol/modelcontextprotocol/pull/3002 moved server identity into the optional result _meta field. 😅
I opened modelcontextprotocol/modelcontextprotocol/pull/3159 to make that post-Final change clear to future implementers.

@github-actionsgithub-actionsBot added the T-examples Example code changes label Jul 28, 2026
@alexhancock
alexhancock self-requested a review July 28, 2026 19:35
@alexhancock
alexhancock merged commit 9a41811 into modelcontextprotocol:mainJul 28, 2026
22 checks passed
gocamille added a commit to gocamille/rust-sdk that referenced this pull request Jul 29, 2026
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (modelcontextprotocol#1065)
and its constructor takes the protocol version directly.
DaleSeo added a commit that referenced this pull request Jul 29, 2026
…t association (#1055)
* refactor(service): express SEP-2260 receive-side association as an enum
Replaces the has_pending_outbound_request bool on
enforce_peer_request_association with PeerRequestAssociation, so a
stream-separating transport can report per-request association (#1033).
Behavior-preserving: the event loop still passes the coarse signal as
Unknown.
* feat(transport): record inbound stream origin on streamable HTTP client (#1033)
The worker attaches an InboundStreamOrigin extension to each inbound
server request: Unassociated for the standalone GET stream,
OutboundRequest(id) for a POST's SSE stream. Mirror of the
OriginatingRequestId marker used by the server side.
* feat(service): enforce SEP-2260 client receive-side check per stream origin (#1033)
The event loop maps InboundStreamOrigin plus the in-flight responder
pool to PeerRequestAssociation: restricted requests arriving on the
standalone GET stream are now rejected with -32602 even while unrelated
outbound requests are in flight.
* test: end-to-end SEP-2260 stream-based enforcement over streamable HTTP (#1033)
* docs: cross-link SEP-2260 stream markers
* test: origin marker survives SSE resumption per SEP-2260 (#1033)
A POST SSE stream resumed via GET + Last-Event-ID (SEP-1699) reconnects
beneath execute_sse_stream, so requests replayed after a resume keep
their OutboundRequest origin. Pins the layering invariant: hoisting
reconnection above the marker attach point would wrongly reject
associated requests with -32602.
* docs: trim SEP-2260 comments to spec rationale
* docs: align SEP-2577 expect reason with sibling suppressions
* test: harden SEP-2260 stream-enforcement e2e (#1033)
Detect handler invocation via a channel asserted empty instead of a
panic in a spawned task (swallowed, cannot fail the test); surface
scripted-server misuse as transport errors rather than panics in the
transport task; bound the tail awaits with 5s timeouts. Correct the
header comment: a 2026-07-28 server minting a session id is not
spec-legal (SEP-2567 removes sessions and the GET endpoint) — the
scripted server is deliberately non-conforming, which is the point of
receive-side enforcement.
* test: adapt SEP-2260 association tests to ServerPeerInfo
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (#1065)
and its constructor takes the protocol version directly.
* chore: add 'Copy' macro to new PeerRequestAssociation
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
---------
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-CIChanges to CI/CD workflows and configurationT-configConfiguration file changesT-coreCore library changesT-examplesExample code changesT-modelModel/data structure changesT-serviceService layer changesT-testTesting related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DiscoverResult contains invalid server_info field

4 participants

@howardjohn@DaleSeo@alexhancock@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: remove server_info from DiscoverResult - #1065

Merged
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info
Jul 28, 2026
Merged

fix: remove server_info from DiscoverResult#1065
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info

Conversation

@howardjohn

Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes#1064

Replaces #1044

This removes the server_info field which is not part of the spec:
https://modelcontextprotocol.io/specification/draft/schema#discoverresult.

1044 add a synthetic server_info field; this PR removes it. This aligns with other types in this repo (using _meta as the source of truth, not a projection) and with other SDKS (Go and TypeScript do not have a server_info) field. Instead, users can use the helper accessors.

How Has This Been Tested?

Tested proxying to copilot MCP

Breaking Changes

Breaking from beta3 - the server_info field is removed

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@howardjohn
howardjohn requested a review from a team as a code ownerJuly 27, 2026 23:15
@github-actionsgithub-actionsBot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-service Service layer changes labels Jul 27, 2026

@tsarlandie-oaitsarlandie-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moving serverInfo into _meta for serialization makes sense, but clients must remain able to consume legacy top-level serverInfo, and must not fail when server identity is absent: the specification makes this metadata optional. The current MissingServerInfo path already causes 15 client conformance failures and bypasses the conservative fallback in #1045. We should preserve the legacy deserialization test, emit only the modern representation, and accept both representations on input.


#[test]
fn discover_result_prefers_top_level_server_info_over_namespaced_metadata() {
fn discover_result_ignores_legacy_top_level_server_info() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should not change this test. I think it's a useful test to make sure we remain compatible with legacy servers.

@DaleSeo

Copy link
Copy Markdown
Member

@howardjohn Can you take a look at the failing checks?

@github-actionsgithub-actionsBot added T-CI Changes to CI/CD workflows and configuration T-model Model/data structure changes labels Jul 28, 2026
@howardjohn
howardjohnforce-pushed the modern/server-info branch 2 times, most recently from 18d2c58 to 9e1c5e2CompareJuly 28, 2026 16:47
@howardjohn

Copy link
Copy Markdown
ContributorAuthor

@howardjohn Can you take a look at the failing checks?

sorry should be good to go now!

Comment threadcrates/rmcp/src/service/client.rs Outdated
Comment on lines +849 to +857
if let Some(server_info) = result.server_info() {
peer.set_peer_info(ServerInfo {
protocol_version: selected.clone(),
capabilities: result.capabilities,
server_info,
instructions: result.instructions,
meta: result.meta,
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since serverInfo is optional, this branch leaves peer_info unset even for a valid discovery response. That means the version, capabilities, and instructions were negotiated, but RoleClient::enforce_peer_request_association still treats the connection as legacy and skips the 2026-07-28 guard against unsolicited sampling, roots, or elicitation requests.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good call.. working on it

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we have 3 options:

  1. Make PeerInfo a new type with server_info optional (breaking type change)
  2. Make an Implementation with just version, a empty/fake name, and the rest None. No breaking change but pretty wonky
  3. Just pass the version through for enforce_peer_request_association but do not give any peer info (they lose instructions etc)

(1) seems the best option but wanted to check we are ok with the break?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@howardjohn I agree that 1 is the cleanest option, and I'm okay with the break since this is already a breaking change.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

👍 sent a change with (1)

@DaleSeo

Copy link
Copy Markdown
Member

For context, I introduced the top-level field in #973 following the accepted SEP-2575 schema. About 15 hours before it merged, modelcontextprotocol/modelcontextprotocol/pull/3002 moved server identity into the optional result _meta field. 😅
I opened modelcontextprotocol/modelcontextprotocol/pull/3159 to make that post-Final change clear to future implementers.

@github-actionsgithub-actionsBot added the T-examples Example code changes label Jul 28, 2026
@alexhancock
alexhancock self-requested a review July 28, 2026 19:35
@alexhancock
alexhancock merged commit 9a41811 into modelcontextprotocol:mainJul 28, 2026
22 checks passed
gocamille added a commit to gocamille/rust-sdk that referenced this pull request Jul 29, 2026
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (modelcontextprotocol#1065)
and its constructor takes the protocol version directly.
DaleSeo added a commit that referenced this pull request Jul 29, 2026
…t association (#1055)
* refactor(service): express SEP-2260 receive-side association as an enum
Replaces the has_pending_outbound_request bool on
enforce_peer_request_association with PeerRequestAssociation, so a
stream-separating transport can report per-request association (#1033).
Behavior-preserving: the event loop still passes the coarse signal as
Unknown.
* feat(transport): record inbound stream origin on streamable HTTP client (#1033)
The worker attaches an InboundStreamOrigin extension to each inbound
server request: Unassociated for the standalone GET stream,
OutboundRequest(id) for a POST's SSE stream. Mirror of the
OriginatingRequestId marker used by the server side.
* feat(service): enforce SEP-2260 client receive-side check per stream origin (#1033)
The event loop maps InboundStreamOrigin plus the in-flight responder
pool to PeerRequestAssociation: restricted requests arriving on the
standalone GET stream are now rejected with -32602 even while unrelated
outbound requests are in flight.
* test: end-to-end SEP-2260 stream-based enforcement over streamable HTTP (#1033)
* docs: cross-link SEP-2260 stream markers
* test: origin marker survives SSE resumption per SEP-2260 (#1033)
A POST SSE stream resumed via GET + Last-Event-ID (SEP-1699) reconnects
beneath execute_sse_stream, so requests replayed after a resume keep
their OutboundRequest origin. Pins the layering invariant: hoisting
reconnection above the marker attach point would wrongly reject
associated requests with -32602.
* docs: trim SEP-2260 comments to spec rationale
* docs: align SEP-2577 expect reason with sibling suppressions
* test: harden SEP-2260 stream-enforcement e2e (#1033)
Detect handler invocation via a channel asserted empty instead of a
panic in a spawned task (swallowed, cannot fail the test); surface
scripted-server misuse as transport errors rather than panics in the
transport task; bound the tail awaits with 5s timeouts. Correct the
header comment: a 2026-07-28 server minting a session id is not
spec-legal (SEP-2567 removes sessions and the GET endpoint) — the
scripted server is deliberately non-conforming, which is the point of
receive-side enforcement.
* test: adapt SEP-2260 association tests to ServerPeerInfo
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (#1065)
and its constructor takes the protocol version directly.
* chore: add 'Copy' macro to new PeerRequestAssociation
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
---------
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-CIChanges to CI/CD workflows and configurationT-configConfiguration file changesT-coreCore library changesT-examplesExample code changesT-modelModel/data structure changesT-serviceService layer changesT-testTesting related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DiscoverResult contains invalid server_info field

4 participants

@howardjohn@DaleSeo@alexhancock@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: remove server_info from DiscoverResult - #1065

Merged
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info
Jul 28, 2026
Merged

fix: remove server_info from DiscoverResult#1065
alexhancock merged 1 commit into
modelcontextprotocol:mainfrom
howardjohn:modern/server-info

Conversation

@howardjohn

Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes#1064

Replaces #1044

This removes the server_info field which is not part of the spec:
https://modelcontextprotocol.io/specification/draft/schema#discoverresult.

1044 add a synthetic server_info field; this PR removes it. This aligns with other types in this repo (using _meta as the source of truth, not a projection) and with other SDKS (Go and TypeScript do not have a server_info) field. Instead, users can use the helper accessors.

How Has This Been Tested?

Tested proxying to copilot MCP

Breaking Changes

Breaking from beta3 - the server_info field is removed

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@howardjohn
howardjohn requested a review from a team as a code ownerJuly 27, 2026 23:15
@github-actionsgithub-actionsBot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-service Service layer changes labels Jul 27, 2026

@tsarlandie-oaitsarlandie-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moving serverInfo into _meta for serialization makes sense, but clients must remain able to consume legacy top-level serverInfo, and must not fail when server identity is absent: the specification makes this metadata optional. The current MissingServerInfo path already causes 15 client conformance failures and bypasses the conservative fallback in #1045. We should preserve the legacy deserialization test, emit only the modern representation, and accept both representations on input.


#[test]
fn discover_result_prefers_top_level_server_info_over_namespaced_metadata() {
fn discover_result_ignores_legacy_top_level_server_info() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should not change this test. I think it's a useful test to make sure we remain compatible with legacy servers.

@DaleSeo

Copy link
Copy Markdown
Member

@howardjohn Can you take a look at the failing checks?

@github-actionsgithub-actionsBot added T-CI Changes to CI/CD workflows and configuration T-model Model/data structure changes labels Jul 28, 2026
@howardjohn
howardjohnforce-pushed the modern/server-info branch 2 times, most recently from 18d2c58 to 9e1c5e2CompareJuly 28, 2026 16:47
@howardjohn

Copy link
Copy Markdown
ContributorAuthor

@howardjohn Can you take a look at the failing checks?

sorry should be good to go now!

Comment threadcrates/rmcp/src/service/client.rs Outdated
Comment on lines +849 to +857
if let Some(server_info) = result.server_info() {
peer.set_peer_info(ServerInfo {
protocol_version: selected.clone(),
capabilities: result.capabilities,
server_info,
instructions: result.instructions,
meta: result.meta,
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since serverInfo is optional, this branch leaves peer_info unset even for a valid discovery response. That means the version, capabilities, and instructions were negotiated, but RoleClient::enforce_peer_request_association still treats the connection as legacy and skips the 2026-07-28 guard against unsolicited sampling, roots, or elicitation requests.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good call.. working on it

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we have 3 options:

  1. Make PeerInfo a new type with server_info optional (breaking type change)
  2. Make an Implementation with just version, a empty/fake name, and the rest None. No breaking change but pretty wonky
  3. Just pass the version through for enforce_peer_request_association but do not give any peer info (they lose instructions etc)

(1) seems the best option but wanted to check we are ok with the break?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@howardjohn I agree that 1 is the cleanest option, and I'm okay with the break since this is already a breaking change.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

👍 sent a change with (1)

@DaleSeo

Copy link
Copy Markdown
Member

For context, I introduced the top-level field in #973 following the accepted SEP-2575 schema. About 15 hours before it merged, modelcontextprotocol/modelcontextprotocol/pull/3002 moved server identity into the optional result _meta field. 😅
I opened modelcontextprotocol/modelcontextprotocol/pull/3159 to make that post-Final change clear to future implementers.

@github-actionsgithub-actionsBot added the T-examples Example code changes label Jul 28, 2026
@alexhancock
alexhancock self-requested a review July 28, 2026 19:35
@alexhancock
alexhancock merged commit 9a41811 into modelcontextprotocol:mainJul 28, 2026
22 checks passed
gocamille added a commit to gocamille/rust-sdk that referenced this pull request Jul 29, 2026
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (modelcontextprotocol#1065)
and its constructor takes the protocol version directly.
DaleSeo added a commit that referenced this pull request Jul 29, 2026
…t association (#1055)
* refactor(service): express SEP-2260 receive-side association as an enum
Replaces the has_pending_outbound_request bool on
enforce_peer_request_association with PeerRequestAssociation, so a
stream-separating transport can report per-request association (#1033).
Behavior-preserving: the event loop still passes the coarse signal as
Unknown.
* feat(transport): record inbound stream origin on streamable HTTP client (#1033)
The worker attaches an InboundStreamOrigin extension to each inbound
server request: Unassociated for the standalone GET stream,
OutboundRequest(id) for a POST's SSE stream. Mirror of the
OriginatingRequestId marker used by the server side.
* feat(service): enforce SEP-2260 client receive-side check per stream origin (#1033)
The event loop maps InboundStreamOrigin plus the in-flight responder
pool to PeerRequestAssociation: restricted requests arriving on the
standalone GET stream are now rejected with -32602 even while unrelated
outbound requests are in flight.
* test: end-to-end SEP-2260 stream-based enforcement over streamable HTTP (#1033)
* docs: cross-link SEP-2260 stream markers
* test: origin marker survives SSE resumption per SEP-2260 (#1033)
A POST SSE stream resumed via GET + Last-Event-ID (SEP-1699) reconnects
beneath execute_sse_stream, so requests replayed after a resume keep
their OutboundRequest origin. Pins the layering invariant: hoisting
reconnection above the marker attach point would wrongly reject
associated requests with -32602.
* docs: trim SEP-2260 comments to spec rationale
* docs: align SEP-2577 expect reason with sibling suppressions
* test: harden SEP-2260 stream-enforcement e2e (#1033)
Detect handler invocation via a channel asserted empty instead of a
panic in a spawned task (swallowed, cannot fail the test); surface
scripted-server misuse as transport errors rather than panics in the
transport task; bound the tail awaits with 5s timeouts. Correct the
header comment: a 2026-07-28 server minting a session id is not
spec-legal (SEP-2567 removes sessions and the GET endpoint) — the
scripted server is deliberately non-conforming, which is the point of
receive-side enforcement.
* test: adapt SEP-2260 association tests to ServerPeerInfo
Rebase onto 3.0.0: RoleClient::PeerInfo is now ServerPeerInfo (#1065)
and its constructor takes the protocol version directly.
* chore: add 'Copy' macro to new PeerRequestAssociation
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
---------
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-CIChanges to CI/CD workflows and configurationT-configConfiguration file changesT-coreCore library changesT-examplesExample code changesT-modelModel/data structure changesT-serviceService layer changesT-testTesting related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DiscoverResult contains invalid server_info field

4 participants

@howardjohn@DaleSeo@alexhancock@tsarlandie-oai