Refa: upgrade MCP SDK to v1.9.4 (#8421)

### What problem does this PR solve?

Upgrade MCP SDK to v1.9.4 (latest).

### Type of change

- [x] Refactoring
This commit is contained in:
Yongteng Lei 2025-06-23 16:53:59 +08:00 committed by GitHub
parent 0427eebe94
commit 03656da4dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 89 additions and 67 deletions

View File

@ -148,9 +148,9 @@ function start_mcp_server() {
"$PY" "${MCP_SCRIPT_PATH}" \
--host="${MCP_HOST}" \
--port="${MCP_PORT}" \
--base_url="${MCP_BASE_URL}" \
--base-url="${MCP_BASE_URL}" \
--mode="${MCP_MODE}" \
--api_key="${MCP_HOST_API_KEY}" &
--api-key="${MCP_HOST_API_KEY}" &
}
# -----------------------------------------------------------------------------

View File

@ -41,11 +41,11 @@ You can start an MCP server either from source code or via Docker.
```bash
# Launch the MCP server to work in self-host mode, run either of the following
uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --api_key=ragflow-xxxxx
# uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --mode=self-host --api_key=ragflow-xxxxx
uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --api-key=ragflow-xxxxx
# uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=self-host --api-key=ragflow-xxxxx
# To launch the MCP server to work in host mode, run the following instead:
# uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --mode=host
# uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=host
```
Where:
@ -194,4 +194,4 @@ The use of an API key depends on the operating mode of your MCP server.
- If launching from source, include the API key in the command.
- If launching from Docker, update the API key in **docker/docker-compose.yml**.
- **Host mode**:
If your RAGFlow MCP server is working in host mode, include the API key in the `headers` of your client requests to authenticate your client with the RAGFlow server. An example is available [here](https://github.com/infiniflow/ragflow/blob/main/mcp/client/client.py).
If your RAGFlow MCP server is working in host mode, include the API key in the `headers` of your client requests to authenticate your client with the RAGFlow server. An example is available [here](https://github.com/infiniflow/ragflow/blob/main/mcp/client/client.py).

View File

@ -19,11 +19,11 @@ from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from functools import wraps
import click
import requests
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import JSONResponse
from starlette.responses import JSONResponse, Response
from starlette.routing import Mount, Route
from strenum import StrEnum
@ -209,50 +209,64 @@ async def call_tool(name: str, arguments: dict, *, connector) -> list[types.Text
async def handle_sse(request):
async with sse.connect_sse(request.scope, request.receive, request._send) as streams:
await app.run(streams[0], streams[1], app.create_initialization_options(experimental_capabilities={"headers": dict(request.headers)}))
class AuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
# Authentication is deferred, will be handled by RAGFlow core service.
if request.url.path.startswith("/sse") or request.url.path.startswith("/messages"):
token = None
auth_header = request.headers.get("Authorization")
if auth_header and auth_header.startswith("Bearer "):
token = auth_header.removeprefix("Bearer ").strip()
elif request.headers.get("api_key"):
token = request.headers["api_key"]
if not token:
return JSONResponse({"error": "Missing or invalid authorization header"}, status_code=401)
return await call_next(request)
return Response()
def create_starlette_app():
middleware = None
if MODE == LaunchMode.HOST:
from starlette.types import ASGIApp, Receive, Scope, Send
class AuthMiddleware:
def __init__(self, app: ASGIApp):
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
path = scope["path"]
if path.startswith("/messages/") or path.startswith("/sse"):
headers = dict(scope["headers"])
token = None
auth_header = headers.get(b"authorization")
if auth_header and auth_header.startswith(b"Bearer "):
token = auth_header.removeprefix(b"Bearer ").strip()
elif b"api_key" in headers:
token = headers[b"api_key"]
if not token:
response = JSONResponse({"error": "Missing or invalid authorization header"}, status_code=401)
await response(scope, receive, send)
return
await self.app(scope, receive, send)
middleware = [Middleware(AuthMiddleware)]
return Starlette(
debug=True,
routes=[
Route("/sse", endpoint=handle_sse),
Route("/sse", endpoint=handle_sse, methods=["GET"]),
Mount("/messages/", app=sse.handle_post_message),
],
middleware=middleware,
)
if __name__ == "__main__":
"""
Launch example:
self-host:
uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --mode=self-host --api_key=ragflow-xxxxx
host:
uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --mode=host
"""
import argparse
@click.command()
@click.option("--base-url", type=str, default="http://127.0.0.1:9380", help="API base URL for RAGFlow backend")
@click.option("--host", type=str, default="127.0.0.1", help="Host to bind the RAGFlow MCP server")
@click.option("--port", type=int, default=9382, help="Port to bind the RAGFlow MCP server")
@click.option(
"--mode",
type=click.Choice(["self-host", "host"]),
default="self-host",
help=("Launch mode:\n self-host: run MCP for a single tenant (requires --api-key)\n host: multi-tenant mode, users must provide Authorization headers"),
)
@click.option("--api-key", type=str, default="", help="API key to use when in self-host mode")
def main(base_url, host, port, mode, api_key):
import os
import uvicorn
@ -260,31 +274,15 @@ if __name__ == "__main__":
load_dotenv()
parser = argparse.ArgumentParser(description="RAGFlow MCP Server")
parser.add_argument("--base_url", type=str, default="http://127.0.0.1:9380", help="api_url: http://<host_address>")
parser.add_argument("--host", type=str, default="127.0.0.1", help="RAGFlow MCP SERVER host")
parser.add_argument("--port", type=str, default="9382", help="RAGFlow MCP SERVER port")
parser.add_argument(
"--mode",
type=str,
default="self-host",
help="Launch mode options:\n"
" * self-host: Launches an MCP server to access a specific tenant space. The 'api_key' argument is required.\n"
" * host: Launches an MCP server that allows users to access their own spaces. Each request must include a Authorization header "
"indicating the user's identification.",
)
parser.add_argument("--api_key", type=str, default="", help="RAGFlow MCP SERVER HOST API KEY")
args = parser.parse_args()
if args.mode not in ["self-host", "host"]:
parser.error("--mode is only accept 'self-host' or 'host'")
if args.mode == "self-host" and not args.api_key:
parser.error("--api_key is required when --mode is 'self-host'")
global BASE_URL, HOST, PORT, MODE, HOST_API_KEY
BASE_URL = os.environ.get("RAGFLOW_MCP_BASE_URL", base_url)
HOST = os.environ.get("RAGFLOW_MCP_HOST", host)
PORT = os.environ.get("RAGFLOW_MCP_PORT", str(port))
MODE = os.environ.get("RAGFLOW_MCP_LAUNCH_MODE", mode)
HOST_API_KEY = os.environ.get("RAGFLOW_MCP_HOST_API_KEY", api_key)
BASE_URL = os.environ.get("RAGFLOW_MCP_BASE_URL", args.base_url)
HOST = os.environ.get("RAGFLOW_MCP_HOST", args.host)
PORT = os.environ.get("RAGFLOW_MCP_PORT", args.port)
MODE = os.environ.get("RAGFLOW_MCP_LAUNCH_MODE", args.mode)
HOST_API_KEY = os.environ.get("RAGFLOW_MCP_HOST_API_KEY", args.api_key)
if MODE == "self-host" and not HOST_API_KEY:
raise click.UsageError("--api-key is required when --mode is 'self-host'")
print(
r"""
@ -293,7 +291,7 @@ __ __ ____ ____ ____ _____ ______ _______ ____
| |\/| | | | |_) | \___ \| _| | |_) \ \ / /| _| | |_) |
| | | | |___| __/ ___) | |___| _ < \ V / | |___| _ <
|_| |_|\____|_| |____/|_____|_| \_\ \_/ |_____|_| \_\
""",
""",
flush=True,
)
print(f"MCP launch mode: {MODE}", flush=True)
@ -306,3 +304,14 @@ __ __ ____ ____ ____ _____ ______ _______ ____
host=HOST,
port=int(PORT),
)
if __name__ == "__main__":
"""
Launch example:
self-host:
uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=self-host --api-key=ragflow-xxxxx
host:
uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=host
"""
main()

View File

@ -126,9 +126,10 @@ dependencies = [
"trio>=0.29.0",
"langfuse>=2.60.0",
"debugpy>=1.8.13",
"mcp>=1.6.0",
"mcp>=1.9.4",
"opensearch-py==2.7.1",
"pluginlib==0.9.4",
"click>=8.1.8",
]
[project.optional-dependencies]

22
uv.lock generated
View File

@ -3010,7 +3010,7 @@ wheels = [
[[package]]
name = "mcp"
version = "1.6.0"
version = "1.9.4"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [
{ name = "anyio" },
@ -3018,13 +3018,14 @@ dependencies = [
{ name = "httpx-sse" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
{ name = "python-multipart" },
{ name = "sse-starlette" },
{ name = "starlette" },
{ name = "uvicorn" },
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
]
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723" }
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/06/f2/dc2450e566eeccf92d89a00c3e813234ad58e2ba1e31d11467a09ac4f3b9/mcp-1.9.4.tar.gz", hash = "sha256:cfb0bcd1a9535b42edaef89947b9e18a8feb49362e1cc059d6e7fc636f2cb09f" }
wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0" },
{ url = "https://mirrors.aliyun.com/pypi/packages/97/fc/80e655c955137393c443842ffcc4feccab5b12fa7cb8de9ced90f90e6998/mcp-1.9.4-py3-none-any.whl", hash = "sha256:7fcf36b62936adb8e63f89346bccca1268eeca9bf6dfb562ee10b1dfbda9dac0" },
]
[[package]]
@ -4748,6 +4749,15 @@ wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" },
]
[[package]]
name = "python-multipart"
version = "0.0.20"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13" }
wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104" },
]
[[package]]
name = "python-pptx"
version = "1.0.2"
@ -4868,6 +4878,7 @@ dependencies = [
{ name = "botocore" },
{ name = "cachetools" },
{ name = "chardet" },
{ name = "click" },
{ name = "cn2an" },
{ name = "cohere" },
{ name = "crawl4ai" },
@ -5016,6 +5027,7 @@ requires-dist = [
{ name = "botocore", specifier = "==1.34.140" },
{ name = "cachetools", specifier = "==5.3.3" },
{ name = "chardet", specifier = "==5.2.0" },
{ name = "click", specifier = ">=8.1.8" },
{ name = "cn2an", specifier = "==0.5.22" },
{ name = "cohere", specifier = "==5.6.2" },
{ name = "crawl4ai", specifier = "==0.3.8" },
@ -5054,7 +5066,7 @@ requires-dist = [
{ name = "langfuse", specifier = ">=2.60.0" },
{ name = "markdown", specifier = "==3.6" },
{ name = "markdown-to-json", specifier = "==2.1.1" },
{ name = "mcp", specifier = ">=1.6.0" },
{ name = "mcp", specifier = ">=1.9.4" },
{ name = "mini-racer", specifier = ">=0.12.4,<0.13.0" },
{ name = "minio", specifier = "==7.2.4" },
{ name = "mistralai", specifier = "==0.4.2" },