agent_client_protocol/mcp_server/mod.rs
1//! Runtime-agnostic MCP server support.
2//!
3//! This module provides infrastructure for serving MCP directly without tying
4//! the core SDK to a particular MCP implementation or async runtime. With the
5//! `unstable_mcp_over_acp` feature, the same servers can be attached to ACP
6//! session setup requests through the `with_mcp_server` builder methods.
7//! Stable protocol v1 and draft protocol v2 both support global proxy
8//! attachment and per-session attachment. V2 uses
9//! `Proxy.v2().with_mcp_server(...)` or
10//! `v2::SessionBuilder::with_mcp_server(...)` for new sessions and
11//! `v2::ResumeSessionBuilder::with_mcp_server(...)` for resumed sessions. With
12//! `unstable_session_fork`, `v2::ForkSessionBuilder::with_mcp_server(...)`
13//! attaches a server to a forked session. V2 attachment additionally requires
14//! `unstable_protocol_v2`.
15//!
16//! ## Building MCP servers with tools
17//!
18//! The opt-in `schemars` feature provides `McpTool`, `McpToolRegistry`
19//! and its metadata types, and the `tool_fn` / `tool_fn_mut` functions for
20//! automatically generating JSON Schemas from Rust input and output types.
21//! The `agent-client-protocol-rmcp` crate provides the builder APIs for MCP
22//! tools backed by the `rmcp` crate and enables this feature.
23//!
24//! Custom servers using [`crate::mcp_server::McpServerConnect`],
25//! [`crate::mcp_server::McpServer`], and the connection types remain available
26//! without `schemars`, including ACP attachment when
27//! `unstable_mcp_over_acp` is enabled.
28//!
29//! ## Custom MCP Server Implementations
30//!
31//! You can implement [`crate::mcp_server::McpServerConnect`] to create custom MCP
32//! servers:
33//!
34//! ```rust,ignore
35//! use agent_client_protocol::mcp_server::{McpConnectionTo, McpServer, McpServerConnect};
36//! use agent_client_protocol::{DynConnectTo, NullRun, Role, role};
37//!
38//! struct MyCustomServer;
39//!
40//! impl<R: Role> McpServerConnect<R> for MyCustomServer {
41//! fn name(&self) -> String {
42//! "my-custom-server".to_string()
43//! }
44//!
45//! fn connect(&self, cx: McpConnectionTo<R>) -> DynConnectTo<role::mcp::Client> {
46//! // Return a component that serves MCP requests
47//! DynConnectTo::new(my_mcp_component(cx))
48//! }
49//! }
50//!
51//! let server = McpServer::new(MyCustomServer, NullRun);
52//! ```
53
54#[cfg(feature = "unstable_mcp_over_acp")]
55mod active_session;
56mod connect;
57mod context;
58#[cfg(feature = "schemars")]
59mod registry;
60mod server;
61#[cfg(feature = "unstable_mcp_over_acp")]
62mod service;
63#[cfg(feature = "schemars")]
64mod tool;
65#[cfg(feature = "schemars")]
66mod tool_fn;
67
68pub use connect::McpServerConnect;
69pub use context::{McpConnectionContext, McpConnectionTo};
70#[cfg(feature = "schemars")]
71#[cfg_attr(docsrs, doc(cfg(feature = "schemars")))]
72pub use registry::{
73 McpToolMetadata, McpToolRegistry, McpToolSchema, RegisteredMcpTool, UnknownToolError,
74};
75pub use server::McpServer;
76#[cfg(feature = "unstable_mcp_over_acp")]
77pub use service::{
78 McpOperationCancellation, McpOutcome, McpRequest, McpRequestContext, McpService,
79};
80
81/// `-33000`: an MCP-over-ACP binding resource limit was exceeded.
82#[cfg(feature = "unstable_mcp_over_acp")]
83pub const MCP_RESOURCE_LIMIT_EXCEEDED: crate::ErrorCode = crate::ErrorCode::new(-33000);
84/// `-33001`: the declared MCP server registration is unavailable.
85#[cfg(feature = "unstable_mcp_over_acp")]
86pub const MCP_SERVER_UNAVAILABLE: crate::ErrorCode = crate::ErrorCode::new(-33001);
87/// `-33002`: the MCP backend or transport failed without a valid MCP outcome.
88#[cfg(feature = "unstable_mcp_over_acp")]
89pub const MCP_BACKEND_FAILURE: crate::ErrorCode = crate::ErrorCode::new(-33002);
90#[cfg(feature = "schemars")]
91#[cfg_attr(docsrs, doc(cfg(feature = "schemars")))]
92pub use tool::McpTool;
93#[cfg(feature = "schemars")]
94#[cfg_attr(docsrs, doc(cfg(feature = "schemars")))]
95pub use tool_fn::{tool_fn, tool_fn_mut};
96
97/// Expands to the first block when this crate was built with
98/// `unstable_mcp_over_acp`, and to the `else` block otherwise.
99///
100/// MCP integrations in other crates use this to pick the native
101/// MCP-over-ACP path from this crate's features rather than their own, so
102/// feature unification cannot leave them on the per-request fallback while
103/// ACP attachment is available. Hidden from the docs, but
104/// `agent-client-protocol-rmcp` depends on it, so it is a stable cross-crate
105/// contract for the 5.x line: renaming it or changing its syntax is a
106/// breaking change.
107#[cfg(feature = "unstable_mcp_over_acp")]
108#[doc(hidden)]
109#[macro_export]
110macro_rules! __if_mcp_over_acp {
111 ({ $($then:tt)* } else { $($else:tt)* }) => {
112 $($then)*
113 };
114}
115
116/// Expands to the first block when this crate was built with
117/// `unstable_mcp_over_acp`, and to the `else` block otherwise.
118///
119/// MCP integrations in other crates use this to pick the native
120/// MCP-over-ACP path from this crate's features rather than their own, so
121/// feature unification cannot leave them on the per-request fallback while
122/// ACP attachment is available. Hidden from the docs, but
123/// `agent-client-protocol-rmcp` depends on it, so it is a stable cross-crate
124/// contract for the 5.x line: renaming it or changing its syntax is a
125/// breaking change.
126#[cfg(not(feature = "unstable_mcp_over_acp"))]
127#[doc(hidden)]
128#[macro_export]
129macro_rules! __if_mcp_over_acp {
130 ({ $($then:tt)* } else { $($else:tt)* }) => {
131 $($else)*
132 };
133}