Complete MCP 2026 client support (#35725)

## What changed

- Drive multi-round `tools/call` and `resources/read` requests through
  `input_required` responses, preserving opaque request state and elicitation
  metadata across JSON, SSE, and stdio transports.
- Decode modern discovery and elicitation result shapes while retaining legacy
  initialization, elicitation defaults, and discovery fallback behavior.
- Apply the 8 MiB modern-protocol response limit to JSON, SSE events, and stdio
  messages, and reject executor stream recovery when output sequence gaps would
  corrupt the protocol stream.

## Testing

Added integration coverage for discovery, multi-round requests, legacy
fallbacks, message limits, stdio launchers, SSE responses, session recovery,
and executor output recovery.

GitOrigin-RevId: d26a08883a2825e8ce2408ac498114b33859225e
This commit is contained in:
thomas authored and copyberry committed 2026-07-28 06:06:58 +00:00
1 parent be2e4afcd7
commit f2bee854a7
13 files changed
+2352 -39

No files matched your search

@@ -356,11 +356,24 @@ impl ExecutorProcessTransport {
.await
.map_err(io::Error::other)?;
for chunk in response.chunks {
let expected_seq = self.last_seq.saturating_add(1);
if chunk.seq > expected_seq {
return Err(self.close_for_lost_output(expected_seq, chunk.seq));
}
self.push_process_output_if_new(chunk);
if self.closed {
return Ok(());
}
}
// Process reads include output chunks but not the sequenced `Exited`
// and `Closed` events. Account for those terminal events without
// allowing an evicted output chunk to be silently spliced into MCP.
let terminal_event_count = u64::from(response.exited) + u64::from(response.closed);
let next_output_seq = response.next_seq.saturating_sub(terminal_event_count);
let expected_seq = self.last_seq.saturating_add(1);
if next_output_seq > expected_seq {
return Err(self.close_for_lost_output(expected_seq, next_output_seq));
}
self.last_seq = self.last_seq.max(response.next_seq.saturating_sub(1));
if let Some(message) = response.failure {
warn!(
@@ -374,6 +387,18 @@ impl ExecutorProcessTransport {
Ok(())
}
fn close_for_lost_output(&mut self, expected_seq: u64, received_seq: u64) -> io::Error {
self.stdout.clear();
self.stderr.clear();
self.closed = true;
io::Error::new(
io::ErrorKind::InvalidData,
format!(
"remote MCP server output stream lost process events: expected sequence {expected_seq}, received {received_seq}"
),
)
}
fn push_process_output_if_new(&mut self, chunk: ProcessOutputChunk) {
if !self.should_accept_seq(chunk.seq) {
return;