GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — SEP-3371: Consistent SDK extension points by sambhav · Pull Request #3371 · modelcontextprotocol/modelcontextprotocol Conversation
|
|
Updated after the Core Maintainers meeting, which asked for three things. Transport-level middleware for auth and Server Card extensions. C# already layers it this way: ASP.NET Core middleware for HTTP and auth ( These were added after checking that SDKs already offer the underlying seams:
Every server can sit behind host-framework middleware, and none blocks a Discoverable, versioned extension points. SDKs document the extension points they support and expose them as a local set, e.g. Works across type systems. Appendix D rates every requirement for these six SDKs. None of the gaps comes from the language itself; each is a missing API, such as Rust's single Still no wire changes. @felixweinberger, could you take another look? SDK maintainers: corrections to Appendix D for your SDK are welcome. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
1eb92f8 to
cc87130
Compare
|
|
||
| Server Card serves a document beneath the MCP endpoint, and authorization extensions serve protected resource metadata at well-known paths, both outside the MCP endpoint's JSON-RPC processing. | ||
|
|
||
| - SDKs **MUST** let applications and extensions serve additional HTTP routes, for any HTTP method including `OPTIONS`, with full control of status, headers, and body. Most SDKs mount the MCP endpoint in a host framework, and documented [host composition](#host-frameworks) that lets an extension package add its routes satisfies this. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Is this for the top level route only? or for any route?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Any route.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| Server Card serves a document beneath the MCP endpoint, and authorization extensions serve protected resource metadata at well-known paths, both outside the MCP endpoint's JSON-RPC processing. | ||
|
|
||
| - SDKs **MUST** let applications and extensions serve additional HTTP routes, for any HTTP method including `OPTIONS`, with full control of status, headers, and body. Most SDKs mount the MCP endpoint in a host framework, and documented [host composition](#host-frameworks) that lets an extension package add its routes satisfies this. | ||
| - Routes **MUST** be possible both beneath the MCP endpoint path, such as `<mcp-endpoint>/server-card`, and at origin-level paths such as `/.well-known/oauth-protected-resource` and `/.well-known/ai-catalog.json`. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Does this apply just to .well-known or outside of it?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Any route as applicable
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I still believe we need some form of versioning that is independent from the protocol version. We are describing an API interface of a certain shape and guarantees. If that changes, we have no way to communicate to people which API interface version we support. I understand in general updating it will be tied to MCP release cadence because of conformance testing, however:
I would rather prefer if we have Extension API Interface version 1 that people can introspect at runtime via Extensions.InterfaceVersion. In the release notes of the SDK we can always refer to which Extension API Interface it implements. For example, if V1 and V2 is backwards compatible, an SDK might chose to ship v2 anytime before the next spec release. There is no way to communicate that an SDK supports a next generation interface in the current SEP.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
|
||
| ```typescript | ||
| // Pseudocode: Handler is an invented message-stream interface. | ||
| function searchMiddleware(next: Handler): Handler { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
What's a handler here? The distinction between JSON-RPC middleware and a transport middleware is exactly in the type information about the middleware. I would have expected that a JSON-RPC Middleware will return some Request or Response JSON-RPC handler, where as a transport or middleware just has a general type T that represents a transport and has some associated interface that is dependent on the SDK that middleware can manipulate.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The Handler in the example is illustrative; its shape depends on the language and SDK. By typed middleware I meant method-specific middleware, like tool calls, alongside arbitrary json rpc middleware. Both can be useful, and method-specific middleware stays a MAY.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
| ### 4. Transport hooks | ||
|
|
||
| Transport hooks let extensions add to a transport without wrapping message processing. They are not middleware: an HTTP route handles its own requests rather than calling the next layer, and a launch setting configures a process rather than intercepting messages. As with transport middleware, extensions use them only for behaviour that cannot be expressed on JSON-RPC messages. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I am not sure I understand the need for transport hooks. I feel a transport hook is strictly a subset of a middleware.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
HTTP routing can be implemented through middleware, so “they are not middleware” is too categorical. Launch arguments and environment variables are configuration before a process starts. I'd describe routing and launch configuration as separate capabilities, without saying that routing cannot be implemented through middleware.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Agreed, routes can be implemented through middleware. Changed the wording to describe route contribution and launch configuration as separate capabilities.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think saying that frameworks must provide a way for routing and launch configuration is enough (to @halter73's point)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@dsp-ant, I'm not sure I see the value of cross-SDK interface versioning. Given that extensions already need compatible SDK package versions to access the concrete APIs, what code would use Extensions.InterfaceVersion to make a decision that package compatibility or individual feature detection cannot make?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| Some methods produce more than one message. Middleware **MUST** be able to act on each message as it is produced or consumed, not only on a final result. Adding middleware **MUST NOT** change delivery order, cancellation, or error propagation, or force buffering of the whole stream. This covers protocol messages, not transport frames. | ||
|
|
||
| In the sending direction, middleware acts on an outgoing message before it is sent and on any messages returned by that operation. For a client request, this covers the outgoing request and the peer's response messages, including supported intermediate results. An outgoing notification has no response of its own. A notification emitted on a `subscriptions/listen` stream can be handled as a yielded message in the receiving chain for that request. A separate sending chain is not required when the receiving chain already covers those outbound messages. SDKs choose the API shape; the generator above illustrates receiving middleware, not a required signature for every direction. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
On stdio, a client cannot reliably associate deprecated logging notifications with a particular operation. Should these messages still be interceptable without request correlation, or can they be excluded from the required client-side middleware coverage?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| #### Host frameworks | ||
|
|
||
| An SDK **MAY** meet the transport middleware and [transport hook](#4-transport-hooks) requirements through the host's HTTP stack, such as ASP.NET Core middleware and endpoints, Express or Hono, ASGI, `net/http` handlers and round trippers, Tower layers and routers, or servlet filters, if it exposes its HTTP handler and HTTP client as composable units and documents how to pass context into message processing. A dedicated MCP API is not required where the host's mechanism suffices, but an extension package **MUST** be able to contribute its transport middleware and hooks through the same registration as its other contributions or through documented host composition. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The host-framework section permits contributions through documented host composition. Does an extension still need to declare every contribution in one SDK registration, or can its package configure SDK services/filters and host middleware/routes separately? I'd like normal host configuration and options validation to satisfy this, with setup failures preventing activation, rather than require a combined plugin registration framework.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Separate host composition makes sense; clarified that a package can configure those contributions separately. I'd still keep dependency declaration and verification as capabilities the SDK provides.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| ### 4. Transport hooks | ||
|
|
||
| Transport hooks let extensions add to a transport without wrapping message processing. They are not middleware: an HTTP route handles its own requests rather than calling the next layer, and a launch setting configures a process rather than intercepting messages. As with transport middleware, extensions use them only for behaviour that cannot be expressed on JSON-RPC messages. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
HTTP routing can be implemented through middleware, so “they are not middleware” is too categorical. Launch arguments and environment variables are configuration before a process starts. I'd describe routing and launch configuration as separate capabilities, without saying that routing cannot be implemented through middleware.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
|
||
| - A dependency **MUST NOT** be considered unmet solely because it was registered or enabled after the extension that requires it. | ||
| - An extension's handlers and middleware **MUST NOT** run unless its dependencies are satisfied by the local configuration applicable to that execution. | ||
| - SDKs choose when to check dependencies and **SHOULD** check before serving where possible. A configuration-time check is sufficient when the relevant configuration cannot change. Where it can change or vary by request, the SDK **MUST** ensure the dependencies remain satisfied before running the extension. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Can we add a concrete example where dependency validation must happen during execution rather than during configuration? The examples here seem satisfiable with startup validation. In C#, we'd prefer required DI services and options validation where those suffice, rather than introduce a parallel dependency system.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
One example is a mode selected from the peer's capabilities that needs another local extension. That dependency would be checked once the mode is selected, before it runs. These cases are fairly niche though; fixed dependencies can still be checked at startup.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| Extension packages and applications need to know which extension points an SDK supports before relying on them, both in documentation and in code. | ||
|
|
||
| - SDKs **MUST** document each extension point that they support, with the APIs that provide it and their ordering rules, in one place reachable from the SDK's main documentation. | ||
| - SDKs **MUST** expose, through a public API, the set of extension points in this SEP that they support, for each role, client and server, they implement. An identifier is included only if the SDK meets every requirement for that extension point in that role. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
What is the concrete use case for requiring runtime support introspection? An extension still needs an SDK-specific implementation and compatible SDK package versions to use these APIs. Could we use the standardized identifiers for documentation and conformance reporting while making the public introspection API optional?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Made this optional. It was added in response to core maintainer feedback, but I'm fairly flexible here. @dsp-ant, did I capture your suggestion correctly? It would help to understand the use case for making it required.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
dynamic languages like Python might use that form of introspection to branch code to guarantee BC. E.g
match mcp.extension.version: case 2: mcp.extension.register(MyV2Extension()) case 1: mcp.extension.register(MyV1Extension())
For me it just guarantees introspection.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
a5320e6 to
cc87130
Compare
Maintainer Activity CheckHi @felixweinberger! You're assigned to this SEP but there hasn't been any activity from you in 18 days. Please provide an update on:
If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer. This is an automated message from the SEP lifecycle bot. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
I see that. I am okay if it's purely something that's worth documenting |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
At least 1 approving review is required to merge this pull request.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.