Skip to main content

soma_gateway/gateway/manager/
mcp_scoped_routes.rs

1use 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;