Disclaimer
I haven't tried using the recent OAuth samples (my source isn't 100% setup to support it yet), but I'm pretty sure this issue applies based on analyzing it. I also did not try implementing a custom transport.
Goal
Get authentication details (specifically the authenticated identity) visible to my tool methods.
What I've done
My source supports API keys - just straight auth, no token exchange or the like. Using the MCP inspector, I've set up the connection with Bearer tokens with the key. I've successfully implemented an Axum middleware that handles the Authentication, returning to the server when it fails (example below).
The problem
Due to the abstractions between Axum and the rest of the MCP framework, there doesn't appear to be a clean way to get any details of the middleware (or anything else from the router for that matter) to the MCP server.
My approach was to assign an Identity object to the MCP toolbox struct and assign it "at some point". But this was just my initial thought. I don't care as long as I can get it from the tool calls.
Ex:
#[derive(Clone)]pubstructCounter{counter:Arc<Mutex<i32>>,identity:Identity}#[tool(tool_box)]implCounter{#[allow(dead_code)]pubfnnew() -> Self{let identity = get_identity();// <- unclear how!Self{counter:Arc::new(Mutex::new(0)),identity: identity // Either set it here or make it optional and assign it after construction}}#[tool(description = "Increment the counter by 1")]asyncfnincrement(&self) -> Result<CallToolResult,McpError>{letmut counter = self.counter.lock().await;*counter += 1;do_something_with_identity(self.identity);Ok(CallToolResult::success(vec![Content::text(
counter.to_string(),)]))}}Since there's no obvious way when constructing the service, I tried assigning it during initialization. For the calls in ServerHandler that have it, the context parameter doesn't expose - publicly or privately - info about the Axum Router. I tried using the Extensions from the SDK (those available in context for some of the calls) but I couldn't find a way to set them from the Axum "side".
The question
Am I missing something obvious?
If not, I think this would be a critical feature. I'm happy to implement it if there's consensus on how (or if not, I can propose a solution).
Example code that sets Axum extensions
I can provide a full example if it helps.
pubasyncfnauth(mutreq:Request,next:Next) -> Result<Response,StatusCode>{// This middlware works as expectedlet auth_header = req
.headers().get(header::AUTHORIZATION).and_then(|header| header.to_str().ok()).ok_or(StatusCode::UNAUTHORIZED)?;// Actual authentication happens hereifletSome(identity) = authorize_current_user(auth_header).await{// Set an Axum extension for the request
req.extensions_mut().insert(identity.clone());Ok(next.run(req).await)}else{Err(StatusCode::UNAUTHORIZED)}}asyncfnserver_entrypoint() -> Result<()>{let mcp_config = MCPServerConfig::default();
tracing::info!("Config for MCP Server: {:?}", mcp_config);let bind_address = format!("{}:{}", mcp_config.mcp_bind_address, mcp_config.mcp_bind_port);let config = SseServerConfig{bind: bind_address.parse()?,sse_path:"/sse".to_string(),post_path:"/message".to_string(),ct: tokio_util::sync::CancellationToken::new(),sse_keep_alive:None};let(sse_server, router) = SseServer::new(config);let listener = tokio::net::TcpListener::bind(sse_server.config.bind).await?;let ct = sse_server.config.ct.child_token();// Add auth middlewarelet router = router
.route_layer(middleware::from_fn(auth));let server = axum::serve(listener, router).with_graceful_shutdown(asyncmove{
ct.cancelled().await;
tracing::info!("SSE server cancelled");});
tokio::spawn(asyncmove{ifletErr(e) = server.await{
tracing::error!(error = %e,"sse server shutdown with error");}});let ct = sse_server.with_service(service::Counter::new);graceful_shutdown().await;
ct.cancel();Ok(())}
Disclaimer
I haven't tried using the recent OAuth samples (my source isn't 100% setup to support it yet), but I'm pretty sure this issue applies based on analyzing it. I also did not try implementing a custom transport.
Goal
Get authentication details (specifically the authenticated identity) visible to my tool methods.
What I've done
My source supports API keys - just straight auth, no token exchange or the like. Using the MCP inspector, I've set up the connection with Bearer tokens with the key. I've successfully implemented an Axum middleware that handles the Authentication, returning to the server when it fails (example below).
The problem
Due to the abstractions between Axum and the rest of the MCP framework, there doesn't appear to be a clean way to get any details of the middleware (or anything else from the router for that matter) to the MCP server.
My approach was to assign an
Identityobject to the MCP toolbox struct and assign it "at some point". But this was just my initial thought. I don't care as long as I can get it from the tool calls.Ex:
Since there's no obvious way when constructing the service, I tried assigning it during initialization. For the calls in
ServerHandlerthat have it, thecontextparameter doesn't expose - publicly or privately - info about the AxumRouter. I tried using theExtensionsfrom the SDK (those available in context for some of the calls) but I couldn't find a way to set them from the Axum "side".The question
Am I missing something obvious?
If not, I think this would be a critical feature. I'm happy to implement it if there's consensus on how (or if not, I can propose a solution).
Example code that sets Axum extensions
I can provide a full example if it helps.