Skip to content

Commit fcec6af

Browse files
committed
docs: update MCP server documentation for new API
- Update module docs with new McpServer::builder() usage - Add McpServerConnect trait documentation with example - Add McpServer type docs explaining handler vs manual usage - Add McpServerBuilder docs with tool registration examples - Expand McpTool trait docs with full implementation example - Fix describe_chain to return server name instead of todo!() - Update sacp-rmcp to use McpServerConnect via RmcpServer wrapper
1 parent 986e0c6 commit fcec6af

13 files changed

Lines changed: 703 additions & 611 deletions

File tree

src/sacp-conductor/tests/mcp_integration/proxy.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use sacp::Component;
44
use sacp::ProxyToConductor;
5-
use sacp::mcp_server::{McpServer, McpServiceRegistry};
5+
use sacp::mcp_server::{McpServerConnect, McpServiceRegistry};
66
use schemars::JsonSchema;
77
use serde::{Deserialize, Serialize};
88

@@ -24,7 +24,7 @@ struct ProxyComponent;
2424

2525
impl Component for ProxyComponent {
2626
async fn serve(self, client: impl Component) -> Result<(), sacp::Error> {
27-
let test_server = McpServer::new()
27+
let test_server = McpServerConnect::new()
2828
.instructions("A simple test MCP server with an echo tool")
2929
.tool_fn(
3030
"echo",

src/sacp-conductor/tests/test_session_id_in_mcp_tools.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
1111
use sacp::Component;
1212
use sacp::ProxyToConductor;
13-
use sacp::mcp_server::{McpServer, McpServiceRegistry};
13+
use sacp::mcp_server::{McpServerConnect, McpServiceRegistry};
1414
use sacp_conductor::Conductor;
1515
use schemars::JsonSchema;
1616
use serde::{Deserialize, Serialize};
@@ -30,7 +30,7 @@ struct EchoOutput {
3030
/// Create a proxy that provides an MCP server with a session_id echo tool
3131
fn create_echo_proxy() -> Result<sacp::DynComponent, sacp::Error> {
3232
// Create MCP server with an echo tool that returns the session_id
33-
let mcp_server = McpServer::new()
33+
let mcp_server = McpServerConnect::new()
3434
.instructions("Test MCP server with session_id echo tool")
3535
.tool_fn(
3636
"echo",

src/sacp-rmcp/src/lib.rs

Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,81 @@
11
//! # sacp-rmcp - rmcp integration for SACP
22
//!
33
//! This crate provides integration between [rmcp](https://docs.rs/rmcp) MCP servers
4-
//! and the SACP proxy framework.
4+
//! and the SACP MCP server framework.
55
//!
66
//! ## Usage
77
//!
8-
//! Add rmcp-based MCP servers to your proxy using the extension trait:
8+
//! Create an MCP server from an rmcp service:
99
//!
1010
//! ```ignore
11-
//! use sacp::mcp_server::McpServiceRegistry;
12-
//! use sacp_rmcp::McpServiceRegistryRmcpExt;
11+
//! use sacp::mcp_server::McpServer;
12+
//! use sacp_rmcp::RmcpServer;
1313
//!
14-
//! let registry = McpServiceRegistry::new();
15-
//! registry.add_rmcp_server("my-server", || MyRmcpService::new())?;
14+
//! let server = McpServer::new(RmcpServer::new("my-server", || MyRmcpService::new()));
15+
//!
16+
//! // Use as a handler
17+
//! ProxyToConductor::builder()
18+
//! .with_handler(server)
19+
//! .serve(client)
20+
//! .await?;
1621
//! ```
1722
1823
use rmcp::ServiceExt;
19-
use sacp::Agent;
20-
use sacp::mcp_server::McpServiceRegistry;
21-
use sacp::{ByteStreams, Component, HasEndpoint, JrRole};
24+
use sacp::mcp_server::{McpContext, McpServerConnect};
25+
use sacp::{ByteStreams, Component, DynComponent, JrRole};
2226
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
2327

24-
/// Extension trait for adding rmcp-based MCP servers to a registry.
25-
pub trait McpServiceRegistryRmcpExt {
26-
/// Return a version of the registry with MCP server implemented using the rmcp crate.
28+
/// Wrapper that implements [`McpServerConnect`] for rmcp services.
29+
///
30+
/// Use this to integrate existing rmcp-based MCP servers with the SACP framework.
31+
///
32+
/// # Example
33+
///
34+
/// ```ignore
35+
/// use sacp::mcp_server::McpServer;
36+
/// use sacp_rmcp::RmcpServer;
37+
///
38+
/// // Create an MCP server from an rmcp service factory
39+
/// let server = McpServer::new(RmcpServer::new("my-server", || MyRmcpService::new()));
40+
/// ```
41+
pub struct RmcpServer<F> {
42+
name: String,
43+
make_service: F,
44+
}
45+
46+
impl<F> RmcpServer<F> {
47+
/// Create a new rmcp server wrapper.
2748
///
2849
/// # Parameters
2950
///
30-
/// - `name`: The name of the server.
31-
/// - `make_service`: A function that creates the service (e.g., `YourService::new`).
32-
///
33-
/// # Example
34-
///
35-
/// ```ignore
36-
/// registry.with_rmcp_server("my-server", || MyRmcpService::new())?
37-
/// ```
38-
fn with_rmcp_server<S>(
39-
self,
40-
name: impl ToString,
41-
make_service: impl Fn() -> S + 'static + Send + Sync,
42-
) -> Result<Self, sacp::Error>
43-
where
44-
S: rmcp::Service<rmcp::RoleServer>,
45-
Self: Sized;
51+
/// - `name`: The name of the server (used in session responses).
52+
/// - `make_service`: A function that creates a new rmcp service instance.
53+
/// This is called for each new connection.
54+
pub fn new(name: impl ToString, make_service: F) -> Self {
55+
Self {
56+
name: name.to_string(),
57+
make_service,
58+
}
59+
}
4660
}
4761

48-
impl<Role: JrRole> McpServiceRegistryRmcpExt for McpServiceRegistry<Role>
62+
impl<Role, F, S> McpServerConnect<Role> for RmcpServer<F>
4963
where
50-
Role: HasEndpoint<Agent>,
64+
Role: JrRole,
65+
F: Fn() -> S + Send + Sync + 'static,
66+
S: rmcp::Service<rmcp::RoleServer>,
5167
{
52-
fn with_rmcp_server<S>(
53-
self,
54-
name: impl ToString,
55-
make_service: impl Fn() -> S + 'static + Send + Sync,
56-
) -> Result<Self, sacp::Error>
57-
where
58-
S: rmcp::Service<rmcp::RoleServer>,
59-
{
60-
self.with_custom_mcp_server(name, move |_| {
61-
let service = make_service();
62-
RmcpServerComponent { service }
63-
})
68+
fn name(&self) -> String {
69+
self.name.clone()
70+
}
71+
72+
fn connect(&self, _cx: McpContext<Role>) -> DynComponent {
73+
let service = (self.make_service)();
74+
DynComponent::new(RmcpServerComponent { service })
6475
}
6576
}
6677

67-
/// Component wrapper for rmcp services
78+
/// Component wrapper for rmcp services.
6879
struct RmcpServerComponent<S> {
6980
service: S,
7081
}

src/sacp/src/jsonrpc.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub use crate::jsonrpc::handlers::NullHandler;
3030
use crate::jsonrpc::handlers::{MessageHandler, NotificationHandler, RequestHandler};
3131
use crate::jsonrpc::outgoing_actor::{OutgoingMessageTx, send_raw_message};
3232
use crate::jsonrpc::task_actor::{PendingTask, Task, TaskTx};
33+
use crate::mcp_server::McpServer;
3334
use crate::role::{HasDefaultEndpoint, HasEndpoint, JrEndpoint, JrRole};
3435
use crate::{Agent, Client, Component};
3536

@@ -569,10 +570,6 @@ impl<H: JrMessageHandler> JrConnectionBuilder<H> {
569570
self
570571
}
571572

572-
/// Returns a reference to the local role.
573-
574-
/// Returns a reference to the remote role.
575-
576573
/// Merge another [`JrConnectionBuilder`] into this one.
577574
///
578575
/// Prefer [`Self::on_receive_request`] or [`Self::on_receive_notification`].
@@ -893,10 +890,10 @@ impl<H: JrMessageHandler> JrConnectionBuilder<H> {
893890
self.with_handler(NotificationHandler::new(endpoint, <H::Role>::default(), op))
894891
}
895892

896-
/// Provide MCP servers to downstream successors.
893+
/// In a proxy, add this MCP server to new sessions passing through the proxy.
897894
///
898895
/// This adds a handler that intercepts `session/new` requests to include the
899-
/// registered MCP servers.
896+
/// registered MCP server.
900897
///
901898
/// # Example
902899
///
@@ -910,10 +907,10 @@ impl<H: JrMessageHandler> JrConnectionBuilder<H> {
910907
/// .serve(connection)
911908
/// .await?;
912909
/// ```
913-
pub fn provide_mcp<Role: JrRole>(
910+
pub fn with_mcp_server<Role: JrRole>(
914911
self,
915-
registry: crate::mcp_server::McpServiceRegistry<Role>,
916-
) -> JrConnectionBuilder<ChainedHandler<H, crate::mcp_server::McpServiceRegistry<Role>>>
912+
registry: McpServer<Role>,
913+
) -> JrConnectionBuilder<ChainedHandler<H, McpServer<Role>>>
917914
where
918915
H: JrMessageHandler<Role = Role>,
919916
Role: HasEndpoint<Client> + HasEndpoint<Agent>,

src/sacp/src/mcp_server/registry/active_session.rs renamed to src/sacp/src/mcp_server/active_session.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use futures::{SinkExt, StreamExt};
33
use fxhash::FxHashMap;
44

55
use crate::mcp::{McpClientToServer, McpServerEnd};
6-
use crate::mcp_server::McpContext;
7-
use crate::mcp_server::registry::RegisteredMcpServer;
6+
use crate::mcp_server::{McpContext, McpServerConnect};
87
use crate::schema::{
98
McpConnectRequest, McpConnectResponse, McpDisconnectNotification, McpOverAcpMessage,
109
};
@@ -19,7 +18,7 @@ use std::sync::Arc;
1918
/// This is added as a 'dynamic' handler to the connection context
2019
/// (see [`JrConnectionCx::add_dynamic_handler`]) and handles MCP-over-ACP messages
2120
/// with the appropriate ACP url.
22-
pub(super) struct McpServerSession<Role: JrRole>
21+
pub(super) struct McpActiveSession<Role: JrRole>
2322
where
2423
Role: HasEndpoint<Agent>,
2524
{
@@ -31,21 +30,21 @@ where
3130
acp_url: String,
3231

3332
/// The MCP server we are managing
34-
mcp_server: Arc<RegisteredMcpServer<Role>>,
33+
mcp_connect: Arc<dyn McpServerConnect<Role>>,
3534

3635
/// Active connections to MCP server tasks
3736
connections: FxHashMap<String, mpsc::Sender<MessageCx>>,
3837
}
3938

40-
impl<Role: JrRole> McpServerSession<Role>
39+
impl<Role: JrRole> McpActiveSession<Role>
4140
where
4241
Role: HasEndpoint<Agent>,
4342
{
44-
pub fn new(role: Role, acp_url: String, mcp_server: Arc<RegisteredMcpServer<Role>>) -> Self {
43+
pub fn new(role: Role, acp_url: String, mcp_connect: Arc<dyn McpServerConnect<Role>>) -> Self {
4544
Self {
4645
role,
4746
acp_url,
48-
mcp_server,
47+
mcp_connect,
4948
connections: FxHashMap::default(),
5049
}
5150
}
@@ -113,7 +112,7 @@ where
113112
};
114113

115114
// Get the MCP server component
116-
let spawned_server = self.mcp_server.spawn.spawn(McpContext {
115+
let spawned_server = self.mcp_connect.connect(McpContext {
117116
acp_url: request.acp_url.clone(),
118117
connection_cx: outer_cx.clone(),
119118
});
@@ -211,7 +210,7 @@ where
211210
}
212211
}
213212

214-
impl<Role: JrRole> JrMessageHandlerSend for McpServerSession<Role>
213+
impl<Role: JrRole> JrMessageHandlerSend for McpActiveSession<Role>
215214
where
216215
Role: HasEndpoint<Agent>,
217216
{

0 commit comments

Comments
 (0)