remote_mcp_kernel/handlers/
streamable_http.rs

1//! Streamable HTTP endpoint with OAuth authorization
2//!
3//! This module provides a streamable HTTP endpoint that requires OAuth authentication
4//! to access MCP services using the centralized middleware.
5
6use axum::{Router, middleware};
7use rmcp::transport::streamable_http_server::{
8    StreamableHttpService, session::local::LocalSessionManager,
9};
10
11use crate::handlers::McpServer;
12use oauth_provider_rs::{DefaultClientManager, OAuthProviderTrait, OAuthStorage, http_integration::middleware::simple_auth_middleware};
13
14/// Streamable HTTP handler with OAuth authorization
15#[derive(Clone)]
16pub struct StreamableHttpHandler<
17    P: OAuthProviderTrait<S, DefaultClientManager<S>> + 'static,
18    S: OAuthStorage + Clone + 'static,
19> {
20    mcp_server: McpServer<P, S>,
21}
22
23impl<P: OAuthProviderTrait<S, DefaultClientManager<S>> + 'static, S: OAuthStorage + Clone + 'static>
24    StreamableHttpHandler<P, S>
25{
26    /// Create new streamable HTTP handler with MCP server
27    pub fn new(mcp_server: McpServer<P, S>) -> Self {
28        Self { mcp_server }
29    }
30
31    /// Create router with streamable HTTP endpoint protected by OAuth
32    pub fn router(self) -> Router {
33        let mcp_server = self.mcp_server.clone();
34        let session_manager = LocalSessionManager::default();
35        let service = StreamableHttpService::new(
36            move || Ok(mcp_server.clone()),
37            session_manager.into(),
38            Default::default(),
39        );
40
41        Router::new()
42            .nest_service("/mcp/streamable", service)
43            .layer(middleware::from_fn(simple_auth_middleware))
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50    use oauth_provider_rs::{GitHubOAuthConfig, GitHubOAuthProvider, OAuthProvider, http_integration::utils::validate_token_simple};
51
52    #[tokio::test]
53    async fn test_streamable_http_handler_creation() {
54        let github_config = GitHubOAuthConfig {
55            client_id: "test_client_id".to_string(),
56            client_secret: "test_client_secret".to_string(),
57            redirect_uri: "http://localhost:8081/oauth/callback".to_string(),
58            scope: "read:user".to_string(),
59            provider_name: "github".to_string(),
60        };
61
62        let oauth_provider = OAuthProvider::new(GitHubOAuthProvider::new_github(github_config));
63        let mcp_server = McpServer::new(oauth_provider);
64        let _handler = StreamableHttpHandler::new(mcp_server);
65
66        // Handler should be created successfully - no assertion needed
67    }
68
69    #[tokio::test]
70    async fn test_token_validation() {
71        // Empty token should be invalid
72        assert!(validate_token_simple("").await.is_err());
73
74        // Non-empty token should be valid (in this simple implementation)
75        assert!(validate_token_simple("some_token").await.is_ok());
76    }
77}