Skip to content

Commit 1b52f96

Browse files
committed
streamable http support added
1 parent d11fa1c commit 1b52f96

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed

mcpgateway/main.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
ToolService,
106106
)
107107
from mcpgateway.transports.sse_transport import SSETransport
108+
from mcpgateway.transports.streamablehttp_transport import handle_streamable_http, session_manager
108109
from mcpgateway.types import (
109110
InitializeRequest,
110111
InitializeResult,
@@ -191,13 +192,19 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
191192
await logging_service.initialize()
192193
await sampling_handler.initialize()
193194
await resource_cache.initialize()
195+
196+
from contextlib import AsyncExitStack
197+
stack = AsyncExitStack()
198+
await stack.enter_async_context(session_manager.run())
199+
# await start_streamablehttp()
194200
logger.info("All services initialized successfully")
195201
yield
196202
except Exception as e:
197203
logger.error(f"Error during startup: {str(e)}")
198204
raise
199205
finally:
200206
logger.info("Shutting down MCP Gateway services")
207+
# await stop_streamablehttp()
201208
for service in [
202209
resource_cache,
203210
sampling_handler,
@@ -2019,6 +2026,9 @@ async def readiness_check(db: Session = Depends(get_db)):
20192026
else:
20202027
logger.warning("Admin API routes not mounted - Admin API disabled via MCPGATEWAY_ADMIN_API_ENABLED=False")
20212028

2029+
# Streamable http Mount
2030+
app.mount("/mcp", app=handle_streamable_http)
2031+
20222032
# Conditional static files mounting and root redirect
20232033
if UI_ENABLED:
20242034
# Mount static files for UI
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# streamablehttptransport.py
2+
3+
import logging
4+
import contextlib
5+
from collections.abc import AsyncIterator
6+
import anyio
7+
import mcp.types as types
8+
from mcp.server.lowlevel import Server
9+
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
10+
from starlette.types import Receive, Scope, Send
11+
from typing import Any, Dict, List, Optional, Union
12+
13+
from mcpgateway.services.tool_service import ToolService
14+
from mcpgateway.db import SessionLocal
15+
16+
17+
logger = logging.getLogger(__name__)
18+
19+
# This is the MCP configuration from your script's CLI options.
20+
# You can get these from environment variables, a config file, or hardcode them.
21+
JSON_RESPONSE_ENABLED = False
22+
23+
24+
tool_service = ToolService()
25+
26+
27+
# Initialize MCP app
28+
mcp_app = Server("mcp-streamable-http-stateless-demo")
29+
30+
@mcp_app.call_tool()
31+
async def call_tool(name: str, arguments: dict) -> List[Union[types.TextContent, types.ImageContent, types.EmbeddedResource]]:
32+
with SessionLocal() as db:
33+
result = await tool_service.invoke_tool(db, name, arguments)
34+
35+
tool_result = [
36+
types.TextContent(
37+
type=result.content[0].type,
38+
text=(
39+
result.content[0].text
40+
),
41+
)
42+
]
43+
return tool_result
44+
45+
@mcp_app.list_tools()
46+
async def list_tools() -> list[types.Tool]:
47+
48+
with SessionLocal() as db:
49+
tools = await tool_service.list_tools(db)
50+
listed_tools = []
51+
for tool in tools:
52+
listed_tools.append(types.Tool(
53+
name=tool.name,
54+
description=tool.description,
55+
inputSchema=tool.input_schema
56+
))
57+
58+
return listed_tools
59+
60+
61+
# --- 2. Create and Configure MCP Session Manager ---
62+
session_manager = StreamableHTTPSessionManager(
63+
app=mcp_app,
64+
event_store=None,
65+
json_response=JSON_RESPONSE_ENABLED,
66+
stateless=True,
67+
)
68+
69+
# This is the handler that will process requests for the mounted path.
70+
async def handle_streamable_http(scope: Scope, receive: Receive, send: Send) -> None:
71+
await session_manager.handle_request(scope, receive, send)
72+
73+
# --- 3. Lifespan Management to Start/Stop the Session Manager ---
74+
# @contextlib.asynccontextmanager
75+
# async def lifespan(app: FastAPI) -> AsyncIterator[None]:
76+
# """Manages the startup and shutdown of the MCP session manager."""
77+
# async with session_manager.run():
78+
# logger.info("Application starting with MCP StreamableHTTPSessionManager!")
79+
# yield
80+
# logger.info("Application shutting down...")
81+
82+
async def start_streamablehttp():
83+
# await session_manager.run()
84+
# session = await session_manager.run().__aenter__()
85+
# logger.info("Application starting with MCP StreamableHTTPSessionManager!")
86+
async with session_manager.run():
87+
logger.info("Application starting with MCP StreamableHTTPSessionManager!")
88+
# yield
89+
90+
91+
async def stop_streamablehttp():
92+
# await session_manager.stop()
93+
# await session_manager.run().__aexit__(None, None, None)
94+
logger.info("StreamableHTTPSessionManager shutting down...")

0 commit comments

Comments
 (0)