remote_mcp_kernel/handlers/
mcp_server.rs

1//! MCP Server handler
2//!
3//! This module provides the MCP server functionality, separated from OAuth concerns
4//! following microkernel design principles. The MCP server handles MCP protocol
5//! operations and depends on an OAuth provider for authentication.
6
7use rmcp::{
8    Error as McpError, RoleServer, ServerHandler, handler::server::router::tool::ToolRouter,
9    model::*, service::RequestContext, tool, tool_handler, tool_router,
10};
11
12use crate::server_info::helpers::*;
13
14/// MCP Server handler focused solely on MCP protocol operations
15///
16/// Following microkernel design principles, this handler handles only
17/// MCP-related concerns without dependencies on OAuth providers.
18#[derive(Clone)]
19pub struct McpServer {
20    /// MCP tool router
21    tool_router: ToolRouter<McpServer>,
22}
23
24impl McpServer {
25    /// Create a new MCP server
26    pub fn new() -> Self {
27        Self {
28            tool_router: Self::tool_router(),
29        }
30    }
31}
32
33/// MCP Server tool implementations
34#[tool_router]
35impl McpServer {
36    /// Get OAuth provider information
37    #[tool(description = "Get OAuth provider information and capabilities")]
38    async fn get_oauth_info(&self) -> Result<CallToolResult, McpError> {
39        Ok(CallToolResult::success(vec![Content::text(
40            oauth_provider_info_response().to_string(),
41        )]))
42    }
43
44    /// Health check for OAuth provider
45    #[tool(description = "Check the health status of the OAuth provider")]
46    async fn health_check(&self) -> Result<CallToolResult, McpError> {
47        Ok(CallToolResult::success(vec![Content::text(
48            health_check_response().to_string(),
49        )]))
50    }
51}
52
53/// MCP Server handler implementation
54#[tool_handler]
55impl ServerHandler for McpServer {
56    fn get_info(&self) -> rmcp::model::ServerInfo {
57        mcp_server_info()
58    }
59
60    async fn list_resources(
61        &self,
62        _request: Option<PaginatedRequestParam>,
63        _: RequestContext<RoleServer>,
64    ) -> Result<ListResourcesResult, McpError> {
65        Ok(ListResourcesResult {
66            resources: vec![],
67            next_cursor: None,
68        })
69    }
70
71    async fn read_resource(
72        &self,
73        ReadResourceRequestParam { uri }: ReadResourceRequestParam,
74        _: RequestContext<RoleServer>,
75    ) -> Result<ReadResourceResult, McpError> {
76        Err(McpError::resource_not_found(
77            "resource_not_found",
78            Some(serde_json::json!({ "uri": uri })),
79        ))
80    }
81
82    async fn list_prompts(
83        &self,
84        _request: Option<PaginatedRequestParam>,
85        _: RequestContext<RoleServer>,
86    ) -> Result<ListPromptsResult, McpError> {
87        Ok(ListPromptsResult {
88            next_cursor: None,
89            prompts: vec![],
90        })
91    }
92
93    async fn get_prompt(
94        &self,
95        GetPromptRequestParam { .. }: GetPromptRequestParam,
96        _: RequestContext<RoleServer>,
97    ) -> Result<GetPromptResult, McpError> {
98        Err(McpError::invalid_params("prompt not found", None))
99    }
100
101    async fn list_resource_templates(
102        &self,
103        _request: Option<PaginatedRequestParam>,
104        _: RequestContext<RoleServer>,
105    ) -> Result<ListResourceTemplatesResult, McpError> {
106        Ok(ListResourceTemplatesResult {
107            next_cursor: None,
108            resource_templates: Vec::new(),
109        })
110    }
111
112    async fn initialize(
113        &self,
114        _request: InitializeRequestParam,
115        _context: RequestContext<RoleServer>,
116    ) -> Result<InitializeResult, McpError> {
117        Ok(mcp_initialize_result())
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[tokio::test]
126    async fn test_mcp_server_creation() {
127        let mcp_server = McpServer::new();
128
129        // Test that server has tool router
130        let _tool_router = &mcp_server.tool_router;
131    }
132
133    #[tokio::test]
134    async fn test_mcp_server_oauth_info_tool() {
135        let mcp_server = McpServer::new();
136        let result = mcp_server.get_oauth_info().await.unwrap();
137
138        // Check that we got some content
139        assert!(!result.content.is_empty());
140
141        // Check that the content contains our provider name
142        let content_str = format!("{:?}", result.content[0]);
143        assert!(content_str.contains("MCP GitHub OAuth Provider"));
144    }
145
146    #[tokio::test]
147    async fn test_mcp_server_health_check() {
148        let mcp_server = McpServer::new();
149        let result = mcp_server.health_check().await.unwrap();
150
151        // Check that we got some content
152        assert!(!result.content.is_empty());
153    }
154}