mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-03 13:55:00 +08:00
fix(mcp): reject non-object requests with protocol errors
This commit is contained in:
@@ -109,13 +109,14 @@ def create_jsonrpc_error(
|
||||
"""
|
||||
创建 JSON-RPC 错误响应
|
||||
"""
|
||||
error_detail: Dict[str, object] = {"code": code, "message": message}
|
||||
error = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": request_id,
|
||||
"error": {"code": code, "message": message},
|
||||
"error": error_detail,
|
||||
}
|
||||
if data is not None:
|
||||
error["error"]["data"] = data
|
||||
error_detail["data"] = data
|
||||
return error
|
||||
|
||||
|
||||
@@ -164,7 +165,7 @@ async def _dispatch_jsonrpc_method(
|
||||
},
|
||||
)
|
||||
async def mcp_jsonrpc(
|
||||
request: Request, _: Annotated[str, Depends(verify_apikey)] = None
|
||||
request: Request, _: Annotated[Union[str, None], Depends(verify_apikey)] = None
|
||||
) -> Union[JSONResponse, Response]:
|
||||
"""
|
||||
MCP 标准 JSON-RPC 2.0 端点
|
||||
@@ -184,7 +185,9 @@ async def mcp_jsonrpc(
|
||||
if not isinstance(body, dict) or body.get("jsonrpc") != "2.0":
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content=create_jsonrpc_error(body.get("id"), -32600, "Invalid Request"),
|
||||
content=create_jsonrpc_error(
|
||||
body.get("id") if isinstance(body, dict) else None, -32600, "Invalid Request"
|
||||
),
|
||||
)
|
||||
|
||||
method = body.get("method")
|
||||
@@ -300,7 +303,7 @@ async def handle_tools_call(params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
},
|
||||
)
|
||||
async def delete_mcp_session(
|
||||
_: Annotated[str, Depends(verify_apikey)] = None,
|
||||
_: Annotated[Union[str, None], Depends(verify_apikey)] = None,
|
||||
) -> Union[JSONResponse, Response]:
|
||||
"""
|
||||
终止 MCP 会话(无状态模式下仅返回成功)
|
||||
@@ -347,7 +350,7 @@ async def list_tools(_: Annotated[str, Depends(verify_apikey)], page: Compatible
|
||||
summary="调用工具",
|
||||
response_model=_SchemaResponse[_SchemaToolCallData],
|
||||
)
|
||||
async def call_tool(request: _SchemaToolCallRequest, _: Annotated[str, Depends(verify_apikey)] = None) -> Any:
|
||||
async def call_tool(request: _SchemaToolCallRequest, _: Annotated[Union[str, None], Depends(verify_apikey)] = None) -> Any:
|
||||
"""
|
||||
调用指定的工具
|
||||
|
||||
|
||||
@@ -51,6 +51,10 @@ Fake-IP 等非公网解析地址需要通过 `IMAGE_PROXY_ALLOWED_PRIVATE_RANGES
|
||||
### 端点
|
||||
**POST** `/api/v1/mcp`
|
||||
|
||||
请求体须为单个 JSON-RPC 2.0 对象。非对象值(包括数组批量请求)或错误的协议版本返回
|
||||
HTTP 400、JSON-RPC 错误码 `-32600`;非对象请求的 `id` 为 `null`。无法解析的 JSON 返回
|
||||
HTTP 400、错误码 `-32700`。
|
||||
|
||||
### 支持的方法
|
||||
- `initialize`: 初始化会话,协商协议版本和能力。
|
||||
- `notifications/initialized`: 客户端确认初始化完成。
|
||||
|
||||
43
tests/test_mcp_requests.py
Normal file
43
tests/test_mcp_requests.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""MCP JSON-RPC 请求格式错误的协议回归。"""
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.api.endpoints import mcp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("body", "request_id"),
|
||||
[
|
||||
(None, None),
|
||||
([], None),
|
||||
([{"jsonrpc": "2.0", "method": "ping", "id": 1}], None),
|
||||
("invalid", None),
|
||||
(1, None),
|
||||
(True, None),
|
||||
({}, None),
|
||||
({"jsonrpc": "1.0", "id": 7}, 7),
|
||||
],
|
||||
)
|
||||
def test_invalid_request_returns_jsonrpc_error(body: object, request_id: object) -> None:
|
||||
"""非对象及错误版本的请求须返回 400 协议错误,不能在取 id 时崩溃。"""
|
||||
app = FastAPI()
|
||||
app.include_router(mcp.router, prefix="/mcp")
|
||||
app.dependency_overrides[mcp.verify_apikey] = lambda: "test-api-key"
|
||||
|
||||
with TestClient(app) as client:
|
||||
response = client.post(
|
||||
"/mcp",
|
||||
content=json.dumps(body),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {
|
||||
"jsonrpc": "2.0",
|
||||
"id": request_id,
|
||||
"error": {"code": -32600, "message": "Invalid Request"},
|
||||
}
|
||||
Reference in New Issue
Block a user