Align capability key to SEP-2133 extensions format (#25)
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. https://modelcontextprotocol.io/seps/2133-extensions --------- Signed-off-by: Ukjae Jeong <jeongukjae@gmail.com>
This commit is contained in:
parent
725d22fd28
commit
99bc7c9bb0
8 changed files with 20 additions and 19 deletions
|
|
@ -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<T>` — we can't use it.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@ namespace ModelContextProtocol.Interceptors;
|
|||
|
||||
internal static class InterceptorProtocolConstants
|
||||
{
|
||||
internal const string ExtensionCapabilityKey = "interceptors";
|
||||
internal const string ExtensionCapabilityKey = "io.modelcontextprotocol/interceptors";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ namespace ModelContextProtocol.Interceptors.Protocol;
|
|||
|
||||
/// <summary>
|
||||
/// Represents the interceptors capability advertised by a server during initialization.
|
||||
/// This is placed in <c>ServerCapabilities.Extensions["interceptors"]</c>.
|
||||
/// This is placed in <c>ServerCapabilities.Extensions["io.modelcontextprotocol/interceptors"]</c>.
|
||||
/// </summary>
|
||||
public sealed class InterceptorsCapability
|
||||
{
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ public class McpInterceptorGatewayTests
|
|||
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 class McpInterceptorGatewayTests
|
|||
|
||||
#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 class McpInterceptorGatewayTests
|
|||
#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
|
||||
}
|
||||
|
|
|
|||
12
docs/sep.md
12
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",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Status of this Go SDK implementation against the SEP-2624 interceptor proposal.
|
|||
| Audit mode | `ModeAudit` records results without blocking; mutated payloads not propagated |
|
||||
| Timeout & context | Per-interceptor timeouts via `InvokeParams.TimeoutMs`, chain-level context cancellation, `InvocationContext` with principal/traceId |
|
||||
| Receiving direction (client → server) | All server-side method calls intercepted via `AddReceivingMiddleware` |
|
||||
| Capability declaration | Interceptor metadata injected into `initialize` response via `Capabilities.Experimental` |
|
||||
| Capability declaration | Interceptor metadata injected into `initialize` response via `Capabilities.Extensions` |
|
||||
| First-party (in-process) deployment | `Server.LocalChain()` creates an in-memory transport `chain.Chain`; interceptors invoked via JSON-RPC even in-process |
|
||||
| Third-party and hybrid deployment | Chain entries can point to any `*mcp.ClientSession` — local (in-memory), stdio, or HTTP transport |
|
||||
| `json.RawMessage` payloads | Interceptor handlers receive `json.RawMessage` via `interceptor/invoke`, matching the SEP's JSON-level payload model |
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ behavior as remote interceptor servers.
|
|||
|
||||
During initialization, a lightweight middleware intercepts the `"initialize"`
|
||||
response and injects interceptor metadata into
|
||||
`Capabilities.Experimental["io.modelcontextprotocol/interceptors"]`. This
|
||||
`Capabilities.Extensions["io.modelcontextprotocol/interceptors"]`. This
|
||||
follows the same pattern as the variants extension
|
||||
(`io.modelcontextprotocol/server-variants`).
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue