From b2b0100eacd42c22713ba5a282c4996c32f6ffbb Mon Sep 17 00:00:00 2001 From: Ukjae Jeong Date: Tue, 16 Jun 2026 18:25:55 +0100 Subject: [PATCH] Align capability key to SEP-2133 extensions format Both SDKs now use Capabilities.Extensions with key "io.modelcontextprotocol/interceptors". Previously C# used the short key "interceptors" and Go used Capabilities.Experimental. SEP capability example updated to match. Signed-off-by: Ukjae Jeong --- csharp/sdk/CLAUDE.md | 2 +- .../InterceptorProtocolConstants.cs | 2 +- .../Protocol/InterceptorsCapability.cs | 2 +- .../McpInterceptorGatewayTests.cs | 6 +++--- docs/sep.md | 12 +++++++----- go/sdk/interceptors/extension/server.go | 11 +++++------ 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/csharp/sdk/CLAUDE.md b/csharp/sdk/CLAUDE.md index 05d2e11..e7d9da5 100644 --- a/csharp/sdk/CLAUDE.md +++ b/csharp/sdk/CLAUDE.md @@ -13,7 +13,7 @@ dotnet test # 66 tests across the interceptor test project **Why message filter, not handlers**: `McpServerHandlers` and `McpServerImpl` are `internal` in the SDK. We can't register handlers for new JSON-RPC methods from outside. Instead we use `McpServerOptions.Filters.Message.IncomingFilters` to intercept `interceptors/list` and `interceptor/invoke`, handle them, send `JsonRpcResponse` via `context.Server.SendMessageAsync()`, and skip calling `next`. See `InterceptorMessageFilter.cs`. Chain execution is no longer a wire method — it's SDK orchestration in `Client/InterceptorChainOrchestrator.cs`. -**Why `ServerCapabilities.Extensions`**: The SDK's intended mechanism for protocol extensions. Requires `#pragma warning disable MCPEXP001`. We advertise `InterceptorsCapability { SupportedEvents }` under `Extensions["interceptors"]`. +**Why `ServerCapabilities.Extensions`**: The SDK's intended mechanism for protocol extensions. Requires `#pragma warning disable MCPEXP001`. We advertise `InterceptorsCapability { SupportedEvents }` under `Extensions["io.modelcontextprotocol/interceptors"]`. **Client `SendRequestAsync`**: The public overload (`McpSession.Methods.cs:24`) takes `JsonSerializerOptions`. We pass `InterceptorJsonUtilities.DefaultOptions` which chains `McpJsonUtilities.DefaultOptions` + our `InterceptorJsonContext`. The internal overload takes `JsonTypeInfo` — we can't use it. diff --git a/csharp/sdk/src/ModelContextProtocol.Interceptors/InterceptorProtocolConstants.cs b/csharp/sdk/src/ModelContextProtocol.Interceptors/InterceptorProtocolConstants.cs index cbb0528..fd341f6 100644 --- a/csharp/sdk/src/ModelContextProtocol.Interceptors/InterceptorProtocolConstants.cs +++ b/csharp/sdk/src/ModelContextProtocol.Interceptors/InterceptorProtocolConstants.cs @@ -2,5 +2,5 @@ namespace ModelContextProtocol.Interceptors; internal static class InterceptorProtocolConstants { - internal const string ExtensionCapabilityKey = "interceptors"; + internal const string ExtensionCapabilityKey = "io.modelcontextprotocol/interceptors"; } diff --git a/csharp/sdk/src/ModelContextProtocol.Interceptors/Protocol/InterceptorsCapability.cs b/csharp/sdk/src/ModelContextProtocol.Interceptors/Protocol/InterceptorsCapability.cs index 41b6957..80b6683 100644 --- a/csharp/sdk/src/ModelContextProtocol.Interceptors/Protocol/InterceptorsCapability.cs +++ b/csharp/sdk/src/ModelContextProtocol.Interceptors/Protocol/InterceptorsCapability.cs @@ -4,7 +4,7 @@ namespace ModelContextProtocol.Interceptors.Protocol; /// /// Represents the interceptors capability advertised by a server during initialization. -/// This is placed in ServerCapabilities.Extensions["interceptors"]. +/// This is placed in ServerCapabilities.Extensions["io.modelcontextprotocol/interceptors"]. /// public sealed class InterceptorsCapability { diff --git a/csharp/sdk/tests/ModelContextProtocol.Interceptors.Tests/McpInterceptorGatewayTests.cs b/csharp/sdk/tests/ModelContextProtocol.Interceptors.Tests/McpInterceptorGatewayTests.cs index 55d3dce..51320ab 100644 --- a/csharp/sdk/tests/ModelContextProtocol.Interceptors.Tests/McpInterceptorGatewayTests.cs +++ b/csharp/sdk/tests/ModelContextProtocol.Interceptors.Tests/McpInterceptorGatewayTests.cs @@ -272,7 +272,7 @@ public async Task ConfigureServerOptions_MirrorsBackendExtensionsInTransparentMo var caps = fixture.ProxyClient.ServerCapabilities; Assert.NotNull(caps.Extensions); Assert.True(caps.Extensions!.ContainsKey("com.example/backend")); - Assert.False(caps.Extensions.ContainsKey("interceptors")); + Assert.False(caps.Extensions.ContainsKey("io.modelcontextprotocol/interceptors")); #pragma warning restore MCPEXP001 } @@ -292,7 +292,7 @@ public async Task ConfigureServerOptions_DoesNotAdvertiseInterceptorsCapabilityB #pragma warning disable MCPEXP001 var caps = fixture.ProxyClient.ServerCapabilities; - Assert.False(caps?.Extensions?.ContainsKey("interceptors") ?? false); + Assert.False(caps?.Extensions?.ContainsKey("io.modelcontextprotocol/interceptors") ?? false); #pragma warning restore MCPEXP001 } @@ -318,7 +318,7 @@ public async Task ConfigureServerOptions_AdvertisesInterceptorsCapabilityWhenEna #pragma warning disable MCPEXP001 var caps = fixture.ProxyClient.ServerCapabilities; Assert.NotNull(caps?.Extensions); - Assert.True(caps!.Extensions!.ContainsKey("interceptors")); + Assert.True(caps!.Extensions!.ContainsKey("io.modelcontextprotocol/interceptors")); Assert.True(caps.Extensions.ContainsKey("com.example/backend")); #pragma warning restore MCPEXP001 } diff --git a/docs/sep.md b/docs/sep.md index 1be3532..4818849 100644 --- a/docs/sep.md +++ b/docs/sep.md @@ -1346,13 +1346,15 @@ During initialization, servers declare interceptor support: jsonrpc: "2.0", id: 1, result: { - protocolVersion: "2024-11-05", + protocolVersion: "2025-06-18", capabilities: { - interceptor?: { - // Events this server's interceptor can handle - supportedEvents: InterceptionEvent[]; - }, // ...existing capabilities + extensions: { + "io.modelcontextprotocol/interceptors": { + // Events this server's interceptors can handle + supportedEvents: InterceptionEvent[]; + } + } }, serverInfo: { name: "example-server", diff --git a/go/sdk/interceptors/extension/server.go b/go/sdk/interceptors/extension/server.go index 8543383..0aa975b 100644 --- a/go/sdk/interceptors/extension/server.go +++ b/go/sdk/interceptors/extension/server.go @@ -15,7 +15,7 @@ import ( "github.com/modelcontextprotocol/ext-interceptors/go/sdk/interceptors/chain" ) -// extensionID is the capability key used in Capabilities.Experimental +// extensionID is the capability key used in Capabilities.Extensions const extensionID = "io.modelcontextprotocol/interceptors" // Option configures an Extension. @@ -163,8 +163,7 @@ func (e *Extension) initMiddleware() mcp.Middleware { // enrichInitResult injects interceptor capability metadata into the // InitializeResult, following the same pattern as the variants extension. -// The capability is declared under Capabilities.Experimental so it works -// without upstream go-sdk changes. +// The capability is declared under Capabilities.Extensions per SEP-2133. func (e *Extension) enrichInitResult(result mcp.Result) (mcp.Result, error) { initResult, ok := result.(*mcp.InitializeResult) if !ok { @@ -174,8 +173,8 @@ func (e *Extension) enrichInitResult(result mcp.Result) (mcp.Result, error) { if initResult.Capabilities == nil { initResult.Capabilities = &mcp.ServerCapabilities{} } - if initResult.Capabilities.Experimental == nil { - initResult.Capabilities.Experimental = make(map[string]any) + if initResult.Capabilities.Extensions == nil { + initResult.Capabilities.Extensions = make(map[string]any) } all := e.getInterceptors() @@ -193,7 +192,7 @@ func (e *Extension) enrichInitResult(result mcp.Result) (mcp.Result, error) { events = append(events, ev) } - initResult.Capabilities.Experimental[extensionID] = map[string]any{ + initResult.Capabilities.Extensions[extensionID] = map[string]any{ "supportedEvents": events, }