# 工具体系

> 模型每回合拿到的工具从哪来、怎样按模型能力与 Agent 配置层层过滤、结果怎样分层截断，以及不在工具清单里的 mcode-tools。

- 作者：David（道雾轩）
- 专栏：MiniMax Code 源码解读（https://daiw.net/manual/minimax-code.md）
- 最后更新：2026-09-19
- 原文：https://daiw.net/manual/minimax-code/tool-system
- 转载与引用：请注明出处并附原文链接（https://daiw.net/about/copyright）

模型在每个回合拿到的是一份临时拼出来的工具清单。原料有四类：从 pi coding-agent 借来的 read、write、edit、bash，MiniMax 在 `packages/agent-tools` 里自己写的其余本地工具，MCP 服务暴露的工具，以及扩展在装配时追加的工具。拼好之后还要按模型能力、Agent 配置和子 Agent 角色逐层过滤，才交给 pi 的 Agent 循环。

本篇只讲“清单怎么来、怎么剪、结果怎么收”。每个工具的具体行为见[文件与 Shell 工具](https://daiw.net/manual/minimax-code/file-and-shell)，调用前的放行判断见[权限模式与规则](https://daiw.net/manual/minimax-code/permission)。

## 怎么用

工具注册表不对用户开放，能影响清单的是几处配置：

| 入口 | 作用 | 出处 |
| --- | --- | --- |
| `config.yaml` 的 `agents.default` 段 | 给默认 Agent 定能力上限：`tools` 从 12 个内置工具 ID 里挑，`builtinTools` 挑内置 Matrix 媒体工具，`features` 开关 `mavis`、`delegation`、`webSearch` | `packages/config/src/agent-capabilities.ts:1`、`:83` |
| 自定义 Agent 的 `tools`、`disallowedTools`、`mcpServers` | 按名单保留或屏蔽工具与 MCP 服务，省略即继承上限 | `packages/local-runtime-v2/src/service/agent/contracts.ts:181` |
| `mcpToolSearch` 段、`MAVIS_MCP_TOOL_SEARCH_*` 环境变量 | MCP 工具是否改为按需检索 | `packages/local-runtime-v2/src/service/turn-system/agent-host/assembly/local-turn-tool-catalog.ts:480` |
| `toolResultCompaction.maxInlineKiB` | 单条工具结果留在上下文里的上限，默认 64KiB | `packages/config/src/tool-result-compaction-config.ts:20` |
| `beta.mcodeTools` | 是否把 mcode-tools 放上 bash 的 PATH | `packages/config/src/config.ts:494` |
| `MAVIS_LOCAL_RUNTIME_DISABLE_TOOLS=1` | 一个工具都不给 | `packages/local-runtime-v2/src/service/turn-system/production-composition.ts:386` |

自定义 Agent 的写法见 [Agent 服务](https://daiw.net/manual/minimax-code/agent-service)。

## 源码分布

| 位置 | 内容 | 在本地 CLI 里的角色 |
| --- | --- | --- |
| `third_party/pi-mono/packages/coding-agent/src/core/tools` | read、bash、edit、write、grep、find、ls 七个工具 | 只借用前四个的实现 |
| `packages/agent-core/src/tools` | `ToolDefinition`、`ToolImpl`、`RuntimeTool` 等契约 | 工具协议 |
| `packages/agent-tools/src/desktop` | `Local*` 工具类与 `buildLocalToolRegistry` | 模型实际调用的本地工具 |
| `packages/agent-tools/src/shared` | 读取守卫、replace_all、ripgrep 排除规则等 | 供 desktop 包装使用；注释说云端包装也用，但那部分不在公开仓库 |
| `packages/agent-tools/src/mcp-disclosure` | `tool_search`、`mcp_invoke` 与检索索引 | MCP 渐进暴露 |
| `packages/agent-tools/src/cloud` | 只有 `matrix-tools`：图像、视频、语音工具的定义与客户端 | 内置 Matrix MCP 子进程的实现，公开构建未启用 |
| `packages/agent-tools/src/plugin-hooks` | 兼容 Claude Code、Codex 格式的工具结果与转录 | 给插件 Hook 用，见[插件系统](https://daiw.net/manual/minimax-code/plugins) |

pi 的七个工具名定义在 `third_party/pi-mono/packages/coding-agent/src/core/tools/index.ts:84`。MiniMax 只从 pi 引入 `createReadTool`、`createWriteTool`、`createEditTool`、`createBashTool` 四个工厂（`packages/agent-tools/src/desktop/local-pi-tools.ts:11`），pi 的 grep、find、ls 在 `packages/` 里没有调用处，搜索由自己的 ripgrep 实现承担。

## 契约：RuntimeTool

`packages/agent-core/src/tools` 主要定义契约。`ToolDefinition` 是模型看到的那一面：`name`、`description`、typebox 写的 `schema`，外加可选的 `prepareArguments`、`executionMode`（`packages/agent-core/src/tools/types.ts:66`）；`ToolImpl.execute(ctx, input, signal, onUpdate)` 是执行的那一面；两者配成 `RuntimeTool`，再挂一个来源标记 `source`，取值 `builtin`、`builtin-matrix`、`configured`（`types.ts:32`、`:142`）。把实现类变成 `RuntimeTool` 有两种写法：装饰器 `@bindTool(def)` 加 `toRuntimeTool(instance)`（`packages/agent-core/src/tools/bind.ts:31`、`:46`），或一次声明的 `defineRuntimeTool`（`packages/agent-core/src/tools/define.ts:24`）。本地文件与 Shell 工具用的是前一种。同目录还有一组通用工具定义（`packages/agent-core/src/tools/builtin-defs.ts:1`），公开仓库里没有使用方。

契约到 pi 的转换在 `packages/agent-core/src/pi-turn-runner/tools.ts:26` 的 `newTools`：逐个把 `RuntimeTool` 包成 pi 的 `AgentTool`，同名直接抛错；执行时把本回合的上下文与 `toolCallId` 放进闭包传给 `impl.execute`。没声明执行模式的一律按 `sequential` 处理（`tools.ts:140`），而且模式作用于整批：pi 的循环只要发现同一条助手消息里有一个串行工具，整批调用都改成串行（`third_party/pi-mono/packages/agent/src/agent-loop.ts:385`）。本地的 read、grep、glob 声明为 `parallel`，write、edit、bash 是 `sequential`（`packages/agent-tools/src/desktop/builtin-defs.ts:19`、`:57`、`:93`、`:110`）。

`newTools` 还带一段后备逻辑：清单里缺 read、write、edit、bash 时，自动补上 pi 的原生实现（`tools.ts:44`）。v2 准备回合时显式关掉了它（`packages/local-runtime-v2/src/service/turn-system/agent-host/assembly/local-turn-execution-preparation.ts:90`），所以本地运行时里这四个名字背后一定是 MiniMax 的包装版；包装版在 pi 之上加了什么，下一篇细说。

## 原料：注册表与几路来源

本地工具的起点是 `buildLocalToolRegistry`。前七个工具无条件创建（`packages/agent-tools/src/desktop/index.ts:118`）：

```ts
export function buildLocalToolRegistry(deps: LocalToolRegistryDeps): Map<string, LocalRuntimeTool> {
  const tools: LocalRuntimeTool[] = [
    toRuntimeTool(new LocalReadTool(deps.workspaceRoot)),
    toRuntimeTool(new LocalWriteTool(deps.workspaceRoot)),
    toRuntimeTool(new LocalEditTool(deps.workspaceRoot)),
    toRuntimeTool(
      new LocalBashTool(
        deps.workspaceRoot,
        deps.bashAdapter,
        deps.bashEnvPolicy,
        deps.hostTrashRuntime,
        deps.sandboxOperationsFactory,
      ),
    ),
    toRuntimeTool(new LocalGrepTool(deps.workspaceRoot)),
    toRuntimeTool(new LocalGlobTool(deps.workspaceRoot)),
    toRuntimeTool(new LocalTodoWriteTool(deps.todoEventSink)),
  ];
```

其余工具看宿主给没给对应的适配器：`skill`、`memory`、`code_review`、`mavis`、`ask_user` 与 `request_feature_enable`、`web_fetch`、`web_search`、`task`、`task_append`、`task_query`、`task_output`、`task_stop`、`website_deploy`。最后按名字建 Map，重名抛错（`desktop/index.ts:188`）。

把注册表、MCP 和线程目标工具凑成一回合原料的，是 `packages/local-runtime/src/api/local-native-tools.ts:258` 的 `buildLocalTurnToolSources`：

- 工具被整体禁用时四路来源全部置空（`local-native-tools.ts:261`）；
- `task` 与 `task_append` 算同一个委派入口，要求宿主允许且能力上限打开 `delegation`（`:276`）；
- MCP 工具由 `mcpService.listToolEntriesForTurn` 列出（`:334`），每个条目都按服务来源打上 `builtin-matrix`、`builtin` 或 `configured`，认不出的一律算 `configured`（`packages/local-runtime-v2/src/service/mcp/tools/runtime-tools.ts:47`）；
- 线程目标（`/goal`）的工具单独一路（`local-native-tools.ts:362`），见 [Plan 模式与目标](https://daiw.net/manual/minimax-code/plan-and-goal)。

命令行宿主另有几处差别：`web_search` 对 `electron`、`tui`、`cli` 三种宿主启用原生实现（`packages/local-runtime/src/api/host-turn-tools.ts:82`）；记忆与定时任务在 `cli`、`tui` 下关掉（`packages/local-runtime-v2/src/application/agent/profile-source.ts:52`）；浏览器工具由应用层在原生工具之后追加（`packages/local-runtime-v2/src/application/agent/runtime-agent-product.ts:73`，见[浏览器工具](https://daiw.net/manual/minimax-code/browser-use)）。内置 Matrix MCP 在公开仓库里没有启用，运行时组装固定传 `builtinMatrix: false`（`packages/local-runtime-v2/src/compat/v1/runtime.ts:580`），所以命令行版的媒体生成与理解主要靠本篇末尾的 mcode-tools。

整条装配线如下：

```mermaid
flowchart TD
  P["pi coding-agent：四个工具工厂"] --> L["agent-tools：Local* 工具与注册表"]
  L --> S["buildLocalTurnToolSources：原生工具、MCP 条目、目标工具"]
  M["MCP 服务：用户配置与插件"] --> S
  S --> F["filterLocalTurnCapabilityInventory：模型、选择器、角色"]
  F --> D["planMcpDisclosure：全部内联，或改用 tool_search"]
  D --> R["AgentRuntime.assembleTurn：扩展追加工具与钩子"]
  R --> N["newTools：转成 pi 的 AgentTool"]
```

最后一步之前，`@mavis/agent-runtime` 的扩展还能再注册工具：装配时清单在前、扩展追加在后（`packages/agent-runtime/src/registry.ts:347`）。Plan 模式的 `EnterPlanMode`、`ExitPlanMode` 就是扩展按回合注册的（`packages/agent-extension/src/plan-mode.ts:124`）。扩展机制见 [Agent 模块](https://daiw.net/manual/minimax-code/agent-modules)。

## 过滤：一回合的清单怎样定下来

过滤集中在 `packages/local-runtime-v2/src/service/turn-system/agent-host/assembly/local-turn-tool-catalog.ts`。注释写得直白：各业务域只提供原始工具，模型门槛、MCP 暴露方式和提示都在这里按冻结的回合事实决定（`local-turn-tool-catalog.ts:183`）。每个工具先过 `isReadyTurnToolAllowed`（`local-turn-tool-catalog.ts:276`）：

```ts
  if (input.excludedNames.has(input.tool.def.name)) return false;
  if (!isDesktopToolAvailable(input.tool, input.cuRuntimeAvailable)) return false;
  if (!input.selector.allowsTool(input.tool.def.name, input.options.selectorAlias)) return false;
  if (!isMcpServerAllowed(input.selector, input.options)) return false;
  if (
    (DELEGATION_TOOL_NAMES.has(input.tool.def.name) ||
      (input.source !== 'builtin' && TASK_CONTROL_TOOL_NAMES.has(input.tool.def.name))) &&
    !isAuthoritativeMainProfile(input.profile)
  ) {
    return false;
  }
  if (!input.profile) return true;
  return isProfileToolAllowed(input.profile, input.tool, input.source);
}
```

依次是：

1. **模型能力**：模型本身能看图、看视频时，去掉多余的 `images_understand`、`videos_understand`（`packages/agent-tools/src/shared/model-tool-capabilities.ts:3`）。
2. **电脑操控**：`desktop_` 开头的工具只在电脑操控（代码里叫 CU）运行时可用时保留（`local-turn-tool-catalog.ts:291`）。
3. **Agent 选择器**：名字经 `trim`、NFKC 归一和小写后比较，`disallowedTools` 命中即剔除，`tools` 有值时只留名单内的（`:328`）。
4. **委派权**：`task`、`task_append` 以及非内置来源的同名任务控制工具，只给“权威主 Agent”——界面是 `interactive` 或 `cli`、可信内置、规范角色为 `mavis`（`:320`）。
5. **档案上限**：作为子任务运行的 Agent 拿不到 `task`、`task_append`、`todowrite`、`ask_user`（`:454`）；`features` 三个开关分别管 `mavis`、委派入口和 `web_search`（`:473`）。

内置子 Agent 还有一层按角色的上限（`packages/agent-tools/src/desktop/canonical-tool-policy.ts:33`）。角色只有 `explore`、`worker`、`verifier` 三个（`packages/shared/src/subagent-roles.ts:1`）：

| 角色 | 去掉的工具 |
| --- | --- |
| `worker` | 只去掉 `task`、`task_append` |
| `verifier` | `write`、`edit`、`website_deploy`、`todowrite`、`task`、`task_append`、`memory`、`ask_user`、`request_feature_enable` 与全部 `desktop_*`（`canonical-tool-policy.ts:11`） |
| `explore` | 同 `verifier`，再加 `task_query`、`task_output`、`task_stop` |

MCP 条目上，`explore` 与 `verifier` 只留 Matrix 的 `web_search`（`canonical-tool-policy.ts:58`）；线程目标工具不给子任务，也不给内置子 Agent（`local-turn-tool-catalog.ts:432`）。子 Agent 的来龙去脉见[子 Agent 与后台任务](https://daiw.net/manual/minimax-code/subagents)。

过滤完的 MCP 条目交给 `planMcpDisclosure` 决定暴露方式（`packages/agent-tools/src/mcp-disclosure/plan.ts:31`）：只有 `configured` 来源的工具可能被延后，内置来源总是内联；要延后还得同时满足开关打开、模型在白名单里、候选数不少于 `minDeferCount`（默认 1，`local-turn-tool-catalog.ts:496`）、按“JSON 长度除以 4”估出的 token 数超过上下文窗口的 `thresholdPct`（默认 0.15，`:490`）。一旦延后，清单里改放 `tool_search` 与 `mcp_invoke` 两个工具，并在用户提示前加一段检索说明。注意白名单默认为空（`local-turn-tool-catalog.ts:536`），不配置就不会延后。细节见 [MCP](https://daiw.net/manual/minimax-code/mcp)。

## 结果截断：三道闸

工具结果进历史之前要过三道闸，越往后越宽：

| 层 | 上限 | 超出后 | 出处 |
| --- | --- | --- | --- |
| pi 工具自身 | 2000 行或 50KB | read 截取开头并提示用 `offset` 续读；bash 保留尾部，全文写进临时文件 | `third_party/pi-mono/packages/coding-agent/src/core/tools/truncate.ts:11` |
| desktop 包装 | read 24KiB、bash 24KiB、grep 内容模式 16KiB | read 保留开头并返回 `next_offset`；bash 头部占 45%，其余留给尾部 | `packages/agent-tools/src/desktop/output-limit.ts:3`、`:103` |
| `toolOutputBudgetExtension` | 默认 64KiB | 文本整体存档，换成一张回执 | `packages/agent-extension/src/tool-output-budget.ts:48` |

第三道闸是挂在 `after_tool_call` 上的扩展。它先确认本回合有 `read` 工具，没有就原样放行，因为回执得靠 read 才能兑现（`tool-output-budget.ts:62`）；超限时只存文本，图片等媒体块留在结果里；存档写在会话 reports 目录的 `tool-outputs/` 下（`packages/local-runtime-v2/src/service/turn-system/persistence/tool-output-artifact.repository.ts:8`），写失败则退回一段 2KiB 的首尾预览（`production-composition.ts:525`）。回执要求模型先从存档里取回事实再作答（`tool-output-budget.ts:280`）：

```text
Recover the needed facts from this artifact now, before answering, inferring, or searching another source.
Search this artifact first with bounded rg/grep when available, then read the relevant ranges with read(path, offset, limit).
If artifact search is unavailable, use bounded read directly.
Do not read or print the entire artifact in a single call.
When complete content is required, continue with bounded reads until all of it has been read.
```

这三道闸只管单条结果。历史里的工具结果累计过多时，自动压缩还会归档旧结果，见[上下文管理与压缩](https://daiw.net/manual/minimax-code/context-management)。

## mcode-tools：不在清单里的工具

`THIRD_PARTY_NOTICES.md` 最后一条提到的 mcode-tools，是一个单文件 CLI：包名 `@minimax/mcode-tools`，版本 0.0.4，入口 `cli.mjs`。它不在仓库源码里，而是构建时从 npm 上已发布的 `@minimax-ai/code@0.3.11` 压缩包里抽出来的。`scripts/lib/mcode-tools-artifact.mjs:7` 钉死了下载地址、压缩包的 SHA-512 与 CLI 的 SHA-256；抽取时只认 `embedded/mcode-tools/` 下的 `cli.mjs`、`manifest.json` 和包里的 `THIRD_PARTY_NOTICES.md` 三个条目，并核对清单里的版本、构建环境和包名（`mcode-tools-artifact.mjs:15`），产物放进 `embedded/mcode-tools/`，原包的第三方声明另存为 `MCODE_TOOLS_NOTICES.md`（`:67`）。构建脚本随后把启动器拷进 `internal-bin/`（`scripts/build.mjs:132`）。

它不注册成 `RuntimeTool`，模型是通过 bash 调它的：

1. **放上 PATH**：TUI 启动时若 `beta.mcodeTools` 为真（`packages/tui/src/runtime/lifecycle.ts:321`），`prepareTuiMcodeToolsIntegration` 校验资源、在临时目录起一个本地租约代理，再设置几项 `__MAVIS_MCODE_TOOLS_*` 环境变量、把 `internal-bin` 插到进程 PATH 最前（`packages/tui/src/runtime/mcode-tools-integration.ts:80`）。bash 子进程继承 PATH，`mcode-tools` 就解析到启动器。
2. **启动器转交**：启动器用当前运行时的 Node 可执行文件运行同目录上一级的 `mcode-tools.js`（`packages/tui/src/cli/mcode-tools-launchers/mcode-tools:9`），后者由 `packages/tui/src/cli/mcode-tools-entry.ts` 打包而来，先把环境改成 `MCODE_AUTH_PROVIDER=shared-broker` 等取值（`packages/tui/src/cli/mcode-tools-environment.ts:124`），再加载内嵌的 `cli.mjs`。
3. **教模型用**：内置技能 `mcode-tools-master` 只在这个 beta 打开时出现（`packages/local-runtime/assets/skills/mcode-tools-master/SKILL.md:11`），教模型先 `mcode-tools connector tools --keyword` 找工具，再 `connector tool` 看参数、`connector call` 调用，生成类命令要把 bash 的 `timeout` 设成 600。对 `MiniMax-M2.7` 系列模型，系统提醒里还会加一段 `<mcode-tools-master-reminder>`，把图像、视频、音频任务都引向这个技能（`packages/agent-modules/system-reminder/src/mcode-tools-master-reminder.ts:1`）。

`beta.mcodeTools` 的默认值：打包的 dev、test、staging、prod 构建在 macOS、Linux、Windows 上都默认打开，配置里写 `false` 可以关掉（`config.ts:642`）。子进程怎样拿到短期令牌、Connector 背后是哪些云端服务，见[账号、OAuth 与云端工具](https://daiw.net/manual/minimax-code/accounts-and-tools)。

下一篇：[文件与 Shell 工具](https://daiw.net/manual/minimax-code/file-and-shell)——读、写、编辑、搜索与 bash 在 pi 之上各加了哪些守卫。
