tower_mcp/proxy/
backend.rs1use std::convert::Infallible;
7use std::future::Future;
8use std::pin::Pin;
9use std::sync::Arc;
10use std::task::{Context, Poll};
11
12use tokio::sync::{RwLock, mpsc};
13use tower_service::Service;
14
15use crate::client::{ClientTransport, McpClient, NotificationHandler};
16use crate::protocol::{
17 McpRequest, McpResponse, PromptDefinition, ResourceDefinition, ResourceTemplateDefinition,
18 ToolDefinition,
19};
20use crate::router::{RouterRequest, RouterResponse};
21use tower_mcp_types::JsonRpcError;
22
23type Result<T> = std::result::Result<T, crate::error::Error>;
24
25#[derive(Debug, Default, Clone)]
27pub(crate) struct CachedCapabilities {
28 pub tools: Vec<ToolDefinition>,
29 pub resources: Vec<ResourceDefinition>,
30 pub resource_templates: Vec<ResourceTemplateDefinition>,
31 pub prompts: Vec<PromptDefinition>,
32}
33
34#[derive(Debug, Clone, Copy)]
36pub(crate) enum ListChanged {
37 Tools,
38 Resources,
39 Prompts,
40}
41
42pub(crate) struct Backend {
44 pub namespace: String,
46 pub separator: String,
48 pub client: Arc<McpClient>,
50 pub cache: Arc<RwLock<CachedCapabilities>>,
52 pub instructions: Option<String>,
54}
55
56impl Backend {
57 pub async fn connect(
60 namespace: impl Into<String>,
61 transport: impl ClientTransport,
62 separator: String,
63 invalidation_tx: mpsc::Sender<ListChanged>,
64 ) -> Result<Self> {
65 let tools_tx = invalidation_tx.clone();
66 let resources_tx = invalidation_tx.clone();
67 let prompts_tx = invalidation_tx;
68
69 let handler = NotificationHandler::new()
70 .on_tools_changed(move || {
71 let _ = tools_tx.try_send(ListChanged::Tools);
72 })
73 .on_resources_changed(move || {
74 let _ = resources_tx.try_send(ListChanged::Resources);
75 })
76 .on_prompts_changed(move || {
77 let _ = prompts_tx.try_send(ListChanged::Prompts);
78 });
79
80 let client = McpClient::connect_with_handler(transport, handler).await?;
81 let cache = Arc::new(RwLock::new(CachedCapabilities::default()));
82
83 Ok(Self {
84 namespace: namespace.into(),
85 separator,
86 client: Arc::new(client),
87 cache,
88 instructions: None,
89 })
90 }
91
92 pub fn from_client(namespace: impl Into<String>, client: McpClient, separator: String) -> Self {
94 let instructions = client
95 .server_info_blocking()
96 .and_then(|info| info.instructions.clone());
97 Self {
98 namespace: namespace.into(),
99 separator,
100 client: Arc::new(client),
101 cache: Arc::new(RwLock::new(CachedCapabilities::default())),
102 instructions,
103 }
104 }
105
106 pub fn service(&self) -> BackendService {
108 BackendService {
109 client: Arc::clone(&self.client),
110 }
111 }
112
113 pub async fn initialize(
117 &self,
118 proxy_name: &str,
119 proxy_version: &str,
120 ) -> Result<Option<String>> {
121 let result = self.client.initialize(proxy_name, proxy_version).await?;
122 let instructions = result.instructions.clone();
123 self.refresh_capabilities().await?;
124 Ok(instructions)
125 }
126
127 pub async fn refresh_capabilities(&self) -> Result<()> {
129 let (tools, resources, templates, prompts) = tokio::join!(
130 self.client.list_all_tools(),
131 self.client.list_all_resources(),
132 self.client.list_all_resource_templates(),
133 self.client.list_all_prompts(),
134 );
135
136 let mut cache = self.cache.write().await;
137 cache.tools = tools.unwrap_or_default();
138 cache.resources = resources.unwrap_or_default();
139 cache.resource_templates = templates.unwrap_or_default();
140 cache.prompts = prompts.unwrap_or_default();
141
142 Ok(())
143 }
144
145 pub async fn refresh_tools(&self) {
147 if let Ok(tools) = self.client.list_all_tools().await {
148 self.cache.write().await.tools = tools;
149 }
150 }
151
152 pub async fn refresh_resources(&self) {
154 let (resources, templates) = tokio::join!(
155 self.client.list_all_resources(),
156 self.client.list_all_resource_templates(),
157 );
158 let mut cache = self.cache.write().await;
159 if let Ok(r) = resources {
160 cache.resources = r;
161 }
162 if let Ok(t) = templates {
163 cache.resource_templates = t;
164 }
165 }
166
167 pub async fn refresh_prompts(&self) {
169 if let Ok(prompts) = self.client.list_all_prompts().await {
170 self.cache.write().await.prompts = prompts;
171 }
172 }
173}
174
175#[derive(Clone)]
189pub struct BackendService {
190 client: Arc<McpClient>,
191}
192
193impl Service<RouterRequest> for BackendService {
194 type Response = RouterResponse;
195 type Error = Infallible;
196 type Future =
197 Pin<Box<dyn Future<Output = std::result::Result<RouterResponse, Infallible>> + Send>>;
198
199 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<std::result::Result<(), Self::Error>> {
200 Poll::Ready(Ok(()))
201 }
202
203 fn call(&mut self, req: RouterRequest) -> Self::Future {
204 let client = Arc::clone(&self.client);
205 let request_id = req.id.clone();
206
207 Box::pin(async move {
208 let result = dispatch_to_client(&client, req.inner).await;
209 Ok(RouterResponse {
210 id: request_id,
211 inner: result,
212 })
213 })
214 }
215}
216
217async fn dispatch_to_client(
219 client: &McpClient,
220 request: McpRequest,
221) -> std::result::Result<McpResponse, JsonRpcError> {
222 match request {
223 McpRequest::CallTool(params) => {
224 let result = client
225 .call_tool(¶ms.name, params.arguments)
226 .await
227 .map_err(|e| JsonRpcError::internal_error(format!("Backend error: {}", e)))?;
228 Ok(McpResponse::CallTool(result))
229 }
230 McpRequest::ReadResource(params) => {
231 let result = client
232 .read_resource(¶ms.uri)
233 .await
234 .map_err(|e| JsonRpcError::internal_error(format!("Backend error: {}", e)))?;
235 Ok(McpResponse::ReadResource(result))
236 }
237 McpRequest::GetPrompt(params) => {
238 let args = if params.arguments.is_empty() {
239 None
240 } else {
241 Some(params.arguments)
242 };
243 let result = client
244 .get_prompt(¶ms.name, args)
245 .await
246 .map_err(|e| JsonRpcError::internal_error(format!("Backend error: {}", e)))?;
247 Ok(McpResponse::GetPrompt(result))
248 }
249 _ => Err(JsonRpcError::method_not_found(
250 "Method not routable to backend",
251 )),
252 }
253}