support streamable http mcp (#1245)

Co-authored-by: luhualin
This commit is contained in:
Betula-L
2025-05-22 05:34:50 +08:00
committed by GitHub
parent 38261fd31c
commit 9fd680c366
3 changed files with 36 additions and 4 deletions

View File

@@ -4,7 +4,7 @@
![PyPI - Downloads](https://img.shields.io/pypi/dd/markitdown-mcp) ![PyPI - Downloads](https://img.shields.io/pypi/dd/markitdown-mcp)
[![Built by AutoGen Team](https://img.shields.io/badge/Built%20by-AutoGen%20Team-blue)](https://github.com/microsoft/autogen) [![Built by AutoGen Team](https://img.shields.io/badge/Built%20by-AutoGen%20Team-blue)](https://github.com/microsoft/autogen)
The `markitdown-mcp` package provides a lightweight STDIO and SSE MCP server for calling MarkItDown. The `markitdown-mcp` package provides a lightweight STDIO, SSE and Streamable HTTP MCP server for calling MarkItDown.
It exposes one tool: `convert_to_markdown(uri)`, where uri can be any `http:`, `https:`, `file:`, or `data:` URI. It exposes one tool: `convert_to_markdown(uri)`, where uri can be any `http:`, `https:`, `file:`, or `data:` URI.
@@ -25,7 +25,7 @@ To run the MCP server, ussing STDIO (default) use the following command:
markitdown-mcp markitdown-mcp
``` ```
To run the MCP server, using SSE use the following command: To run the MCP server, using SSE or Streamable HTTP use the following command:
```bash ```bash
markitdown-mcp --sse --host 127.0.0.1 --port 3001 markitdown-mcp --sse --host 127.0.0.1 --port 3001
@@ -114,6 +114,11 @@ If using SSE:
* input `http://127.0.0.1:3001/sse` as the URL, and * input `http://127.0.0.1:3001/sse` as the URL, and
* click `Connect` * click `Connect`
If using Streamable HTTP:
* select `Streamable HTTP` as the transport type,
* input `http://127.0.0.1:3001/mcp` as the URL, and
* click `Connect`
Finally: Finally:
* click the `Tools` tab, * click the `Tools` tab,
* click `List Tools`, * click `List Tools`,
@@ -122,7 +127,7 @@ Finally:
## Security Considerations ## Security Considerations
The server does not support authentication, and runs with the privileges if the user running it. For this reason, when running in SSE mode, it is recommended to run the server bound to `localhost` (default). The server does not support authentication, and runs with the privileges if the user running it. For this reason, when running in SSE or Streamable HTTP mode, it is recommended to run the server bound to `localhost` (default).
## Trademarks ## Trademarks

View File

@@ -24,7 +24,7 @@ classifiers = [
"Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: PyPy",
] ]
dependencies = [ dependencies = [
"mcp~=1.5.0", "mcp~=1.8.0",
"markitdown[all]>=0.1.1,<0.2.0", "markitdown[all]>=0.1.1,<0.2.0",
] ]

View File

@@ -1,10 +1,14 @@
import contextlib
import sys import sys
from collections.abc import AsyncIterator
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette from starlette.applications import Starlette
from mcp.server.sse import SseServerTransport from mcp.server.sse import SseServerTransport
from starlette.requests import Request from starlette.requests import Request
from starlette.routing import Mount, Route from starlette.routing import Mount, Route
from starlette.types import Receive, Scope, Send
from mcp.server import Server from mcp.server import Server
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
from markitdown import MarkItDown from markitdown import MarkItDown
import uvicorn import uvicorn
@@ -20,6 +24,12 @@ async def convert_to_markdown(uri: str) -> str:
def create_starlette_app(mcp_server: Server, *, debug: bool = False) -> Starlette: def create_starlette_app(mcp_server: Server, *, debug: bool = False) -> Starlette:
sse = SseServerTransport("/messages/") sse = SseServerTransport("/messages/")
session_manager = StreamableHTTPSessionManager(
app=mcp_server,
event_store=None,
json_response=True,
stateless=True,
)
async def handle_sse(request: Request) -> None: async def handle_sse(request: Request) -> None:
async with sse.connect_sse( async with sse.connect_sse(
@@ -33,12 +43,29 @@ def create_starlette_app(mcp_server: Server, *, debug: bool = False) -> Starlett
mcp_server.create_initialization_options(), mcp_server.create_initialization_options(),
) )
async def handle_streamable_http(
scope: Scope, receive: Receive, send: Send
) -> None:
await session_manager.handle_request(scope, receive, send)
@contextlib.asynccontextmanager
async def lifespan(app: Starlette) -> AsyncIterator[None]:
"""Context manager for session manager."""
async with session_manager.run():
print("Application started with StreamableHTTP session manager!")
try:
yield
finally:
print("Application shutting down...")
return Starlette( return Starlette(
debug=debug, debug=debug,
routes=[ routes=[
Route("/sse", endpoint=handle_sse), Route("/sse", endpoint=handle_sse),
Mount("/mcp", app=handle_streamable_http),
Mount("/messages/", app=sse.handle_post_message), Mount("/messages/", app=sse.handle_post_message),
], ],
lifespan=lifespan,
) )