It would be nice if we could configure custom headers, for example, we could pass OAuth access token like this:
spring.ai.mcp.client:
sse:
connections:
server1:
url: http://localhost:8080
headers:
Authorization: "Bearer ***"
I'd like to contribute if the proposal is accepted.
Comment From: Kehrlann
@quaff Access tokens have a limited TTL. Since they expire, I don't think they're a great candidate for configuration. I do see the point in having custom "default" headers, though.
Are you aware of the work that we've done in https://github.com/modelcontextprotocol/java-sdk/pull/388 ? We're integrating it in https://github.com/spring-projects/spring-ai/pull/3994, with the ultimate goal to provide OAuth2 integration. This opens a much broader field, where users can customize the entire MCP client HTTP request for HttpClient-based transport.
Comment From: quaff
@Kehrlann I think it's covered by #3994, thanks.
Comment From: quaff
Access tokens have a limited TTL. Since they expire, I don't think they're a great candidate for configuration.
We could create long live or never expired access token like GitHub Personal Access Token, https://github.com/github/github-mcp-server
{
"servers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ${input:github_mcp_pat}"
}
}
},
"inputs": [
{
"type": "promptString",
"id": "github_mcp_pat",
"description": "GitHub Personal Access Token",
"password": true
}
]
}
I do see the point in having custom "default" headers, though.
I think it's reasonable to configure custom headers.
Comment From: dvag-joerg-winter
I think it's reasonable to configure custom headers.
If this relates to the need to pass an existing (out-of-band acquired) access-token to the MCP Client (which uses this token for MCP Server communication), when is this feature to be expected? in 1.1.0 Release or later?
Comment From: zycovoo
I also have this scenario where calling a remote MCP service requires adding authentication in the headers. How can I solve it
Comment From: dvag-joerg-winter
The latest release 1.1.0-M1 mentions something seemingly related to this.. could the spring-ai-examples repo provide some up-to-date example for this? @quaff
Comment From: Kehrlann
@dvag-joerg-winter out-of-band acquired access token should even be possible in the 1.0.x line.
If you are using spring-ai-starter-mcp-client without WebFlux, then provide your own SSE transport as a bean (and probably exclude the auto-configuration), and set a custom HttpRequest.Builder in that transport (see MCP SseTransport). That request builder can have a default header. Something like:
HttpRequest.newBuilder().header("Authorization", "Bearer " + yourToken);
If you're using WebClient with spring-ai-starter-mcp-client-webflux, provide your own WebClient.Builder bean, which will be picked up by the auto-configuration. Something like:
@Bean
public WebClient.Builder webClientBuilder() {
return WebClient.builder()
.defaultHeader("Authorization", "Bearer " + yourToken);
}
Starting with 1.1.0, for HttpClient (spring-ai-starter-mcp-client), you can also use the dynamic McpSyncHttpClientRequestCustomizer (see MCP docs, in Client Transport > HttpClient). If you provide it as a bean, it will be picked up by Spring AI.
Comment From: paulbakker
We have a non-auth related use case for adding headers in configuration. We have a MCP proxy, which requires to set headers about the target sever. E.g.
x-proxy-server-url: https://targetapp:7004/mcp
x-proxy-server-app-name: targetapp
Ideally, this would be configured something like this:
client:
streamable-http:
connections:
server1:
url: http://mcpgateay:7004
endpoint: /proxy-mcp/targetapp
headers:
x-proxy-server-url: https://targetapp:7004/mcp
x-proxy-server-app-name: targetapp
type: SYNC
I know this can be done with a McpSyncHttpClientRequestCustomizer, but it would be nicer to have this in configuration.
Comment From: dvag-joerg-winter
Starting with 1.1.0, for HttpClient (
spring-ai-starter-mcp-client), you can also use the dynamic >McpSyncHttpClientRequestCustomizer(see MCP docs, in Client Transport > HttpClient). If you provide it as a bean, it will be picked up by Spring AI.
I provided the bean and the customization as documented in the Java MCP documentation, like this:
That worked nicely with 1.1.0-M1, thanks!
In the 'customize' call, I can see initialization-phase requests, as well as actual tool-call requests. The possible JsonRpc methods would be these:
Now my follow-up question is this: Would parsing the jsonrpc & deciding based on the 'method' value, which (out-of-band aquired) token to use, be the best/recommended way?
For example, when method is any of these
{"jsonrpc":"2.0","method":"initialize","id":"d7c69513-0", ... }
{"jsonrpc":"2.0","method":"notifications/initialized","id":"d7c69513-1", ... }
{"jsonrpc":"2.0","method":"tools/list","id":"d7c69513-2", ... }
we would use the client_credentials token. This token is from a 'technical' user on behalf of the MCP host application.
And when method is this
{"jsonrpc":"2.0","method":"tools/call","id":"d7c69513-3", ... }
we would use the authorization_code token, which was aquired on behalf of the user (we need the user context in the MCP servers tool method, in order to verify which user has triggered the tool call, because the tool's returned data have to be strictly filtered for this user.
@Kehrlann So the remaining question is, how the authorization_token of the host application can be accessed from my McpSyncHttpClientRequestCustomizer? Would that be via McpTransportContext?
Comment From: quaff
My question is: Authorization Code Flow requires web UI interaction, what if the application is not web based?
Comment From: Kehrlann
@dvag-joerg-winter
Would parsing the jsonrpc & deciding based on the 'method' value, which (out-of-band aquired) token to use, be the best/recommended way?
That's one possibility, yes. That's why we provided the body of the request.
However, specifically in Spring, you can check that the RequestContextHolder contains the current request ; or that the SecurityContext contains an authentication. This is a good proxy to verify whether a user is involved or not. I'm working on it in springai-community/mcp-security (example).
So the remaining question is, how the authorization_token of the host application can be accessed from my McpSyncHttpClientRequestCustomizer? Would that be via McpTransportContext?
Correct. You'd use something like this snippet, then you can plug it in your client:
@Bean
McpSyncClientCustomizer syncClientCustomizer() {
return (name, syncSpec) -> syncSpec.transportContextProvider(new AuthenticationMcpTransportContextProvider());
}
Comment From: Kehrlann
@quaff then you are free to use the client_credentials flow for machine-to-machine, e.g. with this customizer.
If it's not web-based BUT it has a user involved, then you'd have to use something like the device_code grant. Unfortunately it doesn't have support for non-web apps in Spring Security, so you'd need to write a bit of code yourself (see https://github.com/spring-projects/spring-security/issues/11063)
Comment From: quaff
@quaff then you are free to use the
client_credentialsflow for machine-to-machine, e.g. with this customizer.If it's not web-based BUT it has a user involved, then you'd have to use something like the
device_codegrant. Unfortunately it doesn't have support for non-web apps in Spring Security, so you'd need to write a bit of code yourself (see spring-projects/spring-security#11063)
Alternatively, user could generate long-live access token outside of OAuth flow, just like Github PAT.
client_credentials is not satisfied it's not behalf of the user.