进阶 📋 6 个步骤 第 600 / 602 篇

用 MCPgraphy 给 MCP 工具画一张「状态行为图」:adapter 配置、warn/block 两档策略与无效调用拦截实操

MCPgraphy 是包在任意 MCP 服务器前的本地代理,从 Agent 自己发出的调用里被动学习「哪个工具在哪个资源状态下会成功或失败」,再用 observe / warn / block 三档策略给提示或拦下无效调用。本教程六步跑通两分钟无凭证演示、adapter 配置、宿主接入、行为图解读与渐进切换,全程不需要 API Key。

2026.11.05· 18 分钟阅读· 约 2913 字· 🗺️ MCPgraphy / 🔌 MCP 代理

你有没有遇到过这种情形:Agent 反复调用一个此刻根本不可能成功的工具,靠报错才知道此路不通,然后一次次重试?MCP 服务器会告诉模型「有哪些工具、参数是什么」,却不会告诉它「这个工具在资源处于什么状态时才有效」。MCPgraphy 补的就是这一层:它在 Agent 和 MCP 服务器之间放一个本地代理,从 Agent 自己发出的调用里被动学习,画出一张「状态 → 工具 → 结果」的行为图,然后按你设的策略给提示或直接拦下无效调用。本教程六步跑通:两分钟无密钥演示、写 adapter、三档策略、接进宿主、读懂行为图、渐进切到拦截并验收,每一步都有可复制的命令与配置。

🧩 本教程适合:已经在 Claude Desktop、Cursor 或自写 Agent 里挂过 MCP 服务器、被「无效工具调用 + 反复重试」困扰的开发者。你只需要 Python、pip,以及演示脚本要用的 Node(供 npx 拉官方 filesystem 服务器)。全程不需要任何 API Key,数据不出本机。

一、先搞懂:MCP 少了哪一层信息

一份标准的 MCP 工具清单只有名字、描述、参数 schema,它描述的是「能力」,不是「时机」。于是模型会去编辑一个还不存在的文件、发布一个没跑过测试的版本、移动一张已经关闭的工单,然后从服务端错误里学乖——有时重试的那一下恰好造成了真实改动。MCPgraphy 的做法是把资源在每次调用前后各读一次,把观测到的状态记成节点、把发生的事记成边:

角色谁提供说明
工具清单MCP 服务器有哪些工具、参数怎么填
资源状态只读观测工具调用前后各读一次,由 adapter 指定
行为图MCPgraphy哪个状态→哪个工具→成功或失败,记几次
策略动作MCPgraphy只记录 / 附提示 / 直接拒绝

它是观测型工具,不是权限系统。官方明确写了:授权(Authorization)是独立字段,绝不会从行为里推断——某个工具每次都成功,不等于它被批准了。另外它是 v0.1.0 的早期项目,官方自测数据来自两个模拟服务器上的 200 个有状态任务与脚本化 Agent(无效调用从每任务 2.19 次降到 0.71 次、完成率 0.85 到 1.00),这是作者自建基准而非第三方生产验证,别把它当成你环境里的承诺值。

Step 1:装包并跑一遍两分钟无凭证演示

1 用官方脚本看它怎么学会并拦住错误
# 建个干净目录,避免污染现有项目
mkdir mcpgraphy-lab && cd mcpgraphy-lab

# 安装(MIT,纯本地,无密钥)
pip install mcpgraphy

# 下载官方演示脚本与 filesystem adapter
curl -O https://raw.githubusercontent.com/hemanth1999k/mcpgraphy/main/examples/filesystem_demo.py
curl -O --create-dirs --output-dir adapters https://raw.githubusercontent.com/hemanth1999k/mcpgraphy/main/examples/adapters/filesystem.json

# 跑两幕演示(需要 Node,脚本内部会用 npx 拉官方 filesystem 服务器)
python filesystem_demo.py /tmp/mcpgraphy-demo

演示分两幕:Act 1 以 warn 模式跑一个「不聪明」的 Agent,故意编辑不存在的文件、移动不存在的文件,再各做一次正确操作,代理从这七次调用里学会规律;Act 2 用学到的图重启为 block 模式,同样的错误被带证据拒绝、合法的编辑放行、未批准的 move 被扣住,并列出当前状态下能做什么。

💡 想先看清楚再动手?把演示脚本下载下来读一遍,它把「起代理 → 发调用 → 换策略重启」整条链路都写死了,是理解后续配置的最短路径。演示会生成 mcpgraphy.sqlite,删掉即可从头再来。

Step 2:写 adapter.json,告诉代理怎么读回资源

2 每个服务器一份 JSON,核心只有两个字段

代理不猜你的工具语义,需要你指明「用哪个只读工具把资源读回来」以及「哪个参数代表资源标识」。其余字段都可省。以官方 filesystem adapter 为例:

{
  "server_id": "filesystem",
  "resource_class": "file",
  "observation_tool": "get_file_info",
  "id_param": "path",
  "id_param_aliases": ["source"],
  "observation_format": "kv_text",
  "features": ["isFile", "isDirectory"],
  "effect_classes": {"edit_file": "MUTATING", "create_directory": "MUTATING"},
  "approval_required": ["write_file", "move_file"]
}
字段作用
observation_tool / id_param用哪个只读工具读回资源、哪个参数是标识
id_param_aliases同一标识在别的工具里的别名(move 用 source)
observation_formatfields(结构化,默认)或 kv_text(键值文本)
features哪些字段算状态;省略时自动识别布尔与枚举式字符串
effect_classesREAD_ONLY / MUTATING / DESTRUCTIVE;DESTRUCTIVE 在 block 下需批准
approval_required无论证据如何,block 下都要 --approve 才放行

别把 observation_tool 填成写工具。代理会在每次调用前后各读一次资源,填错等于让观测动作本身产生副作用。另外官方目前只带 filesystem 与两个内置模拟器三种现成 adapter,GitHub / Linear / Slack / Postgres 以及从 schema 自动起草 adapter 的能力都还在 Roadmap 里,接别的服务器要自己写这份 JSON。

Step 3:选策略,从 observe 到 warn 再到 block

3 三档策略与几个必须知道的参数
# stdio 下游(把命令跟在 -- 后面)
mcpgraphy wrap --adapter adapter.json --policy warn -- npx -y @modelcontextprotocol/server-filesystem ~/work

# Streamable HTTP 下游
mcpgraphy wrap --adapter adapter.json --policy block --approve publish_release --url http://localhost:8000/mcp

# 指定数据库与重学频率
mcpgraphy wrap --adapter adapter.json --policy warn --db ./lab.sqlite --refresh-every 20 -- npx -y @modelcontextprotocol/server-filesystem ~/work
--policy行为
observe只记录与学习,不附提示也不拦
warn(默认)全部放行;命中「此状态曾失败」或结果矛盾时附一条说明
block在服务器看到之前拒绝曾失败于此状态的调用,以及 DESTRUCTIVE / approval_required 工具(除非列入 --approve)

--db 选 SQLite 文件(默认 mcpgraphy.sqlite),--refresh-every N 表示每 N 次调用重学一次,--no-hints 关掉附在结果里的状态提示行。官方建议先在 warn 跑一段时间,等提示看着靠谱了,再切到 block 并把自己允许的写操作逐项列进 --approve。

💡 学习是被动的:官方强调它不探测、不重置、不额外发任何 Agent 没发出的调用。没试过的路径会报 UNKNOWN,绝不会当成安全。这既是设计取舍,也意味着冷启动阶段图很稀疏,别指望装上就立刻生效。

Step 4:把宿主指向代理,而不是原来的服务器

4 改一行 command,工具名和参数都不变

在 Claude Desktop、Cursor 或任何以 stdio 启动 MCP 服务器的宿主里,把原来那条命令整体挪到 -- 后面即可。下游工具对模型完全透明:

{
  "mcpServers": {
    "filesystem": {
      "command": "mcpgraphy",
      "args": ["wrap", "--adapter", "/abs/path/adapters/filesystem.json", "--policy", "warn",
               "--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/you/work"]
    }
  }
}

adapter 路径请用绝对路径,宿主启动时的工作目录和你在终端里的目录常常不是同一个;相对路径会让代理起不来且报错不够直观。若宿主报找不到 mcpgraphy,把 command 换成 which mcpgraphy(Windows 用 where mcpgraphy)返回的完整路径。密钥安全方面:本教程全程不需要 API Key;若你接的是需要凭据的下游服务器,凭据仍放在宿主的环境变量或配置文件里,别写进 adapter.json 并顺手提交到 Git。

💡 换完配置记得完全退出并重启宿主,很多桌面客户端关掉窗口并不等于进程结束,配置不会重新加载。

Step 5:读懂行为图——四个工具和四种边状态

5 让 Agent 主动问「现在能做什么」

除了透传下游工具,代理自己会多出四个工具:

工具回答什么
mcpgraphy_affordances(resource_ref, goal?)这个资源此刻:哪些可选、哪些要批准、哪些此状态不支持、哪些没试过
mcpgraphy_explain(offer_id)这条结论背后的守卫、正反观测、反例、还有哪些没验证
mcpgraphy_graph(format)整张图,JSON(权威)或 Mermaid
mcpgraphy_status()记录了多少调用、告警与拦截数、当前模型版本

每次读资源或做动作,结果里都会附一行图状态提示,形如:

mcpgraphy [status=DRAFT, tests_passed=False] ok: run_tests -> tests_passed becomes true | no: archive_release; publish_release | to tests_passed=True: run_tests

被拒绝的调用也会带上理由,例如「观察到此状态 [exists=False] 失败过 2 次」。每条边有四种状态:

状态含义
OBSERVED_SUPPORTED就在这个观测状态下复现成功过,且无矛盾观测
CONDITIONAL由归纳出的守卫推出,或是矛盾双方中的一边
CONTRADICTED同状态下后续调用与早前结论不一致
UNKNOWN没试过;既不算有效也不算无效

服务器改了工具 schema 会触发重测。官方说明:模型指纹不再匹配时,继承来的边会被标记为待重测,改动后的头几次调用要先验过才会重新被信任。所以升级下游 MCP 服务器后,别急着把策略切到 block。

Step 6:验收、渐进切换与边界

6 确认拦得住,也确认没拦错

建议在 warn 阶段先攒够观测,再按下面的顺序验收:

# 1. 看累计记录与告警数(也可直接查 SQLite)
mcpgraphy graph export --server release_sim --format mermaid --db demo.sqlite
mcpgraphy offers --server release_sim --resource release:demo-0-5 --db demo.sqlite
mcpgraphy offers --server release_sim --resource release:demo-0-1 --db demo.sqlite

# 2. 切到 block,只放行你确认过的写操作
mcpgraphy wrap --adapter adapter.json --policy block --approve publish_release -- npx -y @modelcontextprotocol/server-filesystem ~/work

# 3. 复放 Act 1 的错误动作,核对每条拒绝都带证据行

预期效果:同一批错误动作在 block 模式下于服务端看到之前被拒绝,拒绝消息里能指出具体状态与观测次数;合法动作照常放行;未批准的写操作被扣住;mcpgraphy_affordances 能列出当前状态可做与不可做的清单;重启代理后图仍在(存在 SQLite 里)。

三类情况它帮不上:官方基准文档点名了隐藏状态(hidden state)、过期快照(stale snapshots)、非确定性开关(nondeterministic flag)——可见状态图看不见的因果,不会凭空变准。另外它是 v0.1.0 单机代理:--approve 是静态列表而非运行时弹窗审批,stdio 之外以 Streamable HTTP 对外提供代理、本地图浏览页、向宿主发审批请求都还在 Roadmap。生产里请把它当作「减少无效调用的一层」,而不是安全边界。

🚀 想深入:仓库自带 uv sync + uv run pytest(约 100 个测试)、mcpgraphy learn 主动实验、mcpgraphy benchmark run --suite stateful-v1 与 benchmark report,可拿两个内置模拟器复跑官方基准。作者还列出 mcp-scan 这类「这条调用是否被允许」的护栏工具,与 MCPgraphy「这条调用有没有在此状态成功过」是互补关系,可以串在同一条链上。

常见问题速查

现象大概率原因与处理
宿主连不上代理command 找不到可执行文件,或 adapter 用了相对路径;换绝对路径并重启宿主
装上后一条告警都没有冷启动图还很稀疏;先在 warn 下多跑几轮真实调用,或调小 --refresh-every
block 把正常写操作也拦了该工具落在 DESTRUCTIVE 或 approval_required;把它加进 --approve 并复核 effect_classes
升级下游服务器后判断变乱schema 指纹变更触发边重测,等头几次调用验完再恢复 block
UNKNOWN 很多设计如此——没观测过的路径报未知,不报安全;别据此推断可用
← 返回教程中心