mcpField
M
mcp-tool-discovery

mcp-tool-discovery

github.com/serac-labsAgent 工作流7774 次安装9 周前更新
代码仓库

关于 mcp-tool-discovery

面向使用 ServiceNow MCP 服务的 Agent,讲解如何从搜索工具到成功调用。重点说明按会话启用延迟工具、搜索匹配规则、调用方式及常见失败原因。

主要功能

  1. 01搜索并启用延迟工具
  2. 02解释工具可用状态
  3. 03说明会话 ID 与持久化
  4. 04解析搜索排序与查询方法
  5. 05排查调用失败及安全标志

适用场景

  1. 01首次发现并调用 ServiceNow 工具
  2. 02诊断工具仍处于延迟状态
  3. 03按领域或表名定位工具
  4. 04检查执行结果与调用配置

MCP 相关

内容针对 ServiceNow MCP 服务,说明其工具搜索与执行元工具,以及 stdio 和 HTTP 传输下工具发现、启用和调用的差异。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/serac-labs/serac --skill mcp-tool-discovery

安装后在 Claude Code 中输入/mcp-tool-discovery确认已加载。

详细介绍

MCP 工具发现

此服务器在 80 个领域中注册了 440 多个 snow_* 工具,却不列出任何一个。全新的工具列表中 只有两个元工具;其他所有工具都需要通过 tool_search 查找。本指南将介绍如何从“这里 大概有个工具可以做这件事”找到真正能运行的调用。

这里不引用确切总数,因为有两个总数,而且彼此不一致: packages/servicenow-mcp/tools.json 是文档站点发布的内容,注册表是运行中的服务器实际可以分发的工具,而已发布的工具中有 9 个不在注册表中(参见“你可以在文档中找到、但服务器上没有的九个工具”)。

冷启动:两个工具

通过 stdio 传输方式新建一次 tools/list,无论存在多少工具,返回的内容都只有下面这些:

tool_search
tool_execute

这是有意为之。服务器自己的说明指出,完整目录的 schema 约占 71k 个 token,而两个元工具约占 2k 个 token。索引会将所有工具标记为延迟加载,而 handlers/list-tools.ts 只返回元工具,以及当前会话已启用的工具。

HTTP 传输(门户)则恰恰相反:它会将目录注册为非延迟加载,因此其 tools/list 会返回所有工具,无需启用任何工具。你可以从 tool_search 结果的状态判断自己使用的是哪种传输:[AVAILABLE] 表示服务器完全不限制已启用工具集。

循环

// 1. Search. Found tools are enabled for this session by default.
tool_search({ query: "update-sets" })

// 2. Read the status on each hit:
//    [AVAILABLE] — callable now, nothing had to be enabled
//    [ENABLED]   — deferred, and enabled for this session. Callable now.
//    [DEFERRED]  — NOT callable. Nothing was enabled; see the next section.

// 3. Execute.
tool_execute({ tool: "snow_ensure_active_update_set", args: { name: "Feature: SLA fix" } })

如果只想查看而不更改会话状态,请传入 enable: false;这样会执行搜索,但不会更改会话状态。

没有会话 ID,就没有工具

这是会让你白忙一下午的问题。工具启用状态按 (tenant, session) 保存。没有会话 ID,就没有可以关联启用状态的对象,因此 tool_search 不会启用任何工具,所有命中结果都会保持为 [DEFERRED],所有调用都会被拒绝:

⚠ 5 deferred tool(s) were NOT enabled: this request carries no session id, so there is nothing to
attach the enablement to. They stay [DEFERRED] and tool_execute will refuse them.
{
  "success": false,
  "error": "Tool \"snow_query_table\" is [DEFERRED] and must be enabled first",
  "hint": "Use tool_search({query: \"query table\"}) to enable this tool",
  "status": "[DEFERRED]"
}

再次搜索也无济于事。会话 ID 按以下顺序解析:JWT 负载中的 sessionId、x-session-id 标头、SNOW_SESSION_ID 环境变量,最后是 Serac CLI 写入的 current-session.json 文件。使用纯 stdio 时,前两种方式都不存在,而该文件也只有在这台机器上运行过 Serac TUI 时才会存在,因此自行启动服务器的客户端(包括 Claude Code 插件的 npx … servicenow-mcp-stdio)都没有会话 ID,除非你为其指定一个:

{
  "mcpServers": {
    "servicenow": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "--package=@serac-labs/servicenow-mcp", "servicenow-mcp-stdio"],
      "env": { "SNOW_SESSION_ID": "my-session" }
    }
  }
}

插件在 packages/skills/.mcp.json 中提供的就是这个配置项,另加了一个 env。编辑时请保留 command 和 args;仅包含 env 的配置项无法启动任何程序。任何稳定字符串都可以用作 ID。请注意以下两点:

  • 已启用工具集会持久化到磁盘,按会话分别保存,并在重启时恢复:macOS 上的 ~/Library/Application Support/snow-code/enabled-tools/stdio/<session>.json、 Linux 上的 ~/.local/share/snow-code/enabled-tools/、Windows 上的 %APPDATA%\snow-code\。该目录仍名为 snow-code,是因为它沿用了重命名前的名称。复用相同的 SNOW_SESSION_ID,昨天启用的工具就会继续保持启用;删除该文件即可重新开始。
  • HTTP 传输中的存储位于内存中,并按租户分别保存;无法归入任何租户的请求会被拒绝,而不会被放入共享池中。此时提示中会显示 no tenant scope。

按名称调用,以及为什么 tool_execute 更安全

启用工具后,服务端会按名称分派工具——handlers/call-tool.ts 会直接接受 snow_query_table,而 Agent 指令也会告诉你这样做。

但服务端不会通知客户端工具列表有所增加。它声明 capabilities: { tools: {} }, 没有 listChanged,也从不发送 notifications/tools/list_changed (https://modelcontextprotocol.io/specification/2025-06-18/server/tools)。根据规范,该通知会促使客户端重新请求 tools/list。因此,新启用的工具是否会作为可调用工具出现在你的上下文中,完全取决于客户端是否会自行重新列出工具。

tool_execute 在冷启动列表中,因此始终可用。搜索后的首次调用请使用它; 按名称调用只是为了方便,并非保证可用。

查看完整封装,而不只是外层标志

tool_execute 报告的是是否已分派,而非操作是否成功。工具自身的判定嵌套在里面:

{
  "success": true,
  "tool": "snow_query_table",
  "result": { "success": false, "error": "undefined is not an object (evaluating 'context.instanceUrl.replace')" }
}

外层 success: true、内层 success: false 表示操作失败。检查 result.success。

这个特定的内部错误并非工具的缺陷——未配置任何实例时,每个工具都会返回此错误。SNOW_INSTANCE 尚未设置;请设置它,而不是查看执行器。

将防护标志放在 args 中

针对生产级实例的写入,以及没有活动更新集时的配置写入,都会在工具层被拦截;无论通过 tool_execute 还是按名称调用,都是如此。重试标志 __confirmProd 和 __skipUpdateSet 必须放在 args 对象内部,而不是与其并列——它们会从内部参数中读取,并在执行器收到参数前移除:

tool_execute({
  tool: "snow_create_business_rule",
  args: { table: "incident", name: "Auto-assign", __confirmProd: true },
})

查询:哪些内容会匹配,哪些会悄无声息地落空

索引中,每个工具都包含去掉 snow_ 并按 _ 拆分后的名称(保留长度超过两个字符的部分),以及描述中最前面的十个重要词。如果某个用于区分的名词出现在长描述的后面,就无法通过关键词找到它。

评分按完整单词计算,而不是按子串计算。你的查询会按与索引相同的方式拆分,去掉英文停用词,将常规复数还原为单数,并根据每个剩余单词在目录中的稀有程度加权:一半工具都包含的词几乎不会影响评分,而只有三个工具包含的词则能起决定作用。随后,评分会乘以匹配到任何内容的查询词所占比例,因此,能满足请求三分之二的工具会明显胜过只能满足三分之一的工具。即使某个词只是索引词元的前缀,也会以较低权重计入匹配——这就是 "syntax" 能找到描述中写着 "SyntaxErrors" 的工具的原因。

**领域名称是最好的查询词。**它们能找到一整组相关工具,且不会匹配其他内容:

tool_search({ query: "update-sets" })  // all three: snow_update_set_query, snow_ensure_active_update_set, snow_update_set_manage
tool_search({ query: "blast-radius" }) // the whole impact-analysis family
tool_search({ query: "local-sync" })   // the pull/push/status/cleanup family
tool_search({ query: "atf" })          // test, test step, test suite, discover, execute, results

领域名称就是 packages/servicenow-mcp/src/servicenow-mcp-unified/tools/ 下的目录名称,但有几个不是领域:__tests__ 是测试,meta 存放 tool_search 和 tool_execute 本身,并且刻意排除在注册表之外;adapters 是空占位目录。其他目录都可以搜索。

limit 默认为 10,有七个领域的工具数量超过这个上限:security、integration、automation、operations、agile、cmdb、ui-builder。无需记住各领域的确切数量——每次有新工具加入都会变化——因此,如果一次查询恰好返回 10 个结果,就应将其视为结果已截断并调高 limit。

表名也有效,因为表名由单词组成。sys_script_include 会拆分为 sys、script、include,并根据其中罕见的词进行评分:

查询首个结果
"sys_script_include"snow_create_script_include
"sysevent_email_action"snow_email_notification_manage
"sp_widget"snow_create_sp_widget
"sys_user"snow_impersonate_user — 四者中最弱,因为 user 无处不在

最后一行展示了剩余问题的典型情况:如果表名中用于区分的部分是目录里频繁出现的词,结果就会充斥着看似合理的噪声。改为描述操作——"deactivate a user" 能找到 snow_user_manage。

**普通英语也有效,通常效果更好。**排名器使用 101 条符合实际表达习惯的请求进行评估(tool-search-eval.test.ts);其中三分之二能在前五个结果中找到正确的工具。它还了解少量目录未收录、但使用者会用到的词汇——搜索“ticket”也会查找 incidents,搜索“column”也会查找 fields,询问谁在“shift”也会找到 on-call 工具。

如果没有任何匹配结果,响应中会附带一个 suggestion,列出“可用领域”。该列表是按字母顺序排列的前十五个领域名称,范围从 access-control 到 calculators。它既不是完整领域列表,也不是按相关性排序的建议——不要据此认为 operations 或 update-sets 不存在。

索引实际返回的内容

随附索引中的热门结果,已针对当前检出版本重新运行。评分采用稳定排序,因此分数相同的工具会按注册顺序返回;每当有工具加入,确切排名都会变化。关注结果中有哪些工具,而不是它们的位置:

查询热门结果
"query table"snow_query_table、snow_validate_query、snow_graphql_query、snow_record_manage
"incident"snow_analyze_incident、snow_auto_resolve_incident、snow_create_security_incident
"cmdb"snow_cmdb_identify_reconcile、snow_search_cmdb、snow_cmdb_search
"business rule"snow_create_business_rule、snow_disable_business_rule
"attachment"snow_get_attachments、snow_delete_attachment、snow_upload_attachment
"acl"snow_acl_explain、snow_create_acl_role、snow_create_acl — 以及会捏造答案的 snow_test_acl,详见下文
"flow"snow_start_workflow、snow_workflow_manage、snow_manage_flow
"jira"snow_create_oauth_profile、snow_install_spoke — 详见最后一节

注意,"incident" 不会返回你想要的事故 CRUD 工具。该工具是 snow_record_manage ({ action, table: "incident", … }),可通过查询 "record" 或 "query table" 找到。还要注意 snow_search_cmdb 和 snow_cmdb_search——这是两个属于不同领域、功能几乎相同的不同工具。重复的名称在这里很常见;选择前请阅读两者的描述。

排名代表相关性,而非质量

tool_search 按字符串匹配进行排名。它不知道工具是否能正常工作,而且此目录有两种不同的误导方式。两种情况都需要防范,因为针对一种情况的防范措施无法识别另一种。

第一种:它根本不会联系实例。 执行器根据你传入的参数组装一个成功对象并返回。对于明显的查询,排名靠前的结果中有两个属于这种情况:

  • tool_search({ query: "artifact" }) 会将 snow_clone_instance_artifact 排在第一位。它根本不会建立连接。它会返回 success,其中 "message": "Cross-instance cloning requires additional authentication setup",并附带一系列步骤,提到 snow_export_artifact 和 snow_import_artifact——这两个工具在此服务上都不存在。实例间提升请使用更新集;本地往返则使用 snow_pull_artifact / snow_push_artifact。
  • tool_search({ query: "incident" }) 会将 snow_create_security_incident 排在第三位。这是一个尚未实现的存根:它会验证并回显你的参数,并返回 "summary": "Security incident \"…\" prepared with priority: …"。没有任何内容会发送到实例——请注意其中的单词 "prepared"。security 域中的全部六个 snow_create_* 工具都是同类存根。能正常工作的 SIR 工具属于 snow_sir_* 系列(snow_sir_incident_manage 等工具会读取和写入 sn_si_incident),而直接针对该表调用 snow_record_manage 始终有效。

最能说明为什么不能把描述当作证据的例子是:security 中的 snow_create_access_control 声称“会写入 sys_security_acl”,实际上却不会建立连接。真正会向 /api/now/table/sys_security_acl 发送 POST 请求的是 access-control 中的 snow_create_acl。

第二种:它联系了实例,却提出了错误的问题。 一批规模更大的工具会向 /api/now/table/sys_script_execution 发送 POST 请求,并把 Table API 对刚插入行的回显当成脚本的输出。Table API 会插入记录,不会执行脚本。因此什么都不会执行,而且你在第一种情况中学到的所有检查都会通过:确实发出了 HTTP 调用,确实返回了 201,确实有 sys_id,而且 result.success 为 true。 snow_test_acl 是上面 "acl" 搜索结果中的热门项之一,也是最值得了解的工具。它返回 has_access: response.data.result——其中的 result 是插入的行,每次调用都会返回一个真值对象——因此 它无条件报告有访问权限,从不评估 ACL。它会忽略你提供的 operation 和 user。处理 ACL 时,请使用 snow_acl_explain 查看哪些规则匹配以及用户持有哪些角色,并使用 snow_create_acl 写入规则;至于实际判定,请在 UI 中模拟用户,因为平台会针对一条特定记录判定 ACL,而 Table API 中没有任何工具会执行这项操作。

这个工具簇中的工具并非全都在编造结果——有些工具在回显没有输出时会抛出错误,这种失败明确而诚实。关键始终在于:响应描述的是你询问的对象,还是你提交的请求。

两个零成本的习惯:

  • 优先选择描述中注明了表名或 API 端点的工具,然后确认这一点,因为仅凭描述无法证明什么。含糊不清的描述(“处理身份验证、依赖项解析和数据迁移”)是一个可靠的警示信号;注明表名只是起点,并非保证。
  • 对于你关心的任何写入操作,都使用 snow_query_table 或 snow_record_manage 读回结果——并且读回你想要更改的对象,以及你想要更改它所在的表。响应中的 sys_id 本身并不能作为证据:sys_script_execution 工具会返回一个完全可以重新查询的 sys_id,但它属于你从未要求创建的行。如果你创建了业务规则,证明应当是 sys_script 中存在一行,而不是成功查询到了返回的某个 ID。

供参考,可靠的核心工具包括:用于记录的 snow_query_table 和 snow_record_manage,用于工件的 snow_artifact_manage 和 snow_pull_artifact / snow_push_artifact,用于变更跟踪的 snow_update_set_manage / snow_ensure_active_update_set,用于确认你的身份验证身份的 snow_session_context,以及用于排查看似毫无缘由的调用失败的 snow_diagnose_setup。

搜索结果是摘要,不是架构

每条结果都会经过截断:描述最多显示 200 个字符,参数最多显示五个,每个参数的描述最多显示 80 个字符。has_more_params: true 表示还有更多参数,搜索响应不会显示它们。snow_artifact_manage 有 56 个参数;你只能看到五个。

若要查看完整架构,请启用工具并让客户端重新列出工具(tools/list 包含完整的 inputSchema),或者调用工具并查看验证错误。不要因为 tool_search 没有显示某个参数,就推断该参数不存在。

文档列表与服务端列表会相互校验

tools.json——文档站点和门户所呈现的清单——是通过遍历工具文件生成的。 运行中的服务会从 shared/tool-registry.ts 中硬编码的域映射构建注册表。这是对同一目录树的两种不同读取方式,它们曾经出现偏差:九个工具已发布但无法访问(位于 tools/fsm/、tools/predictive-intelligence/ 的工具以及 snow_create_catalog_variable,它们均未加入映射或重新导出),而 snow_comprehensive_search 则恰好相反——可以调用,却未列入清单。这是 serac-labs/serac#307 的问题,现已修复;如果再次发生,packages/servicenow-mcp/src/__tests__/registry-manifest-parity.test.ts 会检测任一方向的偏差并失败。

因此,tools.json 中的名称就是可以调用的名称。如果工具回复

{
  "success": false,
  "error": "Tool not found: snow_something",
  "suggestion": "Use tool_search to find available tools"
}

说明名称有误,而不是工具缺失——请对照 tools.json 检查名称,而不是重新搜索。

清单中有一类条目虽然列出,但不应调用:标记为 "deprecated": true,且描述以 [DEPRECATED - use <replacement>] 开头的条目。snow_comprehensive_search 就是其中之一——它是 snow_search_artifacts 的向后兼容别名。请改用描述中指定的替代工具。

两个名不副实的环境变量

  • SNOW_LAZY_TOOLS=false 的文档称其会在启动时加载整个目录。但在 stdio 上并非如此: 无论该变量的值如何,传输层都会构建索引,并将所有工具标记为延迟加载,因此 tools/list 仍只会返回 tool_search 和 tool_execute。启动日志却声称并非如此。 无论如何,都应通过 tool_search 发现工具。
  • SNOW_TOOL_DOMAINS=cmdb,operations 只会筛选 tools/list。tool_execute 完全不会读取它,因此 即使某个工具属于排除的域,只要已启用,仍然会运行。请将其视为显示筛选器,而非 限制。

企业集成属于另一套服务

jira_*、azdo_*、confluence_*、github_* 和 gitlab_* 都是真实存在的,但不在这里。此服务注册表中的每个工具 都以 snow_ 开头——tool_search 和 tool_execute 除外:它们根本不是注册表工具,而是附加在旁边的两个元工具。集成位于 独立二进制文件 servicenow-mcp-enterprise-proxy 后面;该文件从 Serac 许可证服务器获取目录,并公开自己的一对元工具—— enterprise_tool_search 和 enterprise_tool_execute,其前缀经过精确设计,因此不会与此服务上的两个工具 冲突。

因此,tool_search({ query: "jira" }) 是错误的调用,并非因为这些工具不存在:在此 服务上,它会返回 snow_create_oauth_profile 和 snow_install_spoke。如果你的工具列表中没有 enterprise_tool_search,则说明该服务未为此会话配置——插件的 .mcp.json 只会启动 servicenow-mcp-stdio——并且任何查询都无法触达那些工具。

发现过程应保持静默

不要叙述过程。不要说“让我先启用工具”或“我正在搜索合适的工具”——直接搜索、 启用、调用并报告结果。如果调用失败,就换个词再次搜索,不要出声。

问题不在发现时

如果工具能够解析并启用,但每次调用都失败——出现 401、应为 JSON 的地方返回 HTML,或提示“不是有效的 JSON”—— 就停止搜索并调用 snow_diagnose_setup。它会报告使用了哪个凭据来源、实例是否处于唤醒状态,以及 OAuth 交换是否成功。休眠中的开发者实例会返回 HTML 登录页面,导致每个工具看起来都像是存在解析错误。

症状原因
每次命中都是 [DEFERRED],没有任何工具启用缺少会话 ID——设置 SNOW_SESSION_ID
Tool "x" is [DEFERRED] and must be enabled first此会话中尚未启用;先用 tool_search 搜索它
成功查阅文档后出现 Tool not found: x已发布但从未注册——参见 serac-labs/serac#307
No tools found matching "sys_..."查询的是表名;请改为搜索操作
已启用的工具从未出现在客户端的工具列表中没有 listChanged 通知——请使用 tool_execute
success: true 但实例上没有任何变化读取 result.success;如果它也为 true,请在其自身的表上重新查询该工件——该工具可能只是存根,或是 sys_script_execution 回显
工具通过 HTTP 调用时拒绝运行,但在本地可用传输层允许列表——该工具会访问本地文件系统

评论

支持 Markdown