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