mcpField
B
build-mule-integration

build-mule-integration

github.com/mulesoft开发14317 次安装今天更新
代码仓库

关于 build-mule-integration

build-mule-integration 是用于创建或修改 Mule 集成的开发技能,面向需要构建数据流、API 或事件驱动流程的开发者。它先从 Exchange 发现并描述连接器,再经用户确认生成项目和 XML,并要求构建验证。

主要功能

  1. 01验证 Mule、CLI 与 Java 环境
  2. 02搜索并描述 Exchange 连接器
  3. 03先设计并等待用户批准
  4. 04生成 Mule 项目与流程 XML
  5. 05通过 Maven 构建验证变更

适用场景

  1. 01构建 Salesforce 与 Slack 之间的数据同步
  2. 02创建定时轮询或事件驱动流程
  3. 03在现有 Mule 项目中修改连接器或流程
  4. 04生成 REST API 或 webhook 集成

安装

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

$npx skills add https://github.com/mulesoft/mulesoft-dx --skill build-mule-integration

安装后在 Claude Code 中输入/build-mule-integration确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

lib/
anypoint.mjs
exchange-rank.mjs
fsx.mjs
nearest.mjs
platform.mjs
pom.mjs
template-rank.mjs
references/
connector-catalog.md
jdbc-drivers.md
template-project-creation.md
scripts/
.gitattributes
build_deps.mjs
build_gav.mjs
commit_connectors.mjs
describe_connector.mjs
get_latest_connector.mjs
maybe_add_http_connector.mjs
pick_connector.mjs
search_templates.mjs
validate_before_build.mjs
validate_prerequisites.mjs
write_mule_artifact_env.mjs

详细介绍

Mule 开发人员

通过智能连接器发现和数据驱动的 XML 生成,构建专业的 Mule 集成。

何时使用此技能

在用户提出以下请求时使用此技能:

  • “创建 Mule 应用/集成/流”
  • “构建 X 与 Y 之间的集成”
  • “将数据从 X 同步到 Y”(例如,“从 Salesforce 同步到 Slack”“从 MySQL 同步到 Salesforce”)
  • “查询 Salesforce”“发送 Slack 通知”
  • “安排作业”“每 N 分钟轮询一次数据”
  • “创建 Webhook”“构建事件驱动流”

触发关键词: create、build、integrate、sync、connect · mule、integration、flow、app、project · salesforce、slack、servicenow、jira、netsuite · mysql、postgresql、database · api、http endpoint、rest api · schedule、poll、every N minutes · alert、notify、webhook。

请始终先在 Exchange 中搜索特定于系统的连接器。大多数 SaaS 应用都有专用连接器。


前提条件

anypoint-cli-v4 --version
anypoint-cli-v4 dx --help
echo $JAVA_HOME && java -version   # Java 11+
anypoint-cli-v4 conf

如果缺少工具:

npm install -g @mulesoft/anypoint-cli-v4
npm install -g @salesforce/anypoint-cli-dx-mule-plugin
anypoint-cli-v4 conf username <username>
anypoint-cli-v4 conf password <password>

捆绑脚本

此技能随附小型 Node 脚本(*.mjs,ESM,无 npm 依赖),存放在 scripts/ 下。使用 Bash 工具运行 node scripts/<name>.mjs ... 来调用它们——请勿将脚本内容直接写入回复。这些脚本会将输出持久化到磁盘,供后续步骤以程序方式读取,因此不受 Bash 工具调用返回时消失的 shell 变量影响:

脚本用途输出位置
scripts/validate_prerequisites.mjs步骤 1 — 验证工具链(CLI、DX 插件、Java 11+、JAVA_HOME、Mule 运行时是否存在)。仅验证tmp/mule-dev-env.json(相对于工作区;包含 mule_version、runtime_path、errors[] 等)
scripts/get_latest_connector.mjs <search> [nickname]步骤 3 — 搜索 Exchange 并打印按排名排列的连接器候选项(groupId:assetId:version,每行一个,不含分数,也不提示获选项)。不写入任何内容。仅 stdout
scripts/pick_connector.mjs <nickname> <gav>步骤 3 — 将 Agent 选定的 GAV(经推理或 AskUserQuestion 后)记录为草稿tmp/connector-choices/<nickname>.json({groupId, assetId, version})
scripts/commit_connectors.mjs步骤 8(TDD 获批后)— 将 tmp/connector-choices/ 下的所有草稿移至 Phase 2 读取的固定版本目录 tmp/connector-versions/tmp/connector-versions/*.json
scripts/build_gav.mjs <json>将已保存的连接器 JSON 转换为 groupId:assetId:version 字符串stdout
scripts/build_deps.mjs [versions-dir]步骤 8 — 读取 tmp/connector-versions/ 中的每个连接器固定版本,并输出以逗号连接的 GAV 字符串,可用于 dx mule project create --dependencies。跳过 db-driver.json 和任何非固定版本文件。stdout
scripts/write_mule_artifact_env.mjs <project-dir> [env-json]步骤 8(紧接 dx mule project create 之后)— 将 tmp/mule-dev-env.json 中经验证的 mule_version 和 java_version 写入项目的 mule-artifact.json(minMuleVersion + javaSpecificationVersions),并保留其他键。可重复执行且结果一致。<project-dir>/mule-artifact.json
scripts/describe_connector.mjs <nickname> [--type operation|source --name <name>]步骤 4(无标志)/步骤 13(带标志)— 对草稿 GAV 运行 dx mule describe-connector,保存完整 JSON,将摘要回显到 stdout,并将 .errorTypes 缓存到 tmp/connector-errors/,供步骤 16 的验证器使用tmp/connector-metadata/<nick>[-<name>].json + tmp/connector-errors/<nick>[.<name>].json + stdout 上的摘要
scripts/validate_before_build.mjs <project-dir>步骤 16(mvn 前)— 错误类型白名单(Cluster D)、命名空间↔依赖项一致性(Cluster A2–A5)、规范 XSD URL 格式发现首个违规时输出到 stderr 并以非零状态码退出
scripts/maybe_add_http_connector.mjs --project <dir> <providers...>Phase 2 — 在选择了 OAuth 提供方时,防御性检查 HTTP 连接器是否存在;编辑 <dir>/pom.xml<dir>/pom.xml
scripts/search_templates.mjs <search-term>步骤 1b(模板路径)— 通过并行调用(未限定范围 + 组织范围)搜索 Anypoint Exchange 中 type == "template" 的资产,去重后按令牌重叠度排序,并为排名前 10 的结果补充 description、minMuleVersion 和 sourceLocation。stdout 上的单个 JSON 数组(最多 10 行,私有结果优先)。未找到匹配项时以状态码 1 退出。

工作流分为两个阶段

此工作流包含两个阶段,中间由一道严格的用户审批关卡隔开。

  • 阶段 1:技术设计(步骤 1–7)。 确定系统,搜索 Exchange,描述连接器,选择触发器和提供方,提交技术设计摘要,然后等待用户批准。阶段 1 不会向用户的项目目录写入任何内容——所有产物都位于相对于工作区的路径下:tmp/mule-dev-env.json(由 validate_prerequisites.mjs 管理的环境缓存)、tmp/connector-choices/*.json(连接器选项草稿)和 tmp/connector-metadata/*.json。阶段 2 要读取的固定版本目录 tmp/connector-versions/*.json,只有在用户批准步骤 7 后,才会由 commit_connectors.mjs 写入内容。
  • 阶段 2:构建(步骤 8–18)。 创建实际项目,生成配置和流程 XML,运行构建,并声明完成。阶段 2 是唯一会触及用户项目目录的阶段。

在明确通过步骤 7 的审批关卡之前,绝不能开始阶段 2。跳过阶段 1——或将其简化为单一的“我就用 HTTP”决策——是此技能影响最大的失败模式;两阶段结构正是为防止这种情况而设计的。

更新模式(修改现有项目)

**可观察判定条件:**如果工作区中已存在 pom.xml(以及 src/main/mule/*.xml),则你是在修改现有项目,而不是创建新项目。这包括所有针对性更改——重命名配置、添加或替换连接器操作、编辑流、添加 <logger>/<set-variable>、更改连接属性,或为后续步骤向 pom.xml 添加依赖项。

在更新模式下:

  1. **不要运行 dx mule project create,也不要从头重新生成文件。**跳过项目创建步骤(第 8 步的 dx mule project create)。读取现有文件,使用 Edit/replace_in_file 原地应用更改,并保留所有无关的流、配置和元素。

    **相对于 Phase-1 门槛的入口:**对于用户已具体说明的自包含原地编辑(例如重命名此配置、在此处添加日志记录器、替换此操作),无需通过第 7 步的 TDD 审批门槛——直接进行编辑,然后构建。上文“在第 7 步的审批门槛通过前,绝不能开始 Phase 2”的规则适用于新项目创建,不适用于更新模式下的编辑。如果请求的更改较大或含糊不清(用户描述了目标,却没有说明要更改哪些元素),请先简要说明计划进行的编辑并征求确认,然后再编辑——但无需打包 TDD 流程,也无需创建新项目。

  2. **任何连接器相关工作仍须遵循基于事实的流程。**如果更改涉及添加或替换连接器、操作、源或连接提供程序,则必须在编辑前运行 get_latest_connector.mjs(获取版本)和 describe_connector.mjs(获取操作/配置/提供程序元数据)——具体要求见第 3–6 步和第 13 步。切勿凭记忆硬编码连接器版本、操作名称或连接属性;实际的 GAV 以及元素/属性名称必须从脚本获取,不能依赖训练期间的知识。

  3. **必须按与新项目相同的方式,依次完成第 16 步(构建)→第 17 步(清理)→第 18 步(完成)。**任何更改后都必须运行 mvn clean package 并确认出现 BUILD SUCCESS,无论更改多小。重命名配置、插入一行日志记录器,或添加 pom 依赖项,都可能导致构建失败——构建是证明更改未造成问题的唯一依据。

**只有 mvn clean package 返回 BUILD SUCCESS,更改才算完成。**此规则无一例外地适用于更新模式:

  • “这只是文本编辑/重命名/添加一个元素”——仍须构建。小改动也会导致构建失败(重命名配置会留下悬空的 config-ref;添加没有对应依赖项的 xmlns 会导致 XSD 解析失败)。
  • “我添加的依赖项是供后续步骤使用的,目前还未使用”——仍须构建。无论连接器是否已接入流中,无法解析或拼写错误的 GAV 都会导致构建失败。
  • “这不算真正的流生成任务,所以无需执行构建步骤”——构建步骤仍然适用。项目的任何类型的更改,最终都必须经过验证构建。
  • “我已经编辑了文件,更改显然是正确的”——mvn clean package 的终端输出是证明正确性的唯一依据。没有执行此构建就宣布完成,等同于悄悄交付一个损坏的项目。

如果你准备宣布完成,但最近一次响应没有运行 mvn clean package 并得到 BUILD SUCCESS,请停止——先运行构建(第 16 步)。

全流程规范(Phase 1 前请先阅读)

  • 构建 → 清理 → 完成分离(Step 16 → Step 17 → Step 18)。 按顺序执行三次响应,每次只调用一个工具:先运行 mvn clean package,然后运行 rm -r tmp/,最后发送完成信号。不得合并执行。等待每一步的结果后再继续。
  • 每次响应只运行一次 mvn。 修复后重新运行构建时,该响应中只能发出 mvn 命令。不得将其与后续编辑、后续 Shell 命令或完成信号合并。
  • “完成”意味着构建已通过。 只有在运行了 mvn clean package 的响应返回 BUILD SUCCESS 后,才能声明完成。
  • 连接器版本只能来自 Step 3 流程。 不得从 references/connector-catalog.md、训练时记忆或推断中复制版本。Step 3 是由三个脚本组成的流程:get_latest_connector.mjs 列出按排名排序的候选项(仅 stdout,不生成固定版本文件);pick_connector.mjs <nickname> <gav> 将选中的条目记录为 tmp/connector-choices/ 中的草稿;commit_connectors.mjs(Step 8,在 TDD 获批后执行的第一个操作)将所有草稿提交到 tmp/connector-versions/。任何传入 dx mule project create --dependencies 或 pom.xml 的 GAV,都必须通过 scripts/build_deps.mjs(Step 8 中用于生成完整的 --dependencies 字符串)或 scripts/build_gav.mjs(Phase 2 其他位置用于生成单个连接器的 GAV),从 tmp/connector-versions/*.json 文件中读取。目录中的版本只是可能会变化的快照——只能将其作为连接器身份参考,不能将其作为版本来源。
  • 由 Agent 做出选择,而不是由脚本决定。 get_latest_connector.mjs 有意只输出普通的排名列表,不提供评分、表情符号或“获胜者”信号。如果列表只有一行,选择显而易见;如果有多行,Agent 必须判断哪一项符合用户所述的系统。如果这些条目代表同一系列中真实存在的不同变体(Slack 的 mule4-slack-connector 与 mule-slack-connector;FTP 与 FTPS;Dynamics 365 与 Dynamics GP/NAV/BC;IBM MQ 与 Solace 与 JMS),则应通过 AskUserQuestion 让用户决定,而不是由 Agent 猜测。多问一个问题只需多花一轮;默默选错变体则会导致整个 Phase 2 重写。
  • 没有证据时不得回退到 HTTP。 只有在运行 scripts/get_latest_connector.mjs <system> 并且返回零个匹配项(退出代码 1),或者排名列表中的每一行都明显属于不同产品(除 mule/connector 等无意义词之外,没有任何 assetId 与系统名称共享词元)后,才能将系统归类为“没有专用连接器,使用 HTTP”。禁止在运行搜索之前就宣布使用 HTTP。Exchange 为数百种 SaaS 产品提供专用连接器,仅凭训练时知识推理很容易遗漏这些连接器——辅助脚本才是权威的检查方式。专用连接器可提供元数据发现、类型化操作和正确的身份验证;HTTP 只提供原始请求处理,用户还得手动接线,因此悄悄回退到 HTTP 是实实在在的损失,并非中性选择。请注意,任何 groupId 为 UUID 的排名条目(例如 a50e4364-a38c-4340-b05a-c1f8ebed0748:…)都是用户组织私下发布到 Exchange 的连接器——应将其视为首选候选项,而非噪声。组织特意发布私有连接器,通常就希望使用它,往往是因为它封装了内部端点、自定义身份验证,或公共连接器无法覆盖的标准路径。

Phase 1:技术设计

步骤 1:验证前置条件

运行前置条件验证脚本。该脚本仅进行验证。它会将前置条件验证结果写入当前工作区中的 tmp/mule-dev-env.json;步骤 8 会从中读取 mule_version。

node scripts/validate_prerequisites.mjs

如果脚本以非零状态退出,请立即停止此技能的后续操作,并告知用户根据 tmp/mule-dev-env.json 中的 errors 数组采取措施。不要臆造备用 Mule 版本——未检测到运行时环境时,mule_version 为空。

validate_prerequisites.mjs 的检查内容:已安装 Anypoint CLI v4 · DX 插件可用 · 已设置 JAVA_HOME · Java 11+ · 在 ~/.mule-dx/config.json:.runtimePath 或 ~/AnypointCodeBuilder/runtime/mule-* 下检测到 Mule 运行时。如果缺少运行时或其他工具,脚本会报告错误,并告知用户运行必要命令以正确安装。


步骤 1b:项目源选择

创建新项目时,务必询问此问题。 前置条件通过后,确定用户希望如何创建项目:

<ask_followup_question>
<question>I can create this project in one of three ways. Which would you prefer?</question>
<options>["I want to generate from scratch without a template.", "I want to use organizational templates from Anypoint Exchange.", "I have a local template file (.jar or .zip) I want to use."]</options>
</ask_followup_question>

根据用户的回答:

  1. Exchange 或 Local 模板 → 阅读 references/template-project-creation.md 并遵循相应流程(Exchange E1–E4 或 Local L1–L4)。子技能会以明确的继续或停止提示结束(步骤 E5 / L5)——如果用户选择“停止”,通过 <attempt_completion> 声明完成;如果选择“继续”,则返回此处并继续步骤 2。
  2. 从头创建 → 继续步骤 2。项目将在步骤 8 创建(连接器发现后提供 --dependencies)。

完全跳过此步骤的情况:

  • 用户正在处理现有项目(修改/更新流程,而非创建新项目)

步骤 2:识别系统和触发器提示

[BLOCKER] 步骤 2 绝不能向用户提问。 此处不要输出 <ask_followup_question> 或 <AskUserQuestion>。触发器的决定将在步骤 5 中进行,届时连接器元数据已写入磁盘——现在提问会迫使用户在通用选项(HTTP Listener / Scheduler)中选择,而不是从真实的连接器源中选择。这正是此工作流要避免的影响最大的反模式。

在回复文本中生成两条记录。这些是普通文本,不是思考块或工具调用,因此后续步骤(以及用户)都可以读取。

1. 系统列表。 识别确切的系统名称:源系统(数据来源)和目标系统(数据去向)。使用具体名称(Slack、Jira、ServiceNow、Stripe、Shopify),而非通用术语(聊天、工单、支付、电商)。列表中的每个名称都会在步骤 3 中搜索;每次搜索都必须在磁盘上生成一个 tmp/connector-choices/<nick>.json 草稿(Agent 从排序列表中选择,并调用 pick_connector.mjs)。

反模式——根据目标名称推断后端。 如果提示中提到队列、主题、总线或类似的消息传递目标,目标名称(例如 foo.queue、orders.topic、events-stream)只是一个标签,并非技术类型。它不能标明其背后的代理。除非提示明确指出,否则不要将特定代理(Anypoint MQ、Kafka、IBM MQ、Solace、SQS 等)添加到系统列表中。如果提示只说“队列”/“主题”或只给出一个目标名称,请将系统列为 messaging broker (backend unspecified),并计划在步骤 3 中升级处理——由用户选择后端,而不是 Agent。原因:擅自选择的代理会让第 2 阶段基于错误的连接器系列展开,要纠正就必须彻底重写第 2 阶段。 2. 触发提示。 用一到两句话,记下用户提示中描述流程如何启动或指定执行频率的原话,例如 “每 3 秒一次”、“监听新的 Stripe 扣款” 或 “发起 GET 请求以检索客户”。暂时不要分类。不要给出类别标签、决策树,也不要说“因此触发器是……”。第 5 步会根据目前尚不存在的连接器元数据来判断触发器;现在就确定触发器——哪怕只是隐含地归入某个类别——往往会让 Agent 锚定于第 4 步生成的真实 sources[] 之外的判断。

如果提示中没有提到触发器,或者只描述了出站操作(“发起请求”“获取”“调用”),请明确说明:“没有明确的触发短语——仅描述出站操作。”第 5 步的元数据优先判断流程会处理这种情况。

各系统的连接器策略:

  • 主流 SaaS 平台(Salesforce、ServiceNow、NetSuite、Workday)→ 在第 3 步搜索专用连接器。
  • 标准协议(Database、JMS、FTPS、SFTP)→ 在第 3 步搜索特定协议的连接器。
  • 中端市场 SaaS(Stripe、Shopify、HubSpot、Twilio、Plaid 等)→ 在第 3 步搜索专用连接器。不要假设这些系统没有连接器——Exchange 为其中许多平台提供专用连接器。凭训练数据形成的“Stripe/Shopify 等只有 REST API”的直觉并不可靠;搜索结果才是依据。
  • 指定了后端的队列或 Pub/Sub(“Kafka topic”“IBM MQ”“SQS”“Solace”)→ 在第 3 步搜索对应的专用连接器。
  • 未指定后端的队列或 Pub/Sub(提示中使用“队列”“主题”,或只写目的地名称而未说明代理技术)→ 根据上文第 2 步的反模式,目的地名称并不代表后端。在第 3 步通过 AskUserQuestion 升级处理,让用户选择后端(任何代理提供商的 JMS、Kafka、IBM MQ、Solace、SQS 等)。只有在用户拒绝选择时,才默认使用 mule-jms-connector——JMS 是适用于任何代理的通用协议层,代理将在第 6 步通过连接提供商(active-mq、active-mq-nct、generic)选择。
  • 未知/不明确或自定义内部 API → 仍须先在第 3 步搜索 Exchange;只有当第 3 步的搜索未找到任何可能相关的结果时,才将 HTTP 作为后备方案。

第 2 步之后的下一次工具调用必须是针对 Systems 列表中某个系统的 get_latest_connector.mjs,不能是 ask_followup_question、describe-connector 或其他任何工具。第 3 步是不可跳过的下一步。


第 3 步:在 Exchange 中搜索连接器,做出决定并起草选择

第 3 步是一个三步循环,对第 2 步中的每个具名系统各执行一次:

  1. 使用 get_latest_connector.mjs 列出候选项。
  2. 决定哪一行最合适——如果选择显而易见,则就地说明理由;如果这些行是同一系统系列的真实变体,则使用 AskUserQuestion。
  3. 使用 pick_connector.mjs 起草所选的 GAV。草稿会写入 tmp/connector-choices/<nick>.json,并在整个第 1 阶段保留在该处。

此脚本不会指定胜出项。其输出中没有表情符号、分数或“已选定”一行。列表只有一行时,输出形式就是“单行”;有多行时,输出形式就是“多行”,这时应查看名称并判断意图,或升级处理。

强制搜索规则。 对第 2 步中的每个具名系统运行 get_latest_connector.mjs——包括那些因为你训练数据中对其知名度的印象而让你假设它们没有专用连接器的系统。未运行脚本就宣称“系统 X 没有专用连接器”是禁止的。这条规则可防止静默回退到 HTTP——请参阅工作流通用规范中的“没有证据就不得回退到 HTTP”。

术语范围。 始终优先使用更宽泛的系统名称(databricks、aws-storage、snowflake、database、salesforce),而不是像 mule-amazon-s3-connector 或 mule-db-connector 这样带有限定后缀的窄泛词。窄泛词可能会漏掉 assetId 与公开连接器名称没有共同词元的组织私有连接器——例如,搜索 mule-amazon-s3-connector 不会显示私有连接器 mule-plugin-aws-storage-api,而 mule-db-connector 不会显示私有连接器 <uuid>:mule-plugin-customer-warehouse-database。下文的“常见搜索词”表格为知名系统提供了起始提示,并非限制——如果宽泛词的结果太杂,你随时可以改用更窄的词重新搜索。

版本事实来源规则(强制):

  • 连接器版本号唯一可接受的来源,是在当前会话中针对实时 Exchange 运行 get_latest_connector.mjs,通过 pick_connector.mjs 记录,并随后由 commit_connectors.mjs 提升到 tmp/connector-versions/。这同样适用于 dx mule project create --dependencies、pom.xml <dependency> 块,以及其他任何出现版本号的位置。
  • 不要复制 references/connector-catalog.md 中的版本号。该目录用于帮助识别应使用哪个连接器(asset ID、用途);其中的版本号只是尽力获取的快照,可能会过时。
  • 不要凭记忆或推算编造版本号。Exchange 上的版本号无法预测。
  • 在对应的 tmp/connector-choices/<name>.json 草稿写入磁盘之前,不要写入版本号。
步骤 1——列出

每个具名系统调用一次。昵称将用于命名草稿以及之后的固定版本,因此请选一个简短的昵称并始终保持一致:

node scripts/get_latest_connector.mjs mule-salesforce-connector sfdc

标准输出格式:每行一个 GAV,按最可能匹配的结果优先排列:

com.mulesoft.connectors:mule-salesforce-connector:11.1.0
com.mulesoft.connectors:mule-salesforce-analytics-connector:4.1.0
com.mulesoft.connectors:mule-salesforce-composite-connector:2.5.2
com.mulesoft.connectors:mule-salesforce-marketing-cloud-connector:5.0.0

退出码 0 表示返回了至少一行;退出码 1 表示没有匹配的 Mule 4 扩展,你需要将此系统视为 HTTP 回退范围(请参阅工作流通用规范)。

移动 2 — 决策并确认

阅读列表。按顺序分两种情况:

情况 A — 只有一行。 只有一个 Mule 4 扩展匹配。用一句话在行内确认选择(“HTTP 的唯一匹配项:org.mule.connectors:mule-http-connector:1.11.1”),然后进入移动 3。

情况 B — 多行是同一系统系列的真实变体。 assetId 看起来不可能指向用户所用系统的行属于噪声——在逻辑上将其过滤掉。如果对用户的系统进行逻辑筛选后仍有多个匹配项/选择(例如,Salesforce 查询返回 mule-salesforce-connector,以及三个 Salesforce 子产品的连接器,分别用于 Analytics / Composite / Marketing Cloud 等),则始终通过 AskUserQuestion 升级处理。以下是一些需要用户确认的常见变体系列:

  • slack → mule-slack-connector(社区版,v4.x)与 mule4-slack-connector(MuleSoft 高级版,v2.x)。OAuth 形式和操作各不相同,两者都不是对方的“更新版本”。
  • ftp → mule-ftp-connector(普通)与 mule-ftps-connector(TLS)。始终确认,并建议选择 FTPS 以遵循最佳安全实践。
  • ibm-mq 与 solace、kafka、jms——提示词中的目标名称(例如 foo.queue、bar.topic)并不能确定后端。除非提示词明确指定了消息代理技术,否则应将这些选项列出并升级处理;如果用户不愿选择,则将 mule-jms-connector 作为通用协议回退选项。
  • microsoft-dynamics-*——365、GP、NAV、365-Business-Central、CRM 是不同的产品,用户可能把它们混为一谈。
  • oracle-ebs 与 oracle-ebs-122——不同的 EBS 主版本。
  • 任意一对 assetId,只要它们都包含目标系统名称,且仅在变体后缀、协议标记、主版本标记或 mule4- 与 mule- 前缀上有所不同。
  • 同一系统系列的公共连接器与私有(UUID groupId)连接器——例如,snowflake 搜索中同时出现 com.mulesoft.connectors:mule4-snowflake-connector 和 <uuid>:mule-plugin-snowflake-sys-api-json。组织发布此类资产通常是有原因的(自定义身份验证、内部端点、对公共连接器的封装),因此,如果同一排名列表中存在私有替代项,绝不要默默选择公共连接器——应通过 AskUserQuestion 列出两者,让用户选择。

升级处理时的提示词形式:

<ask_followup_question>
<question>Two Mule 4 Slack connectors exist on Exchange. Which should this integration use?</question>
<options>[
  "org.mule.connectors:mule-slack-connector:4.3.2 — community Slack connector",
  "com.mulesoft.connectors:mule4-slack-connector:2.0.1 — MuleSoft premium Slack connector",
  "Other — describe which Slack variant you need"
]</options>
</ask_followup_question>

存在多个选项时,必须向用户确认;默默选错变体会导致 Phase-2 重写。

如果用户的提示词明确指定了某个变体(“使用 Dynamics 365 连接器,不要用 BC”“只使用高级版 Slack 连接器”),则按指定选择,无需询问。

移动 3 — 起草

将选定的 GAV 记录为草稿。此操作是幂等的——如果 Step 4 或 Step 5 的元数据表明有更合适的选项,可以使用不同的 GAV 重新运行:

node scripts/pick_connector.mjs sfdc   com.mulesoft.connectors:mule-salesforce-connector:11.1.0
node scripts/pick_connector.mjs slack  com.mulesoft.connectors:mule4-slack-connector:2.0.1
node scripts/pick_connector.mjs jms    org.mule.connectors:mule-jms-connector:1.10.3
# → tmp/connector-choices/{sfdc,slack,jms}.json now each contain {groupId, assetId, version}

如果在 Step 4 的元数据摘要之后发现草稿有误,请使用修正后的 GAV 重新运行 pick_connector.mjs。在 Step 8 的 commit_connectors.mjs 将草稿提升之前,草稿都可以修改。

Phase 1 中为何使用草稿而非固定项。 如果用户看到 Technical Design Summary 之前,连接器选择就写入了 tmp/connector-versions/,Agent 及所有下游检查都会将其视为已定案。在 TDD 获批前保留草稿,可以让整个设计保持可逆,并让审批关卡成为真正的承诺点。

脚本内部应用的筛选规则

(仅供参考——你无需重新实现这些规则。列表按最佳猜测优先排序;顺序仅供参考,并非强制要求。)

  • 仅包含 "type": "extension"(兼容 Mule 4)——筛选掉 Mule 3 的 type=connector 资产、模板、示例和 rest-apis。
  • 任何 groupId 只要其资产属于 type="extension",均符合条件;排序时通过 3 层优先级将第一方连接器排在前面(com.mulesoft.connectors > org.mule.connectors > 其他)。
  • 每个 (groupId, assetId) 分组内的最新语义化版本。
  • 根据搜索词进行词元重叠评分;优先级较高的 groupId 和较短的 assetId 用于打破平局。评分仅用于排序,不会输出。
  • 并行抓取两页(偏移量为 0 和 200),避免 salesforce 等宽泛搜索因只抓取单页而漏掉候选项。
常见搜索词

以下是一些知名系统的起始提示。对于其他系统,尤其是你怀疑用户所在组织可能有私有连接器的系统,请先按系统名称搜索。

系统搜索词
Salesforcesalesforce
Databasedatabase
HTTPhttp
NetSuitenetsuite
ServiceNowservicenow
Amazon S3amazon
JMSjms
Slackslack(会同时返回两个 Slack 变体——这是上文提到的易产生歧义的情况)

对于列表中没有的系统,请使用系统名称动态搜索(例如 stripe、shopify、hubspot、databricks、snowflake)——不要臆测命名模式,也不要假设该系统没有连接器。更宽泛的系统名称搜索还会显示用户所在组织在同一领域发布的私有(UUID-groupId)连接器,而较窄的 mule-<name>-connector 搜索词通常会漏掉这些连接器。


步骤 4:描述连接器

对于步骤 3 中解析出的每个连接器,获取其完整元数据,并阅读摘要。请使用 describe_connector.mjs,不要手动编写 describe-connector 管道命令——该包装脚本会解析探测路径,将完整 JSON 保存到磁盘供后续步骤使用,并将 sources[] 和 configs[] 回显到 stdout,让你了解步骤 5 需要哪些内容:

node scripts/describe_connector.mjs sfdc       # nickname from Step 3
node scripts/describe_connector.mjs stripe
node scripts/describe_connector.mjs http

每次调用都会写入 tmp/connector-metadata/<nickname>.json(完整响应,阶段 2 会再次使用),并打印如下格式的摘要:

{
  "namespace_prefix": "stripe",
  "sources": [
    "on-canceled-subscription-listener",
    "on-new-charge-listener",
    ...
  ],
  "configs": [
    { "name": "config", "providers": ["api-key"] }
  ],
  "operations_count": 335,
  "operations_sample": ["createV13dSecure", "..."]
}

阅读返回的 sources[] 数组。 该列表包含连接器支持的真实原生触发器;步骤 5 将根据它进行分支处理。不要略过摘要,直接调用下一个 describe_connector.mjs——步骤 5 的触发器决策取决于你是否了解每个连接器提供了哪些来源,而摘要是获取这些信息最省事的地方。

该脚本还会将连接器级错误类型白名单写入 tmp/connector-errors/<nickname>.json。步骤 16 的验证器会读取此目录;你无需手动调用它。

修复发布故障。 偶尔,连接器最新发布的版本会是有缺陷的制品——jar 缺少其自身 mule-artifact.json 声明为导出的类,导致 describe-connector 以非零状态退出,且没有可用输出。如果 GAV 草案来自步骤 3(tmp/connector-choices/ 草案),且摘要描述失败,包装脚本会自动列出该资产的其他已发布版本,重试低于故障版本的最高可用版本,将草案重新固定到该版本,并打印一行 ⚠️ … fell back to working version <v>。这是对错误发布的机械式恢复,并非选择其他变体——连接器身份保持不变。如果看到此警告,重新固定的版本会自动通过步骤 8 的 commit_connectors.mjs 写入 pom;无需采取任何操作。如果不存在可用的先前版本,脚本仍会失败(退出码为 1)——请将其视为确实无法使用的连接器。

如果需要完整响应(例如,检查 oauth-callback-config 的 childElements[]),请直接读取 tmp/connector-metadata/<nickname>.json。

手动回退——如果包装脚本因某种原因不可用,可以手动复现。在阶段 1 中,草案文件具有权威性;build_gav.mjs 接受以下任一位置:

NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(node scripts/build_gav.mjs tmp/connector-choices/sfdc.json)" \
  --output json > tmp/connector-metadata/sfdc.json
jq '{namespace: .namespace.prefix, sources, configs: [.configs[] | {name, providers: [.connectionProviders[]?]}]}' tmp/connector-metadata/sfdc.json

但通常情况下,请优先使用包装脚本——它只需一行而非三行,而且无需后续调用,就能在本轮工具输出中显示来源列表。


步骤 5:选择触发器

每个顶层流程(不是子流程或 <flow-ref> 的目标)都必须且只能有一个触发器。步骤 5 根据连接器元数据来选择触发器,而不是凭提示文本直觉判断。

[BLOCKER] 先探索,再决策。 在确定触发器之前——无论是直接选择还是通过 AskUserQuestion 选择——步骤 2 的 Systems 列表中列出的每个系统都必须满足以下两项条件:

  1. 磁盘上存在 tmp/connector-choices/<nick>.json(步骤 3,通过 pick_connector.mjs)。
  2. 磁盘上存在 tmp/connector-metadata/<nick>.json(步骤 4)。

此外,你必须能查看 sources[] 的内容。在开始步骤 5 的回复中运行:

for f in tmp/connector-metadata/*.json; do
  echo "--- $f ---"
  jq '{namespace: .namespace.prefix, sources}' "$f"
done

并阅读输出。这与步骤 4 中 describe_connector.mjs 摘要对每个连接器显示的数据相同,但在此处重新输出,可以让所有连接器的 sources 并列显示在一处,方便在决策前查看。不要在当前工具输出中尚未看到这些列表时就确定触发器——过往轮次很快就会滚出上下文。

设置这道门槛的原因:如果 Agent 在读取 sources[] 之前就确定触发器,常见的失败情况是默认使用 http:listener(把提示当作 Webhook),并悄悄忽略步骤 4 刚刚获取的真实连接器 source。连接器的 sources[] 数组是其支持的触发器的权威列表;步骤 5 必须依据该列表分支,而不是凭提示文本直觉判断。

决策阶梯(按顺序评估)

按顺序逐级检查以下各项。每一阶都是一种可能的路径——不存在“后备”排名;第一个满足所有前提条件的路径就是要采取的路径。

第 1 阶 — 连接器 source 路径

对范围内的每个连接器,检查摘要中的 sources[]。对于名称可能与用户所述需求相关的 source(名词匹配:“product”“order”“charge”“customer”;且动词前缀一致:on-new-*、on-updated-*、on-modified-*、on-*-arrived、poll-*、*-listener、*-trigger),使用统一的 describe-connector 命令和 --type source 检查其形状:

NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(node scripts/build_gav.mjs tmp/connector-choices/<nick>.json)" \
  --type source \
  --name <source-name> \
  --output json

不要对每个 source 都调用 source-detail——只检查名称符合上述“名词 + 动词前缀”条件、可能符合用户意图的 source。对于功能丰富的连接器,这能将 CLI 调用次数从 7 次以上减少到 1–2 次(例如 Shopify、Salesforce 等)。

根据返回的形状而非仅凭名称,将其与用户意图进行比较:

  • 如果 source 的 childElements[] 包含 scheduling-strategy 元素,则这是一个轮询 source——连接器本身会原生处理轮询频率。当用户提示中指定了频率(“每 N”“每天”“每小时”),且匹配的 source 是轮询 source 时,频率应放在该 source 的 <scheduling-strategy> 子元素中。不要在其旁边再引入一个独立的顶层 <scheduler>。正确形状示例:
    <shopify:on-updated-product-trigger config-ref="shopifyConfig">
        <scheduling-strategy>
            <fixed-frequency frequency="3000" startDelay="5000"/>
        </scheduling-strategy>
    </shopify:on-updated-product-trigger>
    
  • 如果 source 没有 scheduling-strategy 子元素,且会公开一个事件(on-new-*、on-modified-*),则它是一个事件 source——直接使用即可;无需顶层 scheduler。
  • 如果 source 的形状包含 listenerConfig 引用,则它是一个 Webhook 接收器——适用于“在端点接收回调”这类提示。

当且仅当某个 source 的形状符合用户意图,且匹配理由可以用一句话说明时,直接确定使用该连接器 source。用一行理由说明选择,并引用 source 名称。

当两个或更多 source 均通过针对用户意图的形状检查,或者用户的表述确实无法明确其所需的 source 语义时,通过 AskUserQuestion 向用户提问。选项中应列出真实的 source 名称,而不是通用占位符:

<ask_followup_question>
<question>Which Salesforce event should trigger this flow?</question>
<options>[
  "salesforce:replay-topic-listener — subscribe to a Salesforce streaming topic",
  "salesforce:modified-object-listener — fire when a record of a given sObject is modified",
  "Other — pick a different source"
]</options>
</ask_followup_question>

如果范围内的任何连接器都没有 source 通过形状检查,则进入第 2 阶。

Rung 2 — 通用调度器路径

在以下情况下选择此路径:

  • 没有适合用户意图的连接器来源(Rung 1 已检查候选项,但均不匹配),并且
  • 提示词指定了频率(“每 N”“每天”“每小时”“每隔……轮询”),并且
  • 流程主体将调用连接器操作(而非事件驱动)。

使用 <scheduler>,搭配 <fixed-frequency> 或 <cron>。在行内说明选择。

记录拒绝理由。 选择此路径时,用一句话说明为何拒绝 Rung 1 的来源——例如:“Shopify 的 on-updated-product-trigger 符合‘每 3 秒’,但用户希望按自定义日期范围拉取数据,而非‘自上次轮询以来更新的数据’,因此使用通用的 <scheduler> + shopify:product-list 更合适。”第 7 步的 TDD 需要此列表;趁现在推理还清晰,立即记录。

Rung 3 — HTTP 监听器路径

在以下情况下选择此路径:

  • 提示词明确提到“公开端点”“接收 HTTP 请求”“提供 REST API”“在 /path 提供 webhook”,并且
  • 范围内没有连接器来源属于 webhook 接收器。

使用 <http:listener>。在行内说明选择。记录考虑过哪些连接器来源以及拒绝理由(参见第 7 步)。

Rung 4 — 询问用户

当 Rung 1–3 均不明确适用时,选择此路径——例如,提示词只要求出站操作(“发起请求”“获取”“检索”),没有指定频率,也未提及端点。使用 AskUserQuestion,选项根据范围内连接器的实际 sources[] 加上 Scheduler 和 HTTP Listener 生成:

<ask_followup_question>
<question>The prompt describes outbound calls but does not name a trigger. What should start this flow?</question>
<options>[
  "Scheduler — run on a time-based schedule",
  "HTTP Listener — receive an inbound HTTP request",
  "<connector>:<source-name> — native event from one of the connectors in scope (list any that apply based on sources[])",
  "Other — please describe"
]</options>
</ask_followup_question>
决策之后

记录选定的触发器、其所属连接器(如有),以及——如果选择 Rung 2 或 Rung 3——考虑过的连接器来源列表及每项被排除的一句话理由。第 7 步的 TDD 会显示此列表;如果缺少该列表,TDD 就不完整,Phase 2 无法开始。

[BLOCKER] 如果此步骤要求等待用户响应,则在进入第 6 步之前等待用户响应。


步骤 6:选择连接提供方

仅在确实需要做出选择时询问用户。 对于每个连接器,查看步骤 4 中记录的 configs[] 元数据——具体来说,查看你打算在阶段 2 调用的操作所属配置的 connectionProviders 列表。

决策规则:

  • 有多个配置或多个提供方 → 必须使用 AskUserQuestion。用户的选择将决定你在步骤 11 的 config-detail 调用中传入哪个 config-name 和哪个 --connection-provider,以及你在步骤 12 中编写哪种 XML 结构。
  • 恰好有一个配置和一个提供方 → 不要提示。直接用一行说明选择(“使用 s3:config[connection]——这是连接器提供的唯一选项”),然后继续。只提供一个“选项”的提示显得毫无意义,只会浪费一轮对话。

来自实时 Exchange 元数据的示例:

连接器configs × 提供方操作
S3 连接器config[connection]说明选择,不要提示
VM 连接器config[connection]说明选择,不要提示
HTTP 连接器listener-config[listener-connection]、request-config[request-connection]配置与监听器或请求一一对应——由流程形态(触发器或出站调用)决定,而不是由用户偏好决定。说明选择,不要提示。
支持流式与非流式配置的多配置连接器config[basic, role]、streams-config[streams]提示——选择操作与用例相符的配置;如果该配置有多个提供方,也要选择提供方。
提供 basic/OAuth/JWT/客户端凭据的连接器config[basic, oauth-user-pass, jwt, oauth-client-credentials]提示——这些是真正的替代方案,凭据模型各不相同。
数据库连接器config[my-sql-connection, oracle-connection, data-source-connection, generic-connection, ...]提示用户选择提供方,然后在同一步解析 JDBC 驱动程序的 GAV(见下方“步骤 6b——JDBC 驱动程序解析”)。步骤 9 是机械式的 pom.xml 编辑。

**需要提示时,**只列出真正的替代方案(不要用“如果不确定……”之类的内容凑数)。例如:

此连接器提供四种连接提供方。此集成应使用哪一种?

  • basic — 用户名 + 密码 + 安全令牌
  • oauth-user-pass — 使用用户凭据的 OAuth
  • jwt — JWT Bearer 令牌(服务器到服务器)
  • oauth-client-credentials — OAuth 客户端凭据

**不要把“推荐”作为选项之一,**如果它实际上是唯一选项。只有一个选项时,不要询问。

为每个连接器保存选定的 (config-name, connection-provider) 对,并保存 describe-connector 的 connection-provider 输出,供阶段 2 使用,避免再次调用 CLI。标志语义说明: --name 表示连接提供方名称,--config-name 表示配置名称——很容易弄反:

NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(node scripts/build_gav.mjs tmp/connector-choices/sfdc.json)" \
  --type connection-provider \
  --name basic-connection \
  --config-name sfdc-config \
  --output json > tmp/connector-metadata/sfdc-config.json
步骤 6b — JDBC 驱动解析(仅当 mule-db-connector 在范围内时)

mule-db-connector 是唯一一种需要进一步明确步骤 6 中提供方选择的情况。提供方选择只处理 XML 连接元素(<db:my-sql-connection>、<db:generic-connection> 等),但 JDBC 驱动 JAR 是单独的 Maven 制品,必须通过 <sharedLibrary> 与连接器一同打包。在步骤 6 中解析这两项,这样步骤 7 的设计摘要就能显示驱动完整的 groupId:artifactId:version,步骤 9 也就只需机械地编辑 pom.xml,无需再次询问。

根据步骤 6 中提供方的回答分支处理:

选择的提供方根据规范表自动固定的驱动(步骤 9)是否询问?
my-sqlcom.mysql:mysql-connector-j:8.4.0(8.4 LTS;9.x 需要 JDK 21)否 — 直接告知
oraclecom.oracle.database.jdbc:ojdbc11:23.9.0.25.07(Java 17+)否 — 直接告知
mssqlcom.microsoft.sqlserver:mssql-jdbc:13.4.0.jre11(Java 17+)否 — 直接告知
generic 且目标数据库可确定为 PostgreSQL(用户的提示词中提到 Postgres,之前的对话轮次中提到 Postgres,或 JDBC URL 占位符显示 jdbc:postgresql://)org.postgresql:postgresql:42.7.11 — 规范的 generic 配对否 — 直接告知
generic 且目标数据库是其他数据库或未指定(H2、Snowflake、SAP HANA、Vertica、未知数据库,或用户未提及数据库)无法自动固定 — generic-connection 接受任何 JDBC URL,只有目标数据库才能确定驱动。如果用户选择 generic,请查看 references/jdbc-drivers.md 中的规范选项,然后询问,并将 Postgres 列在首位(这是最常见的 generic 目标)。是
data-source无法自动固定 — 驱动由容器提供,或通过显式的 <sharedLibrary> 声明提供。需要询问。是
derby多个制品 + 依赖 JDK — 如果选择此项,请查看 references/jdbc-drivers.md 中嵌入式与网络客户端的区别,以及 Java 17 与 Java 8 的版本矩阵。需要询问选择 embedded 还是 client。是

规则:如果目标数据库属于上面的前几行(my-sql、oracle、mssql 或使用 postgres 的 generic),则不要询问 — 自动固定并直接告知。 仅对非规范的 generic 目标数据库,以及本身有多种选择的提供方(data-source、derby)进行询问。

所有需要询问的分支都使用相同的提示词格式:

<ask_followup_question>
<question>You picked <provider> for <database>; which JDBC driver should ship as a sharedLibrary?</question>
<options>[
  "<canonical-option-1 from the reference file> — <one-line purpose>",
  "<canonical-option-2> — ...",
  "Other — I will provide a groupId:artifactId:version"
]</options>
</ask_followup_question>

步骤 6 必须为步骤 9 记录的内容:

一个或多个 {groupId, artifactId, version} 元组,以及驱动类,并持久化保存,以便步骤 9 无需再次询问,就能生成 <dependency> + <sharedLibrary> 配对项。请在连接器选择旁边使用一个 sidecar 文件:

# example: after Step 6 picks my-sql
cat > tmp/connector-choices/db-driver.json <<'JSON'
{
  "dependencies": [
    { "groupId": "com.mysql", "artifactId": "mysql-connector-j", "version": "8.4.0" }
  ],
  "driverClass": "com.mysql.cj.jdbc.Driver"
}
JSON

对于 derby:embedded,该文件包含三项;对于使用 PostgreSQL 的 generic,该文件包含一项。步骤 9 的应用程序会读取此文件,并将其中每项应用到 pom.xml。

驱动选择将纳入技术设计。步骤 7 会在“构建时新增项”下显示该选择;用户在步骤 7 的批准即授权步骤 9 编辑 pom.xml。

步骤 7:展示技术设计摘要

[BLOCKER] 仅在完成步骤 1–6 后展示。 每个连接器都必须已起草 GAV(来自 tmp/connector-choices/*.json),每个连接器都必须已记录元数据(来自 tmp/connector-metadata/*.json),并且每项配置都必须已选定提供方。如果缺少其中任何一项,请返回相关步骤——不要在摘要中用“TBD”搪塞。

**Technical Design Summary**

**User Requirement:** "<original user prompt, verbatim>"

**Project Context:**
- Project directory: <absolute path where Phase 2 will create the project>
- Work type: <New / Modification / Post-scaffolding>
- Mule runtime: <mule_version from tmp/mule-dev-env.json>
- Java: <java_version>

**Trigger:**
- Selected: <element name> from <connector or built-in source>
  (e.g., "shopify:on-updated-product-trigger with fixed-frequency 3000ms" or "salesforce:replay-topic-listener from mule-salesforce-connector:10.15.7" or "Built-in Scheduler, every 5 minutes")
- Sources considered: list the `sources[]` entries that were examined via `source-detail` (if any), with one line each
  stating why the source was chosen OR dismissed. If the selected trigger is `<scheduler>` or `<http:listener>` and any
  in-scope connector has a `sources[]` entry, at least one rejection line is required — a TDD whose "Sources considered"
  is empty while a connector source exists is incomplete, and Phase 2 cannot start.
  Example:
  - `shopify:on-updated-product-trigger` — SELECTED: polling source with `scheduling-strategy` child, matches "every 3 seconds" intent.
  - `shopify:on-new-product-trigger` — dismissed: fires only on creation, user wants updates too.
  - `shopify:on-updated-customer-trigger` — dismissed: wrong object (product, not customer).

**Required Connectors:**
1. <System Name>: <GAV> [com.mulesoft.connectors | org.mule.connectors | third-party]
   - Purpose: <one line>
   - Config: <config-name> | Provider: <provider-name>
2. ...

**Build-time additions (auto):**
- `mule-http-connector` — included if the trigger is HTTP Listener OR any Step 6 provider is OAuth-family (callback listener)
- JDBC driver(s) — included if `mule-db-connector` is in scope. List **every** `groupId:artifactId:version` recorded in `tmp/connector-choices/db-driver.json` from Step 6b, plus the driver class. Vague phrasing like "PostgreSQL JDBC driver included" is not acceptable here — the user is approving an explicit build edit and needs the exact coordinates.
  Example:
  - `org.postgresql:postgresql:42.7.11` (driver class `org.postgresql.Driver`) — added as `<dependency>` and `<sharedLibrary>` in `pom.xml`.

**Built-in processors anticipated (if applicable):**
- DataWeave, Logger, error handlers

然后征求明确批准:

<ask_followup_question>
<question>Please review the technical design above. Proceed to build (Phase 2)?</question>
<options>[
  "Yes, proceed to build.",
  "No, I want to change the plan.",
  "No, cancel generation."
]</options>
</ask_followup_question>

[BLOCKER] 在收到明确的“是,继续构建。”之前,请勿进入步骤 8。 如果用户说“否,我想更改计划。”,请询问要更改哪一部分(触发器、连接器、提供方),然后返回相关步骤。如果用户说“否,取消生成。”,请礼貌地停止工作流。

此关卡至关重要:阶段 1 是最后一次发现静默回退到 HTTP、连接器变体错误、触发器错误或缺少澄清问题的机会。阶段 2 一旦开始,项目骨架就会写入磁盘,所有人回退的成本都会更高。摘要是用户纠正方向的机会;请将“否,我想更改计划。”视为一种正常结果,而非例外。

获批后,步骤 8 的第一个操作必须是 commit_connectors.mjs——该脚本会将 tmp/connector-choices/ 中所有阶段 1 草稿提升到固定的 tmp/connector-versions/ 目录,供 dx mule project create 和 pom.xml 读取。切勿跳过;如果固定文件不存在,阶段 2 后续调用 build_deps.mjs / build_gav.mjs 时会失败。

输出: 用户批准继续。


阶段 2:构建

步骤 8:创建项目

第一个操作——将阶段 1 草稿提升为固定版本。 用户刚刚批准了 TDD,因此 tmp/connector-choices/ 中的每项连接器选择现已正式确定。一次性提升所有选择:

node scripts/commit_connectors.mjs
# → copies every tmp/connector-choices/*.json → tmp/connector-versions/*.json
# → exits 1 if no drafts exist (means Step 3 was skipped for some system)

然后读取 tmp/mule-dev-env.json 获取 Mule 版本,并使用 build_deps.mjs 根据磁盘上的固定版本生成完整的 --dependencies 字符串——不要重新输入先前工具输出中的 GAV,也不要为每个连接器内联一次 $(build_gav.mjs ...):

MULE_VERSION=$(jq -r '.mule_version' tmp/mule-dev-env.json)

NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule project create <project-name> \
  --group-id com.example \
  --mule-version "$MULE_VERSION" \
  --dependencies "$(node scripts/build_deps.mjs)" \
  --skip-environment

build_deps.mjs 会读取 tmp/connector-versions/*.json 中的每个固定版本文件,筛除步骤 6b 中的 JDBC 驱动旁文件(db-driver.json),并输出以逗号连接的 GAV 字符串。该目录中的任何固定版本文件都会自动纳入——包括根据下方规则添加的 http.json。

接下来必须执行——使 mule-artifact.json 与已验证的环境保持一致。 脚手架会在 <project-name>/mule-artifact.json 中写入默认的 minMuleVersion 和 javaSpecificationVersions,但它们不一定与步骤 1 中验证的运行时和 Java 版本一致。PRD 要求此技能验证并更新这两项。dx mule project create 成功后,立即运行写入器,使描述符反映 tmp/mule-dev-env.json 中已解析的环境:

node scripts/write_mule_artifact_env.mjs <project-name>

write_mule_artifact_env.mjs 会从 tmp/mule-dev-env.json 读取 mule_version 和 java_version,并将 minMuleVersion 和 javaSpecificationVersions(仅 Java 主版本号)写入 <project-name>/mule-artifact.json,同时保留描述符中的其他所有键。此操作具有幂等性,可安全重复运行。不要手动编辑 mule-artifact.json 中的运行时/Java 值;请执行此步骤,确保这些值来自已验证的环境,而不是凭记忆填写或等构建失败后再处理。

为什么使用一个包装脚本,而不是内联 N 个 $(build_gav.mjs …) 替换: 按照上述调用规则使用绝对脚本路径时,每个内联的 $(…) 约有 165 个字符,因此包含 4 个连接器的项目会生成一条超过 1000 个字符的 dx mule project create 命令。Dev Agent 终端调用框架会在命令过长时丢失完成标记,并导致整个轮次停滞,直至触发 2 分钟超时。无论涉及多少个连接器,build_deps.mjs 都能将命令控制在约 250 个字符以内。 经批准的技术设计摘要中出现的每个连接器,在运行 build_deps.mjs 之前都必须在 tmp/connector-versions/ 中有一个固定版本——commit_connectors.mjs 已经将它们放在那里。以下两种情况会额外添加一个连接器,超出 TDD 中明确列出的系统:

条件添加的连接器
第 5 步选择的触发器是 HTTP Listener(流程中包含 <http:listener>)mule-http-connector
第 6 步选择了任意 OAuth 系列提供方(OAuth、JWT、auth-code)mule-http-connector(用于 OAuth 回调)
任意事件监听器源触发器(例如 <s3:new-object-listener>、<salesforce:replay-topic-listener>)不添加所属源连接器之外的连接器——它已在 TDD 中

如果适用任一 HTTP 触发器条件,且你尚未在第 3 步选择 HTTP,请在运行 dx mule project create 之前运行 get_latest_connector.mjs mule-http-connector http + pick_connector.mjs http <gav> + commit_connectors.mjs,以便 build_deps.mjs 扫描目录时 tmp/connector-versions/http.json 已存在。缺少该文件会导致首次构建失败,例如出现 Can't resolve http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd;虽然可以自行修复,但会多花一轮。

版本的唯一来源(来自第 3 步):--dependencies 中的每个 GAV 都必须来自 tmp/connector-versions/*.json 文件。不要在 --dependencies 字符串中直接写入字面版本号,例如 com.mulesoft.connectors:mule-amazon-s3-connector:6.6.0——如果绕过 build_deps.mjs,且字面版本号与辅助脚本返回的版本不同,mvn clean package 将因“未找到”而失败;而且该失败通常无法自行修复,因为该版本是虚构的。

已创建的项目结构:

  • pom.xml(包含依赖项的 Maven 配置)
  • mule-artifact.json(构件元数据;minMuleVersion 和 javaSpecificationVersions 已通过上文的 write_mule_artifact_env.mjs 步骤与已验证环境保持一致)
  • src/main/mule/<project-name>.xml(流程定义)
  • src/main/resources/(配置文件)

第 9 步:将 JDBC 驱动添加到 pom.xml

驱动 GAV 已在第 6b 步选定,并于第 7 步获得用户批准。第 9 步会读取 tmp/connector-choices/db-driver.json,并将其中的每个条目应用到 <project-name>/pom.xml——一个条目会生成一个 <dependency> 块和一个 <sharedLibrary> 块。如果 mule-db-connector 不在范围内,请完全跳过此步骤。

如果 mule-db-connector 在范围内,但缺少 tmp/connector-choices/db-driver.json,请返回第 6b 步——不要在此处臆造驱动,也不要过早假定驱动。

对于 db-driver.json 的 dependencies[] 数组中的每个条目,都添加以下两项:

  1. 在 <dependencies> 内添加 <dependency>,内容逐字取自 sidecar 文件:
<dependency>
    <groupId>{groupId}</groupId>
    <artifactId>{artifactId}</artifactId>
    <version>{version}</version>
</dependency>
  1. 在 mule-maven-plugin 的 <configuration><sharedLibraries> 内添加 <sharedLibrary>——groupId/artifactId 逐字复制自上方的 <dependency>(此处不包含版本号)。
<configuration>
    <sharedLibraries>
        <sharedLibrary>
            <groupId>{groupId}</groupId>
            <artifactId>{artifactId}</artifactId>
        </sharedLibrary>
        
    </sharedLibraries>
</configuration>

步骤 10:验证 HTTP Connector(OAuth/HTTP Listener 防御性检查)

步骤 8 的 --dependencies 已经包含 mule-http-connector,前提是步骤 5 选择了 HTTP Listener,或步骤 6 选择了 OAuth 系列提供程序——这是因为第一阶段批准的 TDD 已明确体现这一点。通常,此步骤只是防御性空操作检查:运行辅助程序,以防 TDD 因某种原因遗漏了 HTTP 依赖。

如果所选提供程序中没有任何一个匹配 oauth|jwt|auth-code|authorization-code,且触发器也不是 HTTP Listener,请完全跳过此步骤。以“以防万一”为由运行会消耗一次交互。

对于 OAuth / HTTP Listener 场景,运行幂等辅助程序:

node <skill-dir>/scripts/maybe_add_http_connector.mjs \
  --project ./<project-name> \
  "oauth-user-pass"    # one argument per Step-6 provider

<skill-dir> 是你在“skill is now active”消息中收到的绝对路径(包含此 SKILL.md 的目录)。使用绝对路径可以避免因相对路径调用而出现“No such file or directory”错误。

如果任何提供程序参数匹配 oauth、jwt、auth-code 或 authorization-code(不区分大小写),脚本将:

  1. 如果 Agent 已在步骤 3 中选择 HTTP,则复用 <project-name>/tmp/connector-choices/http.json;否则运行 get_latest_connector.mjs mule-http-connector http,并通过 pick_connector.mjs http <gav> 草拟首行结果——HTTP 搜索结果明确,因此无需询问用户。
  2. 在 <project-name>/pom.xml 的 </dependencies> 之前插入一个 <dependency> 块(包含 <classifier>mule-plugin</classifier>)。
  3. 如果 HTTP connector 已存在,则不执行任何操作。

手动备用方案——如果 connector 文档提到“callback URL”或“redirect URI”,或者步骤 11 的 config-detail 显示存在 oauth-callback-config 子元素,也要添加 HTTP connector。缺少 HTTP connector 时,构建会失败并显示:The content of element '<connector>:<connection-provider>' is not complete。


步骤 11:获取配置详情

第一阶段的步骤 6 已将 config-detail 输出保存到 tmp/connector-metadata/<nickname>-config.json。从该文件读取:

cat tmp/connector-metadata/sfdc-config.json

仅当缓存文件缺失时才重新调用 CLI(如果第一阶段正确运行,这种情况不应发生)。标志语义说明:--name 指连接提供程序,--config-name 指配置:

NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(node scripts/build_gav.mjs tmp/connector-versions/sfdc.json)" \
  --type connection-provider \
  --name basic-connection \
  --config-name sfdc-config \
  --output json

响应结构(相同的 attributes + childElements 模式):

{
  "name": "sfdc-config",
  "prefix": "salesforce",
  "elementName": "sfdc-config",
  "attributes": [ { "attributeName": "name", "required": true } ],
  "childElements": [
    { "paramName": "expirationPolicy", "prefix": "salesforce", "elementName": "expiration-policy" }
  ],
  "connectionProviders": [
    {
      "name": "basic-connection",
      "prefix": "salesforce",
      "elementName": "basic-connection",
      "attributes": [
        { "attributeName": "username", "required": true },
        { "attributeName": "password", "required": true },
        { "attributeName": "securityToken", "required": true }
      ],
      "childElements": [
        { "paramName": "reconnection", "prefix": "mule", "elementName": "reconnection" }
      ]
    }
  ]
}

检查所选连接提供程序的 attributes 和 childElements:

jq '.connectionProviders[0].childElements[] | select(.paramName == "oauthCallbackConfig")' tmp/connector-metadata/sfdc-config.json

连接提供程序有以下两种模式之一:

  1. 属性模式(例如 Salesforce basic-connection):

    <salesforce:basic-connection
        username="${salesforce.username}"
        password="${salesforce.password}"
        securityToken="${salesforce.securityToken}" />
    
  2. 子元素模式(例如 Slack OAuth):

    <slack:slack-auth-connection>
        <slack:oauth-authorization-code
            consumerKey="${slack.consumerKey}"
            consumerSecret="${slack.consumerSecret}" />
    </slack:slack-auth-connection>
    

**生成 XML 时:**如果 attributes 有条目,则在连接元素上使用属性;如果 attributes 为空但 childElements 有条目,则使用嵌套子元素。切勿硬编码结构,务必使用元数据。


步骤 12:创建配置文件

根据步骤 11 的元数据创建配置文件。

src/main/resources/config.yaml——提取必需的属性和子元素参数,并生成占位符:

salesforce:
  username: "[email protected]"
  password: "password"
  securityToken: "token"

slack:
  consumerKey: "your-consumer-key"
  consumerSecret: "your-consumer-secret"

配置 XML——完全由元数据决定其结构:

<configuration-properties file="config.yaml" />

<salesforce:sfdc-config name="salesforceConfig">
    <salesforce:basic-connection
        username="${salesforce.username}"
        password="${salesforce.password}"
        securityToken="${salesforce.securityToken}" />
</salesforce:sfdc-config>

<slack:config name="slackConfig">
    <slack:slack-auth-connection>
        <slack:oauth-authorization-code
            consumerKey="${slack.consumerKey}"
            consumerSecret="${slack.consumerSecret}" />
        <slack:oauth-callback-config
            listenerConfig="HTTP_Listener_config"
            callbackPath="/slack/callback"
            authorizePath="/slack/authorize" />
    </slack:slack-auth-connection>
</slack:config>

<http:listener-config name="HTTP_Listener_config">
    <http:listener-connection host="0.0.0.0" port="8081" />
</http:listener-config>

根据元数据生成所有 childElements[] 条目——连接提供程序的 elementName、attributes[] 数组,以及每一个 childElements[] 条目。对于 OAuth connector,oauth-callback-config 需要一个 listenerConfig 属性,用于引用 http:listener-config。缺少必需的 childElements 会导致构建失败。


步骤 13:获取操作/源详情

对于流程将调用的每个操作,获取其元数据:

node scripts/describe_connector.mjs sfdc --type operation --name query

响应结构(与 config-detail 相同的 attributes + childElements 模式):

{
  "name": "query",
  "prefix": "salesforce",
  "elementName": "query",
  "attributes": [
    { "attributeName": "config-ref", "required": true },
    { "attributeName": "target" },
    { "attributeName": "targetValue", "defaultValue": "#[payload]", "expressionRequired": true }
  ],
  "childElements": [
    { "paramName": "salesforceQuery", "prefix": "salesforce", "elementName": "salesforce-query", "required": true }
  ]
}

对于事件驱动的触发器(步骤 5 选择的触发器是连接器源,而非内置 Scheduler 或通用 HTTP Listener),还要获取源详情:

node scripts/describe_connector.mjs sfdc --type source --name replay-topic-listener

结构同样为 attributes + childElements。始终包含所有 required: true 的属性和子元素。

脚本还会写入 tmp/connector-errors/<nick>.<op>.json(每个操作的错误子集),而步骤 4 的调用会填充 tmp/connector-errors/<nick>.json(连接器范围内的错误并集)。这些文件中的错误类型是步骤 14 中 <on-error-propagate type="..."> 和 <on-error-continue type="..."> 唯一允许使用的 NS:ID 字符串。不要根据连接器词汇臆造标识符——SFTP:FILE_NOT_FOUND 不是真实类型;实际名称是 SFTP:FILE_DOESNT_EXIST。

**<raise-error> 有更严格的规则:**它只能抛出 MULE:* 错误或 APP:* 这类自定义命名空间错误,绝不能使用连接器命名空间(例如 EC2:*、SFTP:*、SALESFORCE:*)。连接器错误类型可以在 <on-error-*> 处理器中被捕获,但不能被抛出。如果需要抛出重试耗尽错误,请使用 <raise-error type="MULE:RETRY_EXHAUSTED">,而不是 <raise-error type="EC2:RETRY_EXHAUSTED">。步骤 16 的验证器会通过机制检查这两条规则。

生成操作 XML(示例):

<salesforce:query config-ref="salesforceConfig">
    <salesforce:salesforce-query>
        SELECT Id, Name, Amount, StageName
        FROM Opportunity
        WHERE StageName = 'Closed Won' AND CloseDate = TODAY
    </salesforce:salesforce-query>
</salesforce:query>

步骤 14:生成完整流程

使用步骤 11、13 中的元数据,在 src/main/mule/<project-name>.xml 中生成完整流程。切勿使用硬编码结构。

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
      xmlns:salesforce="http://www.mulesoft.org/schema/mule/salesforce"
      xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core"
      xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
        http://www.mulesoft.org/schema/mule/salesforce http://www.mulesoft.org/schema/mule/salesforce/current/mule-salesforce.xsd
        http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd">
    

    <configuration-properties file="config.yaml" />

    <salesforce:sfdc-config name="salesforceConfig">
        <salesforce:basic-connection
            username="${salesforce.username}"
            password="${salesforce.password}"
            securityToken="${salesforce.securityToken}" />
    </salesforce:sfdc-config>

    <flow name="integration-flow">
        <scheduler>
            <scheduling-strategy>
                <fixed-frequency frequency="300000"/>
            </scheduling-strategy>
        </scheduler>

        <salesforce:query config-ref="salesforceConfig">
            <salesforce:salesforce-query>
                SELECT Id, Name, Amount FROM Opportunity WHERE StageName = 'Closed Won'
            </salesforce:salesforce-query>
        </salesforce:query>

        <ee:transform>
            <ee:message>
                <ee:set-payload><![CDATA[%dw 2.0
output application/json
---
payload]]></ee:set-payload>
            </ee:message>
        </ee:transform>
    </flow>
</mule>

xsi:schemaLocation 构造规则:

对于 pom.xml 中有对应 <dependency> 的每个模块或连接器命名空间(core、ee/core 以及每个连接器,如 http、salesforce、db、anypoint-mq 等),在 xsi:schemaLocation 中恰好包含一条条目。每条条目由命名空间 URI 和该命名空间 XSD 的 URL 组成,两者以空格分隔。

绝不能出现在 xsi:schemaLocation 中的命名空间(完整列表):

命名空间xmlns:* 声明从 schemaLocation 中排除的原因
doc(http://www.mulesoft.org/schema/mule/documentation)使用任何 doc:name / doc:description 时必需(步骤 15)该 URL 下不存在 XSD。doc:* 属性由 mule-core 通过 anyAttribute 接受。添加 schemaLocation 条目会导致 mvn clean package 在 process-classes 阶段失败,并提示 Can't resolve …/mule-documentation.xsd。
xsi(http://www.w3.org/2001/XMLSchema-instance)使用 xsi:schemaLocation 属性本身所必需xsi 是 W3C 标准命名空间,不是 Mule 架构。

命名空间与依赖项的对应关系:如果通过 xmlns:X 声明了某个命名空间,但 pom.xml 中没有对应的 <dependency>,正确的修复方式是添加依赖项或移除该命名空间,而不是添加一个指向不存在的 XSD 的 schemaLocation 条目。这与上述 mule-documentation.xsd 问题的失败模式相同,但根本原因不同;当声明了 mule-scripting、mule-objectstore、mule-validation、mule-http 命名空间,却没有相应连接器依赖项时,同样适用此规则。

生成规则:

  • 所有标签使用元数据中的确切 elementName
  • 所有属性使用元数据中的确切 attributeName
  • 包含每个 required: true 属性和子元素
  • 使用元数据中的正确命名空间前缀
  • 引用步骤 12 中的 config-ref 名称
  • 严格按照 childElements[] 数组中的顺序生成子元素——XSD 架构会强制执行严格的顺序
  • 不要添加元数据中不存在的包装元素(例如,使用 <reconnect>,而不是 <reconnection-strategy><reconnect>)
  • 在配置连接级别而非操作级别设置重连(除非元数据明确将其包含在操作级别)
  • 根据 pom.xml 中的模块/连接器 <dependency> 列表构建 xsi:schemaLocation;绝不包含 doc 或 xsi。参见上方规则。

第 15 步:为画布上可见的元素添加 doc:name 和 doc:description

每个作为可见节点显示在流程画布上的 XML 元素都必须具有 doc:name 和 doc:description 属性。doc:description 会显示为画布节点上的标签文本(如果存在,则优先于 doc:name),因此请保持简洁且有意义。

前提条件 — 命名空间声明:

使用任何 doc:* 属性都需要在 <mule> 根元素上添加 xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"(参见第 14 步)。如果缺少该声明,每个 doc:name 都会触发 The prefix "doc" for attribute "doc:name" associated with an element type "..." is not bound。解决方法是添加 xmlns:doc 属性,并且根据第 14 步中的 xsi:schemaLocation 构造规则,不要将 mule-documentation 添加到 xsi:schemaLocation。

请根据 config-detail、operation-detail 和 source-detail 元数据返回的 description 字段、XML 结构和元素用途、流程注释以及整体集成上下文,推断描述内容。

规则:

  • 使用通俗易懂的句子,说明该元素在此集成中的用途,而非泛泛的文档说明
  • 最多 125 个字符 — 确保画布上的标签清晰易读
  • 使用主动语态;具体说明元素的作用
  • 包含相关细节:端点、对象类型、字段名称、调度间隔
  • 除 doc:description 外,还需添加简短标签 doc:name

为以下画布上可见的元素类型添加 doc:name 和 doc:description:

元素类型示例
流程/子流程<flow>、<sub-flow>
源/触发器<http:listener>、<scheduler>、<salesforce:modified-object-listener>
操作/处理器<logger>、<set-variable>、<set-payload>、<ee:transform>、<http:request>、<salesforce:query>、<flow-ref>、任何连接器操作
作用域/容器<choice>、<scatter-gather>、<round-robin>、<first-successful>、<foreach>、<until-successful>、<parallel-foreach>、<try>
分支/路由<when>、<otherwise>、<route>
全局配置<salesforce:sfdc-config>、<http:listener-config>、<configuration-properties>

不要为内部属性元素添加 doc:description —— 这些元素不会显示在画布上:

  • <scheduling-strategy>、<fixed-frequency>、<cron>
  • <http:body>、<http:headers>、<http:query-params>
  • <reconnection>、<reconnect>
  • <non-repeatable-stream>、<repeatable-in-memory-stream>
  • 操作专用的子内容元素(例如 <salesforce:salesforce-query>、<slack:chatpost-message-content>)
  • 转换内部元素(<ee:message>、<ee:set-payload>、<ee:variables>、<ee:set-variable>)

示例 — 添加 doc:name 和 doc:description 后:

<flow name="contact-sync-flow"
    doc:name="Contact Sync Flow"
    doc:description="Receives HTTP requests and syncs modified Salesforce contacts to Slack">

    <http:listener config-ref="httpConfig" path="/contacts" allowedMethods="GET"
        doc:name="HTTP GET /contacts"
        doc:description="Receives incoming HTTP GET requests on the /contacts endpoint" />

    <salesforce:query config-ref="salesforceConfig"
        doc:name="Query modified contacts"
        doc:description="Queries Salesforce for Contact records modified since the last sync">
        <salesforce:salesforce-query>
            SELECT Id, Name, Email FROM Contact WHERE LastModifiedDate > :lastSync
        </salesforce:salesforce-query>
    </salesforce:query>

    <choice
        doc:name="Contacts found?"
        doc:description="Routes processing based on whether any modified contacts were returned">
        <when expression="#[sizeOf(payload) > 0]"
            doc:name="Has contacts"
            doc:description="Processes each contact when results are not empty">
            <foreach
                doc:name="For each contact"
                doc:description="Iterates through each modified contact to send to Slack">
                <ee:transform
                    doc:name="Map to JSON"
                    doc:description="Transforms Salesforce Contact into a simplified JSON structure with id, name, and email">
                    <ee:message>
                        <ee:set-payload><![CDATA[%dw 2.0
output application/json
---
payload map { id: $.Id, name: $.Name, email: $.Email }]]></ee:set-payload>
                    </ee:message>
                </ee:transform>

                <slack:create-chatpost-message config-ref="slackConfig"
                    doc:name="Post to Slack"
                    doc:description="Sends the transformed contact data as a message to the configured Slack channel">
                    <slack:chatpost-message-content>#[payload]</slack:chatpost-message-content>
                </slack:create-chatpost-message>
            </foreach>
        </when>
        <otherwise
            doc:name="No contacts"
            doc:description="Handles the case when no modified contacts are found">
            <logger level="INFO" message="No contacts found"
                doc:name="Log no contacts"
                doc:description="Logs that no modified contacts were found in this request" />
        </otherwise>
    </choice>
</flow>

第 16 步:构建并验证

构建前验证

作为构建响应中的第一个工具调用,运行:

node <skill-dir>/scripts/validate_before_build.mjs ./<project-name>

验证器会运行三项静态检查(错误类型白名单、命名空间与依赖项一致性、规范 XSD URL 格式),并在发现任何违规时以状态码 1 退出。如果验证失败,请修复报告的问题(重新检查步骤 14),重新运行验证器,并且只有在验证器以状态码 0 退出后,才继续执行 mvn clean package。这样可以遵守每个响应只执行一条命令的要求——验证器和 mvn 必须放在不同的响应中。

cd <project-name>
mvn clean package

成功标志:target/<project-name>-1.0.0-SNAPSHOT-mule-application.jar。

先构建再验证协议(请勿跳过步骤):

  1. 将 mvn clean package 作为此响应中的唯一工具调用。不要同时附带完成信号、后续 ls 或任何其他工具调用。停止并等待构建输出。
  2. 必须等待 mvn clean package 成功。请勿臆测构建是否完成。终端输出是唯一可信来源。
  3. 阅读输出:
    • 如果最后一段输出包含 BUILD SUCCESS,请在新的响应中继续执行步骤 17(清理)。
    • 如果最后一段输出包含 BUILD FAILURE,找到以 Failed to execute goal ... 开头的 [ERROR] 行,诊断根本原因,编辑有问题的文件(重新检查相关的前置步骤,例如 XML 结构对应步骤 14,文档属性和命名空间对应步骤 15),然后返回本协议的步骤 1。诊断问题并应用正确修复时,必须遵循“最佳实践”部分。切勿臆测解决方案。始终以 tmp/ 文件夹下的连接器元数据源和操作为准。

对 pom.xml、流程 XML 或配置 XML 中的文件进行任何编辑后——无论是通过 Edit、Write、<write_to_file> 还是 <replace_in_file>——都必须重新运行 mvn clean package,然后才能声明完成。此要求适用于所有编辑工具,无一例外;使用何种工具进行修改,不会改变这一要求。编辑后不重新验证,就可能悄然交付有问题的构建。


步骤 17:清理工作区 tmp/

**前置条件:**紧邻本响应之前的响应必须是构建响应(符合步骤 16),且其返回的输出包含 BUILD SUCCESS。如果不满足此条件,请勿进入步骤 17——返回步骤 16。

工作区相对路径下的 tmp/ 目录用于承载阶段 1 → 阶段 2 的协作状态,包括 tmp/mule-dev-env.json、tmp/connector-choices/、tmp/connector-versions/、tmp/connector-metadata/、tmp/connector-errors/,以及由 get_latest_connector.mjs 和 describe_connector.mjs 写入的 tmp/mule-dev-*-XXXXXX 临时文件。构建通过后,这些文件就完成了用途——它们只是为了让各步骤之间能够传递状态,并非用户需要的交付物。

请在单独的响应中将删除目录作为唯一的工具调用:

rm -r tmp/

操作要求:

  • 仅删除工作区根目录下的 tmp/。

第 18 步:声明完成

前置条件: 前两条响应必须依次为:(a)构建响应(参见第 16 步),返回 BUILD SUCCESS;紧接着(b)清理响应(参见第 17 步),执行了 rm -r tmp/。如果缺少其中任何一条,请勿进入第 18 步——返回补做缺少的步骤。无论最后尝试成功还是失败,都必须清理 tmp/ 文件夹。

规范:

  • 完成信号是本次响应中唯一的工具调用。 此处不要运行 mvn。不要添加后续 shell 命令。构建已在上一条响应中执行并验证。
  • 即使你认为后续编辑能解决问题,也不要在出现 BUILD FAILURE 后声明完成。 在单独的响应中重新运行 mvn clean package(第 16 步),确认返回 BUILD SUCCESS,然后在下一条响应中声明完成。
  • 如果最近一次构建实际上并未执行,不要声明完成(例如,只显示了命令,但没有返回结果)。在单独的响应中重新运行构建并等待结果。

完成消息内容——务必简洁。 用户可以看到文件和构建日志。完成消息用于证明构建已通过,不是宣传文案。只包含以下内容,不得添加其他内容:

  1. 成功构建的制品路径:target/<project-name>-1.0.0-SNAPSHOT-mule-application.jar
  2. 一句话说明集成的用途(例如:“每隔 5s 轮询 S3,并将新对象事件发布到 JMS 队列”)。
  3. 用户仍需填写的 config.yaml 键(凭据、存储桶名称、队列名称)——以简短项目符号列表列出。这是磁盘上尚未显示的唯一信息。

**不要包含:**冗长的“已实现功能”部分、脱敏的 JSON 负载示例、“后续步骤”(用户会在准备好时自行部署)或回顾表格。


最佳实践

1. 动态连接器版本。 完整要求请参见第 3 步的“版本事实来源规则”。

  • ✅ 阶段 1:get_latest_connector.mjs mule-salesforce-connector sfdc → list → pick_connector.mjs sfdc <gav> → 起草到 tmp/connector-choices/sfdc.json
  • ✅ 阶段 2:commit_connectors.mjs(第 8 步的首个操作)→ build_deps.mjs,用于生成 dx mule project create --dependencies 字符串;或者在其他位置使用 build_gav.mjs tmp/connector-versions/<name>.json 获取单个 GAV
  • ❌ 硬编码的字面值:com.mulesoft.connectors:mule-salesforce-connector:10.20.0
  • ❌ 从 references/connector-catalog.md 复制粘贴(仅为快照,会逐渐过时)
  • ❌ 仅将搜索词作为 GAV:salesforce

2. 基于元数据生成 XML。 切勿手动编写 XML。始终使用 operation-detail、config-detail、source-detail 中的元数据:

  • attributes → 标签上的 XML 属性(原样使用 attributeName)
  • childElements → 嵌套 XML 元素(将 prefix:elementName 用作标签)
  • 始终包含每个 required: true 属性和子元素
  • 除非有特定需求,否则可以省略可选条目
  • description → 了解参数的作用
  • allowedValues → 限定取值
  • defaultValue → 如果可接受,则跳过

3. 每次都优先使用系统专用连接器。 在退而使用 HTTP 之前,始终先在 Exchange 中搜索专用连接器——这里的“之前”是指真正先搜索,而不是“在我做出决定之前”。该技能的规范是:只有 tmp/connector-choices/ 中的阶段 1 草案(或提交后固定在 tmp/connector-versions/ 中的版本),才能作为声明“系统 X 已覆盖”的唯一依据;只有辅助脚本以 1 退出,才能作为声明“没有专用连接器,将回退到 HTTP”的唯一依据。

4. 生成前验证元数据。 使用 describe-connector 查看功能,使用 config-detail / operation-detail / source-detail 获取精确规格。


常见集成模式

#1 HTTP API → 查询 → 响应: http:listener → salesforce:query → ee:transform → response(组件:HTTP + Salesforce/Database)

#2 定时同步 → 查询 → 转换 → 通知: scheduler → salesforce:query → ee:transform → slack:post-message(组件:Scheduler + Salesforce + Slack)

#3 定时同步 → 查询 → 转换 → 批量插入: scheduler → query → ee:transform → foreach → db:insert(组件:Scheduler + 源系统 + Database)

#4 事件监听器 → 转换 → 下游系统: <connector>:<event-source> → ee:transform → target-connector operation(组件:带有原生监听器的源系统 + 目标系统)。当源连接器在 sources[] 中公开了真正的事件源时,这种模式尤其有效——请使用该事件源,而不要通过 Scheduler 轮询。


故障排查

未设置 JAVA_HOME: export JAVA_HOME=$(/usr/libexec/java_home -v 11)

找不到 anypoint-cli-v4: npm install -g @mulesoft/anypoint-cli-v4

找不到 DX 插件: npm install -g @salesforce/anypoint-cli-dx-mule-plugin

找不到连接器: 检查拼写 · 尝试 mule-<name>-connector 和 mule4-<name>-connector · 确认与 Mule 4 兼容。

选择了错误的连接器: 使用具体的搜索词(例如 mule-http-connector,而不是 http)。scripts/get_latest_connector.mjs 会按词元重合度评分,并要求至少匹配一个词元——无效的搜索会以状态码 1 退出,而不会返回随机结果。

需要运行时路径: 首次使用 dx mule describe-connector 或相关命令时,系统会提示输入运行时位置。路径会保存到 ~/.mule-dx/config.json。

缺少数据库驱动程序: 如果 Step 6b 没有记录 tmp/connector-choices/db-driver.json,请返回 Step 6b 进行选择(当提供程序为 generic/data-source/derby 时会出现提示)。如果存在此 sidecar 文件,但 pom.xml 缺少相应条目,请返回 Step 9——它会读取该文件,并将每个条目应用为一组 <dependency> + <sharedLibrary>。请勿在 Step 9 臆造驱动程序 GAV。

The mule application does not contain the following shared libraries: [<artifactId>:<groupId>]: <dependency> 块已存在,但 mule-maven-plugin 中匹配的 <sharedLibrary> 块要么缺失,要么 groupId/artifactId 不匹配。每个驱动程序依赖项都需要两者;请参见 Step 9。

Derby 驱动程序布局: 请参见 references/jdbc-drivers.md。Derby 10.15+ 拆分为多个构件(嵌入式模式使用 derby + derbyshared + derbytools;网络客户端使用 derbyclient + derbyshared),因此 <sharedLibrary> 列表包含多个条目,而非一个。


快速参考

下方的 <skill-dir> 是你在“skill is now active”消息中获得的绝对路径。请始终使用该路径——不要构造相对的 ../scripts/... 路径。

# Step 1: validate toolchain (CLI, DX plugin, Java 11+, Mule runtime presence) - Validation-only
node <skill-dir>/scripts/validate_prerequisites.mjs

# Step 3: connector search — list, decide, draft (one loop per system; search
# EVERY named system including mid-market SaaS; don't pre-judge as "no connector")
node <skill-dir>/scripts/get_latest_connector.mjs <search-term> [<nickname>]   # prints ranked GAVs to stdout
# ... agent reads list, decides (or AskUserQuestion for real variant ambiguity), then:
node <skill-dir>/scripts/pick_connector.mjs <nickname> <groupId:assetId:version>   # drafts to tmp/connector-choices/

# Step 4: describe connectors (Phase 1 — wrapper saves JSON + echoes sources[] digest)
node <skill-dir>/scripts/describe_connector.mjs <nickname>   # one per connector

# Step 6: connection-provider detail (Phase 1 — also cached for Phase 2).
# Flag semantics: --name = connection provider, --config-name = config name.
GAV_A="$(node <skill-dir>/scripts/build_gav.mjs tmp/connector-choices/a.json)"
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector --connector "$GAV_A" \
  --type connection-provider --name <prov> --config-name <name> --output json \
  > tmp/connector-metadata/a-config.json

# Step 8: promote drafts to pinned versions, then create the real project (Phase 2)
node <skill-dir>/scripts/commit_connectors.mjs   # tmp/connector-choices/ → tmp/connector-versions/
MULE_VERSION=$(jq -r '.mule_version' tmp/mule-dev-env.json)
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule project create <name> \
  --group-id com.example \
  --mule-version "$MULE_VERSION" \
  --dependencies "$(node <skill-dir>/scripts/build_deps.mjs)" \   # reads every tmp/connector-versions/*.json pin
  --skip-environment   # scratch projects don't need environment resolution
node <skill-dir>/scripts/write_mule_artifact_env.mjs <name>   # align mule-artifact.json minMuleVersion/javaSpecificationVersions

# Step 10: OAuth → HTTP defensive check (only when Step 6 chose OAuth/JWT/auth-code
# or the trigger is HTTP Listener)
node <skill-dir>/scripts/maybe_add_http_connector.mjs --project ./<name> "<provider1>" "<provider2>"

# Step 13: operation / source details (Phase 2 — wrapper saves JSON + caches errorTypes)
node <skill-dir>/scripts/describe_connector.mjs <nickname> --type operation --name <op>
node <skill-dir>/scripts/describe_connector.mjs <nickname> --type source    --name <src>

# Step 16: pre-mvn validation — error-type whitelist + namespace↔dep parity + XSD shape
node <skill-dir>/scripts/validate_before_build.mjs ./<project>

评论

支持 Markdown