soma_gateway/gateway/manager/
mcp_scoped_routes.rs1use serde_json::Value;
2
3use crate::{
4 gateway::protected_routes::ProtectedRouteScope,
5 upstream::{McpRequestOutcome, McpRoundTrip},
6};
7
8use super::{
9 GatewayManager, GatewayManagerError,
10 mcp_routes::{GatewayPromptRoute, GatewayResourceRoute, GatewayToolRoute},
11};
12
13impl GatewayManager {
14 pub async fn tool_routes_for_subject_and_scope(
15 &self,
16 subject: Option<&str>,
17 scope: Option<&ProtectedRouteScope>,
18 ) -> Result<Vec<GatewayToolRoute>, GatewayManagerError> {
19 Ok(self
20 .tool_routes_for_subject(subject)
21 .await?
22 .into_iter()
23 .filter(|route| route_allowed(scope, &route.upstream))
24 .collect())
25 }
26
27 pub async fn call_mcp_tool_for_subject_and_scope(
28 &self,
29 name: &str,
30 params: Value,
31 subject: Option<&str>,
32 scope: Option<&ProtectedRouteScope>,
33 ) -> Result<Option<Value>, GatewayManagerError> {
34 let allowed = self
35 .tool_routes_for_subject_and_scope(subject, scope)
36 .await?
37 .into_iter()
38 .any(|route| route.name == name);
39 if !allowed {
40 return Ok(None);
41 }
42 self.call_mcp_tool_for_subject(name, params, subject).await
43 }
44
45 pub async fn call_mcp_tool_once_for_subject_and_scope(
46 &self,
47 name: &str,
48 params: Value,
49 round_trip: McpRoundTrip,
50 subject: Option<&str>,
51 scope: Option<&ProtectedRouteScope>,
52 ) -> Result<Option<McpRequestOutcome>, GatewayManagerError> {
53 let allowed = self
54 .tool_routes_for_subject_and_scope(subject, scope)
55 .await?
56 .into_iter()
57 .any(|route| route.name == name);
58 if !allowed {
59 return Ok(None);
60 }
61 self.call_mcp_tool_once_for_subject(name, params, round_trip, subject)
62 .await
63 }
64
65 pub async fn resource_routes_for_subject_and_scope(
66 &self,
67 subject: Option<&str>,
68 scope: Option<&ProtectedRouteScope>,
69 ) -> Result<Vec<GatewayResourceRoute>, GatewayManagerError> {
70 Ok(self
71 .resource_routes_for_subject(subject)
72 .await?
73 .into_iter()
74 .filter(|route| route_allowed(scope, &route.upstream))
75 .collect())
76 }
77
78 pub async fn read_mcp_resource_for_subject_and_scope(
79 &self,
80 uri: &str,
81 subject: Option<&str>,
82 scope: Option<&ProtectedRouteScope>,
83 ) -> Result<Option<Value>, GatewayManagerError> {
84 let allowed = self
85 .resource_routes_for_subject_and_scope(subject, scope)
86 .await?
87 .into_iter()
88 .any(|route| route.uri == uri);
89 if !allowed {
90 return Ok(None);
91 }
92 self.read_mcp_resource_for_subject(uri, subject).await
93 }
94
95 pub async fn read_mcp_resource_once_for_subject_and_scope(
96 &self,
97 uri: &str,
98 round_trip: McpRoundTrip,
99 subject: Option<&str>,
100 scope: Option<&ProtectedRouteScope>,
101 ) -> Result<Option<McpRequestOutcome>, GatewayManagerError> {
102 let allowed = self
103 .resource_routes_for_subject_and_scope(subject, scope)
104 .await?
105 .into_iter()
106 .any(|route| route.uri == uri);
107 if !allowed {
108 return Ok(None);
109 }
110 self.read_mcp_resource_once_for_subject(uri, round_trip, subject)
111 .await
112 }
113
114 pub async fn prompt_routes_for_subject_and_scope(
115 &self,
116 subject: Option<&str>,
117 scope: Option<&ProtectedRouteScope>,
118 ) -> Result<Vec<GatewayPromptRoute>, GatewayManagerError> {
119 Ok(self
120 .prompt_routes_for_subject(subject)
121 .await?
122 .into_iter()
123 .filter(|route| route_allowed(scope, &route.upstream))
124 .collect())
125 }
126
127 pub async fn get_mcp_prompt_for_subject_and_scope(
128 &self,
129 name: &str,
130 arguments: Option<serde_json::Map<String, Value>>,
131 subject: Option<&str>,
132 scope: Option<&ProtectedRouteScope>,
133 ) -> Result<Option<Value>, GatewayManagerError> {
134 let allowed = self
135 .prompt_routes_for_subject_and_scope(subject, scope)
136 .await?
137 .into_iter()
138 .any(|route| route.name == name);
139 if !allowed {
140 return Ok(None);
141 }
142 self.get_mcp_prompt_for_subject(name, arguments, subject)
143 .await
144 }
145 pub async fn get_mcp_prompt_once_for_subject_and_scope(
146 &self,
147 name: &str,
148 arguments: Option<serde_json::Map<String, Value>>,
149 round_trip: McpRoundTrip,
150 subject: Option<&str>,
151 scope: Option<&ProtectedRouteScope>,
152 ) -> Result<Option<McpRequestOutcome>, GatewayManagerError> {
153 let allowed = self
154 .prompt_routes_for_subject_and_scope(subject, scope)
155 .await?
156 .into_iter()
157 .any(|route| route.name == name);
158 if !allowed {
159 return Ok(None);
160 }
161 self.get_mcp_prompt_once_for_subject(name, arguments, round_trip, subject)
162 .await
163 }
164}
165
166fn route_allowed(scope: Option<&ProtectedRouteScope>, upstream: &str) -> bool {
167 match scope {
168 None => true,
169 Some(scope) => scope.upstreams.iter().any(|allowed| allowed == upstream),
170 }
171}
172
173#[cfg(test)]
174#[path = "mcp_scoped_routes_tests.rs"]
175mod tests;