mcp_streamable_proxy/proxy_handler/
mod.rs1use 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
30static REQUEST_COUNTER: AtomicU64 = AtomicU64::new(1);
32
33fn 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
51fn 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
68pub type BackendRunningService = RunningService<RoleClient, BackendNotificationBridge>;
70
71#[derive(Debug)]
74struct PeerInner {
75 peer: Peer<RoleClient>,
77 #[allow(dead_code)]
79 _running: Arc<BackendRunningService>,
80}
81
82#[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#[derive(Clone, Debug)]
105pub struct ProxyHandler {
106 peer: Arc<ArcSwapOption<PeerInner>>,
109 discovery: DiscoveryCache,
111 mcp_id: String,
113 tool_filter: ToolFilter,
115 backend_version: Arc<AtomicU64>,
117 upstream_peers: Arc<UpstreamPeerRegistry>,
119 isolation: BackendIsolation,
121 per_session: Option<PerSessionState>,
123}
124
125mod backend_bridge;
126mod connection_swap;
127mod discovery;
128mod forwarding;
129mod lifecycle;
130mod raw_dispatch;
131mod server_handler;
132
133use discovery::DiscoveryCache;