---
title: "每次 MCP 工具调用应该记录什么"
description: "在客户端眼里，远程 MCP 服务器只是一个看不到内部的 span，目录仪表板上只有汇总数字。只有服务器看到了这次调用。本文逐个字段介绍 Tajo 的 MCP 服务器为每次调用写下的那一行，其中清除了什么，以及还有什么没有构建。"
url: https://tajo.io/zh-cn/blog/what-to-log-for-every-mcp-tool-call/
markdown: https://tajo.io/zh-cn/blog/what-to-log-for-every-mcp-tool-call.md
lang: zh-CN
author: "Set Noa"
author_url: https://tajo.io/zh-cn/set-noa/
published: 2026-11-06
updated: 2026-11-06
alternates: {en-US: /blog/what-to-log-for-every-mcp-tool-call.md, bg-BG: /bg/blog/what-to-log-for-every-mcp-tool-call.md, cs-CZ: /cz/blog/what-to-log-for-every-mcp-tool-call.md, de-DE: /de/blog/what-to-log-for-every-mcp-tool-call.md, da-DK: /dk/blog/what-to-log-for-every-mcp-tool-call.md, es-ES: /es/blog/what-to-log-for-every-mcp-tool-call.md, fr-FR: /fr/blog/what-to-log-for-every-mcp-tool-call.md, el-GR: /gr/blog/what-to-log-for-every-mcp-tool-call.md, hi-IN: /hi/blog/what-to-log-for-every-mcp-tool-call.md, id-ID: /in/blog/what-to-log-for-every-mcp-tool-call.md, hr-HR: /hr/blog/what-to-log-for-every-mcp-tool-call.md, hu-HU: /hu/blog/what-to-log-for-every-mcp-tool-call.md, it-IT: /it/blog/what-to-log-for-every-mcp-tool-call.md, ja-JP: /jp/blog/what-to-log-for-every-mcp-tool-call.md, ko-KR: /kr/blog/what-to-log-for-every-mcp-tool-call.md, ar-SA: /ar/blog/what-to-log-for-every-mcp-tool-call.md, nl-NL: /nl/blog/what-to-log-for-every-mcp-tool-call.md, no-NO: /no/blog/what-to-log-for-every-mcp-tool-call.md, pl-PL: /pl/blog/what-to-log-for-every-mcp-tool-call.md, pt-BR: /pt/blog/what-to-log-for-every-mcp-tool-call.md, ro-RO: /ro/blog/what-to-log-for-every-mcp-tool-call.md, ru-RU: /ru/blog/what-to-log-for-every-mcp-tool-call.md, sv-SE: /se/blog/what-to-log-for-every-mcp-tool-call.md, sl-SI: /sl/blog/what-to-log-for-every-mcp-tool-call.md, sr-Latn-RS: /sr/blog/what-to-log-for-every-mcp-tool-call.md, th-TH: /th/blog/what-to-log-for-every-mcp-tool-call.md, tr-TR: /tr/blog/what-to-log-for-every-mcp-tool-call.md, ur-PK: /ur/blog/what-to-log-for-every-mcp-tool-call.md, vi-VN: /vi/blog/what-to-log-for-every-mcp-tool-call.md, sk-SK: /sk/blog/what-to-log-for-every-mcp-tool-call.md}
---

[AI 智能体](https://tajo.io/zh-cn/blog/?category=ai-agents)

# 每次 MCP 工具调用应该记录什么

在客户端眼里，远程 MCP 服务器只是一个看不到内部的 span，目录仪表板上只有汇总数字。只有服务器看到了这次调用。本文逐个字段介绍 Tajo 的 MCP 服务器为每次调用写下的那一行，其中清除了什么，以及还有什么没有构建。

MCP 服务器 日志

MCP 服务器应该为每次工具调用记录什么？

每次工具调用记录一行，记在服务器上，因为只有服务器看到了它。Tajo 的 MCP 服务器记录谁发起了调用（密钥的 ID，绝不记录其密钥值），哪个服务器和连接器，工具存在时的工具，它的影响类别，ok 或简短的错误代码，耗时，结果大小，追踪 ID，调用涉及的记录，以及清除了密钥和个人数据的参数与响应。这些行通过 logs.list 和 logs.get 读取，30 天后删除。它们与带哈希链的审计日志分开存放，审批及其输入摘要保存在审计日志中。尚未构建：健康评分、错误率告警、按工具或追踪 ID 搜索，以及导出到你自己的追踪系统。

[了解更多](https://tajo.io/zh-cn/blog/what-to-log-for-every-mcp-tool-call/#%E6%AF%8F%E6%AC%A1%E8%B0%83%E7%94%A8%E4%B8%80%E8%A1%8C)

2026年6月8日，Anthropic 为 Claude 目录中连接器的所有者提供了一个仪表板。它显示活跃用户数、工具调用总数和目录排名。它还提供“[健康评分、错误率和延迟一目了然，并按工具细分错误，精准定位哪里出了问题](https://claude.com/resources/articles/observability-for-developers-building-connectors)”。

一年前，Weights & Biases 的 Alex Volkov 从另一端描述过同一个问题。当他的代理开始通过 MCP 调用工具后，“[我习惯的、自己的代码从头到尾运行时所拥有的那种可观测性，变得有点暗了](https://www.youtube.com/watch?v=Lcqat4iP_lE&t=61s)”。与他共同演讲的 Dylibso 的 Benjamin Eckel 说明了原因。远程 MCP 服务器运行在调用方的控制之外，所以它“[在追踪中只表现为一个单一的服务 span](https://www.youtube.com/watch?v=Lcqat4iP_lE&t=447s)”。

这两种视角都在服务器之外。客户端看到的是一个看不到内部的 span。目录看到的是汇总数字。服务器被要求做了什么、拒绝了什么、触及了什么，两者都没有看到。只有服务器看到了，所以也只有服务器能把它记下来。

下面是 Tajo 的 MCP 服务器记下的内容，读自运行它们的代码，[文档中也有说明](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。[什么是 MCP 服务器](https://tajo.io/zh-cn/blog/what-is-an-mcp-server/)介绍了协议本身。本文讲的是日志里的那一行。

## 每次调用一行

仪表板是港务长的视角：进港的船、出港的船、哪些晚到了。请求日志则是船自己的航海日志，在当时、在船上写下。

在 Tajo 的工作区服务器和每个连接器服务器上，每一次 `tools/call` 都[以一行记录结束](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。这一行在响应构建完成之后写入，无论响应是什么。因为密钥没有写入权限或因为端点是只读而被拒绝的调用，也和其他调用一样得到一行。调用一个不存在的工具也是如此。

下表列出这一行保留什么，以及拒绝保留什么。

| 字段 | 保留 | 不保留 |
| --- | --- | --- |
| 谁 | 发起调用的 API 密钥的 ID。通过 OAuth 登录获得的令牌也算作密钥。 | 密钥的密钥值或其前缀。 |
| 在哪里 | 工作区服务器或某个连接器服务器，以及其连接器和配置。 |  |
| 工具 | 当服务器有该名称的工具时，记录其名称。 | 客户端发送的任何其他名称：一律存为 `(unknown)`。 |
| 影响 | `read`、`write`、`send` 或 `destructive`：该调用可能产生的最重影响。 |  |
| 结果 | `ok`，或简短的 snake\_case 错误代码，例如 `unknown_tool`。 | 错误描述文字：任何不是代码的内容都存为 `tool_error`。 |
| 耗时与大小 | 从到达到响应的毫秒数，以及调用方收到的字节数。 |  |
| 追踪 ID | 请求的 ID，取自 `x-request-id`、`x-trace-id` 或 W3C `traceparent` 头，没有则新生成一个。 | 既不是十六进制也不是 UUID 的头部值。 |
| 记录 | 调用指定或产生的记录 ID，最多 20 个：联系人、消息、审批。 | 记录本身。 |
| 正文 | 参数和响应，[经过清洗](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)，每个最多 4 KB。 | 密钥、自由文本，以及下文列出的个人数据。 |

其中有三项拒绝针对的是客户端，而不是数据。工具名、错误代码和追踪 ID 都来自外部。每一项只以服务器能够担保的形式保留。一个照单全收、存下调用方所发任何内容的日志，可以被塞进任何东西，包括在工具名一列里出现客户的邮箱地址。

Benjamin Eckel 在演讲结尾留下的开放问题是：采集器如何知道“[这是一个出错的 MCP 工具调用](https://www.youtube.com/watch?v=Lcqat4iP_lE&t=906s)”。在一台服务器上，答案就是一列。`ok` 为 false，代码说明是哪种错误。

## 参数：我们推翻的一条规则

反对意见很合理，而且它本来就是我们自己的。记录参数，就等于在所有删除请求之外又建了一份客户数据的副本。在 2026年10月18日之前，这正是[我们自己的规则](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。日志既不保留参数，也不保留结果正文，因为两者都可能包含个人数据。

这是一个错误的取舍。一个无法显示发送了什么、返回了什么的请求日志，就无法回答它存在的唯一问题：我的代理的调用为什么失败了？

所以我们保留正文，并用另一种方式解决当初不保留的理由。每个正文在进入队列之前都要经过[一次确定性处理](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。

- **任何密钥都留不下来。** 字段名表示 token、password、secret、signature、cookie、session 或大约二十个类似词语的字段，只保留 `[secret]`。符合已知凭据格式的值也一样，无论出现在哪里。这包括 bearer 令牌、JWT、私钥、带密码的 URL，以及任何连续 40 个或更多密钥字符的不透明字符串。
- **文字内容完全不保留。** 名为 `html`、`text`、`body`、`markdown` 或 `content` 的字段只保留 `[redacted]`。文字中含有任何模式都找不到的姓名和街道地址。
- **个人数据被永久遮盖。** 字段名表明是个人数据的字段，例如邮箱、电话、姓名、地址或备注，只保留 `[redacted]`。其他任何字符串中的邮箱地址、电话号码、银行卡号或 IBAN 都会被遮盖。
- **正文保持精简。** [字符串在 512 个字符处截断](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)，列表在 20 项，对象在 50 个键，嵌套在 6 层。整个正文[在 4 KB 处截断](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)，并标记为已截断。

有两类调用保留得更少。用户的聊天从其他应用带进来的联系人，只记录一个标记和数量，绝不记录正文。读取日志的调用不保留响应，因为响应不过是其他日志行。

再说成本。遮盖是永久性的，而不是某人持有密钥的加密。日志无法显示退信的那个邮箱地址。它显示的是联系人的 ID，而联系人本身在其自身规则的约束下，只需一次调用就能取到。日志不在工作区数据清除流程的范围内，所以遮盖不能等到清除时再做。

## 为什么日志不是审计追踪

Tajo 已经有一个[只追加的审计日志](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。每一行都带有前一行的哈希，所以任何地方的修改都会打断链条。那为什么不把工具调用写进去？

因为链条有代价。每次追加都要获取一个工作区级别的锁，并读取前一行来延长链条。如果每次读取都写进去，工作区的调用就会排在同一把锁后面。审计日志存在的意义是展示状态变更，而这些变更也会被成千上万条读取记录淹没。

所以两份记录按问题分工。请求日志说明代理请求了什么、发生了什么，[保留 30 天](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。审计日志说明什么发生了变化、谁允许了它，以及之后是否有人修改过这条记录。

审批属于审计这一侧。在 Tajo 的连接器配置中，[propose 工具准备一个已声明的操作，apply 工具在它获批后执行](https://tajo.io/zh-cn/docs/mcp/)。在提案获批之前，什么都不会发送到服务提供商，审批可以由人完成，也可以由工作区开启的规则完成。propose 调用的那一行会在其记录中列出它创建的审批。审批本身绑定到确切输入的 SHA-256 摘要上。[审批只覆盖你看到的那份确切输入](https://tajo.io/zh-cn/docs/mcp/agent-specification/)：改动一个字段，它就不再匹配。写入生效时，它的审计记录保留输入摘要和服务提供商响应的摘要，绝不保留响应的值。

## 一行的成本

一行就是一次数据库写入，而写入就要提交。[在 2026年10月8日之前](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)，每次调用都要在响应前等待自己的提交，在我们的主机上大约 2.6 毫秒。对某个客户端来说，这相当于一次小型工具调用的四分之一。

现在，这一行在调用结束时构建并放入队列。刷写操作[最多 100 毫秒后](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)写入队列，每个工作区一个事务，因此许多调用共享一次提交。行的内容没有任何变化，包括时间戳。如果有 [5,000 行在等待](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)，下一次调用会等待刷写完成，而不是丢弃一行。

记录永远不会让调用失败。写入失败时会逐行重试，然后记录并丢弃。进程被强制终止时，丢失的是队列中的内容：[最多是最后 100 毫秒的调用](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。

## 读回日志

[`logs.list`](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log) 返回工作区的调用，最新的在前，不含正文。它可以按时间、按结果或 HTTP 状态、按来源（MCP 或与之共用这张表的 REST API）以及按 API 密钥筛选。默认每页 50 条，最多 100 条。`logs.get` 返回单次调用，附带密钥的当前名称、状态、耗时、清洗后的请求和响应，以及它涉及的记录。

两者都只读取调用方所在的工作区。来自其他工作区的行 ID 是找不到的。

工具错误会带上请求 ID。当这个 ID 由服务器生成，或者以十六进制或 UUID 的形式传入时，它就是这一行的追踪 ID。收到错误的代理可以把这个唯一标识该调用的值交给人。

## 已经构建的和尚未构建的

截至本文写作时已经构建的：

- 在工作区服务器和每个连接器服务器上，每次 `tools/call` 一行，包括被拒绝和未知的调用。
- [密钥 ID、服务器、连接器、配置、工具、影响、结果、耗时、结果大小、追踪 ID，以及最多 20 条涉及的记录](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。
- [清洗后的参数和响应](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)，每个最多 4 KB。
- [`logs.list` 和 `logs.get`](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)，限定在工作区范围内，保留 30 天。
- 绑定到输入摘要的审批，以及在带哈希链的审计日志中连同输入和输出摘要一起记录的已执行写入。

尚未构建的，在依赖它之前值得了解：

- 没有健康评分，没有按工具的图表，也没有错误率上升时的告警。按工具汇总的调用数、错误数和最慢耗时[只提供给 Tajo 的运维人员](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)，不在你能打开的任何界面上。
- `logs.list` 不能按工具名或追踪 ID 筛选。要找到某个请求 ID 对应的行，请按时间和密钥缩小范围。
- 这些行不会导出到你运行的追踪系统。追踪 ID 只从 HTTP 头中读取，不从演讲中用来[传递追踪上下文](https://www.youtube.com/watch?v=Lcqat4iP_lE&t=535s)的 MCP `_meta` 字段中读取。
- [30 天后，行会被删除](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。没有归档，也没有导出。
- 被遮盖的内容无法从日志中恢复。
- [强制终止可能丢失最后 100 毫秒的行](https://tajo.io/zh-cn/docs/mcp/#mcp-request-log)。

演讲最后指出，MCP 中的可观测性“[分布并不均匀](https://www.youtube.com/watch?v=Lcqat4iP_lE&t=841s)”。下游的仪表板解决不了这个问题，止步于服务器门口的追踪也解决不了。[失败的同步需要自己的记录](https://tajo.io/zh-cn/blog/worker-died-sync-didnt/)；每一次提出过请求的调用也是如此。

服务器看到了这次调用。别人都没有看到。就在那里把它记下来。

## 常见问题

**MCP 服务器应该为每次工具调用记录什么？**

每次调用一行，由服务器写入：哪个 API 密钥发起了调用，哪个服务器和连接器，工具存在时的工具名，它的影响类别，ok 或简短的错误代码，耗时，结果大小，请求的追踪 ID，调用指定或产生的记录 ID，以及清除了密钥和个人数据的参数与响应。Tajo 的 MCP 服务器为每一次 tools/call 写下这一行，被拒绝的调用也不例外，并保留 30 天。

**MCP 请求日志应该保留工具参数吗？**

应该，但要先清洗。没有参数的日志无法说明调用为什么失败。Tajo 在 2026年10月18日之前不保留任何正文，现在两者都保留，但要先经过一次确定性处理：密钥变成 \[secret\]，自由文本和个人数据字段变成 \[redacted\]，邮箱、电话、银行卡号和 IBAN 被遮盖，每个正文在 4 KB 处截断。遮盖是永久性的，因此日志通过 ID 指向记录，而不是复制记录。

**为什么要把 MCP 工具调用日志与审计日志分开？**

因为它们以不同的成本回答不同的问题。Tajo 的审计日志是一条记录状态变更的哈希链，每次追加都要获取一个工作区级别的锁来延长链条。如果每次读取都写进去，工作区的调用就会排在这把锁后面，变更也会淹没在读取记录里。工具调用写入一张单独的、不带链条的表，保留 30 天；调用准备的写入操作仍然会留下各自的审计记录。

## 相关文章

### [什么是 MCP 服务器？2026 年 MCP 与 MCP UI 如何运作](https://tajo.io/zh-cn/blog/what-is-an-mcp-server/)

2026年10月3日 · 12 分钟阅读

第 1 步 → 第 6 步

### [Grok Bot 与 Brevo：跟进产品反馈，完成闭环](https://tajo.io/zh-cn/blog/grok-bot-brevo-product-feedback/)

2026年10月7日 · 4 分钟阅读

3 个章节

### [MCP 还是 CLI 问错了问题，该问的是状态存放在哪里](https://tajo.io/zh-cn/blog/mcp-or-cli-is-the-wrong-question/)

2026年11月4日 · 6 分钟阅读

5 个章节
