You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The server half of this is now partly documented in docs/server.md under "Filtering the Tool Listing per Request" (transport contextExtractor, McpTransportContext.create(Map), addToolFilter), so the remaining work is narrower than the issue text. Three pieces, in the order a reader would hit them.
The docs stop at advertisement. That section warns that a filter controls listing only and that a hidden tool called by name still executes, but it never shows how to reach the same extracted context inside the call handler, which is where enforcement has to happen. The context is already there: McpSyncServerExchange.transportContext() (mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServerExchange.java:61). A short example reading exchange.transportContext().get("perms") inside callHandler would close the loop that the warning opens, and it is the exact thing a reader cannot infer from the filter examples.
Thread the principal through the repository argument rather than a thread local. On the modern server the repositories already receive it: list(McpRequestContext ctx, String cursor) and find(McpRequestContext ctx, String name) in docs/server-modern.md. This follows from the doc's own note that sync filters run on a shared scheduler thread where SecurityContextHolder is not visible: a repository that reads request state from a thread local will look correct in tests and be wrong under concurrent load.
The client side, which is the genuinely undocumented half. The only OAuth mention in docs/client.md is ElicitationCompleteNotification for out-of-band URL flows. Nothing says how to wire a transport into an existing OAuth2 framework: where a request customizer or header supplier is applied, how a 401 from the server is meant to reach the client's token refresh, and what the transport does with the refreshed token on retry. For anyone on Spring Security that is the blocking gap, not the server-side explanation.
One caveat worth placing next to the first examples: with STDIO the context is McpTransportContext.EMPTY, so an authorization path has to degrade to an explicit no-op instead of failing closed, which is the opposite of what most readers will write first.
Activity
media-ctrl commentedon Nov 9, 2026
The server half of this is now partly documented in
docs/server.mdunder "Filtering the Tool Listing per Request" (transportcontextExtractor,McpTransportContext.create(Map),addToolFilter), so the remaining work is narrower than the issue text. Three pieces, in the order a reader would hit them.The docs stop at advertisement. That section warns that a filter controls listing only and that a hidden tool called by name still executes, but it never shows how to reach the same extracted context inside the call handler, which is where enforcement has to happen. The context is already there:
McpSyncServerExchange.transportContext()(mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServerExchange.java:61). A short example readingexchange.transportContext().get("perms")insidecallHandlerwould close the loop that the warning opens, and it is the exact thing a reader cannot infer from the filter examples.Thread the principal through the repository argument rather than a thread local. On the modern server the repositories already receive it:
list(McpRequestContext ctx, String cursor)andfind(McpRequestContext ctx, String name)indocs/server-modern.md. This follows from the doc's own note that sync filters run on a shared scheduler thread whereSecurityContextHolderis not visible: a repository that reads request state from a thread local will look correct in tests and be wrong under concurrent load.The client side, which is the genuinely undocumented half. The only OAuth mention in
docs/client.mdisElicitationCompleteNotificationfor out-of-band URL flows. Nothing says how to wire a transport into an existing OAuth2 framework: where a request customizer or header supplier is applied, how a 401 from the server is meant to reach the client's token refresh, and what the transport does with the refreshed token on retry. For anyone on Spring Security that is the blocking gap, not the server-side explanation.One caveat worth placing next to the first examples: with STDIO the context is
McpTransportContext.EMPTY, so an authorization path has to degrade to an explicit no-op instead of failing closed, which is the opposite of what most readers will write first.