@@ -157,6 +157,34 @@ impl McpMuxGatewayHandler {
157157 }
158158 }
159159
160+ /// Resolve the (Space, FeatureSet ids) the gateway should route a
161+ /// session through. The OAuth-context space is *not* used for routing
162+ /// — when a `WorkspaceBinding` matches, the binding's target space is
163+ /// authoritative and may differ from the OAuth-bound space (this is
164+ /// the whole point of workspace-root routing). Pass the returned
165+ /// `space_id` to every `feature_service.get_*_for_grants` /
166+ /// `routing_service.call_tool` invocation; otherwise the lookup queries
167+ /// the wrong space and returns 0 matches.
168+ async fn resolve_routing (
169+ & self ,
170+ session_id : Option < & str > ,
171+ ) -> Result < ( uuid:: Uuid , Vec < String > ) , McpError > {
172+ let resolved = self
173+ . services
174+ . authorization_service
175+ . resolve ( session_id)
176+ . await
177+ . map_err ( |e| McpError :: internal_error ( format ! ( "Failed to resolve: {e}" ) , None ) ) ?;
178+ let space_id = resolved. space_id . ok_or_else ( || {
179+ McpError :: internal_error ( "No space resolved (no default space configured)" , None )
180+ } ) ?;
181+ let feature_set_ids = resolved
182+ . feature_set_id
183+ . map ( |fs| vec ! [ fs] )
184+ . unwrap_or_default ( ) ;
185+ Ok ( ( space_id, feature_set_ids) )
186+ }
187+
160188 /// Build InitializeResult with negotiated protocol version
161189 fn build_initialize_result ( & self , protocol_version : ProtocolVersion ) -> InitializeResult {
162190 let info = self . get_info ( ) ;
@@ -416,28 +444,19 @@ impl ServerHandler for McpMuxGatewayHandler {
416444 _params : Option < PaginatedRequestParams > ,
417445 context : RequestContext < RoleServer > ,
418446 ) -> Result < ListToolsResult , McpError > {
419- let oauth_ctx = self
420- . get_oauth_context ( & context. extensions )
421- . map_err ( |e| McpError :: invalid_params ( e. to_string ( ) , None ) ) ?;
422-
423- // Get client's grants
424- let feature_set_ids = self
425- . services
426- . authorization_service
427- . get_client_grants (
428- & oauth_ctx. client_id ,
429- & oauth_ctx. space_id ,
430- extract_session_id ( & context. extensions ) . as_deref ( ) ,
431- )
432- . await
433- . map_err ( |e| McpError :: internal_error ( format ! ( "Failed to get grants: {}" , e) , None ) ) ?;
434-
435- // Get tools via FeatureService
447+ // Resolve routing once: the resolver returns the authoritative
448+ // (Space, FS) for this session — this may differ from oauth_ctx
449+ // when a WorkspaceBinding redirects to another space.
450+ let ( space_id, feature_set_ids) = self
451+ . resolve_routing ( extract_session_id ( & context. extensions ) . as_deref ( ) )
452+ . await ?;
453+
454+ // Get tools via FeatureService — using the *resolved* space.
436455 let tools = self
437456 . services
438457 . pool_services
439458 . feature_service
440- . get_tools_for_grants ( & oauth_ctx . space_id . to_string ( ) , & feature_set_ids)
459+ . get_tools_for_grants ( & space_id. to_string ( ) , & feature_set_ids)
441460 . await
442461 . map_err ( |e| McpError :: internal_error ( format ! ( "Failed to get tools: {}" , e) , None ) ) ?;
443462
@@ -500,38 +519,35 @@ impl ServerHandler for McpMuxGatewayHandler {
500519 && self . services . meta_tool_registry . contains ( & params. name )
501520 && self . services . meta_tool_registry . is_enabled ( ) . await
502521 {
503- let client_uuid = uuid:: Uuid :: parse_str ( & oauth_ctx. client_id )
504- . map_err ( |e| McpError :: invalid_params ( format ! ( "bad client_id: {e}" ) , None ) ) ?;
522+ // Note: client_id is the OAuth client identity (a URL for DCR-
523+ // registered clients like Claude, a UUID for others). The meta-
524+ // tool registry treats it as an opaque string identity key.
505525 let args: serde_json:: Value = params
506526 . arguments
507527 . map ( |a| serde_json:: to_value ( a) . unwrap_or ( serde_json:: Value :: Null ) )
508528 . unwrap_or ( serde_json:: Value :: Null ) ;
509529 return match self
510530 . services
511531 . meta_tool_registry
512- . call ( & params. name , & client_uuid , session_id, args)
532+ . call ( & params. name , & oauth_ctx . client_id , session_id, args)
513533 . await
514534 {
515535 Ok ( result) => Ok ( result) ,
516536 Err ( e) => Ok ( e. into_call_tool_result ( ) ) ,
517537 } ;
518538 }
519539
520- // Get client's feature set grants for authorization
521- let feature_set_ids = self
522- . services
523- . authorization_service
524- . get_client_grants ( & oauth_ctx. client_id , & oauth_ctx. space_id , session_id)
525- . await
526- . map_err ( |e| McpError :: internal_error ( format ! ( "Failed to get grants: {}" , e) , None ) ) ?;
540+ // Resolve routing — the binding's target space is authoritative,
541+ // which may differ from oauth_ctx.space_id.
542+ let ( space_id, feature_set_ids) = self . resolve_routing ( session_id) . await ?;
527543
528544 // Call tool via routing service (handles auth and routing)
529545 let tool_result = self
530546 . services
531547 . pool_services
532548 . routing_service
533549 . call_tool (
534- oauth_ctx . space_id ,
550+ space_id,
535551 & feature_set_ids,
536552 & params. name ,
537553 serde_json:: to_value ( params. arguments . unwrap_or_default ( ) ) . unwrap_or_default ( ) ,
@@ -605,26 +621,15 @@ impl ServerHandler for McpMuxGatewayHandler {
605621 _params : Option < PaginatedRequestParams > ,
606622 context : RequestContext < RoleServer > ,
607623 ) -> Result < ListPromptsResult , McpError > {
608- let oauth_ctx = self
609- . get_oauth_context ( & context. extensions )
610- . map_err ( |e| McpError :: invalid_params ( e. to_string ( ) , None ) ) ?;
611-
612- let feature_set_ids = self
613- . services
614- . authorization_service
615- . get_client_grants (
616- & oauth_ctx. client_id ,
617- & oauth_ctx. space_id ,
618- extract_session_id ( & context. extensions ) . as_deref ( ) ,
619- )
620- . await
621- . map_err ( |e| McpError :: internal_error ( format ! ( "Failed to get grants: {}" , e) , None ) ) ?;
624+ let ( space_id, feature_set_ids) = self
625+ . resolve_routing ( extract_session_id ( & context. extensions ) . as_deref ( ) )
626+ . await ?;
622627
623628 let prompts = self
624629 . services
625630 . pool_services
626631 . feature_service
627- . get_prompts_for_grants ( & oauth_ctx . space_id . to_string ( ) , & feature_set_ids)
632+ . get_prompts_for_grants ( & space_id. to_string ( ) , & feature_set_ids)
628633 . await
629634 . map_err ( |e| McpError :: internal_error ( format ! ( "Failed to get prompts: {}" , e) , None ) ) ?;
630635
@@ -657,35 +662,23 @@ impl ServerHandler for McpMuxGatewayHandler {
657662 params : GetPromptRequestParams ,
658663 context : RequestContext < RoleServer > ,
659664 ) -> Result < GetPromptResult , McpError > {
660- let oauth_ctx = self
661- . get_oauth_context ( & context. extensions )
662- . map_err ( |e| McpError :: invalid_params ( e . to_string ( ) , None ) ) ?;
665+ let ( space_id , feature_set_ids ) = self
666+ . resolve_routing ( extract_session_id ( & context. extensions ) . as_deref ( ) )
667+ . await ?;
663668
664669 let ( server_id, prompt_name) = self
665670 . services
666671 . pool_services
667672 . feature_service
668- . parse_qualified_prompt_name ( & oauth_ctx . space_id . to_string ( ) , & params. name )
673+ . parse_qualified_prompt_name ( & space_id. to_string ( ) , & params. name )
669674 . await
670675 . map_err ( |e| McpError :: invalid_params ( format ! ( "Invalid prompt name: {}" , e) , None ) ) ?;
671676
672- // Verify authorization
673- let feature_set_ids = self
674- . services
675- . authorization_service
676- . get_client_grants (
677- & oauth_ctx. client_id ,
678- & oauth_ctx. space_id ,
679- extract_session_id ( & context. extensions ) . as_deref ( ) ,
680- )
681- . await
682- . map_err ( |e| McpError :: internal_error ( format ! ( "Failed to get grants: {}" , e) , None ) ) ?;
683-
684677 let authorized_prompts = self
685678 . services
686679 . pool_services
687680 . feature_service
688- . get_prompts_for_grants ( & oauth_ctx . space_id . to_string ( ) , & feature_set_ids)
681+ . get_prompts_for_grants ( & space_id. to_string ( ) , & feature_set_ids)
689682 . await
690683 . map_err ( |e| {
691684 McpError :: internal_error ( format ! ( "Failed to verify authorization: {}" , e) , None )
@@ -706,12 +699,7 @@ impl ServerHandler for McpMuxGatewayHandler {
706699 . services
707700 . pool_services
708701 . pool_service
709- . get_prompt (
710- oauth_ctx. space_id ,
711- & server_id,
712- & prompt_name,
713- params. arguments ,
714- )
702+ . get_prompt ( space_id, & server_id, & prompt_name, params. arguments )
715703 . await
716704 . map_err ( |e| McpError :: internal_error ( format ! ( "Get prompt failed: {}" , e) , None ) ) ?;
717705
@@ -728,26 +716,15 @@ impl ServerHandler for McpMuxGatewayHandler {
728716 _params : Option < PaginatedRequestParams > ,
729717 context : RequestContext < RoleServer > ,
730718 ) -> Result < ListResourcesResult , McpError > {
731- let oauth_ctx = self
732- . get_oauth_context ( & context. extensions )
733- . map_err ( |e| McpError :: invalid_params ( e. to_string ( ) , None ) ) ?;
734-
735- let feature_set_ids = self
736- . services
737- . authorization_service
738- . get_client_grants (
739- & oauth_ctx. client_id ,
740- & oauth_ctx. space_id ,
741- extract_session_id ( & context. extensions ) . as_deref ( ) ,
742- )
743- . await
744- . map_err ( |e| McpError :: internal_error ( format ! ( "Failed to get grants: {}" , e) , None ) ) ?;
719+ let ( space_id, feature_set_ids) = self
720+ . resolve_routing ( extract_session_id ( & context. extensions ) . as_deref ( ) )
721+ . await ?;
745722
746723 let resources = self
747724 . services
748725 . pool_services
749726 . feature_service
750- . get_resources_for_grants ( & oauth_ctx . space_id . to_string ( ) , & feature_set_ids)
727+ . get_resources_for_grants ( & space_id. to_string ( ) , & feature_set_ids)
751728 . await
752729 . map_err ( |e| {
753730 McpError :: internal_error ( format ! ( "Failed to get resources: {}" , e) , None )
@@ -778,15 +755,15 @@ impl ServerHandler for McpMuxGatewayHandler {
778755 params : ReadResourceRequestParams ,
779756 context : RequestContext < RoleServer > ,
780757 ) -> Result < ReadResourceResult , McpError > {
781- let oauth_ctx = self
782- . get_oauth_context ( & context. extensions )
783- . map_err ( |e| McpError :: invalid_params ( e . to_string ( ) , None ) ) ?;
758+ let ( space_id , feature_set_ids ) = self
759+ . resolve_routing ( extract_session_id ( & context. extensions ) . as_deref ( ) )
760+ . await ?;
784761
785762 let server_id = self
786763 . services
787764 . pool_services
788765 . feature_service
789- . find_server_for_resource ( & oauth_ctx . space_id . to_string ( ) , & params. uri )
766+ . find_server_for_resource ( & space_id. to_string ( ) , & params. uri )
790767 . await
791768 . map_err ( |e| {
792769 McpError :: internal_error ( format ! ( "Failed to resolve resource: {}" , e) , None )
@@ -795,23 +772,11 @@ impl ServerHandler for McpMuxGatewayHandler {
795772 McpError :: invalid_params ( format ! ( "Resource '{}' not found" , params. uri) , None )
796773 } ) ?;
797774
798- // Verify authorization
799- let feature_set_ids = self
800- . services
801- . authorization_service
802- . get_client_grants (
803- & oauth_ctx. client_id ,
804- & oauth_ctx. space_id ,
805- extract_session_id ( & context. extensions ) . as_deref ( ) ,
806- )
807- . await
808- . map_err ( |e| McpError :: internal_error ( format ! ( "Failed to get grants: {}" , e) , None ) ) ?;
809-
810775 let authorized_resources = self
811776 . services
812777 . pool_services
813778 . feature_service
814- . get_resources_for_grants ( & oauth_ctx . space_id . to_string ( ) , & feature_set_ids)
779+ . get_resources_for_grants ( & space_id. to_string ( ) , & feature_set_ids)
815780 . await
816781 . map_err ( |e| {
817782 McpError :: internal_error ( format ! ( "Failed to verify authorization: {}" , e) , None )
@@ -832,7 +797,7 @@ impl ServerHandler for McpMuxGatewayHandler {
832797 . services
833798 . pool_services
834799 . pool_service
835- . read_resource ( oauth_ctx . space_id , & server_id, & params. uri )
800+ . read_resource ( space_id, & server_id, & params. uri )
836801 . await
837802 . map_err ( |e| McpError :: internal_error ( format ! ( "Read resource failed: {}" , e) , None ) ) ?;
838803
0 commit comments