momentic-test

momentic-test

创建、运行和维护 Momentic E2E 测试和模块,这些测试和模块会序列化到磁盘上的 *.test.yaml 和 *.module.yaml 文件中。Momentic 使用快速、准确的 AI 代理来自动化浏览器交互,以测试 Web 应用程序。

12Star
0Fork
更新于 2026/6/23
SKILL.md
只读
名称
momentic-test
描述

创建、运行和维护 Momentic E2E 测试和模块,这些测试和模块会序列化到磁盘上的 *.test.yaml 和 *.module.yaml 文件中。Momentic 使用快速、准确的 AI 代理来自动化浏览器交互,以测试 Web 应用程序。

Momentic 背景

执行模型

Momentic 是一个端到端测试框架。测试是使用 Playwright 和 CDP 执行的结构化步骤的有序列表。

  • 交互式步骤(如点击和输入)使用 AI 将自然语言目标解析为具体的浏览器操作。
  • 断言步骤可以使用多模态模型评估页面状态。
  • 基于目标的 AI 操作可以执行更广泛的任务,例如“使用信用卡结账”。

缓存和记忆

Momentic 缓存已解析的步骤元数据,如选择器、XPath、可见文本和坐标,因此大多数运行避免了重复的 AI 调用。这对速度至关重要,但过时的缓存是一个真实的调试可能性:步骤可能点击到错误的元素。AI 断言也可能使用过去的结果记忆来保持跨运行的一致性;错误的记忆可能解释重复的边缘失败。

缓存范围由 git 元数据(包括分支)限定。在受保护的分支(包括配置的主分支)上跳过缓存写入,除非使用 --save-cache 或设置了 CI 环境变量强制保存缓存。在受保护的分支上仍然可以读取缓存。使用 --disable-cache 完全绕过缓存。

强制刷新行为的方法:

  • 当意图改变时,更改步骤描述/断言;这会更改用于缓存匹配的步骤标识。在 v1 中,拼接更改后的步骤也会创建新的内部 UUID。
  • 对每次运行都应动态解析的目标使用 --disable-cache,例如“今天日期的日历单元格”。
  • 通过使用 --cache-id <CacheId> 将良好的预览缓存携带到拼接中。
  • 当先前的 AI 记忆现在具有误导性时,更改断言措辞并添加消歧。

时序

Momentic 在定位步骤之前使用“智能等待”。它会等待最多配置的智能等待超时时间(默认为 5 秒),让页面状态稳定或所需元素出现。在此窗口内,不要添加手动休眠或等待。对于较慢或更语义化的就绪状态,请使用 waitForUrl、文本/元素检查或 AI 断言。

设置优先级

momentic.config.yaml 设置项目默认值,但许多设置可以在测试级别覆盖:浏览器类型、视口、语言环境/时区、地理位置、页面加载超时、智能等待超时、代理、标头、身份验证、扩展等。在假设项目默认值适用之前,始终检查测试自身的元数据。

测试上下文

每次运行都有一个测试范围的 env 上下文,该上下文在步骤(包括模块)之间持久存在。后续步骤可以读取先前步骤写入的值。

  • v2:在具有返回值的步骤上使用 saveAs
  • v1/MCP CLI 字符串:使用 --env-key
  • JavaScript:优先使用 returnsaveAs / --env-key;在设置多个变量时使用 setVariable(name, value)
  • 在 JavaScript 和模块输入表达式中使用 env.NAME
  • 在字符串字段中使用 {{ env.NAME }}{{ ... }} 可以评估 JavaScript,但不要在 JavaScript 步骤源代码中使用它,因为 env 已经在该作用域中。

模块输入是作为字符串的 JavaScript 片段。引用字符串字面量,并使用 env.X 表示变量;它们不是 {{ }} 模板。

JavaScript 上下文

JavaScript 步骤可以在 NODEBROWSER 中运行。

  • NODE:默认用于 API 调用、数据准备、OTP/电子邮件/SMS、数据库查询和变量写入。它具有 Momentic 全局变量和预加载的库。envsetVariableemailsmsaxiosassertfakermomentpgOTPAuthchild_process 等。有关更多详细信息,请查看 JavaScript 命令文档或步骤编写指南。
  • BROWSER:在页面中运行,具有 window / document 和页面全局变量。它没有仅限 Node 的 Momentic 助手。

将简短的一次性 JavaScript 保持内联。在 v2 YAML 中,可重用的实用程序和长脚本可以放在项目脚本文件中,遵循现有项目约定。优先选择诸如 ./scripts/page-utilities/auth-loader.js 之类的位置。首先阅读附近的脚本,并匹配它们的模块风格、助手命名、env 使用和错误风格。

磁盘上的项目状态

测试是 *.test.yaml 文件。模块是可重用的步骤集合,存储为 *.module.yaml 文件。测试 ID 是权威的,位于测试文件的 id 字段中。

有两种主要的文件格式:

  • fileType: momentic/test/v2fileType: momentic/module/v2 -> v2。对于高置信度的更改,直接编辑 YAML 是首选,因为它更快。
  • 缺少 fileType 或任何其他值 -> v1。切勿直接编辑 v1 YAML;仅通过 momentic_test_splice_steps 持久化更改。

momentic.config.yaml 是项目根配置文件。它存储代理、AI 功能、浏览器选项、录制、超时、浏览器类型、文件 glob 和环境的项目默认值。请参阅 https://momentic.ai/docs/configuration/momentic-config.md。

v2 步骤可以通过相对路径引用本地文件:

  • 模块调用:path: ./modules/login.module.yaml
  • JavaScript 步骤:javascript: ./scripts/setup.js
  • 身份验证状态:authLoad: ./auth-state.jsonauthSave: ./auth-state.json

相对路径从包含步骤的 YAML 文件解析,而不是从项目根目录或导入测试解析。使用 ./...../...;不要使用绝对路径或 ~。如果移动、重命名或删除引用的文件,请搜索旧路径并更新每个引用。

v1 YAML 仍应仅通过 MCP 编辑。不要在 v1 YAML 或 MCP CLI 步骤字符串中使用 codeFile;v1 JavaScript 步骤应在 code / --code 中包含可执行代码。仅在 v2 YAML 中使用 JavaScript 文件引用。

不要向 v2 YAML 添加内部或自动生成的字段。

编辑之前

只收集你需要的内容:

  • 测试目标和用户可见的成功标准。
  • 起始点:baseUrl 或命名环境。
  • 身份验证要求和所需的环境变量。
  • 不得运行两次的危险操作:提交、购买、删除、发送、创建。

对于长任务,在编写之前检查附近的测试和模块。重用现有模块通常比内联重建常见流程更好。

在长时间运行的检查、从头开始、破坏性操作或编辑共享模块之前先询问。

选择工作流程

如果用户请求特定的工作流程,除非不安全或不可能,否则请尊重它。否则,当文件是 v2、更改是局部的、步骤序列已知且不需要实时 UI 发现时,使用直接的 v2 YAML 编辑。好的示例:从附近的模式搭建、重新措辞断言、调整目标、更新环境键、修复文件引用或插入一个小的已知步骤。

当文件是 v1 或未知、必须发现 UI 状态、定位器时序不稳定、流程是多步骤且不明确,或者用户要求交互式构建/验证(“有头模式”)时,使用 MCP 浏览器验证工作流程。

对于新测试,使用 momentic_test_create;如果该工具不可见,请搜索它。它需要 name 以及 baseUrlenvironment。仅在请求时传递目标字段,如 pathSegmentsmomentic_session_start 需要现有的 testId;它不会创建测试。

通用编写规则

  • 优先使用自然语言元素描述。仅作为最后手段使用选择器或坐标,用于 AI 无法看到的情况,例如 SVG 内部、画布或用户请求的选择器级别目标。
  • 优先使用原生 Momentic 步骤而不是 JavaScript。仅在没有原生步骤表达行为时才使用 JS。JS 步骤可以在浏览器(客户端)或 Node(服务器端)中运行。
  • 不要在开始时添加导航。会话从测试的基本 URL 开始。
  • 保持断言最小化且由用户驱动。仅在需要使下一个依赖操作可靠时才添加就绪检查。
  • 在应导航或实质性更改状态的点击/操作之后,在依赖操作之前立即添加验证。对于 URL 契约,优先使用 waitForUrl;对于稳定的文本或元素,使用 checkPageContains / checkElement...;对于语义视觉状态,使用 assert
  • 除非用户要求或现有测试已经使用,否则不要使用 AI 操作(actAI_ACTIONAI_ACTION_DYNAMIC)。
  • 除非需要正确性,否则不要添加可选/默认字段。
  • 保持增量小。保留不相关的参数、请求体、环境键、字面量值、引号、注释、顺序和步骤风格。
  • 不要绕过真正的应用程序故障。如果应用程序损坏、数据丢失或后端宕机,请报告故障而不是削弱测试。
  • 除非测试意图要求,否则不要重新组织 before / steps / after 或 setup / main / teardown。

使用 v2 YAML

v2 是人类可编辑的格式。步骤紧凑:每个步骤有一个顶级命令键,例如 click: Submit,或该键下的详细映射。测试使用 before / steps / after;模块使用 steps。持续时间始终以毫秒为单位。没有可见的步骤/命令 ID。

直接编辑循环:

  1. 对于新测试,使用 momentic_test_create 创建它,然后一次性编辑 YAML,而不是通过 MCP 逐个添加已知步骤。
  2. 进行最小的 YAML 编辑。保留风格。如果不确定语法,请获取并阅读 https://static.momentic.ai/v2-format-reference.md 中所需的内容。
  3. 仅在用户要求、风险需要或更改需要实时确认时才进行验证。

常见错误:

  • 将选项放在命令旁边而不是嵌套在命令下。
  • 为命令使用错误的目标字段名称。

npx momentic lint 验证 v2 模式和文件引用。Lint 在 momentic appmomentic run 之前自动运行;如果在编辑后或移动/重命名引用的文件后不确定语法,请手动运行它。

磁盘编辑后的状态刷新:

  • 没有活动的 MCP 会话:准备验证时启动一个新会话。
  • 活动会话加上 momentic_test_reload:在 momentic_run_step 之前重新加载。
  • 活动会话且没有重新加载工具:终止并重新启动会话。
  • momentic_test_get 检查持久化状态;除非工具明确说明,否则它不会刷新活动会话。

MCP 浏览器验证工作流程

对每个 v1 编辑以及需要实时发现的 v2 工作使用此工作流程。工具表面是共享的;持久化不同:v1 使用拼接,而 v2 可以使用拼接或直接 YAML 编辑加重新加载。

发现

  • momentic_get_artifacts():项目上下文、配置路径、cwd 以及测试、模块、环境等的工件文件。只读取你需要的内容。
  • momentic_test_get({ testId | testPath }):检查持久化的测试状态。在会话之前,这很有用。在拼接后的活动会话期间,优先使用拼接响应或 returnTest: true
  • momentic_module_recommend({ userRequest }):查找可重用的流程。
  • momentic_module_get({ selector }):检查模块参数、默认值、枚举和步骤。选择器恰好是 { id }{ name }{ path } 之一。

会话

  • momentic_session_start({ testId, ... }):启动浏览器会话。它返回元数据、步骤编写指南工件和带有活动会话步骤 ID 的测试内容。必需:testId。单独调用它,不要与其他 MCP 工具并行。选项包括环境/配置/项目覆盖、有头模式和视频。
  • 在构建 CLI 风格步骤之前阅读步骤编写指南。
  • momentic_run_step({ sessionId, fromStep, toStep?, targetSection?, resetSession? }):运行现有的活动会话步骤。使用来自测试内容或拼接响应的步骤 ID,切勿使用原始 YAML。对于顶级步骤,使用 parentStepIdChain: []切勿通过此 MCP 工具运行包含 AI 操作的范围。 MCP 工具调用上限为 60 秒,而 AI 操作通常远远超过该时间,并在运行中途被取消——运行被终止,其工作丢失。始终在终端中使用 momentic run-step CLI 执行此类范围(请参阅“执行长时间运行的 AI 操作步骤”),该 CLI 没有此类上限。
  • 如果状态漂移,使用 momentic_run_stepresetSession: true 在同一个 sessionId 上重新启动;不要在每次微编辑之间重置。
  • momentic_session_terminate({ sessionId }):完成后终止。如果使用 video: true 启动,响应包括视频目录。

测试编写循环

以检查点大小的块编写 MCP 步骤。向前预览直到逻辑部分工作,然后拼接该检查点。好的检查点是自然流程边界,例如登录完成、表单提交、页面删除等。避免一次拼接一个步骤,除非它非常危险且非幂等。

  • momentic_preview_step({ sessionId, step }):在浏览器中执行一个步骤而不持久化。如果它返回 CacheId,则在拼接该步骤时包含 --cache-id <CacheId>。响应截图显示步骤后的页面状态。切勿通过此 MCP 工具预览 AI 操作——它将在 60 秒工具调用上限时被取消,其工作丢失。始终在终端中使用 momentic preview-step CLI 预览 AI 操作(请参阅“执行长时间运行的 AI 操作步骤”)。
  • 如果预览截图不足以定位,调用 momentic_get_session_state 并设置 returnBrowserState: true,然后检查工件以获取稳定的名称、角色、可见文本和突出结构,以帮助制定可靠的描述。由于浏览器状态很大,请谨慎使用。
  • 当接下来的几个步骤显而易见且风险较低时,例如填写已知表单字段,将它们拼接在一起并运行该保存的范围,而不是逐个预览每个字段。
  • momentic_test_splice_steps({ sessionId, startIndex, deleteCount, steps, targetSection?, parentStepIdChain?, returnTest? }):插入、替换或删除步骤并持久化。
  • 拼接后,立即读取响应;它是插入/删除的引用和活动会话步骤 ID 的真实来源。
  • 如果 returnTest: true,在继续之前验证返回的结构。
  • 如果下游步骤仍然存在,运行紧接的下一个步骤以确认流程仍然连接。
  • 对于非幂等操作,如提交、购买、删除、发送等,避免重复预览。预览设置步骤,在危险操作之前拼接检查点,然后仅在验证需要时执行一次保存的危险步骤。
  • 对于明显的相邻低风险步骤,批量处理;除非定位器或页面状态不确定,否则不要在每次字段后设置检查点。
  • 当请求的编辑完成时,询问是否通过使用 momentic_run_step 运行相关的保存范围从头开始验证。

读取工具输出

会话是活动的浏览器进程。截图和浏览器状态是即时快照。如果截图在操作后未显示预期状态,再调用一次 momentic_get_session_state;页面可能仍在加载。

MCP 工具可能返回 .momentic-mcp/... 下的工件链接。仅在需要时读取链接的文件:

  • UI 状态文本:优化定位或调试结构。
  • 截图:通常已经以内联图像形式返回。
  • 环境文件:验证 envKey、JavaScript/API 输出或依赖的环境值。
  • momentic_get_session_state 仅在设置 returnBrowserState: true 时返回序列化的 UI 状态;默认返回截图。

CLI 风格步骤字符串

preview_stepsplice_steps 使用 CLI 风格字符串:--step-type <TYPE> [options],例如 CLICKTYPENAVIGATEAI_ASSERTIONMODULEWAIT_FOR_URL

示例:

  • 导航:--step-type NAVIGATE --url "https://example.com"
  • 点击:--step-type CLICK --description "the Sign in button"
  • 输入:--step-type TYPE --description "Search input" --value "hello" --press-enter
  • AI 断言:--step-type AI_ASSERTION --assertion "the page shows a Sign in button" --timeout-seconds 10
  • 模块:--step-type MODULE --module-id <id> --inputs email=env.USER_EMAIL --inputs password=env.USER_PASSWORD

拼接示例:

{
  "sessionId": "SESSION_ID",
  "startIndex": 0,
  "deleteCount": 0,
  "steps": [
    "--step-type NAVIGATE --url \"https://example.com\"",
    "--step-type AI_ASSERTION --assertion \"the page shows a Sign in button\" --timeout-seconds 10 --cache-id UUID_FROM_PREVIEW"
  ],
  "targetSection": "main"
}

对于条件语句,使用 --assertion-type 和匹配的断言字段创建 CONDITIONAL 步骤,然后使用 parentStepIdChain: [conditionalStepId] 拼接嵌套步骤。

执行长时间运行的 AI 操作步骤(CLI)

这是强制性的,不是建议。 AI 操作(AI_ACTION_DYNAMIC)在运行时计划和执行许多子步骤,通常需要远远超过 60 秒。MCP 工具调用上限为 60 秒,无论进度如何都会被取消,因此通过 momentic_preview_step / momentic_run_step 运行 AI 操作会在运行中途被终止,并且你会丢失工作。终端没有此类上限。始终在终端中使用 momentic CLI 执行任何包含 AI 操作的步骤范围——切勿通过 MCP 运行/预览工具。

CLI 命令与 MCP 工具一一对应,并针对同一个长时间运行的守护进程运行。当 MCP 服务器使用 --daemon 启动时,MCP 会话和 CLI 调用共享同一个活动浏览器(由项目配置路径键控):通过 MCP 编写,然后通过 CLI 使用相同sessionId 运行 AI 操作。如果服务器未处于 --daemon 模式,或者你不确定,请通过 CLI 驱动整个流程:session-start,然后预览/拼接/运行,最后 session-terminate

在任何命令上传递 --json 以获取原始工具结果而不是文本。截图和其他工件写入 .momentic/mcp-cli-artifacts/

CLI 命令参考

所有命令也接受 --api-key--server--config--filter

  • momentic session-start <testId> [--env <name>] [--headful-browser] [--video] — 启动会话(镜像 momentic_session_start)。打印 sessionId 和步骤编写指南。仅在不重用 MCP 会话时使用。
  • momentic session-terminate --session <id> — 终止会话(镜像 momentic_session_terminate)。
  • momentic session-state --session <id> — 当前会话状态(镜像 momentic_get_session_state)。
  • momentic session-env --session <id> — 会话可用的环境变量(镜像 momentic_get_environment_variables)。
  • momentic preview-step --session <id> --step "<cli-style step>" — 执行一个步骤而不持久化(镜像 momentic_preview_step)。--step 接受与 MCP 相同的 CLI 风格步骤字符串;传递 --step "--step-type CLICK --help" 以获取步骤编写帮助。
  • momentic run-step --session <id> --from-step <stepId> [--from-parent <ids...>] [--to-step <stepId>] [--to-parent <ids...>] [--section main] [--reset] — 运行保存的步骤范围(镜像 momentic_run_step)。这是用于 AI 操作的命令。--from-parent / --to-parent 是 parentStepIdChain(从根到直接父级;对于顶级步骤省略)。--reset 首先重置浏览器。
  • momentic splice-steps --session <id> --start <index> --delete <count> [--step "<cli-style step>" ...] [--section main] [--parent <ids...>] [--return-test] — 插入/替换/删除步骤并持久化(镜像 momentic_test_splice_steps)。重复 --step 以拼接多个步骤。--delete 0 插入,1 替换一个,N 删除 N 个。--parent 是在嵌套步骤中拼接时的 parentStepIdChain。

通过 MCP 编写,然后通过 CLI 使用共享会话执行 AI 操作(stepId 值来自拼接响应/测试内容):

momentic run-step --session "$SESSION_ID" --from-step "$AI_ACTION_STEP_ID"

完全自包含的 CLI 流程:

momentic session-start my-test-id            # 打印 SESSION_ID
momentic splice-steps --session "$SESSION_ID" --start 0 --delete 0 \
  --step "--step-type NAVIGATE --url https://example.com" \
  --step "--step-type AI_ACTION_DYNAMIC --text \"complete checkout with the saved test card\""
momentic run-step --session "$SESSION_ID" --from-step "$FIRST_STEP_ID"
momentic session-terminate --session "$SESSION_ID"

模块

对于 4 个或更多步骤的逻辑流程(如登录、导航、设置或结账),默认优先使用模块。调用 momentic_module_recommend,使用 momentic_module_get 检查强候选,然后决定使用模块还是内联。

模块不能包含模块。在模块内部拼接 MODULE 步骤会失败。

编辑共享模块需要用户确认。要通过 MCP 修改模块,请将模块步骤替换为携带所需元数据标志的 MODULE 步骤:--parameters--parameter-enum--default-parameter--module-display-name--module-description--module-enableddefaultParametersparameterEnums 中的键必须存在于 parameters 中。

模块 inputs 值是作为字符串的 JavaScript 片段。引用字符串字面量,将 env 引用为 env.X,并严格遵循枚举约束。

验证策略

  • 直接 v2 编辑且未请求实时验证:总结更改并询问是否运行。
  • 直接 v2 编辑且活动会话:如果可用则重新加载,否则重新启动会话,然后运行编辑的范围。
  • MCP 编写的编辑:向前预览,在逻辑检查点拼接,然后运行下一个下游保存的步骤或范围。
  • 长时间的完整测试运行和危险操作需要确认。
  • 完成后终止 MCP 会话。

故障排除

页面状态和时序

  • 错误的页面/UI:读取最新的 UI 状态或调用 momentic_get_session_state
  • 截图未更新:再调用一次 momentic_get_session_state
  • 不稳定的时序:优先使用 AI_ASSERTIONcheckPageContainscheckElement...waitForUrl,而不是通用的 WAIT
  • 长时间运行的后端作业/导入/上传:使用断言或 URL/文本/元素等待并设置适当的超时,而不是休眠。
  • 奇怪的会话状态:使用 momentic_run_step 并设置 resetSession: true

定位和缓存

  • 未找到元素:检查截图/UI 状态。如果可见,使用可见文本、角色和附近上下文改进描述;如果不存在,调试先决步骤。
  • 快速点击了错误的元素或没有 AI:怀疑过时的缓存,尤其是当页面结构与先前运行相似时。
  • 动态目标:更改描述使其稳定,或对该步骤禁用缓存。
  • Playwright 稳定性失败:可以找到目标,但由于隐藏、分离、在视口外、被覆盖或动画中而仍然不可操作。尽可能优先修复页面状态。仅在该步骤上使用 --force 当绕过可操作性是可接受的,或者如果基于坐标的交互是正确的权衡,则启用项目范围的视觉操作。
  • 视觉操作在项目浏览器设置中启用,使用 visualActions: true。Momentic 通过 X/Y 坐标进行交互,并尽力保持元素身份,而不是像 Playwright 可操作性检查那样硬性失败。
  • 引号文本:描述中的引号子字符串被 Momentic AI 按字面处理。仅当该确切文本必须出现在屏幕上或元素的 accessible name 中时才使用引号;对于语义匹配,省略引号。

AI 断言性能

  • 模糊的断言:使预期的视觉/文本条件具体化。包括相关的页面区域、对象、计数或状态。
  • 字面文本不匹配:带引号的字符串被视为必须出现在屏幕上的文本。当意图是语义匹配时,移除引号。尽可能描述元素的用途,而不是特定的文本或标签。
  • 在视口外:视觉条件(如颜色或形状)只有在元素在视口中时才能评估。使用滚动/悬停设置。
  • 视觉上微妙的条件:AI_ASSERTION 支持 VISION_ONLY 模式,具有更好的视觉推理能力。
  • 重复的错误判断:重新措辞断言,使预期条件更清晰,旧记忆不再适用。
  • 瞬态条件:AI 检查在配置的超时时间内多次重试,但每次尝试都是即时快照。因此,极快的变化(如 1 秒内出现和消失的 toast)可能会被错过。此外,无法评估随时间的变化(“屏幕比以前更暗”)。优先为稳定的最终状态制定断言;如有必要,可以使用客户端 JavaScript 观察器。

格式和数据

  • v2 加载/运行失败:运行 npx momentic lint <path>;移动后常见的错误是损坏的相对文件引用。
  • 模块失败:使用 momentic_module_get 重新检查必需的参数、默认值、枚举和 JS 片段输入语法。
  • 环境值缺失:确认生成步骤使用了 saveAs / --env-keysetVariable,并且消费语法是 env.X{{ env.X }}
  • JavaScript 失败:确认环境。浏览器 JS 不能使用 Node 助手;Node JS 不能读取实时 DOM 全局变量。

在同一个问题上大约尝试三次后,停止并向用户询问方向。

决策速查表

  • 已知的 v2 序列:直接 YAML 编辑。
  • v1 或未知 UI 状态:MCP 预览、拼接、验证。
  • 需要正确的步骤索引:使用测试内容、拼接引用或 returnTest;切勿使用原始 YAML 步骤 ID。
  • 需要在步骤 N 处有浏览器:从开始/设置到 N-1 运行一次,然后继续使用同一个会话。
  • 单个新步骤想法:momentic_preview_step
  • 持久化验证的 MCP 步骤:momentic_test_splice_steps
  • 干净重启:momentic_run_step 并设置 resetSession: true
  • 验证直接 v2 编辑:如果活动则使用 momentic_test_reload,否则使用新会话。
  • 步骤范围包含 AI 操作:始终通过终端中的 momentic run-step CLI 执行——切勿使用 momentic_run_step,后者会在 60 秒 MCP 工具调用上限时被取消。