Skip to main content

mocopr_server/
server.rs

1//! High-level MCP server implementation
2
3use crate::middleware::Middleware;
4use crate::registry::*;
5use axum::extract::ws::WebSocket;
6use mocopr_core::monitoring::MonitoringSystem;
7use mocopr_core::prelude::*;
8use serde_json::json;
9use std::sync::Arc;
10use tracing::{debug, error, info, warn};
11
12/// High-level MCP server
13pub struct McpServer {
14    info: Implementation,
15    capabilities: ServerCapabilities,
16    handler: Arc<ServerMessageHandler>,
17    middleware_stack: Vec<Box<dyn Middleware>>,
18    monitoring_system: Option<MonitoringSystem>,
19    bind_address: String,
20    port: u16,
21    enable_http: bool,
22    enable_websocket: bool,
23}
24
25impl McpServer {
26    /// Create a new server builder
27    pub fn builder() -> crate::builder::McpServerBuilder {
28        crate::builder::McpServerBuilder::new()
29    }
30
31    /// Create a new MCP server
32    pub fn new(
33        info: Implementation,
34        capabilities: ServerCapabilities,
35        resource_registry: ResourceRegistry,
36        tool_registry: ToolRegistry,
37        prompt_registry: PromptRegistry,
38        middleware_stack: Vec<Box<dyn Middleware>>,
39        monitoring_system: Option<MonitoringSystem>,
40        bind_address: String,
41        port: u16,
42        enable_http: bool,
43        enable_websocket: bool,
44    ) -> Self {
45        let handler = Arc::new(ServerMessageHandler::new(
46            info.clone(),
47            capabilities.clone(),
48            resource_registry,
49            tool_registry,
50            prompt_registry,
51        ));
52
53        Self {
54            info,
55            capabilities,
56            handler,
57            middleware_stack,
58            monitoring_system,
59            bind_address,
60            port,
61            enable_http,
62            enable_websocket,
63        }
64    }
65
66    /// Get server info
67    pub fn info(&self) -> &Implementation {
68        &self.info
69    }
70
71    /// Get server capabilities
72    pub fn capabilities(&self) -> &ServerCapabilities {
73        &self.capabilities
74    }
75
76    /// Get the middleware stack
77    pub fn middleware(&self) -> &Vec<Box<dyn Middleware>> {
78        &self.middleware_stack
79    }
80
81    /// Get the monitoring system (if enabled)
82    pub fn monitoring(&self) -> Option<&MonitoringSystem> {
83        self.monitoring_system.as_ref()
84    }
85
86    /// Get the configured bind address
87    pub fn bind_address(&self) -> &str {
88        &self.bind_address
89    }
90
91    /// Get the configured port
92    pub fn port(&self) -> u16 {
93        self.port
94    }
95
96    /// Check if HTTP transport is enabled
97    pub fn http_enabled(&self) -> bool {
98        self.enable_http
99    }
100
101    /// Check if WebSocket transport is enabled
102    pub fn websocket_enabled(&self) -> bool {
103        self.enable_websocket
104    }
105
106    /// Run the server using stdio transport
107    pub async fn run_stdio(&self) -> Result<()> {
108        info!("Starting MCP server with stdio transport");
109
110        let transport = mocopr_core::transport::stdio::StdioTransport::current_process();
111        let (session, mut events) =
112            mocopr_core::protocol::Session::new(Box::new(transport), self.handler.clone());
113
114        // Handle session events in the background
115        let session_events = tokio::spawn(async move {
116            while let Some(event) = events.recv().await {
117                match event {
118                    mocopr_core::protocol::SessionEvent::Connected => {
119                        info!("Client connected");
120                    }
121                    mocopr_core::protocol::SessionEvent::Disconnected => {
122                        info!("Client disconnected");
123                        break;
124                    }
125                    mocopr_core::protocol::SessionEvent::Initialized { client_info } => {
126                        info!("Session initialized with client: {}", client_info.name);
127                    }
128                    mocopr_core::protocol::SessionEvent::Error { error } => {
129                        error!("Session error: {}", error);
130                    }
131                    _ => {}
132                }
133            }
134        });
135
136        // Run the session
137        let session_result = session.run().await;
138
139        // Wait for event handler to finish
140        let _ = session_events.await;
141
142        session_result
143    }
144
145    /// Run the server with configured transports
146    ///
147    /// This will start the server using HTTP and/or WebSocket transports
148    /// if they were enabled during building, falling back to stdio if neither is enabled.
149    pub async fn run(&self) -> Result<()> {
150        if self.enable_http && self.enable_websocket {
151            // Both HTTP and WebSocket enabled - start both
152            let addr = format!("{}:{}", self.bind_address, self.port);
153            info!("Starting server with HTTP and WebSocket on {}", addr);
154            self.run_http_with_websocket(&addr).await
155        } else if self.enable_websocket {
156            // Just WebSocket
157            let addr = format!("{}:{}", self.bind_address, self.port);
158            self.run_websocket(&addr).await
159        } else if self.enable_http {
160            // Just HTTP
161            let addr = format!("{}:{}", self.bind_address, self.port);
162            self.run_http(&addr).await
163        } else {
164            // Default to stdio
165            self.run_stdio().await
166        }
167    }
168
169    /// Run the server using HTTP transport
170    pub async fn run_http(&self, addr: &str) -> Result<()> {
171        info!("Starting MCP server with HTTP transport on {}", addr);
172
173        use axum::{Router, routing::post};
174        use tower_http::cors::CorsLayer;
175
176        let handler = self.handler.clone();
177
178        let app = Router::new()
179            .route("/mcp", post(handle_http_request))
180            .layer(CorsLayer::permissive())
181            .with_state(handler);
182
183        let listener = tokio::net::TcpListener::bind(addr).await?;
184        info!("HTTP server listening on {}", addr);
185
186        axum::serve(listener, app).await?;
187        Ok(())
188    }
189
190    /// Run the server with both HTTP and WebSocket support
191    pub async fn run_http_with_websocket(&self, addr: &str) -> Result<()> {
192        info!(
193            "Starting MCP server with HTTP and WebSocket transport on {}",
194            addr
195        );
196
197        use axum::{
198            Router,
199            extract::ws::WebSocketUpgrade,
200            routing::{get, post},
201        };
202        use tower_http::cors::CorsLayer;
203
204        let handler = self.handler.clone();
205        let ws_handler = handler.clone();
206
207        let app = Router::new()
208            .route("/mcp", post(handle_http_request))
209            .route(
210                "/mcp/ws",
211                get(move |ws: WebSocketUpgrade| async move {
212                    ws.on_upgrade(move |socket| handle_websocket(socket, ws_handler))
213                }),
214            )
215            .layer(CorsLayer::permissive())
216            .with_state(handler);
217
218        let listener = tokio::net::TcpListener::bind(addr).await?;
219        info!("HTTP+WebSocket server listening on {}", addr);
220
221        axum::serve(listener, app).await?;
222        Ok(())
223    }
224
225    /// Run the server using WebSocket transport
226    pub async fn run_websocket(&self, addr: &str) -> Result<()> {
227        info!("Starting MCP server with WebSocket transport on {}", addr);
228
229        use axum::{Router, extract::ws::WebSocketUpgrade, routing::get};
230        use tower_http::cors::CorsLayer;
231
232        let handler = self.handler.clone();
233
234        let app = Router::new()
235            .route(
236                "/mcp",
237                get(move |ws: WebSocketUpgrade| async move {
238                    ws.on_upgrade(move |socket| handle_websocket(socket, handler))
239                }),
240            )
241            .layer(CorsLayer::permissive());
242
243        let listener = tokio::net::TcpListener::bind(addr).await?;
244        info!("WebSocket server listening on {}", addr);
245
246        axum::serve(listener, app).await?;
247        Ok(())
248    }
249
250    /// Get a reference to the resource registry
251    pub fn resources(&self) -> &ResourceRegistry {
252        &self.handler.resources
253    }
254
255    /// Get a reference to the tool registry
256    pub fn tools(&self) -> &ToolRegistry {
257        &self.handler.tools
258    }
259
260    /// Get a reference to the prompt registry
261    pub fn prompts(&self) -> &PromptRegistry {
262        &self.handler.prompts
263    }
264}
265
266/// Route MCP method calls to appropriate handlers
267async fn handle_mcp_method(
268    handler: &Arc<ServerMessageHandler>,
269    json_msg: &serde_json::Value,
270) -> Option<serde_json::Value> {
271    let method = match json_msg.get("method").and_then(|m| m.as_str()) {
272        Some(method) => method,
273        None => {
274            return Some(json!({
275                "jsonrpc": "2.0",
276                "error": {
277                    "code": -32600,
278                    "message": "Invalid Request"
279                },
280                "id": json_msg.get("id")
281            }));
282        }
283    };
284
285    let id = json_msg.get("id");
286    let params = json_msg.get("params");
287
288    // Handle different MCP methods
289    let result = match method {
290        "ping" => {
291            let request = match params {
292                Some(p) => match serde_json::from_value::<PingRequest>(p.clone()) {
293                    Ok(req) => req,
294                    Err(_) => PingRequest { message: None },
295                },
296                None => PingRequest { message: None },
297            };
298            handler
299                .handle_ping(request)
300                .await
301                .map(|r| serde_json::to_value(r).unwrap())
302        }
303
304        "resources/list" => {
305            let request = match params {
306                Some(p) => serde_json::from_value::<ResourcesListRequest>(p.clone()),
307                None => Ok(ResourcesListRequest {
308                    pagination: PaginationParams { cursor: None },
309                }),
310            };
311            match request {
312                Ok(req) => handler
313                    .handle_resources_list(req)
314                    .await
315                    .map(|r| serde_json::to_value(r).unwrap()),
316                Err(e) => Err(mocopr_core::Error::InvalidRequest(e.to_string())),
317            }
318        }
319
320        "resources/read" => {
321            let request = match params
322                .and_then(|p| serde_json::from_value::<ResourcesReadRequest>(p.clone()).ok())
323            {
324                Some(req) => req,
325                None => {
326                    return Some(json!({
327                        "jsonrpc": "2.0",
328                        "error": {
329                            "code": -32602,
330                            "message": "Invalid params for resources/read"
331                        },
332                        "id": id
333                    }));
334                }
335            };
336            handler
337                .handle_resources_read(request)
338                .await
339                .map(|r| serde_json::to_value(r).unwrap())
340        }
341
342        "resources/subscribe" => {
343            let request = match params
344                .and_then(|p| serde_json::from_value::<ResourcesSubscribeRequest>(p.clone()).ok())
345            {
346                Some(req) => req,
347                None => {
348                    return Some(json!({
349                        "jsonrpc": "2.0",
350                        "error": {
351                            "code": -32602,
352                            "message": "Invalid params for resources/subscribe"
353                        },
354                        "id": id
355                    }));
356                }
357            };
358            handler
359                .handle_resources_subscribe(request)
360                .await
361                .map(|r| serde_json::to_value(r).unwrap())
362        }
363
364        "resources/unsubscribe" => {
365            let request = match params
366                .and_then(|p| serde_json::from_value::<ResourcesUnsubscribeRequest>(p.clone()).ok())
367            {
368                Some(req) => req,
369                None => {
370                    return Some(json!({
371                        "jsonrpc": "2.0",
372                        "error": {
373                            "code": -32602,
374                            "message": "Invalid params for resources/unsubscribe"
375                        },
376                        "id": id
377                    }));
378                }
379            };
380            handler
381                .handle_resources_unsubscribe(request)
382                .await
383                .map(|r| serde_json::to_value(r).unwrap())
384        }
385
386        "tools/list" => {
387            let request = match params {
388                Some(p) => serde_json::from_value::<ToolsListRequest>(p.clone()),
389                None => Ok(ToolsListRequest {
390                    pagination: PaginationParams { cursor: None },
391                }),
392            };
393            match request {
394                Ok(req) => handler
395                    .handle_tools_list(req)
396                    .await
397                    .map(|r| serde_json::to_value(r).unwrap()),
398                Err(e) => Err(mocopr_core::Error::InvalidRequest(e.to_string())),
399            }
400        }
401
402        "tools/call" => {
403            let request = match params
404                .and_then(|p| serde_json::from_value::<ToolsCallRequest>(p.clone()).ok())
405            {
406                Some(req) => req,
407                None => {
408                    return Some(json!({
409                        "jsonrpc": "2.0",
410                        "error": {
411                            "code": -32602,
412                            "message": "Invalid params for tools/call"
413                        },
414                        "id": id
415                    }));
416                }
417            };
418            handler
419                .handle_tools_call(request)
420                .await
421                .map(|r| serde_json::to_value(r).unwrap())
422        }
423
424        "prompts/list" => {
425            let request = match params {
426                Some(p) => serde_json::from_value::<PromptsListRequest>(p.clone()),
427                None => Ok(PromptsListRequest {
428                    pagination: PaginationParams { cursor: None },
429                }),
430            };
431            match request {
432                Ok(req) => handler
433                    .handle_prompts_list(req)
434                    .await
435                    .map(|r| serde_json::to_value(r).unwrap()),
436                Err(e) => Err(mocopr_core::Error::InvalidRequest(e.to_string())),
437            }
438        }
439
440        "prompts/get" => {
441            let request = match params
442                .and_then(|p| serde_json::from_value::<PromptsGetRequest>(p.clone()).ok())
443            {
444                Some(req) => req,
445                None => {
446                    return Some(json!({
447                        "jsonrpc": "2.0",
448                        "error": {
449                            "code": -32602,
450                            "message": "Invalid params for prompts/get"
451                        },
452                        "id": id
453                    }));
454                }
455            };
456            handler
457                .handle_prompts_get(request)
458                .await
459                .map(|r| serde_json::to_value(r).unwrap())
460        }
461
462        "logging/setLevel" => {
463            let request = match params
464                .and_then(|p| serde_json::from_value::<LoggingSetLevelRequest>(p.clone()).ok())
465            {
466                Some(req) => req,
467                None => {
468                    return Some(json!({
469                        "jsonrpc": "2.0",
470                        "error": {
471                            "code": -32602,
472                            "message": "Invalid params for logging/setLevel"
473                        },
474                        "id": id
475                    }));
476                }
477            };
478            handler
479                .handle_logging_set_level(request)
480                .await
481                .map(|r| serde_json::to_value(r).unwrap())
482        }
483
484        // Handle notifications (no response expected)
485        "notifications/initialized" => {
486            let notification = match params
487                .and_then(|p| serde_json::from_value::<InitializedNotification>(p.clone()).ok())
488            {
489                Some(notif) => notif,
490                None => InitializedNotification {},
491            };
492            match handler.handle_initialized(notification).await {
493                Ok(_) => return None,  // No response for notifications
494                Err(_) => return None, // Still no response for notifications, just log internally
495            }
496        }
497
498        // Unknown method
499        _ => Err(mocopr_core::Error::MethodNotFound(method.to_string())),
500    };
501
502    // Convert result to JSON response
503    match result {
504        Ok(value) => Some(json!({
505            "jsonrpc": "2.0",
506            "result": value,
507            "id": id
508        })),
509        Err(e) => Some(json!({
510            "jsonrpc": "2.0",
511            "error": {
512                "code": match &e {
513                    mocopr_core::Error::MethodNotFound(_) => -32601,
514                    mocopr_core::Error::InvalidRequest(_) => -32602,
515                    _ => -32603,
516                },
517                "message": e.to_string()
518            },
519            "id": id
520        })),
521    }
522}
523
524/// Handle WebSocket connections
525async fn handle_websocket(mut socket: WebSocket, handler: Arc<ServerMessageHandler>) {
526    info!("WebSocket client connected");
527
528    // Handle the MCP initialization handshake
529    let mut initialized = false;
530
531    while let Some(result) = socket.recv().await {
532        match result {
533            Ok(msg) => {
534                if let Ok(text) = msg.to_text() {
535                    debug!("Received WebSocket message: {}", text);
536
537                    // Parse and handle the MCP message
538                    match serde_json::from_str::<serde_json::Value>(text) {
539                        Ok(json_msg) => {
540                            let response_json = if !initialized {
541                                // Handle initialization
542                                if let Some(method) =
543                                    json_msg.get("method").and_then(|m| m.as_str())
544                                {
545                                    if method == "initialize" {
546                                        // Parse the initialize request
547                                        match serde_json::from_value::<InitializeRequest>(
548                                            json_msg.clone(),
549                                        ) {
550                                            Ok(init_request) => {
551                                                match handler.handle_initialize(init_request).await
552                                                {
553                                                    Ok(init_response) => {
554                                                        initialized = true;
555                                                        Some(json!({
556                                                            "jsonrpc": "2.0",
557                                                            "result": init_response,
558                                                            "id": json_msg.get("id")
559                                                        }))
560                                                    }
561                                                    Err(e) => Some(json!({
562                                                        "jsonrpc": "2.0",
563                                                        "error": {
564                                                            "code": -32603,
565                                                            "message": e.to_string()
566                                                        },
567                                                        "id": json_msg.get("id")
568                                                    })),
569                                                }
570                                            }
571                                            Err(e) => Some(json!({
572                                                "jsonrpc": "2.0",
573                                                "error": {
574                                                    "code": -32602,
575                                                    "message": format!("Invalid initialize request: {}", e)
576                                                },
577                                                "id": json_msg.get("id")
578                                            })),
579                                        }
580                                    } else {
581                                        // Send error for non-initialize message before init
582                                        Some(json!({
583                                            "jsonrpc": "2.0",
584                                            "error": {
585                                                "code": -32002,
586                                                "message": "Server not initialized"
587                                            },
588                                            "id": json_msg.get("id")
589                                        }))
590                                    }
591                                } else {
592                                    Some(json!({
593                                        "jsonrpc": "2.0",
594                                        "error": {
595                                            "code": -32600,
596                                            "message": "Invalid Request"
597                                        },
598                                        "id": json_msg.get("id")
599                                    }))
600                                }
601                            } else {
602                                // Handle regular MCP messages after initialization
603                                handle_mcp_method(&handler, &json_msg).await
604                            };
605
606                            // Send response if there is one
607                            if let Some(response) = response_json {
608                                let response_text = serde_json::to_string(&response)
609                                    .unwrap_or_else(|_| {
610                                        json!({
611                                            "jsonrpc": "2.0",
612                                            "error": {
613                                                "code": -32603,
614                                                "message": "Internal error"
615                                            }
616                                        })
617                                        .to_string()
618                                    });
619
620                                if let Err(e) = socket
621                                    .send(axum::extract::ws::Message::Text(response_text))
622                                    .await
623                                {
624                                    error!("Failed to send WebSocket response: {}", e);
625                                    break;
626                                }
627                            }
628                        }
629                        Err(e) => {
630                            error!("Failed to parse JSON message: {}", e);
631                            let error_response = json!({
632                                "jsonrpc": "2.0",
633                                "error": {
634                                    "code": -32700,
635                                    "message": "Parse error"
636                                },
637                                "id": null
638                            });
639                            if let Err(e) = socket
640                                .send(axum::extract::ws::Message::Text(error_response.to_string()))
641                                .await
642                            {
643                                error!("Failed to send error response: {}", e);
644                                break;
645                            }
646                        }
647                    }
648                } else {
649                    warn!("Received non-text WebSocket message, ignoring");
650                }
651            }
652            Err(e) => {
653                error!("WebSocket error: {}", e);
654                break;
655            }
656        }
657    }
658
659    info!("WebSocket client disconnected");
660}
661
662/// Server message handler that implements the MCP protocol
663pub struct ServerMessageHandler {
664    pub info: Implementation,
665    pub capabilities: ServerCapabilities,
666    pub resources: ResourceRegistry,
667    pub tools: ToolRegistry,
668    pub prompts: PromptRegistry,
669}
670
671impl ServerMessageHandler {
672    pub fn new(
673        info: Implementation,
674        capabilities: ServerCapabilities,
675        resources: ResourceRegistry,
676        tools: ToolRegistry,
677        prompts: PromptRegistry,
678    ) -> Self {
679        Self {
680            info,
681            capabilities,
682            resources,
683            tools,
684            prompts,
685        }
686    }
687}
688
689#[async_trait::async_trait]
690impl MessageHandler for ServerMessageHandler {
691    async fn handle_initialize(&self, request: InitializeRequest) -> Result<InitializeResponse> {
692        // Validate protocol version
693        if !Protocol::is_version_supported(&request.protocol_version) {
694            return Err(Error::InvalidRequest(format!(
695                "Unsupported protocol version: {}",
696                request.protocol_version
697            )));
698        }
699
700        info!(
701            "Client initialized: {} v{}",
702            request.client_info.name, request.client_info.version
703        );
704
705        Ok(InitializeResponse {
706            protocol_version: Protocol::latest_version().to_string(),
707            capabilities: self.capabilities.clone(),
708            server_info: self.info.clone(),
709            instructions: None,
710        })
711    }
712
713    async fn handle_resources_list(
714        &self,
715        request: ResourcesListRequest,
716    ) -> Result<ResourcesListResponse> {
717        self.resources.list_resources(request).await
718    }
719
720    async fn handle_resources_read(
721        &self,
722        request: ResourcesReadRequest,
723    ) -> Result<ResourcesReadResponse> {
724        self.resources.read_resource(request).await
725    }
726
727    async fn handle_resources_subscribe(
728        &self,
729        request: ResourcesSubscribeRequest,
730    ) -> Result<ResourcesSubscribeResponse> {
731        self.resources.subscribe_resource(request).await
732    }
733
734    async fn handle_resources_unsubscribe(
735        &self,
736        request: ResourcesUnsubscribeRequest,
737    ) -> Result<ResourcesUnsubscribeResponse> {
738        self.resources.unsubscribe_resource(request).await
739    }
740
741    async fn handle_tools_list(&self, request: ToolsListRequest) -> Result<ToolsListResponse> {
742        self.tools.list_tools(request).await
743    }
744
745    async fn handle_tools_call(&self, request: ToolsCallRequest) -> Result<ToolsCallResponse> {
746        self.tools.call_tool(request).await
747    }
748
749    async fn handle_prompts_list(
750        &self,
751        request: PromptsListRequest,
752    ) -> Result<PromptsListResponse> {
753        self.prompts.list_prompts(request).await
754    }
755
756    async fn handle_prompts_get(&self, request: PromptsGetRequest) -> Result<PromptsGetResponse> {
757        self.prompts.get_prompt(request).await
758    }
759}
760
761/// HTTP request handler for MCP over HTTP
762async fn handle_http_request(
763    axum::extract::State(_handler): axum::extract::State<Arc<ServerMessageHandler>>,
764    axum::Json(request): axum::Json<serde_json::Value>,
765) -> axum::Json<serde_json::Value> {
766    // For now, return a simple response indicating HTTP support is available
767    // This would need full protocol implementation similar to the WebSocket handler
768    axum::Json(json!({
769        "jsonrpc": "2.0",
770        "error": {
771            "code": -32601,
772            "message": "HTTP transport not fully implemented yet - use WebSocket or stdio"
773        },
774        "id": request.get("id").cloned()
775    }))
776}