/v1 托管工具
在对话补全、Messages、Responses 请求中声明平台工具,由平台执行工具循环:三种格式的写法、执行规则、响应形状与计费。
更新于 2026-10-28·阅读约 11 分钟
在 /v1 的请求中声明平台的工具后,模型请求这些工具时由平台执行,结果接入上下文后再次调用模型,直到模型作答,最后返回一个完整的响应。调用方无需实现工具循环。
| 接口 | 声明写法 | 响应中的工具调用 |
|---|---|---|
POST https://api.scirouter.cn/v1/chat/completions | tools 中一项 type: "mcp" | 不出现在 tool_calls 中,记录在扩展字段 scirouter.tool_calls |
POST https://api.scirouter.cn/v1/messages | mcp_servers + tools 中的 mcp_toolset,带 beta 请求头 | mcp_tool_use 与 mcp_tool_result 内容块 |
POST https://api.scirouter.cn/v1/responses | tools 中一项 type: "mcp" | mcp_list_tools 与 mcp_call 输出项 |
可用的工具、入参与出参、按用户计的限额与数据流向与 MCP 服务端相同,见 MCP 服务端与工具「工具」。任何支持函数调用的模型均可使用托管工具。
声明
#对话补全
#json{
"model": "YOUR_MODEL_ID",
"messages": [{ "role": "user", "content": "近一年钙钛矿封装稳定性有哪些新进展?给出处。" }],
"tools": [
{ "type": "mcp", "server_label": "scirouter", "allowed_tools": ["web_search", "fetch_page"], "require_approval": "never" }
],
"scirouter": { "max_tool_rounds": 6 }
}tools 中加入一项 type: "mcp"。该写法沿用 OpenAI Responses 的远程 MCP 工具,官方 SDK 不会拒绝。
| 字段 | 说明 |
|---|---|
type | 固定为 "mcp"。tools 中只能有一项 type: "mcp";调用方自己的 function 工具可同时存在 |
server_label | 只接受 "scirouter",其他取值返回 400 |
allowed_tools | 可省略,省略时为本次部署开放的全部工具。写成字符串数组或 { "tool_names": [...] }。为空数组时返回 400;含不存在的工具名时返回 400,details.available 列出可用的工具 |
server_url | 不接受,写了返回 400。外部 MCP 服务器在调用方自己的客户端中连接 |
require_approval | 只能为 "never" 或不填 |
Messages
#httpPOST https://api.scirouter.cn/v1/messages
anthropic-beta: mcp-client-2025-11-20json{
"model": "YOUR_MODEL_ID",
"max_tokens": 4096,
"messages": [{ "role": "user", "content": "近一年钙钛矿封装稳定性有哪些新进展?给出处。" }],
"mcp_servers": [{ "type": "url", "url": "https://api.scirouter.cn/mcp", "name": "scirouter" }],
"tools": [
{
"type": "mcp_toolset",
"mcp_server_name": "scirouter",
"default_config": { "enabled": false },
"configs": { "web_search": { "enabled": true }, "fetch_page": { "enabled": true } }
}
],
"scirouter": { "max_tool_rounds": 6 }
}写法沿用 Anthropic 的 MCP connector。
| 位置 | 规则 |
|---|---|
请求头 anthropic-beta | 含 mcp-client-2025-11-20 或 mcp-client-2026-09-15。已弃用的 mcp-client-2025-04-04 也接受,此时工具选择写在 mcp_servers[].tool_configuration(enabled、allowed_tools)。都没有时返回 400 |
mcp_servers[] | 只接受一项 name: "scirouter",type 为 "url",url 为 https 且路径为 /mcp(写 https://api.scirouter.cn/mcp)。平台只按这些字段识别声明,不比对域名、不连接该地址。authorization_token 可省略,写了也不使用 |
tools[] 中的 mcp_toolset | 正好一项,mcp_server_name 为 "scirouter"。每个工具是否开启依次取 configs.<工具名>.enabled、default_config.enabled,都没写时开启;configs 中不存在的工具名不报错 |
| 不支持 | defer_loading 与 mcp_toolset.tools(固定的工具列表),写了返回 400 |
| 其他 MCP 服务器 | name 不是 scirouter 的服务器返回 400,details.reason 为 server_tool_unsupported |
POST /messages/count_tokens 可携带同样的声明,见 Messages「统计 token 数」。
Responses
#json{
"model": "YOUR_MODEL_ID",
"input": "近一年钙钛矿封装稳定性有哪些新进展?给出处。",
"tools": [
{ "type": "mcp", "server_label": "scirouter", "allowed_tools": ["web_search", "fetch_page"], "require_approval": "never" }
],
"scirouter": { "max_tool_rounds": 6 }
}tools中的mcp项与对话补全的规则相同。server_label不是scirouter时返回 400,details.reason为server_tool_unsupported。headers、authorization、server_description不使用。tool_choice可写{ "type": "mcp", "server_label": "scirouter", "name": "web_search" }:带name时要求模型调用该工具,不带时要求模型调用一个工具。name不是本次开启的托管工具时返回 400。响应中的tool_choice与请求原文相同。max_tool_calls对托管工具不起作用。
轮数上限
#三种格式都接受顶层扩展字段 scirouter:
| 字段 | 说明 |
|---|---|
scirouter.max_tool_rounds | 1–30 的整数,缺省 6。带工具调用模型的轮数上限,到达后的处理见执行流程 |
- 顶层
scirouter只接受max_tool_rounds,写了其他键返回 400。未声明托管工具却写了max_tool_rounds,同样返回 400。 - 使用 OpenAI 官方 Python SDK 时,将
scirouter放入extra_body,SDK 会将其合并到请求体顶层。 - 本次部署未开放托管工具时,声明返回 400。调用方自己的函数(Messages 的自定义工具)与开启的托管工具重名时返回 400,错误信息中写明名称。
执行流程
#- 平台把开启的托管工具展开成函数定义,追加在调用方的工具之后,调用模型。工具描述按请求头
Accept-Language选择语言:以en开头时为英文,其余为中文。 - 模型只请求托管工具时,平台执行这些工具,结果按模型给出的顺序接入上下文,再调用模型。同一轮最多 4 个工具同时执行。
- 模型请求调用方自己的函数时,循环结束,该轮返回调用方;调用方执行后发送下一轮,与不使用托管工具时相同。同一轮也请求了托管工具时,托管工具不执行(状态为
skipped),调用方回复后模型会重新请求。 - 执行过一轮托管工具后,要求必须调用工具或指名某个托管工具的
tool_choice改为auto。 - 达到
max_tool_rounds后,平台再调用一次模型:工具定义照带,tool_choice设为不调用工具,模型根据已有资料作答,因此请求总能得到回答。这一轮模型仍请求托管工具时不执行,状态为skipped。 - 带托管工具的请求只发往支持工具调用的线路。模型没有支持工具调用的线路时返回 503
model_unavailable(同对话补全),不退回文本协议。
工具失败与限频
#下列情况不报错,循环照常继续:原因作为该次工具调用的结果交回模型,状态为 failed,由模型决定重试或换用其他方法。
| 情况 | 交回模型的结果 |
|---|---|
| 入参不符合工具的 schema、工具不存在 | 哪个参数不合法、为什么;该次不执行 |
| 被限频(按用户计的核对、找先例次数,见 MCP 服务端与工具「限额与错误码」) | 「调用太频繁,这次没有执行:稍后再试或换个办法」 |
| 收费工具遇到余额不足或达到消费上限 | 原因;该次不执行 |
| 工具已执行但未成功(网站无法打开、被策略拒绝、库中没有该记录) | 原因 |
MCP 服务端按 Key 计的两项限额不作用于托管工具。平台内部出错时,同一轮的其他工具调用被取消,整个请求返回错误。
响应
#对话补全的响应
#响应仍为 OpenAI 格式。托管工具的调用不出现在 choices[].message.tool_calls 中,顶层 scirouter 扩展字段(见可观测性)增加 tool_rounds 与 tool_calls。以下示例省略 choices 与 usage:
json{
"scirouter": {
"request_id": "req_…",
"cost_quota": 23180,
"tool_rounds": 3,
"tool_calls": [
{ "round": 0, "id": "call_…", "name": "web_search", "status": "done", "summary": "web 8 条", "ms": 1320 },
{ "round": 1, "id": "call_…", "name": "fetch_page", "status": "failed", "summary": "读网页失败:不允许的地址", "ms": 45 }
]
}
}数值为示例。
| 字段 | 说明 |
|---|---|
tool_rounds | 调用模型的次数 |
tool_calls | 每项为一次托管工具调用。round 从 0 起,与请求日志的 v1_tool_round 标签口径相同;status 为 done / failed / skipped。不含入参与正文 |
cost_quota | 各轮模型调用之和,不含工具费,见计费 |
request_id | 每轮使用不同的 request_id,此处为最后一轮的 |
usage 为各轮之和,prompt_tokens_details 等嵌套字段逐项相加。
Messages 的响应
#响应为一条 message:每个执行了托管工具的轮次依次给出模型的内容块(托管工具的 tool_use 换成 mcp_tool_use)与各次调用的 mcp_tool_result,最后是最后一轮的内容块。以下示例省略 usage:
json{
"id": "msg_…",
"type": "message",
"role": "assistant",
"content": [
{ "type": "mcp_tool_use", "id": "toolu_…", "name": "web_search", "server_name": "scirouter", "input": { "query": "钙钛矿 封装 稳定性" } },
{ "type": "mcp_tool_result", "tool_use_id": "toolu_…", "is_error": false, "content": [{ "type": "text", "text": "网页搜索「钙钛矿 封装 稳定性」,8 条:…" }] },
{ "type": "text", "text": "近一年…" }
],
"stop_reason": "end_turn",
"scirouter": { "request_id": "req_…", "cost_quota": 23180, "tool_rounds": 2, "tool_calls": [{ "round": 0, "id": "toolu_…", "name": "web_search", "status": "done", "summary": "web 8 条", "ms": 1320 }] }
}数值为示例。
| 字段 | 说明 |
|---|---|
id | 第一轮的 id |
mcp_tool_result.is_error | 为 true 时该次调用的状态为 failed,content[0].text 为原因 |
usage | 各轮之和;usage.iterations 逐轮列出每轮的用量 |
scirouter | 与对话补全的扩展字段相同 |
- 状态为
skipped的托管调用不出现在content中;去掉后不剩工具调用时,stop_reason为end_turn。 - 继续对话时,将整条助手消息原样放回
messages。平台把其中server_name为scirouter的mcp_tool_use与对应的mcp_tool_result还原成普通的工具调用往返,再发给模型。
Responses 的响应
#output 开头一项 mcp_list_tools,列出本次给模型的托管工具;每次执行一项 mcp_call;最后是最后一轮的输出项。以下示例省略 usage:
json{
"id": "resp_…",
"object": "response",
"output": [
{ "id": "mcpl_req_…", "type": "mcp_list_tools", "server_label": "scirouter", "tools": [{ "name": "web_search", "description": "…", "input_schema": { "type": "object" }, "annotations": { "readOnlyHint": true, "destructiveHint": false, "idempotentHint": true, "openWorldHint": true } }] },
{ "id": "call_…", "type": "mcp_call", "server_label": "scirouter", "name": "web_search", "arguments": "{\"query\":\"钙钛矿 封装 稳定性\"}", "approval_request_id": null, "status": "completed", "output": "网页搜索「钙钛矿 封装 稳定性」,8 条:…", "error": null },
{ "type": "message", "role": "assistant", "content": [{ "type": "output_text", "text": "近一年…" }] }
],
"scirouter": { "request_id": "req_…", "cost_quota": 23180, "tool_rounds": 2, "tool_calls": [{ "round": 0, "id": "call_…", "name": "web_search", "status": "done", "summary": "web 8 条", "ms": 1320 }] }
}数值为示例。
| 字段 | 说明 |
|---|---|
id | 第一轮的 id |
mcp_list_tools | tools[] 含 name、description、input_schema 与 annotations。input 中已有 server_label 为 scirouter 的 mcp_list_tools 时不再列出 |
mcp_call.id | 模型给出的调用 id(call_…) |
mcp_call.status | completed 或 failed。failed 时 output 为 null,error 见下表 |
usage | 各轮之和 |
tools / tool_choice | 与请求原文相同 |
scirouter | 与对话补全的扩展字段相同 |
error.type | 情况 | 内容 |
|---|---|---|
mcp_tool_execution_error | 工具已执行但未成功、余额不足 | content[0].text 为原因 |
mcp_protocol_error | 入参不合法、工具不存在 | code 为 -32602,message 为原因 |
mcp_protocol_error | 被限频 | code 为 -32000,message 为原因 |
- 状态为
skipped的托管调用不出现在output中。 - 继续对话时,将
output原样放入下一次请求的input。平台把mcp_call还原成function_call与function_call_output,去掉mcp_list_tools。
日志与缓存
#- 控制台中每轮记录一条请求日志,标签为
v1_tool_round;请求的metadata已有 8 个键时不添加该标签。 - 带托管工具的请求不进入响应缓存(结果随网页内容变化),
Idempotency-Key照常生效。
流式响应
#对话补全的流式响应
#- 中间轮的正文照常流出;托管工具的
tool_calls增量不下发。 - 每次工具开始与结束各发送一个
choices: []的分片,scirouter.tool为该次工具调用:开始分片的status为空、ms为 0,结束分片带status/summary/ms。该机制与可观测性中的扩展分片相同,官方 SDK 会跳过这些分片。 [DONE]之前的收尾分片带各轮合计;末尾的 usage 分片同样为各轮之和。- 扩展分片沿用该流第一个上游分片的
id/created/model。
textdata: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[],"scirouter":{"tool":{"round":0,"id":"call_…","name":"web_search","status":"","ms":0}}}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[],"scirouter":{"tool":{"round":0,"id":"call_…","name":"web_search","status":"done","summary":"web 8 条","ms":1320}}}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"近一年…"}}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[],"scirouter":{"request_id":"req_…","tool_rounds":3,"cost_quota":23180,"tool_calls":[…]}}
data: [DONE]Messages 的流式响应
#- 整个循环是一条 message:
message_start只发送一次,内容块的index按下发顺序连续编号。 - 托管工具调用以
mcp_tool_use块下发:content_block_start(input为{})→input_json_delta→content_block_stop。每次执行完发送一个mcp_tool_result块,content_block_start带完整内容。一轮有多个托管调用时,先是各mcp_tool_use,再是各mcp_tool_result,顺序相同。 - 只有最后一轮发送
message_delta与message_stop:message_delta的usage为各轮之和并带iterations,message_stop带scirouter字段。
Responses 的流式响应
#response.created与response.in_progress只发送一次。随后是mcp_list_tools项:response.output_item.added→response.mcp_list_tools.in_progress→response.mcp_list_tools.completed→response.output_item.done。- 每次托管调用:
response.output_item.added(mcp_call)→response.mcp_call_arguments.delta→response.mcp_call_arguments.done→response.mcp_call.in_progress→response.mcp_call.completed或response.mcp_call.failed→response.output_item.done(带结果)。 output_index按下发顺序编号,sequence_number全程递增。- 只有最后一轮发送终态事件(
response.completed/response.incomplete):response.output为下发过的全部输出项,id为第一轮的,usage为各轮之和,事件顶层带scirouter字段。
计费
#- 每一轮按普通模型调用计费,
cost_quota为各轮之和。 web_search、fetch_page读到的 PDF 与run_code另按平台服务计费,由调用所用 Key 的账户承担,计入该 Key 的月度上限,不计入cost_quota。计费方式见 MCP 服务端与工具「计费」。- 控制花费:调小
max_tool_rounds;用allowed_tools(Messages 用configs)只开启需要的工具。
相关
#- 工具清单、限额、数据流向与单价:MCP 服务端与工具
scirouter扩展字段与流式扩展分片:可观测性- 三种接口:对话补全、Messages、Responses