关于 autorouter
autorouter 是一个 MCP 服务,可索引已配置服务、技能和插件中的能力,并通过搜索呈现少量选项。它面向管理多个能力的编码 Agent 环境,用于减少常驻加载的工具架构,并将选中的调用转发至来源服务。
主要功能
- 01搜索并重排能力
- 02将调用路由至下游服务
- 03索引工具、提示词和资源
- 04查找技能、命令与 Agent
- 05导入宿主配置
适用场景
- 01减少 Agent 上下文中加载的架构
- 02通过任务描述查找工具
- 03将选定调用转发至对应服务
- 04使用 audit 和 doctor 检查上下文用量
MCP 相关
该服务提供搜索、描述和调用工具,并连接已配置的下游 MCP 服务。它索引这些服务的工具、提示词和资源,并通过路由器调用选定能力。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y autorouter-mcpAUTOROUTER_CONFIGPath to autorouter.json. Defaults to the standard lookup chain (./.autorouter.json, ~/.config/autorouter/config.json, …).AUTOROUTER_IMPORTComma-separated harness configs to import: claude, codex, cursor, vscode, plugins.AUTOROUTER_SELECTOR_MODEReranker backend: auto | sampling | cli | api | off.AUTOROUTER_SELECTOR_MODELModel the selector reranks with, e.g. haiku.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
4收录的工具列表 · 更新于 1 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| kind | enum | 否 | 可选:将结果限制为一种能力类型。tool | skill | prompt | resource | command | agent |
| limit | number | 否 | 最大结果数(默认值为 8)。 |
| query | string | 是 | 你要完成的目标,用通俗语言描述。请描述目标,而不是猜测工具名称。 |
| server | string | 否 | 可选:将结果限制为一个提供方。 |
详细介绍
autorouter
一个工具,替代所有工具。
每个注册的 MCP 服务都会在每一轮对话中,将其完整工具列表——名称、描述、JSON 架构——注入系统提示词。五个服务加上几个技能,通常会产生 20–30k 个令牌的固定开销,无论任务是否涉及它们,这笔开销都照付不误。这还会让工具选择变得_更差_:候选项越多,选错的可能性就越大。
autorouter 是一个 MCP 服务,它提供少量稳定的工具,并将其他所有工具隐藏在搜索之后。模型用自然语言请求所需内容,获得几个附带架构的排序候选项,然后通过路由器调用其中一个。模型实际使用的工具随后会被提升到真实工具列表中,因此上下文会用于满足会话需求的内容,而不是用于它可能需要的内容。
find_capabilities({ query: "inspect recent deployment errors" })
→ mcp:deployments/get_logs (tool)
→ mcp:monitoring/search_events (tool)
→ skill:incident-triage (skill)
describe_capability({ id: "mcp:deployments/get_logs" })
call_capability({ id: "mcp:deployments/get_logs", arguments: { lines: 50 } })
安装
npm install -g autorouter-mcp # or: npx autorouter-mcp <command>
autorouter init --target claude # claude | codex | cursor | vscode
autorouter adopt --target claude # ← the step that actually saves context
这是对现有配置的一次性迁移。新服务无需执行此操作——请参阅添加 MCP 服务。
adopt 不是可选项。 将路由器与现有服务一同注册,只会让总量_增加_:这些服务的架构仍会加载,路由器还会额外添加四个工具。adopt 会将下游条目从宿主配置移至路由器自身的配置中,这样宿主只需加载一个服务,而路由器仍可访问所有下游服务。
MCP 服务并非全部开销。技能和插件通过完全独立的机制加载——每个 SKILL.md 都会将其名称和描述加入系统提示词;已启用的插件只要安装,就会贡献自己的技能、命令_和_服务。在 Claude Code 中,adopt 也会处理这些内容,使用宿主提供的仅有的两种调控方式:
| 项目 | 方式 | 效果 |
|---|---|---|
| 技能 | skillOverrides[name] = "name-only" | 描述不再进入上下文;Claude 仍可调用该技能,/name 仍可使用 |
| 插件 | enabledPlugins[id] = false | 插件的技能、命令和服务都会停止加载 |
路由器仍能找到它们——它会直接读取 ~/.claude/skills 和
installed_plugins.json,这些记录的是_安装情况_,而不是宿主启用了哪些内容。正因如此,禁用插件是迁移,而不是删除。
有两种情况会被拒绝处理,而不会悄然执行。对于路由器无法访问其 MCP 服务的插件(通常是 OAuth 的情况,因为令牌由宿主持有,而路由器没有),插件会保持启用状态——禁用它会导致当前可用的服务无法使用。若路由器从未获知某插件的服务,也会如此;如果从 import 中移除了 "plugins",就会发生这种情况。这两种情况都会以 skip 行报告原因。
使用 --servers-only 保留旧行为,使用 --skill-mode off 同时移除斜杠命令,并使用 --keep-skill / --keep-plugin 豁免个别项目。Codex、Cursor 和 VS Code 没有技能或插件的概念,因此无需额外处理。
运行 autorouter doctor 查看差异:
## Context cost
exposing everything: ~87,524 tokens
router surface: ~752 tokens
best case: ~86,772 tokens saved (99%)
per harness (a session only ever runs in one):
codex still loaded ~35,500 → saves ~51,272 (59%)
claude, cursor, vscode: fully adopted → saves ~86,772 (99%)
## Not yet adopted
codex
servers: project-tools, docs-search, issue-tracker
→ autorouter adopt --target codex
“仍在加载”是 adopt 要移除的内容,并且会按宿主分别报告,而不是汇总:一个会话只会在一个宿主中运行,因此仍注册在 Codex 中的服务不会给 Claude Code 会话带来任何开销。在写入任何内容之前,每项移除都会逐字备份到 ~/.autorouter/adopted/;
autorouter restore --target claude 可逐字节还原。
Doctor 还会报告哪些内容会占用上下文,但无法通过 adopt 迁移。它只会报告并提出建议,不会编辑以下内容:
- 钩子:输出会进入上下文的
SessionStart钩子,并根据你的对话记录测量其中位大小。匹配器为空的钩子会在每次压缩、/clear和恢复会话后再次运行。 - 过时的工具引用:
~/.claude/CLAUDE.md、CLAUDE.md或AGENTS.md中将工具写作mcp__<server>__<tool>的行,而对应服务现在位于路由器之后。模型会查找一个并不存在的工具。 路由器自身的引导内容块会被跳过。 - 注入的服务:启动器通过命令行传递的服务(例如 cmux 使用
cmux-cua时的claude --mcp-config ...)。这些服务不在任何配置文件中,因此adopt无法迁移它们。如果路由器也导入了这些服务的副本,Doctor 同样会标记出来。
一个会话实际要花多少费用:autorouter audit
Schema 只是费用的一部分。Claude Code 已经会将 MCP Schema 延迟到用到时再加载,并且每次 API 调用都会重新读取窗口中仍然保留的所有内容,因此实际成本取决于上下文大小 × 调用次数。autorouter audit 会读取 Claude Code 的对话记录(~/.claude/projects/*/*.jsonl),并使用每次调用记录的用量数据:
## Context bill
avg context per call 181k
input processed 4.79B (96% cache reads)
compactions 269 (median 317k before → 90k after)
## What fills it (tokens × calls each stays in context)
30.1% 1.44B startup overhead (system prompt, tools, skills, CLAUDE.md)
23.2% 1.11B carried over through compaction (kept turns, re-attached files)
15.4% 735.5M Claude's thinking (kept in context)
13.9% 666.0M Bash output
...
## Compaction window (replay, API-equivalent cost)
autoCompactWindow: 350000 (~/.claude/settings.json)
window change compactions avg context
140k -26.9% 834 102k
170k -25.9% 559 116k ← recommended
350k 0.0% 173 196k ← current
归因计算会将每个内容块计费一次,适用于之后每次重新读取该内容的调用,直到下一次压缩。窗口表会在每种 autoCompactWindow 设置下重放你自己的会话,并根据对话记录中的压缩情况进行校准。成本按 API 价格比加权(缓存读取 ×0.1、缓存写入 ×1.25、输出 ×5),以此作为订阅用量的估算值。百分比应视为相对值,而非精确值。在与最低成本相差不超过 1.5 个百分点的窗口中,工具会推荐最大的那个,因为每次压缩都会丢失一些细节。
--days N(默认为 14,也可以设为 all)和 --project DIR 可缩小范围;
--json 会输出完整报告。
如何查找内容
先检索,再重新排序。
- BM25 覆盖所有能力,始终启用,无需依赖。分词器会拆分 camelCase 和 snake_case(
get_user→get、user、getuser),并为字段加权:名称 ×3、关键词 ×2、描述 ×1、Schema ×0.5。 - 嵌入向量,可选。支持 Voyage 或 OpenAI;分数经过最小-最大归一化后融合:
0.5 × bm25 + 0.5 × cosine。提供方缺失或无法访问并不算错误——系统会退化为纯词法检索。 - 选择器模型会对候选列表重新排序。它应该是你所用运行环境中成本最低的模型,因此当
init无法推断时,会询问你使用哪一个,并将答案存储在该运行环境自己的服务条目中。
选择器后端
在 "mode": "auto" 下,按以下顺序尝试三种调用模型的方式:
| 后端 | 方式 | 触发条件 |
|---|---|---|
sampling | 向主机回调 sampling/createMessage | 主机声明了该能力——很少有主机支持;Claude Code 不支持 |
api | 直接通过 HTTPS 连接 Anthropic / OpenAI / Ollama | selector.apiKeyEnv 指定的变量已实际设置 |
cli | 以无头模式运行 claude -p 或 codex exec | 某个框架 CLI 位于 PATH 中 |
通常会触发的是 CLI 后端,因为另外两种方式通常不可用。Sampling 是协议自身给出的解决方案,但几乎没有实现它。API 后端则要求提供 ANTHROPIC_API_KEY,而 Claude Code 订阅用户没有理由持有该密钥——他们只是登录了,并没有购买密钥——于是路由器会退化为按原始索引排序,同时让用户去购买他们已经付费使用的模型权限。与此同时,claude 已位于 PATH 中,并且已经完成身份验证。调用它可以复用现有登录,无需配置,也不会产生第二笔费用。
代价是进程启动开销,因此 CLI 选择器会比直接 HTTPS 请求慢。这就是为什么已配置的 API 密钥仍优先于它。选择结果会按查询和候选集缓存,在进程生命周期内有效,因此每个不同的搜索只需支付一次开销,而不是每轮都支付。
框架被精简为一个重排器。默认的无头 Agent 可能会启动钩子、语言服务器、插件同步和项目指令发现,而简短的排序任务并不需要这些。因此,子进程会使用 --bare --tools "" --setting-sources "" 和 MAX_THINKING_TOKENS=0 运行(Codex:--ephemeral -s read-only)。工具列表为空首先是正确性要求,其次才是节省资源:一个能编辑文件的选择器就成了另一个程序。
"selector": {
"mode": "auto", // auto | sampling | cli | api | off
"provider": "cli",
"cliCommand": "claude", // only needed if the binary is not under its usual name
"model": "haiku",
"candidates": 30,
"maxResults": 8,
"timeoutMs": 20000 // raised automatically to the backend's own floor
}
它还会使用空的 MCP 配置启动(--strict-mcp-config、mcp_servers={})——继承路由器自身的配置会加载路由器本来就要排除在上下文之外的完整目录;情况不妙时,还可能递归调用路由器自身。
只有在你设置 model 时才会传递该参数。Codex 会拒绝账户套餐不支持的模型名称,因此未设置时会使用账户默认模型。
mode: "off" 会完全跳过重排,直接返回原始索引顺序。这种方式快速、免费,但效果明显更差。
下游服务不会在启动时启动。目录只构建一次,并持久化到 ~/.cache/autorouter/catalog.json;只有在首次调用某项能力时,才会冷启动相应服务。
索引内容
| kind | source |
|---|---|
tool、prompt、resource | 每个已配置的 MCP 服务(遵循 nextCursor 分页) |
skill | 技能路径下的 **/SKILL.md 和插件 skills/ 目录 |
command、agent | 插件 commands/*.md 和 agents/*.md |
插件命令和 Agent 也会重新发布为斜杠命令; 技能可以搜索,但默认不会重新发布,因为用于承载它们的提示词列表 属于永久上下文——请参阅斜杠命令。
Harness 配置会被导入,而不是重复配置:~/.claude.json(全局和
按项目配置)以及仓库的 .mcp.json(请参阅仓库技能和服务)、~/.codex/config.toml、~/.cursor/mcp.json、
~/.vscode/mcp.json,以及已安装的 Claude Code 插件。cmux 终端运行时,
也会接入它的 Computer Use 服务
(cmux-cua),因为 cmux 会按会话注入该服务,而不是将其写入任何配置。
已被某个 Harness 关闭的服务(disabled: true,
或 Codex 的 enabled = false)会被跳过;仅会打开桌面应用
而不是启动 MCP 服务的命令也绝不会执行。
仓库技能和服务
在仓库中或其任意子目录中打开 Claude Code,路由器就会接入该
仓库的 .mcp.json 服务和 .claude/skills——仅限于该仓库,因此不会
出现在其他项目中——并让 Claude Code 停止预加载它们。
- 哪个仓库。 主机的
roots/list(Claude Code 会返回其启动 目录和所有--add-dir,并通知变更),然后是CLAUDE_PROJECT_DIR,最后是路由器自身的工作目录。 - 哪些技能。 工作目录及其所有父目录(直到 git 根目录)中的
.claude/skills,这与 Claude Code 的读取方式一致;此外还有根目录下所有嵌套的<subdir>/.claude/skills。Claude Code 只有在访问某个子目录中的文件后才会加载其中的技能;路由器则可以从顶层搜索单体仓库中各应用的技能。 - 它们如何退出上下文。 仅通过仓库自身的私有
.claude/settings.local.json:使用disabledMcpjsonServers处理路由的服务,使用skillOverrides: "name-only"处理技能(这样 Claude 仍可按名称调用它们,/name也仍然有效)。早期版本写入的"user-invocable-only"条目——它会阻止 Skill 工具——会在下次接入时改为"name-only"。如果路由器创建了该文件,就会将其添加到.git/info/exclude。绝不会编辑已提交的内容;你自行设置的值——即使是"on"——也会保持原样。 - 哪些服务。
.mcp.json会提交到仓库,因此路由器只会启动 Claude Code 会运行的服务:文件夹必须受信任,且服务必须使用 HTTP 或已获批准——在 Claude Code 中通过enabledMcpjsonServers批准,或使用autorouter approve <name>,后者会固定确切的命令。信任和批准适用于同一仓库的所有 git worktree。未获批准的服务仍由 Claude Code 处理,并照常向你询问。
autorouter adopt --project [dir] # the same thing by hand (--dry-run to preview)
autorouter restore --project [dir] # take back exactly what that added
autorouter approve playwright # let the router launch a .mcp.json stdio server
设置 "autoAdoptProject": false 即可保持手动操作。
从较早版本升级? 在此之前,adopt 会清空仓库中已提交的
.mcp.json,并将其中的服务移入路由器的全局配置。
autorouter repair 会从 adopt 备份中列出已移动的内容;--apply 会将
条目还原(在备份允许的情况下逐字节还原),将私有的按项目配置的服务限定到各自项目,并按新方式接入每个仓库。
如果有需要修复的内容,doctor 会发出提示。
添加 MCP 服务
直接向路由器注册——它不会进入任何人的上下文:
autorouter add linear --url https://mcp.linear.app/mcp
autorouter add foo -- npx -y foo-mcp # stdio, like `claude mcp add`
autorouter add --json '{"mcpServers":{"linear":{"url":"…"}}}' # paste the vendor snippet
autorouter remove linear
条目会在检查连接之前保存,因为新服务通常无法响应的原因是它还没有 OAuth 授权——而
autorouter login <name> 需要先有该条目,才能为其授权。
你也可以直接使用宿主。 claude mcp add foo … 仍然有效:运行中的路由器会注意到宿主配置已更改,将该条目连同常规备份一起移入自己的配置,并在下一次搜索时告知你。因此,你不必记住第二条命令,也不会出现忘记执行它、每轮都要为该服务的架构付费的情况。设置 "autoAdopt": false 可保持手动接管。
在会话内,设置 "allowAddServer": true 并用文字提出请求——路由器随后会公开一个 add_server 工具。该选项默认关闭,因为工具定义会出现在每次请求的上下文中,而 autorouter add 已能满足同样的需求。注册 stdio 服务意味着此后这台机器会运行该命令,因此第一次调用只会报告将要注册的内容;还需再次调用并设置 confirm: true 才会写入任何内容。
配置
autorouter.json — 按以下路径查找:$AUTOROUTER_CONFIG、./.autorouter.json、
./autorouter.json、~/.config/autorouter/config.json、~/.autorouter.json。
{
"import": ["claude", "codex", "cursor", "vscode", "plugins", "cmux"],
"servers": { "custom": { "command": "…", "args": [] } },
"skillPaths": ["~/.claude/skills", ".claude/skills"],
"exclude": ["media.generate_video"], // never surfaced at all
"alwaysExpose": ["code-search.search"], // stays first-class, never adopted
"confirm": ["database.execute_statement"], // first call is rejected, must re-issue
"autoAdopt": true, // move servers added to a harness behind the router
"autoAdoptProject": true, // route the current repo's .mcp.json servers and skills
"allowAddServer": false, // expose the add_server tool to the model
"promptMode": "commands", // slash commands: "all" | "commands" | "none"
"activation": "lazy", // promote to a real tool: "eager" | "lazy" | "off"
"spillOverTokens": 3000, // larger results are saved, not inlined
"spillOverrides": { "mcp:linear/*": 8000 }, // per-id thresholds; 0 = never
"resultsMode": "auto", // "auto" | "file" | "page" | "off"
"resultsDir": "", // default <cache>/results; never inside the working tree
"selector": { "mode": "auto" },
"embeddings": {
"provider": "voyage",
"model": "voyage-3-lite",
"apiKeyEnv": "VOYAGE_API_KEY",
},
}
环境变量覆盖项(AUTOROUTER_IMPORT、AUTOROUTER_SELECTOR_MODEL、
AUTOROUTER_SELECTOR_MODE、AUTOROUTER_PROMPT_MODE、AUTOROUTER_ACTIVATION、
AUTOROUTER_AUTO_ADOPT、AUTOROUTER_ALLOW_ADD_SERVER、
AUTOROUTER_EMBEDDINGS_PROVIDER、AUTOROUTER_RESULTS_MODE、
AUTOROUTER_SPILL_OVER_TOKENS,……)可让每个宿主固定自己的行为,而不必使用共享的全局文件。AUTOROUTER_HOME 会重定向所有相对于主目录的路径,供测试和容器使用。
斜杠命令
将插件接管到路由器中,原本会导致你失去它的斜杠命令:
Claude Code 会从磁盘读取 commands/*.md,而 MCP 无法将内容添加到该列表中。它确实会将 MCP 提示词显示为斜杠命令,因此目录中的插件命令和子 Agent 会重新发布为提示词:
/mcp__autorouter__find <what you want to do>
/mcp__autorouter__plugin_name_command_name
默认不会重新发布技能(promptMode: "commands")。提示词列表属于永久上下文——宿主只获取一次,并在整个会话中携带它——而技能是其中的大头:一个包含 141 个技能的插件,每轮会消耗约 11.5k 个 token,比接管该插件所节省的还多。这个别名大多也是多余的,因为 adopt 会将本地技能隐藏为 name-only,这样原生的 /name 和 Skill 工具调用仍然有效,而且插件的技能会由宿主按需显示,而不是注入上下文。无论如何,技能都可以通过搜索完整检索;设置 "promptMode": "all" 可恢复 /mcp__autorouter__skill_name,设置 "none" 则只发布 find。
正文会像原生加载器那样替换——$ARGUMENTS 表示你输入的所有内容,$1..$9 按位置对应参数——因此,为 Claude Code 编写的命令文件通过路由器运行时行为完全相同。argument-hint frontmatter 会作为参数说明传递。名称会添加所属插件作为前缀,因此,两个插件即使都提供 setup 命令,也都能正常使用,不会出现其中一个被悄然覆盖的情况。
这里有意不发布工具。提示词会返回供模型执行的文本,无法返回工具结果,因此 database_write 的斜杠命令看起来能调用,实际上却什么也不做。模型通过搜索获取工具。
Harness 支持
搜索会返回包含完整描述和输入架构的工具,而不是摘要——这与原生工具列表携带的信息相同,因为被截断的描述会让模型猜测参数名称,而猜对参数与基本可靠之间的差别,往往就在于此。
更进一步,如果客户端支持 notifications/tools/list_changed,模型使用的工具就会被提升为真正的一等工具。从那时起,模型会发起普通的工具调用:主机会根据真实架构验证参数,权限提示会显示真实工具名称,而不是 call_capability,路由器也完全不会参与执行流程。它会成为一个决定工具列表中包含哪些工具的加载器,而不是每次调用都要经过的中间人。
提升会延迟到首次成功调用时进行(activation: "lazy"),因为被提升的工具会永久占用上下文,而搜索命中并不代表有使用需求——搜索匹配的大多数内容都不会被调用。设置 "activation": "eager",即可像以前一样提升每个命中项;设置为 "off",则会通过 call_capability 路由所有请求,且不携带任何工具定义。
支持 listChanged | 仅代理 |
|---|---|
| Claude Code ≥ 2.1.232、GitHub Copilot、opencode | Codex、Gemini CLI、Claude Desktop、Vercel AI SDK |
不支持时,架构会直接内嵌在搜索结果中,并且不会公布 activate_capabilities(它也能取消固定,使用 off: true),因此模型不会收到它无法使用的工具信息。可通过 AUTOROUTER_DYNAMIC=on|off|auto 覆盖检测结果。
Claude Code 中,在一轮对话中途注册的工具可能要到下一轮才能调用,因此 call_capability 始终会保留为同轮备用方案,并返回相同的结果。
一步完成搜索和运行
find_capabilities({ query, arguments, auto: true }) 会在同一请求中运行排名最高的命中项,从而节省一次往返,但仅限命中项明确无歧义的情况:
- 它是一个工具,并且
- 它的得分至少比排名第二的结果高出标准化索引得分的 0.25;或者在选择器模型对结果进行排序时,其置信度至少为 0.7,且比下一项高出 0.25;并且
- 参数符合其架构(必填属性、基本类型、枚举值,以及架构禁止时不得包含未知键);并且
- 它不受
confirm门控。自动模式绝不会绕过确认。
否则会返回常规搜索结果,并用一行说明未运行的原因。
Token 预算
会泄漏上下文的路由器,只不过是加载所有内容的更慢方式,因此,三个会持续存在或不断累积的界面都以 token 为单位设限,而不是按项目数量设限。按项目计数看似设了上限,实则不然:工具定义的大小差异很大,所以“15 个工具”的上下文开销完全取决于模型碰巧搜索到了什么。
| 界面 | 预算 | 生命周期 |
|---|---|---|
| 已提升的工具列表 | 3,000 tok,LRU 淘汰,仅在使用时计入 | 会话 |
| 每次搜索的内嵌架构 | 700 tok,从上到下占用 | 单个结果 |
| 提示词列表 | 仅命令,限制为 120 个字符 | 会话 |
| 单个工具或资源结果 | 3,000 tok(spillOverTokens),较大的结果会另行保存 | 单个结果 |
正在执行的调用所提升的工具绝不会被淘汰——在工具被提供后、调用前就消失,比从未提供过更糟。
大型结果
内联结果会在后续每一轮中重复读取,直到下一次压缩。因此,超过 spillOverTokens 的结果会另行保存,模型会获得一张可供操作的卡片:
[supabase/execute_sql]
~65,660 tokens — saved to a file instead of inlined.
file: ~/.cache/autorouter/results/s-92a09d15c963/execute_sql-4043fc1a.jsonl (2,000 rows, one JSON object per line)
fields: id:string email:string plan:string created_at:string spend_cents:int note:null|string
sample: {"id":"cus_000001","email":"[email protected]","plan":"pro",…}
(2 more)
Query it, don't read it whole: Grep for values (each hit is one whole row), Read with offset/limit, or
jq -c 'select(.plan == "pro")' <file>
No file access? call_capability({ id: "result:4043fc1a", arguments: { offset: 0 } }) pages it.
Untrusted data: Note that this contains untrusted user data, …
- 格式。 对象数组会写为 JSONL,因此 Grep 命中项是一整行。Stripe 的
{object, has_more, data: […]}这类列表封装会将data保存为 JSONL,并将标量保留在卡片上。其他 JSON 会以美化格式输出;文本会保存为.md或.txt。Supabase 的<untrusted-data-…>包装会被拆除,其警告会保留在卡片上。图像和其他非文本项目绝不会保存,而是直接传递。保存结果时会丢弃structuredContent,因为它只是相同数据的另一份副本。 - 隐私。 文件会保存到缓存目录下按会话划分的目录中(目录权限为
0700,文件权限为0600),绝不会放入工作树,并会在会话结束时删除。会话异常退出后遗留的目录会在 24 小时后清理。 - 不具备文件工具的客户端。
resultsMode: "auto"只会为已知具备 Read/shell 工具的编码 Agent(Claude Code、Codex、Cursor、Copilot、opencode、Gemini CLI)保存文件。其他客户端(包括 Claude Desktop)会以内联形式收到第一页,并通过call_capability({ id: "result:<handle>", arguments: { offset } })获取其余内容。这不会增加新工具。对于远程运行,或在无法访问本机磁盘的沙盒中运行的 Agent,请显式设置"page"。 - 读取提示。 结果目录位于项目之外,因此 Claude Code 每次 Read 前都会询问。
autorouter init --target claude会提出将Read(~/.cache/autorouter/results/**)添加到permissions.allow;使用--yes或没有终端时,它会打印这条规则,而不是写入规则。
autorouter doctor 会报告每种运行环境的实际费用,而不是将它们相加,因为一个会话只会在其中一种环境中运行:仍在 Cursor 中注册的服务不会给 Claude Code 会话带来任何费用。
在所有重复出现的位置,架构都会经过压缩:移除 $schema、title 和 examples,并精简说明文字,且层级越深,精简得越多。所有结构字段都会原样保留——名称、类型、枚举值、required、嵌套结构和 additionalProperties: false——因为删掉其中任何一项都会让有效调用变成猜测调用。这样可以减少重复架构的大小,同时不改变可调用内容。
describe_capability 是例外,它会原样返回架构:该工具的存在目的正是还原预算受限的结果不得不省略的内容。
CLI
对于有 shell 但没有 MCP 的 Agent,可以直接使用同一引擎:
autorouter search "chart a csv"
autorouter search "query a database" --raw --limit 5
autorouter describe skill:dataviz
autorouter call mcp:deployments/get_logs --args '{"lines":50}'
autorouter list --kind skill
autorouter doctor
autorouter audit --days 14 # the per-call bill from transcripts
autorouter reindex
autorouter add linear --url https://mcp.linear.app/mcp
autorouter add foo -- npx -y foo-mcp
autorouter remove linear
autorouter adopt --target claude --dry-run # preview, change nothing
autorouter adopt --target claude --servers-only # skip skills and plugins
autorouter adopt --target claude --keep project-tools --keep-plugin ui-toolkit
autorouter restore --target claude # undo the most recent adopt
autorouter adopt --project # route this repo's .mcp.json servers and skills
autorouter restore --project # take that back
autorouter approve playwright # let the router launch a repo stdio server
autorouter repair # what older adopts moved out of repos (--apply)
autorouter update # upgrade in place
autorouter update --check # is there a newer version?
autorouter login # which servers need a grant
autorouter login remote-server # authorize one (opens a browser)
autorouter login remote-server --device # headless: enter a code elsewhere
autorouter login remote-server --no-reindex # authorize, index later
autorouter logout remote-server # forget a stored grant
更新
autorouter update 会原地升级,并使用安装当前运行副本的包管理器:
autorouter update # detect, then upgrade
autorouter update --check # report the available version, install nothing
autorouter update --dry-run # print the command, do not run it
使用哪个包管理器取决于正在运行的文件在磁盘上的位置,而不是 PATH 中的内容——对由 pnpm 管理的全局安装运行 npm i -g,会安装另一个副本并覆盖原副本;这样你升级的是一个安装,运行的却是另一个。
以下三种情况不会运行包管理器,而是会说明原因:由 npx/bunx/pnpm dlx 解包的副本(无需升级,因为每次运行都会重新获取)、git 检出(git pull && bun install && bun run build),以及无法识别的目录布局。这三种情况都会报告是否存在更新版本。
路由器是一个长期运行的 stdio 服务,因此已经运行它的运行环境会继续使用旧版本构建,直到重新启动该服务——更新后请重启运行环境,或重新连接 MCP 服务。
OAuth 服务
一些远程 MCP 服务的可见配置中不包含凭据。可用的令牌是由运行环境获取的 OAuth 授权,存储在运行环境的凭据存储区中,并专门颁发给该运行环境。
路由器不会读取它。路由器会运行自己的授权码流程并持有自己的授权,因此在 Codex 和 Cursor 下也能使用,这两者都没有可借用的令牌:
autorouter login remote-server
获取新的授权后会自动重新索引,因为在目录看到相关能力之前,这些能力无法搜索到——单独获得授权不会改变路由器可访问的内容。连续授权多个服务时,如果希望最后只索引一次,可以使用 --no-reindex 跳过重新索引。
只有实际存储了新授权的登录才会触发重新索引。--list-scopes 和已有授权的服务都不会改变任何内容,因此都不会导致重建索引。
注册采用 RFC 7591 动态客户端注册,因此无需事先创建应用。令牌存储在 ~/.autorouter/oauth/<server>.json 中,权限为 0600,并会自动刷新;logout 会删除这些令牌。环回重定向使用固定端口(33418,可通过 --port 或 $AUTOROUTER_OAUTH_PORT 修改),因为提供方存储的注册信息中包含了重定向 URI——在一个端口获取的授权无法从另一个端口刷新。
无头计算机
浏览器流程无法通过 SSH 使用:它会绑定一个环回侦听器,并要求同一主机上的浏览器将请求重定向到该侦听器。以下两种流程可以替代它;在没有显示器的计算机上,autorouter 会自行选择其中一种,而不是一直等待一个根本不会打开的浏览器,直到超时。
autorouter login remote-server --device # RFC 8628: enter a code on your phone
autorouter login remote-server --manual # paste the redirect URL back
如果提供方支持,--device 是更好的选择。无需任何请求回传到计算机:它会显示一个短代码和一个 URL,你可以在其他设备的浏览器中输入它们,无头端会持续轮询,直到操作完成。通过提供方元数据中的 device_authorization_endpoint 判断是否支持此流程。
对于未实现 RFC 8628 的提供方,可使用 --manual 作为备用方案。它会显示授权 URL,你在其他设备上打开该链接后,浏览器会重定向到 http://localhost:33418/callback,但页面无法加载——因为没有侦听器。这是预期行为:从地址栏复制 URL 并粘贴回来。最好粘贴完整 URL,而不只是代码,因为其中的 state 可用于证明该代码来自你发起的登录。
这两种流程都不会绑定端口。设备登录会注册一个客户端,该客户端也可用于之后在同一台计算机上通过浏览器登录,因此如果这台计算机后来配备了显示器,就不必重新操作。在检测判断错误的计算机上,可设置 AUTOROUTER_ASSUME_HEADLESS=1 强制启用无头模式检测。
选择权限
动态注册的客户端可能默认获得提供方公布的所有作用域,包括写入或管理权限。这样一来,搜索工具持有的令牌就可能具备执行破坏性操作的能力。作用域是唯一能在提示词注入后仍然有效的限制,因此请谨慎选择:
autorouter login remote-server --list-scopes
autorouter login remote-server --read-only
autorouter login remote-server --scopes "projects:read,data:read"
--read-only 会保留名称未表明可进行修改的作用域。这只是基于命名惯例(:write、admin、manage、all)的启发式判断,无法推断不透明作用域名称背后的权限。启发式判断无法确定时,可以精确使用 --scopes。如果提供方完全不提供只读子集,--read-only 会明确报错,而不是请求空作用域;大多数提供方会将空作用域理解为“授予默认权限”。
授权发放后,作用域便会固定,因此要缩小现有授权的范围,必须使用 --force 并重新授权。重新登录时会沿用之前的权限缩减,除非你传入新的设置;不带参数运行 autorouter login,会显示每项已存储授权的实际权限范围。
服务器尚未获得授权时,adopt 会拒绝移动该服务器,或禁用提供它的插件。移动路由器无法访问的服务器会导致原本可用的功能失效,因此 doctor 和 adopt 都会打印确切的 login 命令。
权衡,直说
路由意味着宿主的权限提示会显示 call_capability,而不是 database.execute_statement。这确实会降低权限粒度,本工具不会掩饰这一点。三种缓解措施:exclude 表示永不公开某项能力;confirm 会拒绝首次尝试,明确显示解析后的目标,并要求模型使用 confirm: true 重新发起;每个结果都会添加解析后的 server/tool 前缀,以便审计记录。
采用技能和插件会修改 ~/.claude/settings.json,你也可以手动编辑这个文件。整个文件会与服务器移动操作一起原样备份,而 restore 会逐字节还原;不过这是一个共享文件,值得留意。出于同样的原因,隐藏技能默认采用 name-only:Claude 仍然可以调用它们,/name 也能继续使用,因此只是迁移了能力,并未移除它。--skill-mode user-invocable-only 也会移除名称,但 Claude Code 随后会拒绝对它进行任何 Skill 工具调用(“disabled for model invocation”);off 还会移除 /name。
开发
bun install
bun test
bun run src/cli.ts doctor
bun run build # bun build src/cli.ts --target=node --outfile dist/cli.js
构建目标为 Node,因此没有安装 Bun 的用户也能使用 npx。
更改任何内容之前,请参阅 CONTRIBUTING.md,了解需要注意的不变量——最重要的是,启动时不会生成任何子进程;adopt 所做的每项写入都必须能够逐字节还原;选择器子进程按设计不配备任何工具。
许可证
MIT。安全问题请通过 SECURITY.md 报告,不要提交到公开问题跟踪器。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。