Skip to main content
Glama

tutu-mcp-proxy

lint lint-pages tests pages

在 mcp.tutu.ru 之前的压缩/接地 MCP-прокси,为 Туту 黑客马拉松(第 2 赛道——“工具优化”)而做。同样的 16 个工具,同样的行为,外加:

  • 精简的常驻目录。 真实服务器上的 tools/list 在第一次搜索前就约有 108 KB(执行 uv run python tutu.py measure 可验证,并且可用 fixtures/ 复现)。三个最重的工具(search_rail、get_rail_seatmap、search_hotels)获得一个简短顶层 description;被裁掉的文案不会丢失,而是移入成对 get_<domain>_instructions 的调用结果中(tutu_mcp/proxy/compact_tools.py)——只有真正读取该结果的会话才会付出代价。inputSchema 在任意位置都未被触碰。

  • check_groundedness。 将回答草稿与它所依据的 tool_result 进行确定性比对:从文本中抽取价格/时间/列车航班号/链接,并检查它们是否真实存在于 JSON 中,无需任何 LLM 评判者(tutu_mcp/groundedness.py)。

  • 对空结果的说明。 最常见的失败:代理(агент)把过滤后为空的搜索结果读成“这趟列车不运行”,而工具只是返回了在售的选项。Туту 自己的计数器 meta.post_filter_dropped_* 能指出是哪个过滤条件清空了列表——代理把它们展开成一句话并附加到结果上(_empty_result_note),让代理承认事实,而不是去猜测它从未获得的时刻表(tutu_mcp/proxy/empty_results.py)。

  • 前提门槛 + assess_request。 check_groundedness 检查回合的输出,而这是输入:用于收窄搜索的取值必须来自用户或之前的 tool_result。代理凭空捏造的过滤条件(经典场景:默默假定活动结束时间,并按这个时间过滤晚班车次)会得到 clarification_required 而不是数据(tutu_mcp/premises.py)。

  • Mock 模式。 用已录好的 fixtures 响应替代真实服务器——可以随时随意运行,不消耗黑客马拉松的共享速率限制。

追踪查看器

每次 eval 运行都会变成一个自包含 HTML 文件:双击即开,无需服务器,也无需网络。已发布的可视化展示见 https://trum-ok.github.io/tutu-mcp-hackathon/trace-viewer.html。

追踪查看器:一次运行的指标头部和单个场景的拆解

make viewer        # из последнего настоящего прогона эвалов
make viewer-demo   # из рукописных демо-трейсов — без модели и без ключа

在界面中:概览模式——把全部场景 × 变体的矩阵放在一张表里;只看失败把列表收窄到出问题的项;再一个并排展示同一场景的两个变体,并高亮两者不一致的检查。点击响应中任意高亮值,会打开一个抽屉,显示该值取自的服务端响应片段;如果它不存在于任何响应中,则直接明确说明。合成运行(demo:/scripted:)会带“非真实测试”的琥珀色徽标——不会把手写演示与真实测量混淆。

它如何构建——见 docs/deploy.md。

Related MCP server: TripWeaver MCP Server

结果

在 gpt-5.6-luna 上用 --effort low 跑了四次,每次 22 个场景,时间为 2026 年 8 月 19 日。多次运行之间的分散来自模型自身:后端相同,场景集也相同。

指标

baseline

proxy

工具暴露面,tokens

25 269

15 364

同样,按字节计

115 329

74 971

任务成功

17–18 / 22

19–21 / 22

断言的合理性

97–98 %

99 %

每次运行中的虚构断言数

4

1

每次运行输入 tokens

3.3–4.4 百万

减少 0.43–0.67 百万

前提门槛触发次数

0

8–12

多余澄清提问

0

0

关于虚构断言这一行,应该先看绝对数字:189 条可检查断言中的 4 条和 184 条中的 1 条,对应 97.9% 与 99.5%,差距看起来像噪声。而绝对数量上,它意味着到达用户那里的错误事件少了四倍——用户接收的正是错的事实,而不是比例。百分比的分母是“可检查断言”:用户自己指定的阈值(“低于 3000 卢比”)不用在 payload 里再被确认,也不计入分母。

工具暴露面的节省是 39%,而且它不依赖每一次 run:这是一个静态数字,代理在每次会话的第一次搜索之前都要花掉它。其余所有变化都来自模型行为,因此给出的是一段区间。

最后一行一点也不比第一行不重要:门槛在每次运行中触发 8–12 次,但与此同时从未一次没在不需要提问的场景上多问一句(负向控制场景 no_overask 加检查 did_not_over_ask)。一个会对所有东西都去追问的机制,get到完美的 premise 指标,却会毁掉产品。

两个变体在五个场景中有所分化,而且这五个场景全部由 proxy 赢下:空的过滤结果不再被读成“列车停运”,星期几的拼写错误在搜索前就能被发现,默默带入的客人数量被门槛拦下,相邻座位用正确的工具来查找。proxy 唯一稳定缺陷是 multitransport_basic:代理计算出价格差额(2 275,07 − 1 700 = 575),它的两个输入都已被 payload 验证,但这个数字本身并不存在。确定性检查无法把这样的算术同错误的算术(例如把酒店价格乘以住宿天数——这是另一个场景,在那里确实是错误)区分开;这是该方法的边界,而非代理的缺陷。

为了报告的诚实,有两件事需要说明:夹具失配与工具本地错误是分别统计的——记录里的缺口不应被理解成 Туту 跑挂了;以及** token 数字如果估算值,就会标上 ~**。OpenAI 没有 token 计数的端点,精确数字只能来自一次真实试运行(usage.prompt_tokens);而 --estimate-tokens 会用 tiktoken 的离线估计代替。

模型与推理强度按整次运行传入(--model/OPENAI_MODEL、--effort/OPENAI_EFFORT);两者都不传时,reasoning 字段完全不发送,模型使用自己的默认值——这和显式 --effort none 不是一回事。runner 默认指向 /v1/responses;对当前带推理能力的模型,Chat Completions 不能与 function calls 同时使用;--api chat 用于没有 /v1/responses 的 OpenAI 兼容网关。适配器在做 fixture 匹配时会忽略来自 inputSchema 的默认值(模型总会写 page: 1、sort: "price_asc" 等,而人在录制 fixture 时不会写这些)——否则几乎每次模型调用都会发生在父匹配。

具体一次运行如何组织,每个指标到底在算什么,以及 harness 为什么在 CI 里有自检——见 docs/evals.md。

快速开始

需要 uv 和 Python ≥ 3.13(uv sync 会安装它)。

git clone https://github.com/Trum-ok/tutu-mcp-hackathon
cd tutu-mcp-hackathon
uv sync
uv run python tutu.py serve            # mock-режим (по умолчанию) — http://127.0.0.1:8800/mcp
TUTU_PROXY_MODE=live uv run python tutu.py serve   # проксирует настоящий mcp.tutu.ru

任意 MCP 客户端都可以连接到 http://127.0.0.1:8800/mcp(Streamable HTTP,无鉴权,与上游一致)。下文中的 <URL> 既可以是这个地址,也可以是已部署的代理地址(见 docs/deploy.md)。

claude mcp add --transport http tutu <URL>          # Claude Code
// Cursor · ~/.cursor/mcp.json
{ "mcpServers": { "tutu": { "url": "<URL>" } } }

// Claude Desktop · claude_desktop_config.json — через mcp-remote, он не умеет HTTP напрямую
{ "mcpServers": { "tutu": { "command": "npx", "args": ["-y", "mcp-remote", "<URL>"] } } }

应该看到什么。 日志里有两行:模式和监听地址。客户端连接后应被列出 18 个工具:16 个原生 Туту 工具,加上 assess_request 和 check_groundedness。如果只有 16 个,说明客户端连的是 Туту 本身,而不是 proxy。

削减了多少

tools/list:110 164 → 79 411 字节(−27.9 %);如果连 initialize 时两侧的说明一起算,则为 −33.1 %(proxy 输出自己的说明块只有 1.9 KB,而 Туту 是 11.2 KB)。这两个数都已加入 proxy 自己的两个工具(assess_request 1 313 字节,check_groundedness 1 100 字节)。

目录按层的拆分、明确标出的压缩代价,以及我们刻意不越过的边界,见 docs/compaction.md。

文档

更贴近用户视角的说明是单独的一页:make docs 会在 site/index.html 下生成,也可以直接打开已发布页:https://trum-ok.github.io/tutu-mcp-hackathon/。

文件

内容

docs/findings.md

对真实服务器的原生测量数据,以及启动这个项目的原因

docs/compaction.md

具体压缩了什么、付出的代价是什么、哪些事情没有做

docs/evals.md

Eval harness 的结构、fixture、一次运行的完整产出

docs/structure.md

仓库结构,以及依赖的方向

docs/configuration.md

环境变量,以及所有 make 目标

docs/deploy.md

Docker、Render、GitHub Pages,以及两个页面的构建

Команда rezo

  • Артамонов Аркадий (@OpSonata)

许可

MIT

Maintenance

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers