Skip to content

sdk: Surface verbose MCP diagnostics (mcp.diagnostic) - #2127

Draft
connor4312 wants to merge 1 commit into
github:mainfrom
connor4312:sdk/mcp-diagnostic
Draft

sdk: Surface verbose MCP diagnostics (mcp.diagnostic)#2127
connor4312 wants to merge 1 commit into
github:mainfrom
connor4312:sdk/mcp-diagnostic

Conversation

@connor4312

Copy link
Copy Markdown

What

Adds SDK support for the runtime's new opt-in MCP diagnostic stream (mcp.diagnostic), which exposes MCP protocol traffic (JSON-RPC frames), HTTP request/response metadata for remote servers, and stderr/process detail for stdio servers.

Two parts:

  1. Regenerated session-event bindings for all five languages (TypeScript, Python, Go, C#, Rust).
  2. A first-class onMcpDiagnostic handler on SessionConfig for the Node SDK.

Why the handler matters

The event is interest-gated. The runtime emits nothing unless a consumer has called session.eventLog.registerInterest({ eventType: "mcp.diagnostic" }). Subscribing through the normal event stream does not register interest — so a consumer who only called session.on(...) would silently receive nothing, with no error and no way to tell whether their MCP server was simply quiet.

Supplying onMcpDiagnostic registers interest automatically on both the create and resume paths, mirroring how onMcpAuthRequest opts in to mcp.oauth_required. Omitting it registers nothing, preserving the zero-cost default.

await using session = await client.createSession({
    mcpServers: { ... },
    onMcpDiagnostic: (d) => {
        switch (d.detail.kind) {
            case "wire_message":
                console.log(`${d.serverName} ${d.detail.direction} ${d.detail.method}`);
                break;
            case "http_exchange":
                console.log(`${d.serverName} ${d.detail.phase} ${d.detail.statusCode}`);
                break;
            case "server_log":
                console.error(`${d.serverName}: ${d.detail.line}`);
                break;
            case "process_lifecycle":
                console.log(`${d.serverName} pid=${d.detail.pid}`);
                break;
        }
    },
});

The payload is a flat object (serverName, transport) with variant-specific data under a kind-discriminated detail union, so consumers narrow on detail.kind. Payloads are redacted and size-capped by the runtime; truncated signals that a captured payload was clipped.

Blocked on the runtime change

This cannot merge until github/copilot-agent-runtime#13976 merges and ships in a published @github/copilot package. Opened as a draft for that reason.

The Codegen Check workflow runs npm ci and regenerates from the schemas inside the published @github/copilot package. Until that package contains mcp.diagnostic, regeneration will produce output without it and the committed files here will differ — so that check is expected to fail until the runtime lands. The generated files in this PR were produced against a locally built runtime containing the change.

Note on unrelated regenerated drift

Regeneration also picks up several session-event types that had drifted ahead of these bindings — for example AutoApprovalJudgeFailureReason, CompactionTrigger, FactoryRunUpdated, PermissionRequestShellCommandSegment, ScheduleOrigin, SystemNotificationUnclassified.

These are unrelated to MCP diagnostics; it is simply codegen catching up to the runtime schema. They are self-consistent (added to both the type definitions and the serializer registrations), and excluding them would leave the generated files out of sync with what npm run generate produces.

Validation

  • npm run typecheck — clean
  • npm run format:check — clean
  • npm run lint — 3 warnings, all pre-existing on main (verified by stashing)
  • McpDiagnostic types verified present and populated in all five languages: nodejs/src/generated/session-events.ts, python/copilot/generated/session_events.py, go/rpc/zsession_events.go, dotnet/src/Generated/SessionEvents.cs, rust/src/generated/session_events.rs

Verified end to end against a locally built runtime with a real stdio MCP server and a real streamable-HTTP MCP server (@modelcontextprotocol/server-everything): 30 diagnostics delivered to onMcpDiagnostic across both transports, Authorization redacted while unrelated headers passed through, delivered exactly once (push count matched event-log count), and a control run without the handler received zero events while both tool calls still succeeded.

Scope

Only the Node SDK gets the ergonomic onMcpDiagnostic handler. The other languages get regenerated types; equivalent handlers there can follow separately if wanted.

Adds SDK support for the runtime's new opt-in MCP diagnostic stream, which
exposes MCP protocol traffic (JSON-RPC frames), HTTP request/response
metadata for remote servers, and stderr/process detail for stdio servers.

Regenerates the session-event bindings for all five languages, and adds a
first-class `onMcpDiagnostic` handler to `SessionConfig` for TypeScript.

The handler is what makes this usable. The event is interest-gated: the
runtime emits nothing unless a consumer has called
`session.eventLog.registerInterest({ eventType: "mcp.diagnostic" })`.
Subscribing through the normal event stream does not register interest, so
a consumer who only called `session.on(...)` would silently receive
nothing. Supplying `onMcpDiagnostic` registers interest automatically on
both the create and resume paths, mirroring how `onMcpAuthRequest` opts in
to `mcp.oauth_required`; omitting it registers nothing and preserves the
zero-cost default.

The payload is a flat object (`serverName`, `transport`) with the
variant-specific data under a `kind`-discriminated `detail` union, so
consumers narrow on `detail.kind`. Payloads are redacted and size-capped
by the runtime; `truncated` signals that a captured payload was clipped.

Note the regenerated output also picks up several session-event types that
had drifted ahead of these bindings (for example
`AutoApprovalJudgeFailureReason`, `CompactionTrigger`, `FactoryRunUpdated`).
Those are unrelated to MCP diagnostics and are simply codegen catching up.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 29, 2026 16:33
case _: raise ValueError(f"Unknown CitationLocation type: {kind!r}")


def _load_McpDiagnosticDetail(obj: Any) -> "McpDiagnosticDetail":

Copilot AI 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.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds opt-in SDK support for the runtime’s new verbose MCP diagnostic stream (mcp.diagnostic), including regenerated schema bindings across languages and a first-class Node SDK handler that auto-registers event-log interest.

Changes:

  • Regenerated session-event bindings across Rust/Python/.NET (and updated Node generated types) to include mcp.diagnostic and other schema drift.
  • Added onMcpDiagnostic / McpDiagnosticHandler to the Node SDK, wiring event dispatch + automatic interest registration on create/resume.
  • Added Node tests, docs, and an example demonstrating diagnostic handling.
Show a summary per file
File Description
rust/src/generated/session_events.rs Adds mcp.diagnostic and other regenerated event/type updates for Rust.
python/copilot/generated/session_events.py Adds mcp.diagnostic types and related regenerated schema drift for Python.
nodejs/test/session-event-types.test.ts Verifies new diagnostic-related exports/types are available from the package root.
nodejs/test/client.test.ts Tests dispatch of diagnostics + correct interest registration behavior on create/resume.
nodejs/src/types.ts Introduces McpDiagnosticHandler and SessionConfig.onMcpDiagnostic.
nodejs/src/session.ts Dispatches mcp.diagnostic events to the configured handler.
nodejs/src/index.ts Re-exports the new McpDiagnosticHandler type.
nodejs/src/generated/session-events.ts Adds generated TS types for mcp.diagnostic and other schema drift.
nodejs/src/client.ts Implements create/resume interest registration for mcp.diagnostic when handler is provided.
nodejs/examples/mcp-diagnostics-example.ts Demonstrates using onMcpDiagnostic with an MCP server.
dotnet/src/Generated/SessionEvents.cs Adds mcp.diagnostic event/types and other regenerated schema drift for .NET.
dotnet/src/Generated/Rpc.cs Registers new generated session-event types for .NET JSON serialization.
docs/features/mcp.md Documents Node SDK MCP diagnostics and how interest-gating works.

Review details

Files not reviewed (3)
  • go/rpc/zsession_encoding.go: Generated file
  • go/rpc/zsession_events.go: Generated file
  • go/zsession_events.go: Generated file
  • Files reviewed: 8/16 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment thread nodejs/src/session.ts
Comment on lines +765 to +771
private async _executeMcpDiagnosticHandler(diagnostic: McpDiagnosticData): Promise<void> {
try {
await this.mcpDiagnosticHandler!(diagnostic, { sessionId: this.sessionId });
} catch {
// Diagnostic handlers are observational and must not interrupt session event dispatch.
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants