Skip to content

Commit 6f20bf9

Browse files
committed
Fix MCP tool name conflicts
1 parent c642af1 commit 6f20bf9

3 files changed

Lines changed: 24 additions & 27 deletions

File tree

model/mcp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Mcp(Base):
1616
type: Mapped[int] = mapped_column(default=0, comment='MCP 类型(0stdio 1sse 2streamable_http)')
1717
description: Mapped[str | None] = mapped_column(UniversalText, default=None, comment='MCP 描述')
1818
url: Mapped[str | None] = mapped_column(sa.String(256), default=None, comment='MCP 端点链接')
19-
headers: Mapped[str | None] = mapped_column(UniversalText, default=None, comment='请求 MCP 端点时的请求头')
19+
headers: Mapped[str | None] = mapped_column(sa.JSON(), default=None, comment='请求 MCP 端点时的请求头')
2020
args: Mapped[str | None] = mapped_column(sa.JSON(), default=None, comment='MCP 命令参数')
2121
env: Mapped[str | None] = mapped_column(sa.JSON(), default=None, comment='MCP 环境变量')
2222
timeout: Mapped[float | None] = mapped_column(default=5, comment='客户端初始化超时时间(秒)')

schema/mcp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class McpSchemaBase(SchemaBase):
1515
type: McpType = Field(McpType.stdio, description='MCP 类型')
1616
description: str | None = Field(None, description='MCP 描述')
1717
url: HttpUrl | None = Field(None, description='MCP 端点链接')
18-
headers: str | None = Field(None, description='请求 MCP 端点时的请求头')
18+
headers: dict[str, Any] | None = Field(None, description='请求 MCP 端点时的请求头')
1919
args: list[str] | None = Field(None, description='MCP 命令参数')
2020
env: dict[str, Any] | None = Field(None, description='MCP 环境变量')
2121
timeout: float | None = Field(5, description='客户端初始化超时时间(秒)')

service/mcp_service.py

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Any
55

66
from pydantic_ai.mcp import MCPServerSSE, MCPServerStdio, MCPServerStreamableHTTP
7+
from pydantic_ai.toolsets import AbstractToolset, PrefixedToolset
78
from sqlalchemy.ext.asyncio import AsyncSession
89

910
from backend.common.exception import errors
@@ -13,7 +14,7 @@
1314
from backend.plugin.ai.model import Mcp
1415
from backend.plugin.ai.schema.mcp import CreateMcpParam, UpdateMcpParam
1516

16-
McpToolset = MCPServerStdio | MCPServerSSE | MCPServerStreamableHTTP
17+
McpToolset = AbstractToolset[Any]
1718

1819

1920
class McpService:
@@ -55,47 +56,43 @@ async def get_toolsets(*, db: AsyncSession, mcp_ids: list[int]) -> list[McpTools
5556
mcps = await mcp_dao.get_by_ids(db, mcp_ids)
5657
toolsets: list[McpToolset] = []
5758
for mcp in mcps:
58-
headers = json.loads(mcp.headers) if isinstance(mcp.headers, str) else mcp.headers
59-
if headers is not None and not isinstance(headers, dict):
59+
headers = json.loads(mcp.headers) if isinstance(mcp.headers, str) else (mcp.headers or {})
60+
if not isinstance(headers, dict):
6061
raise errors.RequestError(msg=f'MCP 请求头格式非法: {mcp.name}')
61-
parsed_headers = None if headers is None else {str(key): str(value) for key, value in headers.items()}
62+
parsed_headers = {str(key): str(value) for key, value in headers.items()}
6263
if mcp.type == McpType.stdio:
6364
args = json.loads(mcp.args) if isinstance(mcp.args, str) else (mcp.args or [])
6465
env = json.loads(mcp.env) if isinstance(mcp.env, str) else (mcp.env or {})
6566
if not isinstance(args, list):
6667
raise errors.RequestError(msg=f'MCP 命令参数格式非法: {mcp.name}')
6768
if not isinstance(env, dict):
6869
raise errors.RequestError(msg=f'MCP 环境变量格式非法: {mcp.name}')
69-
toolsets.append(
70-
MCPServerStdio(
71-
command=mcp.command,
72-
args=[str(arg) for arg in args],
73-
env={str(key): str(value) for key, value in env.items()},
74-
timeout=mcp.timeout,
75-
)
70+
toolset = MCPServerStdio(
71+
command=mcp.command,
72+
args=[str(arg) for arg in args],
73+
env={str(key): str(value) for key, value in env.items()},
74+
timeout=mcp.timeout,
7675
)
7776
elif mcp.type == McpType.sse:
7877
if not mcp.url:
7978
raise errors.RequestError(msg=f'MCP 缺少 SSE URL: {mcp.name}')
80-
toolsets.append(
81-
MCPServerSSE(
82-
url=mcp.url,
83-
headers=parsed_headers,
84-
timeout=mcp.timeout,
85-
read_timeout=mcp.read_timeout,
86-
)
79+
toolset = MCPServerSSE(
80+
url=mcp.url,
81+
headers=parsed_headers,
82+
timeout=mcp.timeout,
83+
read_timeout=mcp.read_timeout,
8784
)
8885
else:
8986
if not mcp.url:
9087
raise errors.RequestError(msg=f'MCP 缺少 Streamable HTTP URL: {mcp.name}')
91-
toolsets.append(
92-
MCPServerStreamableHTTP(
93-
url=mcp.url,
94-
headers=parsed_headers,
95-
timeout=mcp.timeout,
96-
read_timeout=mcp.read_timeout,
97-
)
88+
toolset = MCPServerStreamableHTTP(
89+
url=mcp.url,
90+
headers=parsed_headers,
91+
timeout=mcp.timeout,
92+
read_timeout=mcp.read_timeout,
9893
)
94+
# 此举是为了为避免 MCP 工具名称冲突
95+
toolsets.append(PrefixedToolset(toolset, prefix=f'mcp_{mcp.id}'))
9996
return toolsets
10097

10198
@staticmethod

0 commit comments

Comments
 (0)