Macro MCP 的两个搜索工具:ContentSearch 搜正文、NameSearch 搜标题,别用混
把 Macro 的 MCP 服务接进 Claude Code 或者 Codex CLI 之后,模型第一个会去调的十有八九是搜索工具——它得先找到东西,才谈得上读、改、发。而 Macro 的工具注册表里跟”找”沾边的有三个:ContentSearch、NameSearch、ListEntities。
麻烦在于前两个长得太像了。都只有两个参数,都有一个叫 entityTypes 的数组,另一个参数一个叫 query、一个叫 name。从工具名上看差别似乎很直观,但真让模型自己挑,它经常挑错:用 NameSearch 去搜一段记得住内容却记不住标题的话,或者用 ContentSearch 去搜一个邮件主题。搜不到不会报错,只会返回空,然后模型开始换关键词重试,一路把上下文烧光。
这篇就把这两个工具按官方文档写明的参数拆开对着看,顺便说清楚搜到之后该怎么接下一步。需要先说明的是:这些工具页在官方文档里标注为”Generated from the Macro Rust tool registry”,也就是从 Macro 的 Rust 工具注册表自动生成的参数说明,篇幅很短,只有参数名、类型、是否必填和一句描述,没有返回值结构、没有示例调用。所以下面凡是文档没写的,我会明确说没写,不替它补。
两个工具的参数逐字对照
先把文档里的原始参数表并排放一起:
| 工具 | 参数 | 类型 | 必填 | 文档里的说明 |
|---|---|---|---|---|
ContentSearch | entityTypes | array | Yes | 要搜哪些类型的条目。留空则搜索所有类型 |
ContentSearch | query | string | Yes | 要搜的文本内容,在文档、邮件、消息的正文里搜 |
NameSearch | entityTypes | array | Yes | 同上,字面完全一致 |
NameSearch | name | string | Yes | 要搜的名称或标题。对邮件来说这是主题行;对频道来说可以是频道名,也可以是参与者的名字 |
entityTypes 的说明在两个工具里是同一段话,连举的例子都一样:['documents']、['emails', 'documents']、['channels']。所以这个参数不用分开记,一套写法两边通用。
真正的分水岭是第二个参数落在哪个字段上。query 打的是正文——文档正文、邮件正文、消息正文;name 打的是名称层——文档标题、邮件主题行、频道名。这两层在 Macro 的数据里是分开的,不存在”搜标题搜不到就自动降级去搜正文”这种兜底,文档里没有任何这类描述。
还有一个细节值得留意:NameSearch 的 name 参数说明里额外提了一句,对频道来说,这个字段除了频道名,还能匹配参与者的名字。这是 ContentSearch 没有的能力——想找”我和某某在的那个频道”,正文搜索是搜不出来的,因为你要找的信息根本不在消息正文里,而在频道的成员列表上。
关于”Required: Yes”的一个坑
文档表格里,ContentSearch 和 NameSearch 的两个参数都标了 Required: Yes。但 entityTypes 的描述又写着”留空则搜索所有类型”。这两句话摆在一起是有点矛盾的:既然必填,怎么还能留空。
合理的读法是:这个字段在调用协议上必须出现,但可以传一个空数组来表示”不限类型”。不过我要说清楚——这是我按文档字面推出来的读法,官方文档并没有明说传空数组等价于全类型。如果你在写自动化脚本,稳妥做法是显式把想要的类型列出来,别赌空值的行为。
同样的标注风格在别的工具上也能看到。比如 ListEntities 的 sortBy 参数标了必填,描述里却写着 recently_viewed (default);GetThread 的 limit 标了必填,描述里写”default 10”。有默认值却标必填,基本可以判断是注册表生成器的行为,而不是每个字段真的经过了单独设计。心里有个数就行,实际调用还是以显式传值为准。
什么时候用哪个
给一张按”你手上有什么线索”来分的选择表:
| 你记得的是 | 该用 | 参数怎么给 |
|---|---|---|
| 文件叫什么名字 | NameSearch | name 填标题关键词 |
| 邮件的主题行 | NameSearch | name 填主题片段,entityTypes 收窄到 ['emails'] |
| 频道名,或者只记得跟谁在一个频道里 | NameSearch | name 填频道名或参与者名,entityTypes 给 ['channels'] |
| 正文里出现过的一句话、一个术语、一个数字 | ContentSearch | query 填那段文本 |
| 某封邮件里提过的条款、报价 | ContentSearch | query 填内容关键词,entityTypes 给 ['emails'] |
| 什么关键词都想不起来,只想看最近动过的 | ListEntities | 见下一节 |
实操上还有一条经验:entityTypes 能收窄就收窄。这不只是为了搜得准,更是为了省上下文。MCP 工具的返回是要整段进模型上下文的,一个不限类型的宽泛搜索,返回的条目会横跨文档、邮件、频道消息几类,模型读完这一坨再决定下一步,token 就已经花掉一大截了。先想清楚要找的东西属于哪一类,再让它去搜。
反过来,如果你实在不确定东西存在哪个块里——Macro 把邮件、任务、文档、频道这些放在同一套数据模型上,同一件事的痕迹可能同时落在邮件和文档里——那就先用宽类型搜一轮定位,再用窄类型精搜。别在一次调用里既想全又想准。
搜到之后:结果怎么接到读取类工具上
搜索工具的定位是”定位”,不是”取内容”。官方文档没有写这两个工具返回什么结构,但从整套工具的设计能看出接力关系——下游那批读取类工具,参数全是 id:
| 下游工具 | 必填参数 | 文档里的说明 |
|---|---|---|
ReadContent | documentId (string) | 要取正文的文档 id |
ReadMetadata | documentId (string) | 要取元数据的文档 id |
GetThread | threadId (string)、limit (integer) | 邮件线程 id;最多返回多少条消息,默认 10 |
ReadThread | contentType (string)、ids (array)、messagesSince (string) | 内容类型、内容 id 列表、频道记录的起始时间 |
所以一次典型的链路是:NameSearch 或 ContentSearch 拿到候选条目 → 从里面挑出目标的 id → 丢给对应的读取工具取全文。搜索给的是”是哪一条”,读取给的是”这一条里写了什么”,两步不能省成一步。
这里面 ReadThread 的参数最需要单独说两句,因为它有两个别的工具没有的约束:
ids是数组,但并不是所有类型都能传多个。文档原文特别用大写强调,channel-message、chat-message和content这几种类型支持传多个 id;而channel、chat-thread这类只能传单个 id。批量读之前先看类型,别一股脑塞一个长数组进去。messagesSince要求是 ISO 8601 格式的本地时间,而且文档明确说只对频道生效——读一个长期活跃的频道时用它切个时间窗,不然一次拉回来的记录量会很难看。
至于邮件,GetThread 走的是 threadId 加 limit 这一套,limit 的默认值是 10。搜到一封邮件想看完整往来时,记得按需要把 limit 调上去,默认值只给你最近的一小截。
关于实体类工具怎么在这条链路里接手改属性,我在实体类工具:列实体、取属性、改属性那篇里单独拆过;读取类工具的完整参数和建文档的部分,则在内容类工具:读内容、读元数据、建文档里。
没有关键词的时候,别硬搜
有一类需求是搜索工具解决不了的:“把我最近改过的文档列出来”、“我上周看过的那几个东西是什么”。这两个搜索工具都必须给一个匹配字符串,没有关键词就无从下手。
这时候该换 ListEntities。它的两个参数是:
includeTypes(array):过滤到指定的条目类型。文档给的例子是["document", "email"],表示只返回文档和邮件。sortBy(string):排序方式,可选recently_viewed(默认)、recently_updated、recently_created。
注意一个容易踩的地方:ListEntities 的类型值在文档示例里是单数形式(document、email),而两个搜索工具的 entityTypes 示例是复数形式(documents、emails)。文档里就是这么写的,两边不一致。写脚本的时候按各自工具页上的示例来,别把一边的写法照搬到另一边。
sortBy 的三个值差别也别混:recently_viewed 是按你最近看过的排,recently_updated 是按最近被改动的排,recently_created 是按创建时间排。找”我刚才那个东西”用第一个,找”团队最近在动什么”用第二个。
这些工具文档没告诉你的事
老实说,这两个工具页的信息量非常有限。以下几项,官方文档里一个字都没写:
- 返回什么。没有返回值 schema,没有示例响应。条目里有没有 id、有没有摘要片段、有没有高亮,全都不知道,只能实际调一次看。
- 匹配规则。是全词匹配、前缀匹配还是模糊匹配,有没有分词,中文怎么切,文档没说。
- 结果条数与分页。没有 limit 参数,也没有 offset 或者游标,返回多少条由服务端定,文档未说明。
- 排序。搜索结果按相关度还是按时间排,没写。
- 权限边界。搜索会不会跨出你有权限的范围,工具页没有提。
有一点可以对照着看:Macro 的产品端搜索文档写到,多关键词搜索时用引号可以做精确匹配,否则默认是各个词独立匹配;产品端还支持按标签筛选,选中两个以上标签后可以在 Any(命中任一标签)和 All(命中全部标签)之间切换。但这些描述属于产品界面里的统一搜索,MCP 工具页上并没有说 ContentSearch 的 query 也吃这套引号语法或标签筛选。想当然地把界面搜索的语法套到 MCP 参数上,是个很容易犯的错。产品端搜索本身的用法,可以看统一搜索怎么用那篇。
什么时候这套工具不合适
几个明确不适用的场景:
需要稳定的结构化查询时。 这两个工具本质上是关键词入口,没有条件表达式,不能按”创建时间在某区间且属性等于某值”这类条件筛。要按结构化字段捞数据,方向应该是实体和属性那条线,不是搜索。
需要精确控制返回量时。 没有分页参数意味着你没法保证一次调用的返回规模,写在自动化流程里会有不确定性。真要控量,思路是把 entityTypes 收到最窄,而不是指望参数。
搜刚刚写完的东西时。 索引更新有没有延迟、多久生效,文档没有任何说明。刚创建完一个文档就马上去搜它,搜不到不代表创建失败,可能只是索引还没跟上——这种情况下别让 agent 陷进”没搜到就重建一份”的循环里。
跨账号或跨团队找东西时。 MCP 连接走的是 OAuth 登录的那个身份,能搜到什么范围取决于这个身份的权限,工具参数里没有任何切换范围的选项。
要把这两个工具用顺,说到底就三件事:先分清你手上的线索是”名称”还是”正文”,再把 entityTypes 收到最窄,最后老老实实走”搜索拿 id、读取拿内容”两步。MCP 服务本身怎么接、连不上怎么排查,可以看Macro 的 MCP 服务能做什么。
延伸阅读
- 从头读起:Macro 是什么:邮件、任务、文档、CRM 共用一个双向数据库的开源工作区
- 本专题共 40 篇,完整分组目录见专题页
- Macro MCP 内容类工具怎么用:ReadContent、ReadMetadata 与 CreateDocument 的参数与陷阱
- Macro MCP 的会话与邮件四工具:读会话、发邮件、改标签各要什么参数
本文依据 Macro 官方仓库(github.com/macro-inc/macro,AGPL-3.0 协议)的 apps/docs/ 产品文档、
MCP 工具参考与自托管说明整理,核对日 2026-08-17。
我们没有注册或运行过 Macro,因此不涉及界面外观与操作手感;
官方标注为计划中的能力文中已如实标明,不代表当前可用。
价格与额度以官网 macro.com 最新页面为准;许可证相关问题请咨询专业人士并以官方许可证原文为准。