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