Skip to content

Commit 682a225

Browse files
committed
README: explain McpTransportContext in the client case
Signed-off-by: Daniel Garnier-Moiroux <[email protected]>
1 parent 1ab03e3 commit 682a225

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,118 @@ class McpConfiguration {
507507
}
508508
```
509509

510+
### Customize HTTP requests beyond MCP Security's OAuth2 support
511+
512+
MCP Security's default client support integrates with Spring Security to add OAuth2 support. Essentially, it gets a
513+
token on behalf of the user, and modifies the HTTP request from the Client to the Server, adding that token in an
514+
Authorization header.
515+
516+
If you'd like to modify HTTP requests beyond what MCP Security provide, you can create your own
517+
`McpSyncHttpClientRequestCustomizer` or `ExchangeFilterFunction`.
518+
519+
For HTTP clients:
520+
521+
```java
522+
523+
@Configuration
524+
class McpConfiguration {
525+
526+
@Bean
527+
McpSyncHttpClientRequestCustomizer requestCustomizer() {
528+
return (builder, method, endpoint, body, context) ->
529+
builder
530+
.header("x-custom-header", "custom-value")
531+
.header("x-life-the-universe-everything", "42");
532+
}
533+
534+
}
535+
```
536+
537+
For web clients:
538+
539+
```java
540+
541+
@Configuration
542+
class McpConfiguration {
543+
544+
@Bean
545+
WebClient.Builder mcpWebClientBuilder() {
546+
return WebClient.builder().filter((request, next) -> {
547+
var newRequest = ClientRequest.from(request)
548+
.header("x-custom-header", "custom-value")
549+
.header("x-life-the-universe-everything", "42")
550+
.build();
551+
return next.exchange(newRequest);
552+
});
553+
}
554+
555+
}
556+
```
557+
558+
There is no way to guarantee on which thread these request customizers will run.
559+
As such, thread-locals are not available in these lambda functions.
560+
If you would like to use thread-locals in this context, use a `McpTransportContextProvider` bean.
561+
It can extract thread-locals and make them available in an `McpTransportContext` object.
562+
563+
For HttpClient-based request customizers, the `McpTransportContext` will be available in the `customize` method. See, for example, with a Sync client (async works similarly):
564+
565+
```java
566+
567+
@Configuration
568+
class McpConfiguration {
569+
570+
@Bean
571+
McpSyncClientCustomizer syncClientCustomizer() {
572+
return (name, syncSpec) -> syncSpec.transportContextProvider(() -> {
573+
var myThing = MyThreadLocalThing.get();
574+
return McpTransportContext.create(Map.of("custom-key", myThing));
575+
});
576+
}
577+
578+
@Bean
579+
McpSyncHttpClientRequestCustomizer requestCustomizer() {
580+
return (builder, method, endpoint, body, context) ->
581+
builder.header("x-custom-header", context.get("custom-key"));
582+
}
583+
584+
}
585+
```
586+
587+
For WebClient-based filter functions, the `McpTransportContext` will be available in the Reactor context, under
588+
`McpTransportContext.KEY`:
589+
590+
```java
591+
592+
@Configuration
593+
class McpConfiguration {
594+
595+
@Bean
596+
McpSyncClientCustomizer syncClientCustomizer() {
597+
return (name, syncSpec) -> syncSpec.transportContextProvider(() -> {
598+
var myThing = MyThreadLocalThing.get();
599+
return McpTransportContext.create(Map.of("custom-key", myThing));
600+
});
601+
}
602+
603+
@Bean
604+
WebClient.Builder mcpWebClientBuilder() {
605+
return WebClient.builder()
606+
.filter((request, next) ->
607+
Mono.deferContextual(reactorCtx -> {
608+
var transportCtx = reactorCtx.get(McpTransportContext.class);
609+
String customThing = transportCtx.get("custom-key").toString();
610+
var newRequest = ClientRequest.from(request)
611+
.header("x-custom-header", customThing)
612+
.build();
613+
614+
return next.exchange(newRequest);
615+
})
616+
);
617+
}
618+
619+
}
620+
```
621+
510622
### Work around Spring AI autoconfiguration
511623

512624
Spring AI integrates MCP tools as if they were regular "tools" (e.g. `@Tool` methods).

0 commit comments

Comments
 (0)