MCP 能力与接口
本页汇总当前已实现的 MCP(Model Context Protocol)出站能力。
范围
- ✅ 已实现:Agent 作为 MCP Client 调用外部 MCP Server(出站)。
- ✅ 已实现:
stdio与http_sse两种出站传输。 - ✅ 已实现:
http_sse下的URL Import模式,可将普通 HTTP URL 映射为 MCP 工具。 - ❌ 未实现:Chameleon 作为 MCP Server 对外提供入站能力。
功能清单
- MCP Server 注册与健康检查
- MCP Server 在线测试调用(控制面
tools/list/tools/call) - MCP 凭据管理(加密存储 + 轮换)
- Agent 级 MCP 挂载(优先级、allowlist、capability、超时、调用预算)
- Agent 级 MCP 响应控制面(direct-response rules + quick-match rules + intent-gate rules)
- Agent 级 MCP 真实预览与字段目录提取
- 对话编排中的受控工具调用回路
- 单次真实调用下的 MCP direct-response / raw JSON 直返
- MCP 调用审计查询(按会话/按 Agent)
传输配置约束
stdio
- 必填:
connection_config.command - 可选:
connection_config.args(字符串或字符串数组)
示例:
{
"transport_type": "stdio",
"connection_config": {
"command": "uv",
"args": ["run", "python", "scripts/mock_mcp_stdio.py"]
}
}
部署注意:
- 在线上部署环境中,
stdio命令是在ai-service运行容器内执行,不是在 Dokploy 宿主机或开发机上执行。 - 因此脚本路径必须填写容器内路径,例如
/app/scripts/dewell_tracking_stdio.py,不要填写开发机路径/home/atahang/codes/aibot/...。 - 当前
ai-service镜像构建时会复制仓库内scripts/到容器中的/app/scripts/。
Dokploy / 生产环境示例:
{
"transport_type": "stdio",
"connection_config": {
"command": "uv",
"args": ["run", "python", "/app/scripts/dewell_tracking_stdio.py"]
}
}
http_sse
- 必填:
connection_config.url(必须是http://或https://) - 可选:
connection_config.headers(JSON 对象) - 可选:
connection_config.import_mode:mcp或url mcp:远端按 MCP action 处理请求url:将 URL 映射为单工具模式(用于快速导入 GET/POST 接口)import_mode=url时可选:connection_config.http_method:GET/POST(默认GET)connection_config.tool_name:默认imported_http_tool
示例:
{
"transport_type": "http_sse",
"connection_config": {
"url": "https://api.example.com/tracking",
"headers": {
"Authorization": "Bearer {{token}}"
},
"import_mode": "url",
"http_method": "GET",
"tool_name": "tracking_lookup"
}
}
接口入口
MCP Server 在线测试调用
新增端点:POST /mcp-servers/{mcp_server_id}/test-call
- 用途:在 Admin Frontend 直接验证 MCP Server 返回结果,无需先挂载到 Agent。
- 支持动作:
tools/list:返回工具列表和原始响应tools/call:按tool_name + arguments执行一次工具调用- 可选参数:
credential_id:指定凭据timeout_ms:覆盖默认超时
Agent 级 MCP 响应预览
新增端点:POST /agents/{agent_id}/mcp-response-preview
- 用途:在 Agent 设置页里直接基于当前已挂载 MCP 执行一次真实调用,拿到字段目录,再配置 direct-response rule。
- 请求体:
mcp_server_idtool_namearguments- 响应体:
statuslatency_msraw_response_payloadbusiness_response_payloadfield_catalog[]error_message- 限制:
- 仅
chatAgent 支持 - 仅允许当前 Agent 已挂载且 active 的 MCP Server
- 若挂载配置了
allowed_tools_json,预览也必须遵守该 allowlist
tools/list 推荐返回结构
为提升参数生成稳定性,建议 MCP Server 在工具定义中返回 input_schema(JSON Schema)。
编排器会将该 schema 交给模型生成参数,并在本地做 required 字段预检。
如果 tools/list 返回多个工具,编排器会先过滤空名称工具并应用 Agent 挂载 allowlist。运行时会先走确定性选择(例如追踪意图优先 tracking tool),再在需要时使用有界上下文工具选择;如果当前消息并不明确需要任何工具,编排器会返回 no_tool,而不是盲用返回列表里的第一个工具。
当前运行时会把 query_tracking 与 query_tracking9 视为同一个 tracking capability,用于:
- 确定性工具选择
- allowlist 匹配
- Agent direct-response rule 匹配
- legacy raw passthrough key 匹配
- pre/post gate user guidance
- quick-match usage guidance
这些 capability 级判定与 guidance 文本现在由 ai_service/services/mcp_tool_capabilities.py 中的 capability registry 持有;ai_service/services/mcp_tool_runtime_strategies.py 负责把 capability 解析到对应的 runtime strategy;ai_service/services/mcp_tool_runtime_models.py 提供 capability-generic 的 runtime result shape;tracking 的具体运行时解析与门控逻辑则位于 ai_service/services/mcp_tool_tracking_runtime.py。MCPToolAdapterService 统一消费这几层能力资产,orchestrator 与 /stream API 只消费抽象后的能力接口,不再直接分支判断 tracking tool,也不再直接 import 某个 concrete tracking runtime 常量。缺参/歧义 guidance payload 也统一使用 capability-generic key:missing_identifiers 与 candidate_identifier_arguments,不再把 tracking-specific key shape 暴露到外层。
除内建 tracking capability 外,管理员现在还可以在 Agent 的某个 MCP 挂载上声明任意 tool_capability 标签,例如 shipment_eta、booking_status。这类自定义 capability 当前不会自动获得 identifier 解析、歧义澄清、上下文续问等 runtime strategy;它们只用于把挂载接入 Agent mcp_response_config.intent_gate_rules[] 的挂载级前门禁。
另外:
- 当唯一 eligible 工具就是 tracking tool 且当前消息没有任何追踪信号时,运行时会确定性跳过,不再额外跑一次 LLM 工具选择;但若最近可信
user历史已建立 tracking 上下文,则像SSHAS0172993这样的纯标识回复仍可被识别为安全的补参回合。 mcp-tool-selection与mcp-argument-generation都优先使用 structured output;其中工具选择会一次返回selected_tool_name + should_call_now + missing_required_arguments + reason,仅在模型/网关不支持时回退到 JSON 文本解析。- 若工具来自 AI selector,运行时会直接复用这份结构化执行决策进入
mcp-pre-generation-gate,避免再做一次松耦合的“先选工具、再猜能否立即执行”判断。 - 若命中 legacy
mcp.raw_passthrough_tool_keys且未命中 Agent direct-response rule,可通过mcp.raw_passthrough_wrap_json_code_block=true(或环境变量MCP_RAW_PASSTHROUGH_WRAP_JSON_CODE_BLOCK=true)让默认 raw JSON 直返包成```jsonfenced block。
示例:
{
"tools": [
{
"name": "query_tracking9",
"description": "Query De Well tracking by selectNo",
"input_schema": {
"type": "object",
"properties": {
"selectNo": {"type": "string"},
"query": {"type": "string"},
"round": {"type": "integer"}
},
"required": ["selectNo"]
}
}
]
}
query_tracking9 请求示例
{
"action": "tools/call",
"tool_name": "query_tracking9",
"arguments": {
"selectNo": "S55ES0000134"
},
"timeout_ms": 30000
}
query_tracking9 响应结构
query_tracking9 的返回分为两层:
- 顶层:工具调用元信息
response:业务响应体
当前响应是“部分投影”而不是完整透传,规则如下:
- 顶层
selectNo:本次实际用于查询的单号 response.selectNo:与顶层相同,便于消费方在业务层直接读取response.tracking:保留tracking数组tracking[].trackingSub[]:仅保留以下字段equipmentcurrentStatusshipmentTypecontainerNumberListMapcontainerNumberListMap[]:仅保留以下字段currentNewStatuscurrentTimecurrentLocationcurriconcurrListcurrList[]:返回完整事件列表,但每个事件只保留以下字段tiletimedesaddtype
换句话说:
currList是完整列表containerNumberListMap不是完整原始对象,而是裁剪后的对象列表curricon取自currList最后一条事件
示例响应:
{
"tool": "query_tracking9",
"selectNo": "S55ES0000134",
"response": {
"selectNo": "S55ES0000134",
"tracking": [
{
"trackingSub": [
{
"equipment": "CONT-1",
"currentStatus": "In Transit",
"shipmentType": "FCL",
"containerNumberListMap": [
{
"currentNewStatus": "Departed",
"currentTime": "2026-03-09 09:30:00",
"currentLocation": "Shanghai",
"curricon": "ship",
"currList": [
{
"tile": "Loaded",
"time": "2026-03-08 08:00:00",
"des": "Container loaded",
"addtype": "system"
},
{
"tile": "Departed",
"time": "2026-03-09 09:30:00",
"des": "Vessel departed",
"addtype": "system"
}
]
}
]
}
]
}
]
}
}
消费建议
- 如果消费的是 MCP 服务器工具原始返回,查询单号读取
response.selectNo - 如果消费的是聊天接口在 Agent direct-response
raw_json模式下的最终response_content,查询单号直接读取selectNo - 如果需要运输状态,读取
tracking[].trackingSub[].currentStatus - 如果需要整段轨迹事件,读取
tracking[].trackingSub[].containerNumberListMap[].currList - 如果需要
containerNumberListMap的其他原始字段,当前脚本不会返回,需要额外扩展
配置入口
- 配置说明:见 使用指南 / 配置
- Agent 级
mcp_response_config.direct_response_rules[]支持wrap_json_code_block: - 适用于
raw_json - 适用于
json_subset - 也适用于
text/json_subset无法渲染字段时回退到raw_json的场景 - 开启后返回形态为 Markdown
```jsonfenced block,便于下游直接按代码块展示原始 JSON - 关键项:
[mcp]section、MCP_SECRET_KEY等环境变量
Agent 级 Direct Return、Quick Match 与 Intent Gate(2026-03-18)
当前 MCP 控制面以 Agent 配置 mcp_response_config 为主,支持三类规则。查询前门禁关键词匹配的主入口是 Agent settings,而不是全局 ai_service/utils/settings.py。
direct_response_rules[]- 绑定
mcp_server_id + tool_name - 在“单次真实 MCP 调用”的回合里决定是否绕过最终 LLM 总结
- 输出模式支持:
raw_jsonjson_subsettext
quick_match_rules[]- 仅在
POST /stream的message_type=user_chat上生效 - 通过显式前缀命令直接映射到一个 MCP 工具,例如
#get_tracking123556 - 这是 API 层快路径:命中后直接返回 SSE,不进入 orchestrator / 最终 LLM 总结
- 可通过
suffix_parameter_name把后缀文本映射到 MCP 参数 - 在真正调用前仍执行 MCP 挂载/allowlist 校验、预门控/后门控、required 字段校验与审批 checkpoint;任一步阻断都不会发起
invoke_tool(...) - 若命中 quick-match 但
suffix_parameter_name后缀为空,或工具 schema 仍缺少 required 字段,则直接返回确定性的 usage guidance,而不是回退到普通聊天;例如:Quick-match '#get_tracking' requires one cargo number after the prefix. Use HBL No., FBA No., CTN No., or PO No. - 可选
response_rule_key复用 direct-response rule;若该字段未配置、留空或为空字符串,则 quick-match 不会自动套用同工具的 direct-response rule,而是直接返回 raw JSON intent_gate_rules[]tool_capability允许管理员填写任意非空 capability 标签- 对
tracking,用于配置 tracking/query 前门禁关键词与 Python regex 匹配 - 对自定义 capability,运行时会在
discover_tools()之前用该 rule 的keywords[]/regex_patterns[]对当前用户消息做一次挂载级过滤 match_source_mode支持default、append、replaceallow_contextual_follow_up=true时,像“帮我查一下 / 那个呢”这类省略式续问可复用最近唯一可信标识;当前该逻辑仍只对 tracking 生效- 自定义 capability 若未配置 enabled rule,则运行时继续保持旧行为,不会额外拦截该挂载
- 自定义 capability 目前没有内建默认 matcher,因此实际匹配来源就是管理员在 rule 中填写的关键词和 regex
direct-response 的运行语义:
- 触发条件:
- 本轮真实进入
invoke_tool(...)的 MCP 调用次数恰好为1 - 命中了该 Agent 的某个 enabled direct-response rule
- 返回方式:
raw_json:返回业务 payload 的 JSON 序列化结果json_subset:只保留selected_fields[]text:按prefix_text + selected_fields + suffix_text渲染文本- 兜底行为:
- 若
json_subset/text因字段缺失或全空无法渲染,运行时会回退为 raw JSON - 只要本轮真实 MCP 调用次数大于
1,就退回默认“聚合后总结”路径
产品约束说明:
- direct-response、quick-match 与 intent-gate 的产品入口统一是 Agent 级
mcp_response_config - 挂载级 capability 的产品入口是 Agent MCP mount 的
tool_capability [mcp]配置仍负责运行时总开关、轮次数、超时和加密等底层能力,但不再作为业务关键词匹配或直返产品语义的主控制面
Agent 级 MCP Runtime Strategy(2026-03-31)
在不改变现有 mcp_response_config 语义的前提下,聊天 Agent 现在额外支持 mcp_runtime_config,用于覆盖 MCP 运行时策略:
history_user_message_window- 覆盖全局
[mcp].history_user_message_window - 控制 MCP 前门禁与参数生成可引用的最近用户消息条数
0表示禁用最近历史补全,只看当前消息tool_selection_prompt_guidance- 追加到 Chameleon runtime 的 MCP 工具选择提示词末尾
- 适合放管理员自定义的门禁偏好、优先级或保守策略
tool_argument_prompt_guidance- 追加到 Chameleon runtime 的 MCP 参数生成提示词末尾
- 适合放参数抽取约束,例如“不要猜测缺失单号”
兼容性约束:
intent_gate_rules[]仍然保留在mcp_response_configmcp_runtime_config不重复存储 gate 规则,只负责 prompt/context 这类运行时覆盖history_user_message_window同时影响chameleon_chat_v1与langgraph_react_agent_v1- 两个 prompt guidance 字段当前仅影响
chameleon_chat_v1,因为只有它显式构造 MCP tool-selection / argument-generation helper prompt
失败时的最小 raw JSON payload 如下:
{
"server_name": "tracking-server",
"tool_name": "query_tracking9",
"status": "error",
"error_message": "upstream failed"
}
相关观测字段:
response_sourcellm_generated、mcp_raw、mcp_direct_response、mcp_quick_matchmcp_raw_passthrough- 布尔值,兼容标记当前响应是否来自 MCP 直返链路
mcp_direct_response- 当前响应是否命中 Agent direct-response 或 quick-match 直返
mcp_invocation_count- 当前轮次真实进入
invoke_tool(...)的次数 mcp_output_mode- 当前直返输出模式,例如
raw_json、json_subset、text mcp_response_rule_key- 命中的 direct-response rule key;若 quick-match 未绑定 formatter,则为
null mcp_quick_match_prefix- 命中的 quick-match prefix
mcp_format_fallback_used- 字段格式化失败后是否回退为 raw JSON
query_tracking9 调用门控与参数生成(2026-04)
为避免”无号也查”与 AI 幻觉参数,tracking tool(query_tracking / query_tracking9)在每次 MCP 循环内按以下 6 个阶段顺序执行:
阶段一:mcp-tool-selection-decision
确定本轮是否选定 tracking tool,以及是否需要进入参数生成阶段。
- 若当前消息包含可识别的追踪标识(单号、提单号、PO 号等),确定性选工具,并可跳过 AI 参数生成。
- 若当前消息没有追踪标识但有追踪意图(含关键词/正则),选工具但进入参数生成。
- 若消息属于闲聊或普通问答,静默跳过整条工具路径,不返回补参提示。
- 若 AI 选择器返回
no_tool,同样跳过。
阶段二:mcp-pre-generation-gate(参数生成前门控)
进一步校验是否满足触发 AI 参数生成的条件。判断依据:
- 当前消息含非歧义追踪标识 →
should_attempt=true,允许进入生成。 - 历史中含唯一追踪标识,且当前消息是省略式续问(如”查一下”、”继续”、”那个呢”)→
should_attempt=true,允许进入生成。 - 历史中存在多个竞争标识 →
should_attempt=false,返回歧义澄清提示,break。 - 追踪意图但当前消息与历史均无标识 →
should_attempt=false,返回结构化补参提示,break。 - 非追踪内容 →
should_attempt=false,静默跳过,不返回补参提示,break。 message_type=rule/form/other且无显式追踪标识 →should_attempt=false,break。
注意:此阶段”历史标识”仅用于判断是否值得进入参数生成,不会直接填入工具参数。
阶段三:mcp-argument-generation(AI 参数生成)
调用 tool_call_model 生成工具参数。
- 模型接收当前消息、工具 Schema、最近 user 历史(窗口大小由
history_user_message_window控制,默认 3 条)作为上下文。 - 优先使用 structured output,不支持时回退到 JSON 文本解析。
- AI 的输出是权威信号:AI 输出了哪些字段,就代表它在上下文中找到了哪些信息。若 AI 未输出某个 required 字段(如
selectNo),说明当前上下文不足以提供该参数,不应强行填充。
阶段四:build_tool_arguments(参数组装,非独立 span)
将 AI 生成的参数与当前消息的确定性提取结果合并,得到最终工具调用 payload。
此阶段分两个来源,优先级明确:
来源 A — AI 生成参数(主要来源)
- AI 输出的参数先经过 canonicalize(别名归一化)。
- 对追踪标识字段做可追溯性校验:只有能从当前消息(或允许历史复用时从最近 user 历史)确定性回溯到的值才保留,无法追溯的视为幻觉丢弃。
- 通过校验的字段直接写入 payload。
来源 B — 当前消息确定性提取(兜底来源)
- 仅对当前消息(
resolution_source=current_user_message)做规则提取。 - 典型场景:用户在上一轮被询问后,当前轮直接回复了单号(如
S55ES0000134),此时当前消息就是单号本身,直接提取补入。 - 不从历史消息中自动填充:历史中的旧单号不会在此阶段注入 payload。这是故意的设计——若 AI 已经看到历史却没有生成该字段,说明它判断当前上下文不足,不应由规则层覆盖这一判断。
最终 payload 还包含两个固定字段:query(原始用户消息)和 round(当前执行轮次)。
阶段五:mcp-tool-attempt-gate(参数生成后门控)
对已组装的参数做二次追踪标识校验。
- 若 payload 中的追踪标识字段可从用户消息确定性回溯 →
should_attempt=true。 - 若无可用标识 →
should_attempt=false,返回补参提示,break。 - 若存在歧义 →
should_attempt=false,返回歧义澄清提示,break。
阶段六:mcp-required-argument-check(必填字段校验)
对照工具 Schema 的 input_schema.required 数组,逐一检查 payload 中每个必填字段是否有有效值。
- 若全部必填字段均有值 → 放行,进入 mcp-call。
- 若存在缺失字段 → 记录审计(status=blocked),返回结构化缺参提示,break。
总结:门控触发条件
| 场景 | 在哪个阶段拦截 | 是否返回补参提示 |
|---|---|---|
| 非追踪内容(闲聊、问答) | pre-generation-gate | 否,静默跳过 |
| 追踪意图但无标识 | pre-generation-gate | 是 |
| 历史中标识歧义 | pre-generation-gate / tool-attempt-gate | 是(歧义澄清) |
| AI 未生成 required 字段,且当前消息无单号 | required-argument-check | 是 |
| AI 生成了幻觉字段(无法从消息追溯) | build_tool_arguments 丢弃,若由此导致必填缺失 | 由 required-argument-check 处理 |
| 省略式续问(”查一下”)+ 历史唯一标识 | 允许通过至 mcp-call | — |
| 用户直接回复单号(当前消息即单号) | 由来源 B 兜底补入,允许通过 | — |
其他约束
- 工具选择与参数生成读取最近
3条user历史(可通过mcp_runtime_config.history_user_message_window覆盖);assistant历史不参与标识提取。 - 解析层兼容多种标识格式:
selectNo=...、BL、container、booking、纯数字单号、混合字母数字单号(如555E898665)、常见 typo(如traking 212234566)。 message_type=rule/form/other默认不触发参数生成;仅在消息含显式追踪标识时允许调用。- 参数生成提示词可通过
mcp_runtime_config.tool_argument_prompt_guidance追加自定义约束。
MCP 审计状态语义(2026-03)
blocked:表示系统本来打算进入该工具路径,但被策略、歧义或缺参阻断。skipped:表示系统根据当前上下文决定不进入该工具路径。- 当当前消息上下文没有明确匹配任何工具时,审计会以
tool_name="_tool_selection"记录一次skipped决策,而不是把普通聊天错误挂到某个业务工具名上。
本地 Mock MCP 脚本测试
项目内提供了一个最小可用脚本:/home/atahang/codes/aibot/scripts/mock_mcp_stdio.py。
执行方式
echo '{"action":"tools/call","tool_name":"echo_time"}' | uv run python /home/atahang/codes/aibot/scripts/mock_mcp_stdio.py
示例输出
{"tool": "echo_time", "utc_time": "2026-02-10T03:16:48.411520+00:00", "arguments": {}}
说明:utc_time 为实时时间,每次执行都会不同。
其他常用测试
# Health check
echo '{"action":"health/check"}' | uv run python /home/atahang/codes/aibot/scripts/mock_mcp_stdio.py
# List tools
echo '{"action":"tools/list"}' | uv run python /home/atahang/codes/aibot/scripts/mock_mcp_stdio.py
部署平台添加本地 stdio MCP
若你是在 Dokploy 或其他部署平台上通过 Admin Frontend 添加 MCP Server,需注意:
Command和Args是两个独立表单字段。Args输入框不是 JSON 数组输入框,而是普通文本框;前端会按空格拆分参数。- 因此在 UI 中应填写:
Command:uvArgs:run python /app/scripts/dewell_tracking_stdio.py- 不要在
Args中填写["run", "python", "/app/scripts/dewell_tracking_stdio.py"]这种 JSON 数组字符串,否则后端会把整串当成一个参数,出现类似No such file or directory: '[\"run\", ...]'的错误。
推荐上线后立即执行两步验证:
Health CheckTest Call -> tools/list
常见工具测试示例:
{
"action": "tools/list"
}
{
"action": "tools/call",
"tool_name": "query_tracking9",
"arguments": {
"selectNo": "S55ES0000134"
}
}