Skip to main content

mcp_streamable_proxy/
proxy_handler.rs

1/**
2 * Create a local SSE server that proxies requests to a stdio MCP server.
3 */
4use crate::backend_client::{
5    BackendNotificationBridge, ProgressRouteGuard, UpstreamPeerRegistry, UpstreamPeerSlot,
6};
7use crate::backend_connector::{BackendConnector, BackendIsolation};
8use arc_swap::ArcSwapOption;
9pub use mcp_common::ToolFilter;
10use rmcp::{
11    ErrorData, RoleClient, RoleServer, ServerHandler, ServiceError,
12    model::{
13        CallToolRequest, CallToolRequestParams, CallToolResponse, CallToolResult, CancelTaskParams,
14        CancelTaskRequest, ClientRequest, ContentBlock, CreateTaskResult, GetPromptResponse,
15        GetTaskParams, GetTaskPayloadParams, GetTaskPayloadRequest, GetTaskRequest, GetTaskResult,
16        Implementation, InitializeRequestParams, InitializeResult, ListTasksRequest,
17        ListTasksResult, ListToolsResult, PaginatedRequestParams, ProtocolVersion,
18        ReadResourceResponse, RequestMetaObject, RequestParamsMeta, ServerInfo, ServerResult,
19        SetLevelRequestMethod, SubscribeRequestMethod, SubscribeRequestParams,
20        UnsubscribeRequestMethod, UnsubscribeRequestParams,
21    },
22    service::{NotificationContext, Peer, RequestContext, RunningService},
23};
24use std::future::Future;
25use std::sync::Arc;
26use std::sync::atomic::{AtomicU64, Ordering};
27use std::time::Instant;
28use tracing::{debug, error, info, warn};
29
30/// 全局请求计数器,用于生成唯一的请求 ID
31static REQUEST_COUNTER: AtomicU64 = AtomicU64::new(1);
32
33/// Merge `_meta` from the inbound request context into outbound params.
34///
35/// rmcp lifts wire `params._meta` into [`RequestContext::meta`], leaving
36/// `params.meta` often empty. Without this merge, `call_tool_once` / similar
37/// helpers send requests to the backend without `progressToken`.
38fn merge_context_meta_into_params<P: RequestParamsMeta>(
39    params: &mut P,
40    context_meta: &RequestMetaObject,
41) {
42    if context_meta.is_empty() {
43        return;
44    }
45    let dest = params.meta_or_default();
46    for (key, value) in context_meta.iter() {
47        dest.entry(key.clone()).or_insert_with(|| value.clone());
48    }
49}
50
51/// Echo client-requested protocol version when known (mirrors rmcp negotiate).
52fn negotiate_protocol_version(
53    client_requested: &ProtocolVersion,
54    server_fallback: ProtocolVersion,
55) -> ProtocolVersion {
56    if ProtocolVersion::KNOWN_VERSIONS.contains(client_requested) {
57        client_requested.clone()
58    } else {
59        warn!(
60            client_requested = %client_requested,
61            server_fallback = %server_fallback,
62            "client requested unsupported protocol version; falling back to server default"
63        );
64        server_fallback
65    }
66}
67
68/// Running backend service with notification bridge.
69pub type BackendRunningService = RunningService<RoleClient, BackendNotificationBridge>;
70
71/// 包装后端连接和运行服务
72/// 用于 ArcSwap 热替换
73#[derive(Debug)]
74struct PeerInner {
75    /// Peer 用于发送请求
76    peer: Peer<RoleClient>,
77    /// 保持 RunningService 的所有权,确保服务生命周期
78    #[allow(dead_code)]
79    _running: Arc<BackendRunningService>,
80}
81
82/// Lazy-connect state for [`BackendIsolation::PerSession`].
83#[derive(Clone)]
84struct PerSessionState {
85    connector: Arc<dyn BackendConnector>,
86    notify_slot: UpstreamPeerSlot,
87    connect_lock: Arc<tokio::sync::Mutex<()>>,
88}
89
90impl std::fmt::Debug for PerSessionState {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        f.debug_struct("PerSessionState")
93            .field("notify_slot", &self.notify_slot)
94            .finish_non_exhaustive()
95    }
96}
97
98/// A proxy handler that forwards requests to a client based on the server's capabilities
99///
100/// **Isolation**:
101/// - [`BackendIsolation::Shared`]: one backend, clones share Arcs (stdio).
102/// - [`BackendIsolation::PerSession`]: factory creates a disconnected handler; backend
103///   is opened during `initialize` (so capabilities are real) and dropped with the session.
104#[derive(Clone, Debug)]
105pub struct ProxyHandler {
106    /// 后端连接(ArcSwap 支持无锁原子替换)
107    /// None 表示后端断开/重连中
108    peer: Arc<ArcSwapOption<PeerInner>>,
109    /// 缓存的服务器信息(ArcSwap:swap_backend 时刷新,所有 clone 可见)
110    cached_info: Arc<arc_swap::ArcSwap<ServerInfo>>,
111    /// MCP ID 用于日志记录
112    mcp_id: String,
113    /// 工具过滤配置
114    tool_filter: ToolFilter,
115    /// 后端版本号(每次 swap_backend 递增)
116    backend_version: Arc<AtomicU64>,
117    /// 上游 Streamable HTTP session peers(shared 模式通知桥)
118    upstream_peers: Arc<UpstreamPeerRegistry>,
119    /// Backend isolation mode
120    isolation: BackendIsolation,
121    /// Per-session lazy connect (URL); `None` in shared mode
122    per_session: Option<PerSessionState>,
123}
124
125impl ServerHandler for ProxyHandler {
126    fn get_info(&self) -> ServerInfo {
127        (**self.cached_info.load()).clone()
128    }
129
130    /// Connect per-session backends before returning capabilities.
131    async fn initialize(
132        &self,
133        request: InitializeRequestParams,
134        context: RequestContext<RoleServer>,
135    ) -> Result<InitializeResult, ErrorData> {
136        context.peer.set_peer_info(request.clone());
137
138        if let Some(session) = &self.per_session {
139            // Bind notify slot before connect so backend notifications during handshake work.
140            session.notify_slot.set(context.peer.clone());
141            self.ensure_connected().await?;
142            info!(
143                "[initialize] Per-session backend ready - MCP ID: {}",
144                self.mcp_id
145            );
146        }
147
148        let mut info = self.get_info();
149        info.protocol_version =
150            negotiate_protocol_version(&request.protocol_version, info.protocol_version);
151        Ok(info)
152    }
153
154    #[tracing::instrument(skip(self, request, context), fields(
155        mcp_id = %self.mcp_id,
156        request = ?request,
157    ))]
158    async fn list_tools(
159        &self,
160        request: Option<PaginatedRequestParams>,
161        context: RequestContext<RoleServer>,
162    ) -> Result<ListToolsResult, ErrorData> {
163        match self.capabilities().tools {
164            Some(_) => {
165                let result = self
166                    .forward_backend(
167                        &context,
168                        |peer| async move { peer.list_tools(request).await },
169                    )
170                    .await?;
171
172                let filtered_tools: Vec<_> = if self.tool_filter.is_enabled() {
173                    result
174                        .tools
175                        .into_iter()
176                        .filter(|tool| self.tool_filter.is_allowed(&tool.name))
177                        .collect()
178                } else {
179                    result.tools
180                };
181
182                info!(
183                    "[list_tools] Tool list results - MCP ID: {}, number of tools: {}{}",
184                    self.mcp_id,
185                    filtered_tools.len(),
186                    if self.tool_filter.is_enabled() {
187                        " (filtered)"
188                    } else {
189                        ""
190                    }
191                );
192
193                debug!(
194                    "Proxying list_tools response with {} tools",
195                    filtered_tools.len()
196                );
197                Ok(ListToolsResult {
198                    tools: filtered_tools,
199                    next_cursor: result.next_cursor,
200                    meta: result.meta,
201                    result_type: result.result_type,
202                    ttl_ms: result.ttl_ms,
203                    cache_scope: result.cache_scope,
204                })
205            }
206            None => {
207                warn!("Server doesn't support tools capability");
208                Ok(ListToolsResult::default())
209            }
210        }
211    }
212
213    #[tracing::instrument(skip(self, request, context), fields(
214        mcp_id = %self.mcp_id,
215        tool_name = %request.name,
216        tool_arguments = ?request.arguments,
217    ))]
218    async fn call_tool(
219        &self,
220        mut request: CallToolRequestParams,
221        context: RequestContext<RoleServer>,
222    ) -> Result<CallToolResponse, ErrorData> {
223        let request_id = REQUEST_COUNTER.fetch_add(1, Ordering::Relaxed);
224        let start = Instant::now();
225        let tool_name = request.name.clone();
226
227        info!(
228            "[call_tool:{}] Start - Tool: {}, MCP ID: {}",
229            request_id, tool_name, self.mcp_id
230        );
231
232        if !self.tool_filter.is_allowed(&tool_name) {
233            info!(
234                "[call_tool:{}] Tool is filtered - MCP ID: {}, Tool: {}",
235                request_id, self.mcp_id, tool_name
236            );
237            return Ok(CallToolResult::error(vec![ContentBlock::text(format!(
238                "Tool '{tool_name}' is not allowed by filter configuration"
239            ))])
240            .into());
241        }
242
243        if self.capabilities().tools.is_none() {
244            error!(
245                "[call_tool:{}] The server does not support tools capability - MCP ID: {}",
246                request_id, self.mcp_id
247            );
248            return Ok(CallToolResult::error(vec![ContentBlock::text(
249                "Server doesn't support tools capability",
250            )])
251            .into());
252        }
253
254        merge_context_meta_into_params(&mut request, &context.meta);
255        let _progress_guard = self.maybe_progress_guard(&context);
256
257        info!(
258            "[call_tool:{}] Send request to backend... - Tool: {}, Elapsed time: {}ms",
259            request_id,
260            tool_name,
261            start.elapsed().as_millis()
262        );
263
264        let call_result = self
265            .forward_backend_with_heartbeat(
266                &context,
267                request_id,
268                tool_name.as_ref(),
269                |peer| async move { peer.call_tool_once(request).await },
270            )
271            .await;
272
273        let elapsed = start.elapsed();
274        let result = match call_result {
275            Ok(response) => {
276                match &response {
277                    CallToolResponse::Complete(call_result) => {
278                        let is_error = call_result.is_error.unwrap_or(false);
279                        info!(
280                            "[call_tool:{}] Response received - tool: {}, time taken: {}ms, is_error: {}, MCP ID: {}",
281                            request_id,
282                            tool_name,
283                            elapsed.as_millis(),
284                            is_error,
285                            self.mcp_id
286                        );
287                        if is_error {
288                            debug!(
289                                "[call_tool:{}] Error response content: {:?}",
290                                request_id, call_result.content
291                            );
292                        }
293                    }
294                    CallToolResponse::InputRequired(_) => {
295                        info!(
296                            "[call_tool:{}] InputRequired received - tool: {}, time taken: {}ms, MCP ID: {}",
297                            request_id,
298                            tool_name,
299                            elapsed.as_millis(),
300                            self.mcp_id
301                        );
302                    }
303                    _ => {
304                        info!(
305                            "[call_tool:{}] Response received - tool: {}, time taken: {}ms, MCP ID: {}",
306                            request_id,
307                            tool_name,
308                            elapsed.as_millis(),
309                            self.mcp_id
310                        );
311                    }
312                }
313                Ok(response)
314            }
315            Err(err) => {
316                // Preserve historical CallToolResult::error shape for callers.
317                let message = err.message.clone();
318                if message == "Request cancelled" {
319                    warn!(
320                        "[call_tool:{}] Request canceled - Tool: {}, Time taken: {}ms, MCP ID: {}",
321                        request_id,
322                        tool_name,
323                        elapsed.as_millis(),
324                        self.mcp_id
325                    );
326                    Ok(CallToolResult::error(vec![ContentBlock::text("Request cancelled")]).into())
327                } else if message.contains("Backend connection is not available")
328                    || message.contains("Backend unavailable")
329                    || message.contains("Backend connect")
330                {
331                    error!(
332                        "[call_tool:{}] Backend unavailable - Tool: {}, Error: {}, MCP ID: {}",
333                        request_id, tool_name, message, self.mcp_id
334                    );
335                    Ok(CallToolResult::error(vec![ContentBlock::text(format!(
336                        "Backend unavailable: {message}"
337                    ))])
338                    .into())
339                } else if message.contains("Backend connection closed") {
340                    error!(
341                        "[call_tool:{}] Backend transport is closed - MCP ID: {}",
342                        request_id, self.mcp_id
343                    );
344                    Ok(CallToolResult::error(vec![ContentBlock::text(
345                        "Backend connection closed, please retry",
346                    )])
347                    .into())
348                } else {
349                    error!(
350                        "[call_tool:{}] Backend returns error - Tool: {}, Time: {}ms, Error: {}, MCP ID: {}",
351                        request_id,
352                        tool_name,
353                        elapsed.as_millis(),
354                        message,
355                        self.mcp_id
356                    );
357                    Ok(
358                        CallToolResult::error(vec![ContentBlock::text(format!(
359                            "Error: {message}"
360                        ))])
361                        .into(),
362                    )
363                }
364            }
365        };
366
367        info!(
368            "[call_tool:{}] Completed - Tool: {}, total time taken: {}ms",
369            request_id,
370            tool_name,
371            start.elapsed().as_millis()
372        );
373        result
374    }
375
376    async fn list_resources(
377        &self,
378        request: Option<PaginatedRequestParams>,
379        context: RequestContext<RoleServer>,
380    ) -> Result<rmcp::model::ListResourcesResult, ErrorData> {
381        match self.capabilities().resources {
382            Some(_) => {
383                let result = self
384                    .forward_backend(&context, |peer| async move {
385                        peer.list_resources(request).await
386                    })
387                    .await?;
388                info!(
389                    "[list_resources] Resource list results - MCP ID: {}, resource quantity: {}",
390                    self.mcp_id,
391                    result.resources.len()
392                );
393                debug!("Proxying list_resources response");
394                Ok(result)
395            }
396            None => {
397                warn!("Server doesn't support resources capability");
398                Ok(rmcp::model::ListResourcesResult::default())
399            }
400        }
401    }
402
403    async fn read_resource(
404        &self,
405        mut request: rmcp::model::ReadResourceRequestParams,
406        context: RequestContext<RoleServer>,
407    ) -> Result<ReadResourceResponse, ErrorData> {
408        match self.capabilities().resources {
409            Some(_) => {
410                merge_context_meta_into_params(&mut request, &context.meta);
411                let _progress_guard = self.maybe_progress_guard(&context);
412                let uri = request.uri.clone();
413                let result = self
414                    .forward_backend(&context, |peer| async move {
415                        peer.read_resource_once(request).await
416                    })
417                    .await?;
418                info!(
419                    "[read_resource] Resource read result - MCP ID: {}, URI: {}",
420                    self.mcp_id, uri
421                );
422                debug!("Proxying read_resource response for {}", uri);
423                Ok(result)
424            }
425            None => {
426                error!("Server doesn't support resources capability");
427                Ok(rmcp::model::ReadResourceResult::new(vec![]).into())
428            }
429        }
430    }
431
432    async fn list_resource_templates(
433        &self,
434        request: Option<PaginatedRequestParams>,
435        context: RequestContext<RoleServer>,
436    ) -> Result<rmcp::model::ListResourceTemplatesResult, ErrorData> {
437        match self.capabilities().resources {
438            Some(_) => {
439                let result = self
440                    .forward_backend(&context, |peer| async move {
441                        peer.list_resource_templates(request).await
442                    })
443                    .await?;
444                debug!("Proxying list_resource_templates response");
445                Ok(result)
446            }
447            None => {
448                warn!("Server doesn't support resources capability");
449                Ok(rmcp::model::ListResourceTemplatesResult::default())
450            }
451        }
452    }
453
454    async fn list_prompts(
455        &self,
456        request: Option<PaginatedRequestParams>,
457        context: RequestContext<RoleServer>,
458    ) -> Result<rmcp::model::ListPromptsResult, ErrorData> {
459        match self.capabilities().prompts {
460            Some(_) => {
461                let result =
462                    self.forward_backend(&context, |peer| async move {
463                        peer.list_prompts(request).await
464                    })
465                    .await?;
466                debug!("Proxying list_prompts response");
467                Ok(result)
468            }
469            None => {
470                warn!("Server doesn't support prompts capability");
471                Ok(rmcp::model::ListPromptsResult::default())
472            }
473        }
474    }
475
476    async fn get_prompt(
477        &self,
478        mut request: rmcp::model::GetPromptRequestParams,
479        context: RequestContext<RoleServer>,
480    ) -> Result<GetPromptResponse, ErrorData> {
481        match self.capabilities().prompts {
482            Some(_) => {
483                merge_context_meta_into_params(&mut request, &context.meta);
484                let _progress_guard = self.maybe_progress_guard(&context);
485                let result = self
486                    .forward_backend(&context, |peer| async move {
487                        peer.get_prompt_once(request).await
488                    })
489                    .await?;
490                debug!("Proxying get_prompt response");
491                Ok(result)
492            }
493            None => {
494                warn!("Server doesn't support prompts capability");
495                let messages = Vec::new();
496                Ok(rmcp::model::GetPromptResult::new(messages).into())
497            }
498        }
499    }
500
501    async fn complete(
502        &self,
503        request: rmcp::model::CompleteRequestParams,
504        context: RequestContext<RoleServer>,
505    ) -> Result<rmcp::model::CompleteResult, ErrorData> {
506        let result = self
507            .forward_backend(&context, |peer| async move { peer.complete(request).await })
508            .await?;
509        debug!("Proxying complete response");
510        Ok(result)
511    }
512
513    async fn subscribe(
514        &self,
515        request: SubscribeRequestParams,
516        context: RequestContext<RoleServer>,
517    ) -> Result<(), ErrorData> {
518        match self
519            .capabilities()
520            .resources
521            .as_ref()
522            .and_then(|r| r.subscribe)
523        {
524            Some(true) => {
525                self.forward_backend(
526                    &context,
527                    |peer| async move { peer.subscribe(request).await },
528                )
529                .await
530            }
531            _ => Err(ErrorData::method_not_found::<SubscribeRequestMethod>()),
532        }
533    }
534
535    async fn unsubscribe(
536        &self,
537        request: UnsubscribeRequestParams,
538        context: RequestContext<RoleServer>,
539    ) -> Result<(), ErrorData> {
540        match self
541            .capabilities()
542            .resources
543            .as_ref()
544            .and_then(|r| r.subscribe)
545        {
546            Some(true) => {
547                self.forward_backend(
548                    &context,
549                    |peer| async move { peer.unsubscribe(request).await },
550                )
551                .await
552            }
553            _ => Err(ErrorData::method_not_found::<UnsubscribeRequestMethod>()),
554        }
555    }
556
557    #[allow(deprecated)]
558    async fn set_level(
559        &self,
560        request: rmcp::model::SetLevelRequestParams,
561        context: RequestContext<RoleServer>,
562    ) -> Result<(), ErrorData> {
563        if self.capabilities().logging.is_none() {
564            return Err(ErrorData::method_not_found::<SetLevelRequestMethod>());
565        }
566        self.forward_backend(
567            &context,
568            |peer| async move { peer.set_level(request).await },
569        )
570        .await
571    }
572
573    async fn enqueue_task(
574        &self,
575        mut request: CallToolRequestParams,
576        context: RequestContext<RoleServer>,
577    ) -> Result<CreateTaskResult, ErrorData> {
578        let tasks = self.capabilities().tasks.clone();
579        if tasks.as_ref().is_none_or(|t| !t.supports_tools_call()) {
580            return Err(ErrorData::internal_error(
581                "Backend does not support task-based tools/call".to_string(),
582                None,
583            ));
584        }
585        merge_context_meta_into_params(&mut request, &context.meta);
586        let _progress_guard = self.maybe_progress_guard(&context);
587        let result = self
588            .forward_backend(&context, |peer| async move {
589                let result = peer
590                    .send_request(ClientRequest::CallToolRequest(CallToolRequest::new(
591                        request,
592                    )))
593                    .await?;
594                match result {
595                    ServerResult::CreateTaskResult(r) => Ok(r),
596                    _ => Err(ServiceError::UnexpectedResponse),
597                }
598            })
599            .await?;
600
601        // Shared isolation: route task notifications via registry.
602        // Per-session: bridge delivers 1:1 via notify_slot; no registry entry needed.
603        if self.isolation == BackendIsolation::Shared {
604            self.upstream_peers
605                .register_task(result.task.task_id.clone(), context.peer.clone());
606        }
607        Ok(result)
608    }
609
610    async fn list_tasks(
611        &self,
612        request: Option<PaginatedRequestParams>,
613        context: RequestContext<RoleServer>,
614    ) -> Result<ListTasksResult, ErrorData> {
615        let tasks = self.capabilities().tasks.clone();
616        if tasks.as_ref().is_none_or(|t| !t.supports_list()) {
617            return Err(ErrorData::method_not_found::<rmcp::model::ListTasksMethod>());
618        }
619        self.forward_backend(&context, |peer| async move {
620            let result = peer
621                .send_request(ClientRequest::ListTasksRequest(ListTasksRequest {
622                    method: Default::default(),
623                    params: request,
624                    extensions: Default::default(),
625                }))
626                .await?;
627            match result {
628                ServerResult::ListTasksResult(r) => Ok(r),
629                _ => Err(ServiceError::UnexpectedResponse),
630            }
631        })
632        .await
633    }
634
635    async fn get_task_info(
636        &self,
637        request: GetTaskParams,
638        context: RequestContext<RoleServer>,
639    ) -> Result<GetTaskResult, ErrorData> {
640        if self.capabilities().tasks.is_none() {
641            return Err(ErrorData::method_not_found::<rmcp::model::GetTaskMethod>());
642        }
643        self.forward_backend(&context, |peer| async move {
644            let result = peer
645                .send_request(ClientRequest::GetTaskRequest(GetTaskRequest::new(request)))
646                .await?;
647            match result {
648                ServerResult::GetTaskResult(r) => Ok(r),
649                _ => Err(ServiceError::UnexpectedResponse),
650            }
651        })
652        .await
653    }
654
655    async fn get_task_result(
656        &self,
657        request: GetTaskPayloadParams,
658        context: RequestContext<RoleServer>,
659    ) -> Result<rmcp::model::GetTaskPayloadResult, ErrorData> {
660        if self.capabilities().tasks.is_none() {
661            return Err(ErrorData::method_not_found::<
662                rmcp::model::GetTaskPayloadMethod,
663            >());
664        }
665        self.forward_backend(&context, |peer| async move {
666            let result = peer
667                .send_request(ClientRequest::GetTaskPayloadRequest(
668                    GetTaskPayloadRequest::new(request),
669                ))
670                .await?;
671            match result {
672                ServerResult::GetTaskPayloadResult(r) => Ok(r),
673                _ => Err(ServiceError::UnexpectedResponse),
674            }
675        })
676        .await
677    }
678
679    async fn cancel_task(
680        &self,
681        request: CancelTaskParams,
682        context: RequestContext<RoleServer>,
683    ) -> Result<rmcp::model::CancelTaskResult, ErrorData> {
684        let tasks = self.capabilities().tasks.clone();
685        if tasks.as_ref().is_none_or(|t| !t.supports_cancel()) {
686            return Err(ErrorData::method_not_found::<rmcp::model::CancelTaskMethod>());
687        }
688        let task_id = request.task_id.clone();
689        let result = self
690            .forward_backend(&context, |peer| async move {
691                let result = peer
692                    .send_request(ClientRequest::CancelTaskRequest(CancelTaskRequest::new(
693                        request,
694                    )))
695                    .await?;
696                match result {
697                    ServerResult::CancelTaskResult(r) => Ok(r),
698                    _ => Err(ServiceError::UnexpectedResponse),
699                }
700            })
701            .await?;
702        self.upstream_peers.unregister_task(&task_id);
703        Ok(result)
704    }
705
706    async fn on_initialized(&self, context: NotificationContext<RoleServer>) {
707        if let Some(session) = &self.per_session {
708            // Refresh slot (initialize already connected); keep idempotent ensure.
709            session.notify_slot.set(context.peer.clone());
710            if let Err(err) = self.ensure_connected().await {
711                error!(
712                    "[on_initialized] Per-session backend ensure failed - MCP ID: {}, err: {:?}",
713                    self.mcp_id, err
714                );
715            }
716            return;
717        }
718
719        let id = self.upstream_peers.register(context.peer.clone());
720        info!(
721            "[on_initialized] Registered upstream peer {} - MCP ID: {}, peers: {}",
722            id,
723            self.mcp_id,
724            self.upstream_peers.len()
725        );
726    }
727
728    async fn on_progress(
729        &self,
730        notification: rmcp::model::ProgressNotificationParam,
731        _context: NotificationContext<RoleServer>,
732    ) {
733        let peer = {
734            let inner_guard = self.peer.load();
735            let inner = match inner_guard.as_ref() {
736                Some(inner) => inner,
737                None => {
738                    error!(
739                        "Backend connection is not available, cannot forward progress notification"
740                    );
741                    return;
742                }
743            };
744            if inner.peer.is_transport_closed() {
745                error!("Backend transport is closed, cannot forward progress notification");
746                return;
747            }
748            inner.peer.clone()
749        };
750
751        match peer.notify_progress(notification).await {
752            Ok(_) => {
753                debug!("Proxying progress notification");
754            }
755            Err(err) => {
756                error!("Error notifying progress: {:?}", err);
757            }
758        }
759    }
760
761    async fn on_cancelled(
762        &self,
763        notification: rmcp::model::CancelledNotificationParam,
764        _context: NotificationContext<RoleServer>,
765    ) {
766        let peer = {
767            let inner_guard = self.peer.load();
768            let inner = match inner_guard.as_ref() {
769                Some(inner) => inner,
770                None => {
771                    error!(
772                        "Backend connection is not available, cannot forward cancelled notification"
773                    );
774                    return;
775                }
776            };
777            if inner.peer.is_transport_closed() {
778                error!("Backend transport is closed, cannot forward cancelled notification");
779                return;
780            }
781            inner.peer.clone()
782        };
783
784        match peer.notify_cancelled(notification).await {
785            Ok(_) => {
786                debug!("Proxying cancelled notification");
787            }
788            Err(err) => {
789                error!("Error notifying cancelled: {:?}", err);
790            }
791        }
792    }
793}
794
795impl ProxyHandler {
796    /// 获取 capabilities(从共享 cached_info 加载)
797    #[inline]
798    fn capabilities(&self) -> rmcp::model::ServerCapabilities {
799        self.cached_info.load().capabilities.clone()
800    }
801
802    /// Ensure backend is connected (lazy connect for per-session isolation).
803    async fn ensure_connected(&self) -> Result<(), ErrorData> {
804        if self.is_backend_available() {
805            return Ok(());
806        }
807        let Some(session) = &self.per_session else {
808            return Err(ErrorData::internal_error(
809                "Backend connection is not available, reconnecting...".to_string(),
810                None,
811            ));
812        };
813
814        let _lock = session.connect_lock.lock().await;
815        if self.is_backend_available() {
816            return Ok(());
817        }
818
819        let timeout = session.connector.connect_timeout();
820        let slot = session.notify_slot.clone();
821        let connect = session.connector.connect_session(slot);
822        let result = tokio::time::timeout(timeout, connect).await;
823
824        match result {
825            Ok(Ok(running)) => {
826                self.install_backend(running, true);
827                Ok(())
828            }
829            Ok(Err(err)) => {
830                error!(error = %err, "Per-session backend connect failed");
831                Err(ErrorData::internal_error(
832                    format!("Backend connect failed: {err}"),
833                    None,
834                ))
835            }
836            Err(_) => {
837                error!(?timeout, "Per-session backend connect timed out");
838                Err(ErrorData::internal_error(
839                    format!("Backend connect timed out after {timeout:?}"),
840                    None,
841                ))
842            }
843        }
844    }
845
846    /// Install a backend without going through shared-mode registry checks.
847    fn install_backend(&self, client: BackendRunningService, bump_version: bool) {
848        use std::ops::Deref;
849        let info = Self::extract_server_info(&client, &self.mcp_id);
850        self.cached_info.store(Arc::new(info));
851        let peer = client.deref().clone();
852        let inner = PeerInner {
853            peer,
854            _running: Arc::new(client),
855        };
856        self.peer.store(Some(Arc::new(inner)));
857        if bump_version {
858            let new_version = self.backend_version.fetch_add(1, Ordering::SeqCst) + 1;
859            info!(
860                "[ProxyHandler] Backend version update: {} - MCP ID: {}",
861                new_version, self.mcp_id
862            );
863        }
864    }
865
866    fn maybe_progress_guard(
867        &self,
868        context: &RequestContext<RoleServer>,
869    ) -> Option<ProgressRouteGuard> {
870        if self.isolation != BackendIsolation::Shared {
871            return None;
872        }
873        ProgressRouteGuard::try_from_meta(
874            self.upstream_peers.clone(),
875            &context.meta,
876            context.peer.clone(),
877        )
878    }
879
880    /// Load backend peer, check closed, run op with cancel race.
881    async fn forward_backend<T, Fut, F>(
882        &self,
883        context: &RequestContext<RoleServer>,
884        op: F,
885    ) -> Result<T, ErrorData>
886    where
887        F: FnOnce(Peer<RoleClient>) -> Fut,
888        Fut: Future<Output = Result<T, ServiceError>>,
889    {
890        self.ensure_connected().await?;
891
892        let inner_guard = self.peer.load();
893        let inner = inner_guard.as_ref().ok_or_else(|| {
894            error!("Backend connection is not available (reconnecting)");
895            ErrorData::internal_error(
896                "Backend connection is not available, reconnecting...".to_string(),
897                None,
898            )
899        })?;
900
901        if inner.peer.is_transport_closed() {
902            error!("Backend transport is closed");
903            return Err(ErrorData::internal_error(
904                "Backend connection closed, please retry".to_string(),
905                None,
906            ));
907        }
908
909        let peer = inner.peer.clone();
910        drop(inner_guard);
911
912        tokio::select! {
913            result = op(peer) => {
914                result.map_err(|err| {
915                    error!("Backend request error: {:?}", err);
916                    ErrorData::internal_error(format!("Backend error: {err}"), None)
917                })
918            }
919            _ = context.ct.cancelled() => {
920                info!("[forward_backend] Request canceled - MCP ID: {}", self.mcp_id);
921                Err(ErrorData::internal_error(
922                    "Request cancelled".to_string(),
923                    None,
924                ))
925            }
926        }
927    }
928
929    /// Like [`Self::forward_backend`], but logs heartbeat while waiting (long tools/call).
930    async fn forward_backend_with_heartbeat<T, Fut, F>(
931        &self,
932        context: &RequestContext<RoleServer>,
933        request_id: u64,
934        tool_name: &str,
935        op: F,
936    ) -> Result<T, ErrorData>
937    where
938        F: FnOnce(Peer<RoleClient>) -> Fut,
939        Fut: Future<Output = Result<T, ServiceError>>,
940    {
941        self.ensure_connected().await?;
942
943        let peer = {
944            let inner_guard = self.peer.load();
945            let inner = inner_guard.as_ref().ok_or_else(|| {
946                error!("Backend connection is not available (reconnecting)");
947                ErrorData::internal_error(
948                    "Backend connection is not available, reconnecting...".to_string(),
949                    None,
950                )
951            })?;
952
953            if inner.peer.is_transport_closed() {
954                error!("Backend transport is closed");
955                return Err(ErrorData::internal_error(
956                    "Backend connection closed, please retry".to_string(),
957                    None,
958                ));
959            }
960
961            let peer = inner.peer.clone();
962            drop(inner_guard);
963            peer
964        };
965
966        let call_future = op(peer.clone());
967        tokio::pin!(call_future);
968
969        const HEARTBEAT_INTERVAL: std::time::Duration = std::time::Duration::from_secs(30);
970        let mut heartbeat_interval = tokio::time::interval(HEARTBEAT_INTERVAL);
971        heartbeat_interval.tick().await;
972
973        loop {
974            tokio::select! {
975                biased;
976                result = &mut call_future => {
977                    return result.map_err(|err| {
978                        error!("Backend request error: {:?}", err);
979                        ErrorData::internal_error(format!("Backend error: {err}"), None)
980                    });
981                }
982                _ = context.ct.cancelled() => {
983                    info!(
984                        "[forward_backend_with_heartbeat:{}] Request canceled - Tool: {}, MCP ID: {}",
985                        request_id, tool_name, self.mcp_id
986                    );
987                    return Err(ErrorData::internal_error(
988                        "Request cancelled".to_string(),
989                        None,
990                    ));
991                }
992                _ = heartbeat_interval.tick() => {
993                    info!(
994                        "[forward_backend_with_heartbeat:{}] Waiting for backend... - Tool: {}, transport_closed: {}, MCP ID: {}",
995                        request_id,
996                        tool_name,
997                        peer.is_transport_closed(),
998                        self.mcp_id
999                    );
1000                }
1001            }
1002        }
1003    }
1004
1005    /// Shared upstream peer registry (for reconnect with the same notification bridge).
1006    pub fn upstream_peers(&self) -> Arc<UpstreamPeerRegistry> {
1007        self.upstream_peers.clone()
1008    }
1009
1010    /// 创建一个默认的 ServerInfo(用于断开状态)
1011    fn default_server_info(mcp_id: &str) -> ServerInfo {
1012        warn!(
1013            "[ProxyHandler] Create default ServerInfo - MCP ID: {}",
1014            mcp_id
1015        );
1016        ServerInfo::new(rmcp::model::ServerCapabilities::default())
1017            .with_server_info(Implementation::new("MCP Proxy", "0.1.0"))
1018    }
1019
1020    /// 从 RunningService 提取 ServerInfo
1021    fn extract_server_info(client: &BackendRunningService, mcp_id: &str) -> ServerInfo {
1022        client
1023            .peer_info()
1024            .map(|peer_info| {
1025                ServerInfo::new(peer_info.capabilities.clone())
1026                    .with_protocol_version(peer_info.protocol_version.clone())
1027                    .with_server_info(Implementation::new(
1028                        peer_info.server_info.name.clone(),
1029                        peer_info.server_info.version.clone(),
1030                    ))
1031                    .with_instructions(peer_info.instructions.clone().unwrap_or_default())
1032            })
1033            .unwrap_or_else(|| Self::default_server_info(mcp_id))
1034    }
1035
1036    /// 创建断开状态的 handler(用于初始化)
1037    /// 后续通过 swap_backend() 注入实际的后端连接
1038    pub fn new_disconnected(
1039        mcp_id: String,
1040        tool_filter: ToolFilter,
1041        default_info: ServerInfo,
1042    ) -> Self {
1043        info!(
1044            "[ProxyHandler] Create a disconnected handler - MCP ID: {}",
1045            mcp_id
1046        );
1047
1048        // 记录过滤器配置
1049        if tool_filter.is_enabled() {
1050            if let Some(ref allow_list) = tool_filter.allow_tools {
1051                info!(
1052                    "[ProxyHandler] Tool whitelist enabled - MCP ID: {}, allowed tools: {:?}",
1053                    mcp_id, allow_list
1054                );
1055            }
1056            if let Some(ref deny_list) = tool_filter.deny_tools {
1057                info!(
1058                    "[ProxyHandler] Tool blacklist enabled - MCP ID: {}, excluded tools: {:?}",
1059                    mcp_id, deny_list
1060                );
1061            }
1062        }
1063
1064        Self {
1065            peer: Arc::new(ArcSwapOption::empty()),
1066            cached_info: Arc::new(arc_swap::ArcSwap::from_pointee(default_info)),
1067            mcp_id,
1068            tool_filter,
1069            backend_version: Arc::new(AtomicU64::new(0)), // 断开状态版本为 0
1070            upstream_peers: Arc::new(UpstreamPeerRegistry::new()),
1071            isolation: BackendIsolation::Shared,
1072            per_session: None,
1073        }
1074    }
1075
1076    /// Create a disconnected per-session handler (URL isolation).
1077    ///
1078    /// Backend is opened during [`ServerHandler::initialize`] so the initialize
1079    /// response carries real backend capabilities.
1080    pub fn new_per_session(
1081        connector: Arc<dyn BackendConnector>,
1082        mcp_id: String,
1083        tool_filter: ToolFilter,
1084    ) -> Self {
1085        info!(
1086            "[ProxyHandler] Create per-session disconnected handler - MCP ID: {}",
1087            mcp_id
1088        );
1089        let default_info = Self::default_server_info(&mcp_id);
1090        Self {
1091            peer: Arc::new(ArcSwapOption::empty()),
1092            cached_info: Arc::new(arc_swap::ArcSwap::from_pointee(default_info)),
1093            mcp_id,
1094            tool_filter,
1095            backend_version: Arc::new(AtomicU64::new(0)),
1096            upstream_peers: Arc::new(UpstreamPeerRegistry::new()),
1097            isolation: BackendIsolation::PerSession,
1098            per_session: Some(PerSessionState {
1099                connector,
1100                notify_slot: UpstreamPeerSlot::new(),
1101                connect_lock: Arc::new(tokio::sync::Mutex::new(())),
1102            }),
1103        }
1104    }
1105
1106    /// Isolation mode for this handler.
1107    pub fn isolation(&self) -> BackendIsolation {
1108        self.isolation
1109    }
1110
1111    pub fn new(client: BackendRunningService) -> Self {
1112        Self::with_mcp_id(client, "unknown".to_string())
1113    }
1114
1115    pub fn with_mcp_id(client: BackendRunningService, mcp_id: String) -> Self {
1116        Self::with_tool_filter(client, mcp_id, ToolFilter::default())
1117    }
1118
1119    /// 创建带工具过滤器的 ProxyHandler(带初始后端连接,shared 隔离)
1120    pub fn with_tool_filter(
1121        client: BackendRunningService,
1122        mcp_id: String,
1123        tool_filter: ToolFilter,
1124    ) -> Self {
1125        use std::ops::Deref;
1126
1127        // 提取 ServerInfo 与共享 upstream peer registry
1128        let cached_info = Self::extract_server_info(&client, &mcp_id);
1129        let upstream_peers = client
1130            .service()
1131            .upstream_peers()
1132            .cloned()
1133            .unwrap_or_else(|| Arc::new(UpstreamPeerRegistry::new()));
1134
1135        // 克隆 Peer 用于并发请求(无需锁)
1136        let peer = client.deref().clone();
1137
1138        // 记录过滤器配置
1139        if tool_filter.is_enabled() {
1140            if let Some(ref allow_list) = tool_filter.allow_tools {
1141                info!(
1142                    "[ProxyHandler] Tool whitelist enabled - MCP ID: {}, allowed tools: {:?}",
1143                    mcp_id, allow_list
1144                );
1145            }
1146            if let Some(ref deny_list) = tool_filter.deny_tools {
1147                info!(
1148                    "[ProxyHandler] Tool blacklist enabled - MCP ID: {}, excluded tools: {:?}",
1149                    mcp_id, deny_list
1150                );
1151            }
1152        }
1153
1154        // 创建 PeerInner
1155        let inner = PeerInner {
1156            peer,
1157            _running: Arc::new(client),
1158        };
1159
1160        Self {
1161            peer: Arc::new(ArcSwapOption::from(Some(Arc::new(inner)))),
1162            cached_info: Arc::new(arc_swap::ArcSwap::from_pointee(cached_info)),
1163            mcp_id,
1164            tool_filter,
1165            backend_version: Arc::new(AtomicU64::new(1)), // 初始版本为 1
1166            upstream_peers,
1167            isolation: BackendIsolation::Shared,
1168            per_session: None,
1169        }
1170    }
1171
1172    /// 原子性替换后端连接
1173    /// - Some(client): 设置新的后端连接
1174    /// - None: 标记后端断开
1175    ///
1176    /// **版本控制**:每次调用都会递增 backend_version,使旧 session 失效
1177    ///
1178    /// **注意**:
1179    /// - 新 client 应使用与本 handler 相同的 [`UpstreamPeerRegistry`]
1180    ///   (见 `StreamClientConnection::connect_with_peers`)。
1181    /// - 对 [`BackendIsolation::PerSession`] 的 **management stub**(builder 返回值)
1182    ///   调用此方法没有运维意义:真实后端由各 session handler 持有。
1183    pub fn swap_backend(&self, new_client: Option<BackendRunningService>) {
1184        use std::ops::Deref;
1185
1186        if self.isolation == BackendIsolation::PerSession && self.peer.load().is_none() {
1187            warn!(
1188                "[ProxyHandler] swap_backend on disconnected per-session handler \
1189                 (likely management stub) - MCP ID: {}",
1190                self.mcp_id
1191            );
1192        }
1193
1194        match new_client {
1195            Some(client) => {
1196                if let Some(reg) = client.service().upstream_peers()
1197                    && !Arc::ptr_eq(reg, &self.upstream_peers)
1198                {
1199                    warn!(
1200                        "[ProxyHandler] swap_backend registry Arc differs from handler - MCP ID: {}",
1201                        self.mcp_id
1202                    );
1203                }
1204                // 刷新 cached capabilities / server info
1205                let info = Self::extract_server_info(&client, &self.mcp_id);
1206                self.cached_info.store(Arc::new(info));
1207
1208                let peer = client.deref().clone();
1209                let inner = PeerInner {
1210                    peer,
1211                    _running: Arc::new(client),
1212                };
1213                self.peer.store(Some(Arc::new(inner)));
1214                info!(
1215                    "[ProxyHandler] Backend connection updated - MCP ID: {}",
1216                    self.mcp_id
1217                );
1218            }
1219            None => {
1220                self.peer.store(None);
1221                info!(
1222                    "[ProxyHandler] Backend connection disconnected - MCP ID: {}",
1223                    self.mcp_id
1224                );
1225            }
1226        }
1227
1228        // 关键:递增版本号,使所有旧 session 失效
1229        let new_version = self.backend_version.fetch_add(1, Ordering::SeqCst) + 1;
1230        info!(
1231            "[ProxyHandler] Backend version update: {} - MCP ID: {}",
1232            new_version, self.mcp_id
1233        );
1234    }
1235
1236    /// 检查后端是否可用(快速检查,不发送请求)
1237    pub fn is_backend_available(&self) -> bool {
1238        let inner_guard = self.peer.load();
1239        match inner_guard.as_ref() {
1240            Some(inner) => !inner.peer.is_transport_closed(),
1241            None => false,
1242        }
1243    }
1244
1245    /// 检查 mcp 服务是否正常(异步版本,会发送验证请求)
1246    pub async fn is_mcp_server_ready(&self) -> bool {
1247        !self.is_terminated_async().await
1248    }
1249
1250    /// 检查后端连接是否已关闭(同步版本,仅检查 transport 状态)
1251    pub fn is_terminated(&self) -> bool {
1252        !self.is_backend_available()
1253    }
1254
1255    /// 异步检查后端连接是否已断开(会发送验证请求)
1256    pub async fn is_terminated_async(&self) -> bool {
1257        let peer = {
1258            let inner_guard = self.peer.load();
1259            let inner = match inner_guard.as_ref() {
1260                Some(inner) => inner,
1261                None => return true,
1262            };
1263            if inner.peer.is_transport_closed() {
1264                return true;
1265            }
1266            inner.peer.clone()
1267        };
1268
1269        match peer.list_tools(None).await {
1270            Ok(_) => {
1271                debug!("Backend connection status check: OK");
1272                false
1273            }
1274            Err(e) => {
1275                info!("Backend connection status check: Disconnected, reason: {e}");
1276                true
1277            }
1278        }
1279    }
1280
1281    /// 获取 MCP ID
1282    pub fn mcp_id(&self) -> &str {
1283        &self.mcp_id
1284    }
1285
1286    /// 获取后端 ServerInfo 的 JSON 表示
1287    ///
1288    /// 用于跨 rmcp 版本桥接:将 rmcp 1.4.0 的 ServerInfo 序列化为 JSON,
1289    /// 供 rmcp 0.10 侧反序列化使用。
1290    pub fn get_server_info_json(&self) -> serde_json::Value {
1291        serde_json::to_value(&**self.cached_info.load()).unwrap_or_default()
1292    }
1293
1294    /// 获取当前后端版本号
1295    ///
1296    /// 版本号用于跟踪后端连接变化:
1297    /// - 0: 断开状态
1298    /// - 1+: 已连接,每次 swap_backend 递增
1299    ///
1300    /// **用途**:配合 ProxyAwareSessionManager 实现 session 版本控制
1301    pub fn get_backend_version(&self) -> u64 {
1302        self.backend_version.load(Ordering::SeqCst)
1303    }
1304
1305    /// 直接调用后端 peer 的方法(用于 BackendSession trait 实现)
1306    ///
1307    /// 这是一个低级接口,直接操作 peer 而不经过 ServerHandler 的封装
1308    pub async fn call_peer_method(
1309        &self,
1310        method: &str,
1311        params: serde_json::Value,
1312    ) -> Result<serde_json::Value, String> {
1313        use rmcp::model::PaginatedRequestParams;
1314
1315        let peer = {
1316            let inner_guard = self.peer.load();
1317            let inner = inner_guard
1318                .as_ref()
1319                .ok_or_else(|| "Backend connection is not available (reconnecting)".to_string())?;
1320
1321            if inner.peer.is_transport_closed() {
1322                return Err("Backend transport is closed".to_string());
1323            }
1324            inner.peer.clone()
1325        };
1326
1327        match method {
1328            "tools/list" => {
1329                let request: Option<PaginatedRequestParams> = serde_json::from_value(params).ok();
1330                let result = peer
1331                    .list_tools(request)
1332                    .await
1333                    .map_err(|e| format!("list_tools error: {:?}", e))?;
1334                serde_json::to_value(result).map_err(|e| format!("serialize error: {}", e))
1335            }
1336            "tools/call" => {
1337                let request: rmcp::model::CallToolRequestParams = serde_json::from_value(params)
1338                    .map_err(|e| format!("Invalid params for tools/call: {}", e))?;
1339                if request.task.is_some() {
1340                    let result = peer
1341                        .send_request(ClientRequest::CallToolRequest(CallToolRequest::new(
1342                            request,
1343                        )))
1344                        .await
1345                        .map_err(|e| format!("enqueue_task error: {:?}", e))?;
1346                    match result {
1347                        ServerResult::CreateTaskResult(r) => {
1348                            serde_json::to_value(r).map_err(|e| format!("serialize error: {}", e))
1349                        }
1350                        other => Err(format!("unexpected enqueue_task response: {other:?}")),
1351                    }
1352                } else {
1353                    let result = peer
1354                        .call_tool_once(request)
1355                        .await
1356                        .map_err(|e| format!("call_tool error: {:?}", e))?;
1357                    let value = match result {
1358                        CallToolResponse::Complete(r) => serde_json::to_value(r),
1359                        CallToolResponse::InputRequired(r) => serde_json::to_value(r),
1360                        other => {
1361                            return Err(format!("unsupported CallToolResponse variant: {other:?}"));
1362                        }
1363                    };
1364                    value.map_err(|e| format!("serialize error: {}", e))
1365                }
1366            }
1367            "resources/subscribe" => {
1368                let request: SubscribeRequestParams = serde_json::from_value(params)
1369                    .map_err(|e| format!("Invalid params for resources/subscribe: {}", e))?;
1370                peer.subscribe(request)
1371                    .await
1372                    .map_err(|e| format!("subscribe error: {:?}", e))?;
1373                Ok(serde_json::json!({}))
1374            }
1375            "resources/unsubscribe" => {
1376                let request: UnsubscribeRequestParams = serde_json::from_value(params)
1377                    .map_err(|e| format!("Invalid params for resources/unsubscribe: {}", e))?;
1378                peer.unsubscribe(request)
1379                    .await
1380                    .map_err(|e| format!("unsubscribe error: {:?}", e))?;
1381                Ok(serde_json::json!({}))
1382            }
1383            "logging/setLevel" => {
1384                #[allow(deprecated)]
1385                {
1386                    let request: rmcp::model::SetLevelRequestParams =
1387                        serde_json::from_value(params)
1388                            .map_err(|e| format!("Invalid params for logging/setLevel: {}", e))?;
1389                    peer.set_level(request)
1390                        .await
1391                        .map_err(|e| format!("set_level error: {:?}", e))?;
1392                }
1393                Ok(serde_json::json!({}))
1394            }
1395            "tasks/list" => {
1396                let request: Option<PaginatedRequestParams> = serde_json::from_value(params).ok();
1397                let result = peer
1398                    .send_request(ClientRequest::ListTasksRequest(ListTasksRequest {
1399                        method: Default::default(),
1400                        params: request,
1401                        extensions: Default::default(),
1402                    }))
1403                    .await
1404                    .map_err(|e| format!("list_tasks error: {:?}", e))?;
1405                match result {
1406                    ServerResult::ListTasksResult(r) => {
1407                        serde_json::to_value(r).map_err(|e| format!("serialize error: {}", e))
1408                    }
1409                    other => Err(format!("unexpected list_tasks response: {other:?}")),
1410                }
1411            }
1412            "tasks/get" => {
1413                let request: GetTaskParams = serde_json::from_value(params)
1414                    .map_err(|e| format!("Invalid params for tasks/get: {}", e))?;
1415                let result = peer
1416                    .send_request(ClientRequest::GetTaskRequest(GetTaskRequest::new(request)))
1417                    .await
1418                    .map_err(|e| format!("get_task error: {:?}", e))?;
1419                match result {
1420                    ServerResult::GetTaskResult(r) => {
1421                        serde_json::to_value(r).map_err(|e| format!("serialize error: {}", e))
1422                    }
1423                    other => Err(format!("unexpected get_task response: {other:?}")),
1424                }
1425            }
1426            "tasks/result" => {
1427                let request: GetTaskPayloadParams = serde_json::from_value(params)
1428                    .map_err(|e| format!("Invalid params for tasks/result: {}", e))?;
1429                let result = peer
1430                    .send_request(ClientRequest::GetTaskPayloadRequest(
1431                        GetTaskPayloadRequest::new(request),
1432                    ))
1433                    .await
1434                    .map_err(|e| format!("get_task_result error: {:?}", e))?;
1435                match result {
1436                    ServerResult::GetTaskPayloadResult(r) => {
1437                        serde_json::to_value(r).map_err(|e| format!("serialize error: {}", e))
1438                    }
1439                    other => Err(format!("unexpected get_task_result response: {other:?}")),
1440                }
1441            }
1442            "tasks/cancel" => {
1443                let request: CancelTaskParams = serde_json::from_value(params)
1444                    .map_err(|e| format!("Invalid params for tasks/cancel: {}", e))?;
1445                let result = peer
1446                    .send_request(ClientRequest::CancelTaskRequest(CancelTaskRequest::new(
1447                        request,
1448                    )))
1449                    .await
1450                    .map_err(|e| format!("cancel_task error: {:?}", e))?;
1451                match result {
1452                    ServerResult::CancelTaskResult(r) => {
1453                        serde_json::to_value(r).map_err(|e| format!("serialize error: {}", e))
1454                    }
1455                    other => Err(format!("unexpected cancel_task response: {other:?}")),
1456                }
1457            }
1458            "resources/list" => {
1459                let request: Option<PaginatedRequestParams> = serde_json::from_value(params).ok();
1460                let result = peer
1461                    .list_resources(request)
1462                    .await
1463                    .map_err(|e| format!("list_resources error: {:?}", e))?;
1464                serde_json::to_value(result).map_err(|e| format!("serialize error: {}", e))
1465            }
1466            "resources/read" => {
1467                let request: rmcp::model::ReadResourceRequestParams =
1468                    serde_json::from_value(params)
1469                        .map_err(|e| format!("Invalid params for resources/read: {}", e))?;
1470                let result = peer
1471                    .read_resource_once(request)
1472                    .await
1473                    .map_err(|e| format!("read_resource error: {:?}", e))?;
1474                let value = match result {
1475                    ReadResourceResponse::Complete(r) => serde_json::to_value(r),
1476                    ReadResourceResponse::InputRequired(r) => serde_json::to_value(r),
1477                    other => {
1478                        return Err(format!(
1479                            "unsupported ReadResourceResponse variant: {other:?}"
1480                        ));
1481                    }
1482                };
1483                value.map_err(|e| format!("serialize error: {}", e))
1484            }
1485            "prompts/list" => {
1486                let request: Option<PaginatedRequestParams> = serde_json::from_value(params).ok();
1487                let result = peer
1488                    .list_prompts(request)
1489                    .await
1490                    .map_err(|e| format!("list_prompts error: {:?}", e))?;
1491                serde_json::to_value(result).map_err(|e| format!("serialize error: {}", e))
1492            }
1493            "prompts/get" => {
1494                let request: rmcp::model::GetPromptRequestParams =
1495                    serde_json::from_value(params)
1496                        .map_err(|e| format!("Invalid params for prompts/get: {}", e))?;
1497                let result = peer
1498                    .get_prompt_once(request)
1499                    .await
1500                    .map_err(|e| format!("get_prompt error: {:?}", e))?;
1501                let value = match result {
1502                    GetPromptResponse::Complete(r) => serde_json::to_value(r),
1503                    GetPromptResponse::InputRequired(r) => serde_json::to_value(r),
1504                    other => {
1505                        return Err(format!("unsupported GetPromptResponse variant: {other:?}"));
1506                    }
1507                };
1508                value.map_err(|e| format!("serialize error: {}", e))
1509            }
1510            "resources/templates/list" => {
1511                let request: Option<PaginatedRequestParams> = serde_json::from_value(params).ok();
1512                let result = peer
1513                    .list_resource_templates(request)
1514                    .await
1515                    .map_err(|e| format!("list_resource_templates error: {:?}", e))?;
1516                serde_json::to_value(result).map_err(|e| format!("serialize error: {}", e))
1517            }
1518            "completion/complete" => {
1519                let request: rmcp::model::CompleteRequestParams = serde_json::from_value(params)
1520                    .map_err(|e| format!("Invalid params for completion/complete: {}", e))?;
1521                let result = peer
1522                    .complete(request)
1523                    .await
1524                    .map_err(|e| format!("complete error: {:?}", e))?;
1525                serde_json::to_value(result).map_err(|e| format!("serialize error: {}", e))
1526            }
1527            _ => Err(format!("Unsupported method: {}", method)),
1528        }
1529    }
1530
1531    /// Update backend from a StreamClientConnection
1532    ///
1533    /// This method allows updating the backend connection using the high-level
1534    /// `StreamClientConnection` type, which is more convenient than the raw
1535    /// `RunningService` type.
1536    ///
1537    /// # Arguments
1538    /// * `conn` - Some(connection) to set new backend, None to mark disconnected
1539    pub fn swap_backend_from_connection(
1540        &self,
1541        conn: Option<crate::client::StreamClientConnection>,
1542    ) {
1543        match conn {
1544            Some(c) => {
1545                let running = c.into_running_service();
1546                self.swap_backend(Some(running));
1547            }
1548            None => {
1549                self.swap_backend(None);
1550            }
1551        }
1552    }
1553}
1554
1555impl mcp_common::BackendBridge for ProxyHandler {
1556    fn mcp_id(&self) -> &str {
1557        self.mcp_id()
1558    }
1559
1560    fn get_server_info_json(&self) -> serde_json::Value {
1561        self.get_server_info_json()
1562    }
1563
1564    fn is_backend_available(&self) -> bool {
1565        self.is_backend_available()
1566    }
1567
1568    fn is_mcp_server_ready(
1569        &self,
1570    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = bool> + Send + '_>> {
1571        Box::pin(self.is_mcp_server_ready())
1572    }
1573
1574    fn is_terminated_async(
1575        &self,
1576    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = bool> + Send + '_>> {
1577        Box::pin(self.is_terminated_async())
1578    }
1579
1580    fn call_peer_method(
1581        &self,
1582        method: &str,
1583        params: serde_json::Value,
1584    ) -> std::pin::Pin<
1585        Box<dyn std::future::Future<Output = Result<serde_json::Value, String>> + Send + '_>,
1586    > {
1587        let method = method.to_string();
1588        Box::pin(async move { ProxyHandler::call_peer_method(self, &method, params).await })
1589    }
1590}
1591
1592#[cfg(test)]
1593mod meta_merge_tests {
1594    use super::merge_context_meta_into_params;
1595    use rmcp::model::{
1596        CallToolRequestParams, NumberOrString, ProgressToken, RequestMetaObject, RequestParamsMeta,
1597    };
1598
1599    #[test]
1600    fn merge_copies_progress_token_from_context() {
1601        let mut params = CallToolRequestParams::new("echo");
1602        assert!(params.meta.is_none());
1603
1604        let mut context_meta = RequestMetaObject::new();
1605        context_meta.set_progress_token(ProgressToken(NumberOrString::Number(42)));
1606
1607        merge_context_meta_into_params(&mut params, &context_meta);
1608
1609        assert_eq!(
1610            params.progress_token(),
1611            Some(ProgressToken(NumberOrString::Number(42)))
1612        );
1613    }
1614
1615    #[test]
1616    fn merge_keeps_existing_params_meta_keys() {
1617        let mut params = CallToolRequestParams::new("echo");
1618        params.set_progress_token(ProgressToken(NumberOrString::Number(1)));
1619
1620        let mut context_meta = RequestMetaObject::new();
1621        context_meta.set_progress_token(ProgressToken(NumberOrString::Number(99)));
1622        context_meta.insert("traceId".to_string(), serde_json::json!("abc"));
1623
1624        merge_context_meta_into_params(&mut params, &context_meta);
1625
1626        assert_eq!(
1627            params.progress_token(),
1628            Some(ProgressToken(NumberOrString::Number(1))),
1629            "params win on conflict"
1630        );
1631        assert_eq!(
1632            params.meta.as_ref().and_then(|m| m.get("traceId")),
1633            Some(&serde_json::json!("abc"))
1634        );
1635    }
1636}