Skip to main content

soma_mcp/
state.rs

1use std::sync::{
2    atomic::{AtomicU64, Ordering},
3    Arc,
4};
5
6use soma_application::{ExecutionContext, GatewayRouteScope, SomaApplication};
7use soma_config::McpConfig;
8use soma_domain::{AuthorizationMode, Principal, RequestId, Surface, TraceContext};
9use soma_mcp_server::ResponsePageStore;
10
11#[cfg(test)]
12#[path = "state_tests.rs"]
13mod tests;
14
15#[derive(Clone)]
16pub struct McpState {
17    application: Arc<SomaApplication>,
18    config: McpConfig,
19    authorization_mode: AuthorizationMode,
20    response_pages: ResponsePageStore,
21}
22
23impl McpState {
24    pub fn new(
25        application: Arc<SomaApplication>,
26        config: McpConfig,
27        authorization_mode: AuthorizationMode,
28        response_pages: ResponsePageStore,
29    ) -> Self {
30        Self {
31            application,
32            config,
33            authorization_mode,
34            response_pages,
35        }
36    }
37
38    pub fn application(&self) -> &SomaApplication {
39        self.application.as_ref()
40    }
41
42    pub fn config(&self) -> &McpConfig {
43        &self.config
44    }
45
46    pub fn with_server_name(mut self, server_name: impl Into<String>) -> Self {
47        self.config.server_name = server_name.into();
48        self
49    }
50
51    pub fn authorization_mode(&self) -> AuthorizationMode {
52        self.authorization_mode
53    }
54
55    pub fn response_pages(&self) -> &ResponsePageStore {
56        &self.response_pages
57    }
58
59    pub fn execution_context(
60        &self,
61        principal: Option<Principal>,
62        trace: Option<TraceContext>,
63    ) -> ExecutionContext {
64        ExecutionContext {
65            principal,
66            authorization_mode: self.authorization_mode,
67            surface: Surface::Mcp,
68            trace,
69            destructive_confirmation: Default::default(),
70            response_limit: None,
71            request_id: next_request_id(),
72        }
73    }
74}
75
76fn next_request_id() -> RequestId {
77    static REQUEST_SEQUENCE: AtomicU64 = AtomicU64::new(1);
78    let sequence = REQUEST_SEQUENCE.fetch_add(1, Ordering::Relaxed);
79    RequestId::new(format!("mcp-{}-{sequence}", std::process::id()))
80        .expect("generated MCP request ids are valid")
81}
82
83/// MCP route filtering supplied by the product composition layer.
84pub type McpRouteScope = GatewayRouteScope;