瀏覽代碼

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
tags/v0.20.0
Yongteng Lei 4 月之前
父節點
當前提交
03656da4dd
沒有連結到貢獻者的電子郵件帳戶。
共有 5 個檔案被更改,包括 90 行新增68 行删除
  1. 2
    2
      docker/entrypoint.sh
  2. 4
    4
      docs/develop/mcp/launch_mcp_server.md
  3. 65
    56
      mcp/server/server.py
  4. 2
    1
      pyproject.toml
  5. 17
    5
      uv.lock

+ 2
- 2
docker/entrypoint.sh 查看文件

"$PY" "${MCP_SCRIPT_PATH}" \ "$PY" "${MCP_SCRIPT_PATH}" \
--host="${MCP_HOST}" \ --host="${MCP_HOST}" \
--port="${MCP_PORT}" \ --port="${MCP_PORT}" \
--base_url="${MCP_BASE_URL}" \
--base-url="${MCP_BASE_URL}" \
--mode="${MCP_MODE}" \ --mode="${MCP_MODE}" \
--api_key="${MCP_HOST_API_KEY}" &
--api-key="${MCP_HOST_API_KEY}" &
} }


# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------

+ 4
- 4
docs/develop/mcp/launch_mcp_server.md 查看文件



```bash ```bash
# Launch the MCP server to work in self-host mode, run either of the following # 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: # 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: Where:
- If launching from source, include the API key in the command. - If launching from source, include the API key in the command.
- If launching from Docker, update the API key in **docker/docker-compose.yml**. - If launching from Docker, update the API key in **docker/docker-compose.yml**.
- **Host mode**: - **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).

+ 65
- 56
mcp/server/server.py 查看文件

from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from functools import wraps from functools import wraps


import click
import requests import requests
from starlette.applications import Starlette from starlette.applications import Starlette
from starlette.middleware import Middleware 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 starlette.routing import Mount, Route
from strenum import StrEnum from strenum import StrEnum


async def handle_sse(request): async def handle_sse(request):
async with sse.connect_sse(request.scope, request.receive, request._send) as streams: 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)})) 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(): def create_starlette_app():
middleware = None middleware = None
if MODE == LaunchMode.HOST: 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)] middleware = [Middleware(AuthMiddleware)]


return Starlette( return Starlette(
debug=True, debug=True,
routes=[ routes=[
Route("/sse", endpoint=handle_sse),
Route("/sse", endpoint=handle_sse, methods=["GET"]),
Mount("/messages/", app=sse.handle_post_message), Mount("/messages/", app=sse.handle_post_message),
], ],
middleware=middleware, 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 os


import uvicorn import uvicorn


load_dotenv() 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'")

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)
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)

if MODE == "self-host" and not HOST_API_KEY:
raise click.UsageError("--api-key is required when --mode is 'self-host'")


print( print(
r""" r"""
| |\/| | | | |_) | \___ \| _| | |_) \ \ / /| _| | |_) | | |\/| | | | |_) | \___ \| _| | |_) \ \ / /| _| | |_) |
| | | | |___| __/ ___) | |___| _ < \ V / | |___| _ < | | | | |___| __/ ___) | |___| _ < \ V / | |___| _ <
|_| |_|\____|_| |____/|_____|_| \_\ \_/ |_____|_| \_\ |_| |_|\____|_| |____/|_____|_| \_\ \_/ |_____|_| \_\
""",
""",
flush=True, flush=True,
) )
print(f"MCP launch mode: {MODE}", flush=True) print(f"MCP launch mode: {MODE}", flush=True)
host=HOST, host=HOST,
port=int(PORT), 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()

+ 2
- 1
pyproject.toml 查看文件

"trio>=0.29.0", "trio>=0.29.0",
"langfuse>=2.60.0", "langfuse>=2.60.0",
"debugpy>=1.8.13", "debugpy>=1.8.13",
"mcp>=1.6.0",
"mcp>=1.9.4",
"opensearch-py==2.7.1", "opensearch-py==2.7.1",
"pluginlib==0.9.4", "pluginlib==0.9.4",
"click>=8.1.8",
] ]


[project.optional-dependencies] [project.optional-dependencies]

+ 17
- 5
uv.lock 查看文件



[[package]] [[package]]
name = "mcp" name = "mcp"
version = "1.6.0"
version = "1.9.4"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" } source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [ dependencies = [
{ name = "anyio" }, { name = "anyio" },
{ name = "httpx-sse" }, { name = "httpx-sse" },
{ name = "pydantic" }, { name = "pydantic" },
{ name = "pydantic-settings" }, { name = "pydantic-settings" },
{ name = "python-multipart" },
{ name = "sse-starlette" }, { name = "sse-starlette" },
{ name = "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 = [ 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]] [[package]]
{ url = "https://mirrors.aliyun.com/pypi/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" }, { 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]] [[package]]
name = "python-pptx" name = "python-pptx"
version = "1.0.2" version = "1.0.2"
{ name = "botocore" }, { name = "botocore" },
{ name = "cachetools" }, { name = "cachetools" },
{ name = "chardet" }, { name = "chardet" },
{ name = "click" },
{ name = "cn2an" }, { name = "cn2an" },
{ name = "cohere" }, { name = "cohere" },
{ name = "crawl4ai" }, { name = "crawl4ai" },
{ name = "botocore", specifier = "==1.34.140" }, { name = "botocore", specifier = "==1.34.140" },
{ name = "cachetools", specifier = "==5.3.3" }, { name = "cachetools", specifier = "==5.3.3" },
{ name = "chardet", specifier = "==5.2.0" }, { name = "chardet", specifier = "==5.2.0" },
{ name = "click", specifier = ">=8.1.8" },
{ name = "cn2an", specifier = "==0.5.22" }, { name = "cn2an", specifier = "==0.5.22" },
{ name = "cohere", specifier = "==5.6.2" }, { name = "cohere", specifier = "==5.6.2" },
{ name = "crawl4ai", specifier = "==0.3.8" }, { name = "crawl4ai", specifier = "==0.3.8" },
{ name = "langfuse", specifier = ">=2.60.0" }, { name = "langfuse", specifier = ">=2.60.0" },
{ name = "markdown", specifier = "==3.6" }, { name = "markdown", specifier = "==3.6" },
{ name = "markdown-to-json", specifier = "==2.1.1" }, { 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 = "mini-racer", specifier = ">=0.12.4,<0.13.0" },
{ name = "minio", specifier = "==7.2.4" }, { name = "minio", specifier = "==7.2.4" },
{ name = "mistralai", specifier = "==0.4.2" }, { name = "mistralai", specifier = "==0.4.2" },

Loading…
取消
儲存