Skip to main content

agent_client_protocol/mcp_server/
context.rs

1use crate::{ConnectionTo, role::Role};
2
3#[cfg(feature = "unstable_mcp_over_acp")]
4use crate::schema::v1::{McpConnectionId, McpServerAcpId};
5
6/// Describes how an MCP server connection was established.
7#[derive(Clone, Debug, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum McpConnectionContext {
10    /// The MCP server was connected directly, without an ACP transport.
11    Standalone,
12
13    /// The MCP server was attached to an ACP session.
14    #[cfg(feature = "unstable_mcp_over_acp")]
15    Acp {
16        /// The identifier advertised in the session's `McpServer::Acp` declaration.
17        server_id: McpServerAcpId,
18
19        /// The identifier for this active `mcp/connect` connection.
20        connection_id: McpConnectionId,
21    },
22}
23
24impl McpConnectionContext {
25    /// Whether this MCP connection was established without an ACP transport.
26    #[must_use]
27    pub fn is_standalone(&self) -> bool {
28        matches!(self, Self::Standalone)
29    }
30
31    /// The identifier advertised in the session's `McpServer::Acp` declaration.
32    ///
33    /// Returns `None` for a standalone MCP connection.
34    #[cfg(feature = "unstable_mcp_over_acp")]
35    #[must_use]
36    pub fn server_id(&self) -> Option<&McpServerAcpId> {
37        match self {
38            Self::Standalone => None,
39            Self::Acp { server_id, .. } => Some(server_id),
40        }
41    }
42
43    /// The identifier for the active `mcp/connect` connection.
44    ///
45    /// Returns `None` for a standalone MCP connection.
46    #[cfg(feature = "unstable_mcp_over_acp")]
47    #[must_use]
48    pub fn connection_id(&self) -> Option<&McpConnectionId> {
49        match self {
50            Self::Standalone => None,
51            Self::Acp { connection_id, .. } => Some(connection_id),
52        }
53    }
54}
55
56/// Connection information available to an MCP server.
57#[derive(Clone, Debug)]
58pub struct McpConnectionTo<Counterpart: Role> {
59    pub(super) context: McpConnectionContext,
60    pub(super) connection: ConnectionTo<Counterpart>,
61}
62
63impl<Counterpart: Role> McpConnectionTo<Counterpart> {
64    /// Describes whether this is a standalone or ACP-attached MCP connection.
65    #[must_use]
66    pub fn context(&self) -> &McpConnectionContext {
67        &self.context
68    }
69
70    /// The identifier advertised in the session's `McpServer::Acp` declaration.
71    ///
72    /// Returns `None` for a standalone MCP connection.
73    #[cfg(feature = "unstable_mcp_over_acp")]
74    #[must_use]
75    pub fn server_id(&self) -> Option<&McpServerAcpId> {
76        self.context.server_id()
77    }
78
79    /// The identifier for the active `mcp/connect` connection.
80    ///
81    /// Returns `None` for a standalone MCP connection.
82    #[cfg(feature = "unstable_mcp_over_acp")]
83    #[must_use]
84    pub fn connection_id(&self) -> Option<&McpConnectionId> {
85        self.context.connection_id()
86    }
87
88    /// Borrow the host protocol connection.
89    ///
90    /// For an ACP-attached server, this is its host ACP connection. For a
91    /// standalone server, this is the direct MCP client connection.
92    #[must_use]
93    pub fn connection(&self) -> &ConnectionTo<Counterpart> {
94        &self.connection
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::McpConnectionContext;
101
102    #[test]
103    fn standalone_context_is_explicit() {
104        let context = McpConnectionContext::Standalone;
105
106        assert!(context.is_standalone());
107
108        #[cfg(feature = "unstable_mcp_over_acp")]
109        {
110            assert_eq!(context.server_id(), None);
111            assert_eq!(context.connection_id(), None);
112        }
113    }
114
115    #[cfg(feature = "unstable_mcp_over_acp")]
116    #[test]
117    fn acp_context_exposes_server_and_connection_ids() {
118        use crate::schema::v1::{McpConnectionId, McpServerAcpId};
119
120        let server_id = McpServerAcpId::new("server-id");
121        let connection_id = McpConnectionId::new("connection-id");
122        let context = McpConnectionContext::Acp {
123            server_id: server_id.clone(),
124            connection_id: connection_id.clone(),
125        };
126
127        assert!(!context.is_standalone());
128        assert_eq!(context.server_id(), Some(&server_id));
129        assert_eq!(context.connection_id(), Some(&connection_id));
130    }
131}