build-mule-integration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Mule Developer

Mule开发人员

Build professional Mule integrations with intelligent connector discovery and data-driven XML generation.
借助智能连接器发现和数据驱动的XML生成功能,构建专业的Mule集成。

When to Use This Skill

何时使用此技能

Use this skill when users request:
  • "Create a Mule app/integration/flow"
  • "Build integration between X and Y"
  • "Sync data from X to Y" (e.g., "Salesforce to Slack", "MySQL to Salesforce")
  • "Query Salesforce", "Send Slack notifications"
  • "Schedule jobs", "Poll data every N minutes"
  • "Create webhooks", "Build event-driven flows"
Trigger keywords: 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.
Always search Exchange for system-specific connectors first. Most SaaS applications have dedicated connectors.

当用户提出以下请求时使用此技能:
  • "创建一个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应用都有专用连接器。

Prerequisites

前提条件

bash
anypoint-cli-v4 --version
anypoint-cli-v4 dx --help
echo $JAVA_HOME && java -version   # Java 11+
anypoint-cli-v4 conf
If tools are missing:
bash
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>

bash
anypoint-cli-v4 --version
anypoint-cli-v4 dx --help
echo $JAVA_HOME && java -version   # Java 11+
anypoint-cli-v4 conf
如果缺少工具:
bash
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>

Bundled scripts

捆绑脚本

This skill ships small bash scripts under
scripts/
. Invoke them with the
Bash
tool — do not inline their contents into a response. The scripts persist their output to disk so later steps can consume it mechanically and are not at the mercy of shell variables that vanish when a Bash tool call returns:
ScriptPurposeOutput location
scripts/validate_prerequisites.sh
Step 1 — validate toolchain (CLI, DX plugin, Java 11+, JAVA_HOME, Mule runtime presence). Validation-ONLY
tmp/mule-dev-env.json
(workspace-relative; contains
mule_version
,
runtime_path
,
errors[]
, ...)
scripts/get_latest_connector.sh <search> [nickname]
Step 3 — search Exchange and print ranked connector candidates (
groupId:assetId:version
, one per line, no score, no winner cue). Writes nothing.
stdout only
scripts/pick_connector.sh <nickname> <gav>
Step 3 — record the agent's chosen GAV (after reasoning or an
AskUserQuestion
) as a draft
tmp/connector-choices/<nickname>.json
(
{groupId, assetId, version}
)
scripts/commit_connectors.sh
Step 8 (post-TDD-approval) — promote every draft under
tmp/connector-choices/
to the pinned
tmp/connector-versions/
directory that Phase 2 reads
tmp/connector-versions/*.json
scripts/build_gav.sh <json>
Turn a saved connector JSON into its
groupId:assetId:version
string
stdout
scripts/build_deps.sh [versions-dir]
Step 8 — read every connector pin in
tmp/connector-versions/
and emit a comma-joined GAV string, ready for
dx mule project create --dependencies
. Skips
db-driver.json
and any non-pin file.
stdout
scripts/describe_connector.sh <nickname> [--type operation|source --name <name>]
Step 4 (no flags) / Step 13 (with flags) — run
dx mule describe-connector
for the drafted GAV, save full JSON, echo digest to stdout, AND cache
.errorTypes
to
tmp/connector-errors/
for the Step 16 validator
tmp/connector-metadata/<nick>[-<name>].json
+
tmp/connector-errors/<nick>[.<name>].json
+ digest on stdout
scripts/validate_before_build.sh <project-dir>
Step 16 pre-mvn — error-type whitelist (Cluster D), namespace↔dependency parity (Cluster A2–A5), canonical XSD URL shapestderr + non-zero exit on first violation
scripts/maybe_add_http_connector.sh --project <dir> <providers...>
Phase 2 — defensive check that HTTP connector is present when OAuth providers were chosen; edits
<dir>/pom.xml
<dir>/pom.xml
Invoke scripts by the absolute path you were given in the "skill is now active" message (it is the directory containing this
SKILL.md
). Do not construct relative paths like
../scripts/...
— Cline's working directory shifts across turns and relative paths have produced "No such file or directory" errors in real runs. The inline step examples below write
scripts/...
as shorthand; substitute
<skill-dir>/scripts/...
when you actually execute them.
Why scripts instead of inline bash: in earlier iterations connector search was a shell function defined inside a single
Bash
tool call. When the call returned the subshell died and the resolved GAV went with it. By the time a later step assembled
dx mule project create
, the only trace of the version was in scrolled-past tool output — and the agent frequently pasted a fictional version from training-time memory instead. Persisting to a file on disk makes the version something we can
jq
at the command site, which removes that failure mode entirely.

此技能在
scripts/
目录下附带小型bash脚本。请使用
Bash
工具调用它们——不要将其内容内联到响应中。脚本会将输出持久化到磁盘,以便后续步骤可以机械地使用,而不会依赖于Bash工具调用返回后消失的shell变量:
脚本用途输出位置
scripts/validate_prerequisites.sh
步骤1 — 验证工具链(CLI、DX插件、Java 11+、JAVA_HOME、Mule运行时是否存在)。仅用于验证
tmp/mule-dev-env.json
(相对于工作区;包含
mule_version
runtime_path
errors[]
等)
scripts/get_latest_connector.sh <search> [nickname]
步骤3 — 搜索Exchange并打印排名靠前的连接器候选(
groupId:assetId:version
,每行一个,无分数,无选中提示)。不写入任何内容。
仅输出到stdout
scripts/pick_connector.sh <nickname> <gav>
步骤3 — 将代理选择的GAV(经过推理或
AskUserQuestion
后)记录为草稿
tmp/connector-choices/<nickname>.json
(包含
{groupId, assetId, version}
scripts/commit_connectors.sh
步骤8(TDD批准后) — 将
tmp/connector-choices/
下的所有草稿升级到固定的
tmp/connector-versions/
目录,供第二阶段读取
tmp/connector-versions/*.json
scripts/build_gav.sh <json>
将保存的连接器JSON转换为
groupId:assetId:version
字符串
stdout
scripts/build_deps.sh [versions-dir]
步骤8 — 读取
tmp/connector-versions/
中的每个固定连接器,输出逗号分隔的GAV字符串,可直接用于
dx mule project create --dependencies
。跳过
db-driver.json
和任何非固定文件。
stdout
scripts/describe_connector.sh <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.sh <project-dir>
步骤16预构建验证 — 错误类型白名单(Cluster D)、命名空间与依赖项一致性(Cluster A2–A5)、规范XSD URL格式错误输出到stderr,首次违规时返回非零退出码
scripts/maybe_add_http_connector.sh --project <dir> <providers...>
第二阶段 — 防御性检查:当选择了OAuth提供商时,确保HTTP连接器已存在;编辑
<dir>/pom.xml
<dir>/pom.xml
请使用“技能已激活”消息中提供的绝对路径调用脚本(即包含此
SKILL.md
的目录)。请勿构造
../scripts/...
之类的相对路径——Cline的工作目录会在不同回合中切换,相对路径在实际运行中曾导致“找不到文件或目录”错误。下面的内联步骤示例使用
scripts/...
作为简写;实际执行时请替换为
<skill-dir>/scripts/...
为什么使用脚本而非内联bash: 在早期版本中,连接器搜索是单个
Bash
工具调用中定义的shell函数。调用返回后,子shell终止,解析后的GAV也随之丢失。当后续步骤组装
dx mule project create
时,版本的唯一痕迹是滚动过的工具输出——代理经常会粘贴训练时记忆中的虚构版本。将版本持久化到磁盘文件中,我们可以在命令行使用
jq
读取,从而完全避免这种失败模式。

Workflow shape (two phases)

工作流结构(两个阶段)

This workflow has two phases separated by a hard user-approval gate.
  • Phase 1: Technical Design (Steps 1–7). Identify systems, search Exchange, describe connectors, pick trigger and providers, present a Technical Design Summary, wait for the user to approve. Phase 1 writes nothing to the user's project directory — all artifacts live under workspace-relative paths:
    tmp/mule-dev-env.json
    (env cache owned by
    validate_prerequisites.sh
    ),
    tmp/connector-choices/*.json
    (draft connector picks), and
    tmp/connector-metadata/*.json
    . The pinned
    tmp/connector-versions/*.json
    directory that Phase 2 reads is only populated after Step 7's approval, by
    commit_connectors.sh
    .
  • Phase 2: Build (Steps 8–17). Create the real project, generate config and flow XML, run the build, declare completion. Phase 2 is the only phase that touches the user's project directory.
Phase 2 MUST NOT start until Step 7's approval gate has been passed explicitly. Skipping Phase 1 — or collapsing it into a single "I'll just use HTTP" decision — is the single highest-impact failure mode of this skill and is what the two-phase structure exists to prevent.
此工作流分为两个阶段,中间有一个严格的用户批准关卡。
  • 第一阶段:技术设计(步骤1–7)。识别系统、搜索Exchange、描述连接器、选择触发器和提供商、呈现技术设计摘要,等待用户批准。第一阶段不会向用户的项目目录写入任何内容——所有工件都存储在相对于工作区的路径下:
    tmp/mule-dev-env.json
    (由
    validate_prerequisites.sh
    管理的环境缓存)、
    tmp/connector-choices/*.json
    (草稿连接器选择)和
    tmp/connector-metadata/*.json
    。第二阶段读取的固定
    tmp/connector-versions/*.json
    目录仅在步骤7获得批准后,由
    commit_connectors.sh
    填充。
  • 第二阶段:构建(步骤8–17)。创建真实项目、生成配置和流XML、运行构建、宣布完成。第二阶段是唯一会触及用户项目目录的阶段。
在明确通过步骤7的批准关卡之前,绝对不能启动第二阶段。跳过第一阶段——或将其简化为单一的“我就用HTTP”决策——是此技能最严重的失败模式,而两阶段结构正是为了防止这种情况而设计的。

Workflow-wide discipline (read before Phase 1)

全工作流规范(进入第一阶段前阅读)

  • Build → cleanup → completion separation (Step 16 → Step 17 → Step 18). Three responses, in order, each with a single tool call:
    mvn clean package
    , then
    rm -r tmp/
    , then the completion signal. Do not bundle them. Wait for each result before moving on.
  • One mvn invocation per response. When re-running a build after a fix, emit only the
    mvn
    command in that response. Do not bundle it with further edits, follow-up shell commands, or the completion signal.
  • "Completion" means the build already passed. You may only declare completion after a response that ran
    mvn clean package
    came back with
    BUILD SUCCESS
    .
  • Connector versions come ONLY from the Step 3 flow. Never paste a version from
    references/connector-catalog.md
    , from training-time memory, or from extrapolation. Step 3 is a three-script dance:
    get_latest_connector.sh
    lists ranked candidates (stdout only, no pin file),
    pick_connector.sh <nickname> <gav>
    records the chosen row as a draft in
    tmp/connector-choices/
    , and
    commit_connectors.sh
    (Step 8, first action after TDD approval) promotes every draft to
    tmp/connector-versions/
    . Every GAV that reaches
    dx mule project create --dependencies
    or
    pom.xml
    must be pulled from a
    tmp/connector-versions/*.json
    file via
    scripts/build_deps.sh
    (for the full
    --dependencies
    string at Step 8) or
    scripts/build_gav.sh
    (for a single connector's GAV elsewhere in Phase 2). The catalog's versions are snapshots that drift — treat it only as a connector-identity reference, not as a version source.
  • The agent does the picking, not the script.
    get_latest_connector.sh
    deliberately emits a plain ranked list with no score, no emoji, and no "winner" signal. When the list has one row the choice is obvious. When it has several rows the agent must decide which one matches the user's stated system — and if the rows represent real variants of the same family (Slack
    mule4-slack-connector
    vs
    mule-slack-connector
    ; FTP vs FTPS; Dynamics 365 vs Dynamics GP/NAV/BC; IBM MQ vs Solace vs JMS), the decision belongs to the user via
    AskUserQuestion
    , not to the agent's guess. The cost of one extra prompt is one turn; the cost of a silent wrong variant is a full Phase-2 rewrite.
  • No HTTP fallback without evidence. You may only classify a system as "no dedicated connector exists, use HTTP" AFTER
    scripts/get_latest_connector.sh <system>
    has run AND returned zero matches (exit 1) OR every row in the ranked list is obviously a different product (no assetId shares tokens with the system name beyond noise words like
    mule
    /
    connector
    ). Declaring HTTP as the answer before the search has run is forbidden. Exchange carries dedicated connectors for hundreds of SaaS products that are easy to miss when reasoning from training-time knowledge alone — the helper script is the authoritative check. A dedicated connector gives metadata discovery, typed operations, and correct authentication; HTTP gives raw request plumbing the user would then have to wire up by hand, so quietly falling back to HTTP is a real loss, not a neutral choice. Note that any ranked row whose
    groupId
    is a UUID (e.g.
    a50e4364-a38c-4340-b05a-c1f8ebed0748:…
    ) is a connector the user's organization has published privately to Exchange — treat these as first-class candidates, not noise. An org that took the trouble to publish a private connector usually wants it used, often because it wraps internal endpoints, custom auth, or a golden-path the public connector can't cover.

  • 构建→清理→完成分离(步骤16→步骤17→步骤18)。分三个响应依次执行,每个响应仅包含一个工具调用:
    mvn clean package
    ,然后
    rm -r tmp/
    ,最后是完成信号。不要将它们捆绑在一起。等待每个步骤的结果后再进行下一步。
  • 每个响应仅执行一次mvn调用。修复后重新运行构建时,该响应中仅输出
    mvn
    命令。不要将其与进一步的编辑、后续shell命令或完成信号捆绑在一起。
  • “完成”意味着构建已成功。只有在运行
    mvn clean package
    的响应返回
    BUILD SUCCESS
    后,才能宣布完成。
  • 连接器版本仅来自步骤3的流程。绝不要从
    references/connector-catalog.md
    、训练时记忆或推断中粘贴版本。步骤3是一个三步脚本流程:
    get_latest_connector.sh
    列出排名候选(仅输出到stdout,无固定文件),
    pick_connector.sh <nickname> <gav>
    将所选条目记录为
    tmp/connector-choices/
    中的草稿,
    commit_connectors.sh
    (步骤8,TDD批准后的第一个操作)将所有草稿升级到
    tmp/connector-versions/
    。所有进入
    dx mule project create --dependencies
    pom.xml
    的GAV必须通过
    scripts/build_deps.sh
    (步骤8的完整
    --dependencies
    字符串)或
    scripts/build_gav.sh
    (第二阶段其他地方的单个连接器GAV)从
    tmp/connector-versions/*.json
    文件中获取。目录中的版本是快照,会随时间变化——仅将其视为连接器身份参考,而非版本来源。
  • 由代理而非脚本做出选择
    get_latest_connector.sh
    故意输出一个纯排名列表,无分数、无表情符号、无“选中”提示。当列表只有一行时,选择很明显。当有多行时,代理必须决定哪一行符合用户指定的系统——如果行代表同一系统家族的真实变体(Slack的
    mule4-slack-connector
    mule-slack-connector
    ;FTP与FTPS;Dynamics 365与Dynamics GP/NAV/BC;IBM MQ与Solace与JMS),则必须通过
    AskUserQuestion
    让用户决定,而非代理猜测。多一次提示的成本是一个回合;而静默选择错误变体的成本是整个第二阶段的重写。
  • 无证据不得回退到HTTP。只有在
    scripts/get_latest_connector.sh <system>
    运行返回零匹配(退出码1),或者排名列表中的每一行显然都是不同产品(除了
    mule
    /
    connector
    等通用词外,assetId与系统名称无共同标记)时,才能将系统归类为“无专用连接器,使用HTTP”。在搜索运行前就宣布HTTP为解决方案是被禁止的。Exchange包含数百个SaaS产品的专用连接器,仅靠训练时知识推理很容易遗漏——辅助脚本是权威检查。专用连接器提供元数据发现、类型化操作和正确的身份验证;HTTP仅提供原始请求管道,用户随后必须手动配置,因此静默回退到HTTP是实际的功能损失,而非中性选择。请注意,任何groupId为UUID的排名行(例如
    a50e4364-a38c-4340-b05a-c1f8ebed0748:…
    )都是用户组织私下发布到Exchange的连接器——将这些视为一等候选,而非干扰项。组织费心发布私有连接器通常是有原因的(自定义身份验证、内部端点、公共连接器无法覆盖的黄金路径),因此当同一排名列表中存在私有替代方案时,绝不要静默选择公共连接器——通过
    AskUserQuestion
    展示两者,让用户选择。

Phase 1: Technical Design

第一阶段:技术设计

Step 1: Validate Prerequisites

步骤1:验证前提条件

Run the prerequisite validation script. It only handles validation. It writes the prereq validation findings to
tmp/mule-dev-env.json
in the current workspace; Step 8 reads
mule_version
from there.
bash
bash scripts/validate_prerequisites.sh
If the script exits non-zero, STOP progressing any further in the skill and inform the user to act on the
errors
array in
tmp/mule-dev-env.json
. Do not invent a fallback Mule version —
mule_version
is empty when no runtime is detected.
What
validate_prerequisites.sh
checks: Anypoint CLI v4 installed · DX plugin available ·
JAVA_HOME
set · Java 11+ · Mule runtime detected at
~/.mule-dx/config.json:.runtimePath
or under
~/AnypointCodeBuilder/runtime/mule-*
. If runtime or other tools are missing, the script reports the error and informs the user to run the necessary commands for proper installation.

运行前提条件验证脚本。它仅处理验证。它会将前提条件验证结果写入当前工作区的
tmp/mule-dev-env.json
;步骤8将从该文件读取
mule_version
bash
bash scripts/validate_prerequisites.sh
如果脚本返回非零退出码,停止技能的任何进一步进展,并告知用户根据
tmp/mule-dev-env.json
中的
errors
数组采取行动。不要自行发明备用Mule版本——未检测到运行时时
mule_version
为空。
validate_prerequisites.sh
检查的内容:Anypoint CLI v4已安装 · DX插件可用 ·
JAVA_HOME
已设置 · Java 11+ · 在
~/.mule-dx/config.json:.runtimePath
~/AnypointCodeBuilder/runtime/mule-*
下检测到Mule运行时。如果缺少运行时或其他工具,脚本会报告错误并告知用户运行必要的命令进行正确安装。

Step 2: Identify Systems and Trigger Hints

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

[BLOCKER] Step 2 MUST NOT prompt the user. Do not emit an
<ask_followup_question>
or
<AskUserQuestion>
here. The trigger decision happens in Step 5 after connector metadata is on disk — prompting now would force generic options (HTTP Listener / Scheduler) instead of real connector sources, which is the single highest-impact anti-pattern this workflow exists to prevent.
Produce two records in your response text. These are plain prose — not a thinking block, not a tool call — so later steps (and the user) can read them.
1. Systems list. Identify EXACT system names: source systems (where data comes from), target systems (where data goes to). Use specific names (Slack, Jira, ServiceNow, Stripe, Shopify), NOT generic terms (chat, ticketing, payments, commerce). Every name on this list will be searched in Step 3; every search must result in a
tmp/connector-choices/<nick>.json
draft on disk (the agent picks from the ranked list and calls
pick_connector.sh
).
Anti-pattern — inferring a backend from a destination name. When the prompt mentions a queue, topic, bus, or similar messaging destination, the string that names the destination (e.g.
foo.queue
,
orders.topic
,
events-stream
) is a label, not a technology. It does NOT identify which broker is behind it. Do NOT add a specific broker (Anypoint MQ, Kafka, IBM MQ, Solace, SQS, etc.) to the Systems list unless the prompt names it explicitly. If the prompt only says "queue" / "topic" / names a bare destination, list the system as
messaging broker (backend unspecified)
and plan to escalate in Step 3 — the user picks the backend, not the agent. Why this matters: a silently-chosen broker anchors Phase 2 against the wrong connector family, which is a full Phase-2 rewrite to correct.
2. Trigger hints. In one or two sentences, note the verbatim phrases from the user prompt that describe what starts the flow or names a cadence — e.g. "every 3 seconds" or "listens for new Stripe charges" or "makes a GET request to retrieve customers". Do NOT classify yet. No class label, no decision tree, no "so the trigger is…". Step 5 does the trigger decision from connector metadata that does not exist yet; committing to a trigger now — even implicitly via a class — tends to anchor the agent against the real
sources[]
that Step 4 will produce.
If the prompt mentions no trigger or only describes outbound work ("makes a request", "fetches", "calls"), say so explicitly: "No explicit trigger phrase — outbound-only description." Step 5's metadata-first ladder handles this.
Connector strategy per system:
  • Major SaaS platforms (Salesforce, ServiceNow, NetSuite, Workday) → search for dedicated connector in Step 3.
  • Standard protocols (Database, JMS, FTPS, SFTP) → search for protocol-specific connector in Step 3.
  • Mid-market SaaS (Stripe, Shopify, HubSpot, Twilio, Plaid, etc.) → search for dedicated connector in Step 3. Do NOT assume these have no connector — Exchange has dedicated connectors for many of them. Training-data intuition that "Stripe/Shopify/etc. is a REST-API-only system" is unreliable; the search is the authority.
  • Queue or Pub/Sub with a named backend ("Kafka topic", "IBM MQ", "SQS", "Solace") → search for that specific connector in Step 3.
  • Queue or Pub/Sub without a named backend (the prompt uses "queue", "topic", or a bare destination name without naming the broker technology) → per the Step-2 anti-pattern above, the destination name is not the backend. In Step 3, escalate via
    AskUserQuestion
    to let the user pick the backend (JMS via any broker provider, Kafka, IBM MQ, Solace, SQS, etc.). Only if the user declines to choose should you default to
    mule-jms-connector
    — JMS is the generic protocol layer that fits any broker, with the broker selected in Step 6 via the connection provider (active-mq, active-mq-nct, generic).
  • Unknown/Unclear / custom internal APIs → still search Exchange first in Step 3; HTTP is the fallback only when Step 3's search returns nothing plausibly related.
Your next tool call after Step 2 MUST be
get_latest_connector.sh
for a system from your Systems list — NOT an
ask_followup_question
, NOT a describe-connector, NOT anything else. Step 3 is the non-negotiable next step.

[阻塞点] 步骤2不得提示用户。此处不得发出
<ask_followup_question>
<AskUserQuestion>
。触发器决策在步骤5中进行,此时连接器元数据已存储在磁盘上——现在提示会强制使用通用选项(HTTP监听器/调度器),而非真实的连接器源,这是此工作流旨在防止的最严重反模式之一。
在响应文本中生成两条记录。这些是纯文本——不是思考块,不是工具调用——以便后续步骤(和用户)可以读取。
1. 系统列表。识别确切的系统名称:源系统(数据来自哪里)、目标系统(数据发送到哪里)。使用具体名称(Slack、Jira、ServiceNow、Stripe、Shopify),而非通用术语(聊天、工单、支付、商务)。此列表中的每个名称都将在步骤3中搜索;每次搜索必须在磁盘上生成一个
tmp/connector-choices/<nick>.json
草稿(代理从排名列表中选择并调用
pick_connector.sh
)。
反模式——从目标名称推断后端。当提示提到队列、主题、总线或类似的消息传递目标时,命名目标的字符串(例如
foo.queue
orders.topic
events-stream
)是一个标签,而非技术。它不能识别背后的消息代理。除非提示明确命名,否则不要将特定代理(Anypoint MQ、Kafka、IBM MQ、Solace、SQS等)添加到系统列表中。如果提示仅说“队列”/“主题”或命名了一个裸目标,则将系统列为
messaging broker (backend unspecified)
,并计划在步骤3中升级——由用户选择后端,而非代理。为什么这很重要:静默选择错误的代理会导致第二阶段基于错误的连接器家族构建,需要重写整个第二阶段才能纠正。
2. 触发器提示。用一到两句话,记录用户提示中描述流启动方式或节奏的原句短语——例如*“每3秒”“监听新的Stripe收费”“发起GET请求检索客户”*。不要进行分类。不要添加类别标签、决策树或“因此触发器是…”。步骤5将根据尚未存在的连接器元数据做出触发器决策;现在就承诺一个触发器——即使是隐式地通过类别——往往会让代理忽略步骤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的系统”的直觉不可靠;搜索是权威来源。
  • 指定后端的队列或发布/订阅(“Kafka主题”、“IBM MQ”、“SQS”、“Solace”)→ 在步骤3中搜索该特定连接器。
  • 未指定后端的队列或发布/订阅(提示使用“队列”、“主题”或裸目标名称,未命名代理技术)→ 根据步骤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之后的下一个工具调用必须是对步骤2系统列表中的某个系统调用
get_latest_connector.sh
——不能
ask_followup_question
describe-connector
或其他任何操作。步骤3是不可协商的下一步。

Step 3: Search Exchange for Connectors, Decide, Draft the Choice

步骤3:搜索Exchange连接器、决策、记录草稿选择

Step 3 is a three-move loop run once per named system from Step 2:
  1. List candidates with
    get_latest_connector.sh
    .
  2. Decide which row is the right fit — inline rationale if the choice is obvious,
    AskUserQuestion
    if the rows are real variants of the same system family.
  3. Draft the chosen GAV with
    pick_connector.sh
    . The draft lands in
    tmp/connector-choices/<nick>.json
    and stays there through Phase 1.
The script does not pin a winner. There is no emoji, no score, no "Picked" line in its output. When the list has one row the shape of the output is "one row"; when it has several the shape is "several rows" and that is the cue to read the names and reason about intent — or to escalate.
Mandatory search rule. Run
get_latest_connector.sh
for EVERY named system from Step 2 — including systems whose prominence in your training data leads you to assume they have no dedicated connector. Declaring "system X has no dedicated connector" without the script having run is forbidden. This is the rule that prevents silent HTTP fallback — see "No HTTP fallback without evidence" in the workflow-wide discipline.
Term breadth. Always prefer the broader system name (
databricks
,
aws-storage
,
snowflake
,
database
,
salesforce
) over a narrow suffix-pinned term like
mule-amazon-s3-connector
or
mule-db-connector
. Narrow terms can miss org-private connectors whose assetId shares no tokens with the public connector's name — e.g. searching
mule-amazon-s3-connector
won't surface a private
mule-plugin-aws-storage-api
, and
mule-db-connector
won't surface a private
<uuid>:mule-plugin-customer-warehouse-database
. The "Common search terms" table further down is a starting hint for well-known systems, not a constraint — if the broader term is noisy, you can always re-run with a narrower one.
Version source-of-truth rule (MANDATORY):
  • The only acceptable source for a connector's version number is
    get_latest_connector.sh
    run against live Exchange in the current session, recorded via
    pick_connector.sh
    , and later promoted to
    tmp/connector-versions/
    by
    commit_connectors.sh
    . This applies equally to
    dx mule project create --dependencies
    ,
    pom.xml
    <dependency>
    blocks, and every other place a version appears.
  • Do not paste a version from
    references/connector-catalog.md
    . The catalog exists to help identify which connector to use (asset ID, purpose); its version numbers are best-effort snapshots that drift.
  • Do not invent a version from memory or by extrapolating. Version numbers on Exchange are not predictable.
  • Do not write a version before the corresponding
    tmp/connector-choices/<name>.json
    draft exists on disk.
步骤3是一个三步循环,针对步骤2中的每个命名系统运行一次
  1. 列出候选:使用
    get_latest_connector.sh
  2. 决策哪一行是合适的选择——如果选择明显,内联说明理由;如果行是同一系统家族的真实变体,使用
    AskUserQuestion
  3. 记录所选GAV为草稿:使用
    pick_connector.sh
    。草稿存储在
    tmp/connector-choices/<nick>.json
    中,并在第一阶段保留。
脚本不会固定选中项。其输出中没有表情符号、分数或“已选中”行。当列表只有一行时,输出形状为“一行”;当有多行时,输出形状为“多行”,这是提示读取名称并推理意图——或升级的信号。
强制搜索规则。对步骤2中的每个命名系统运行
get_latest_connector.sh
——包括那些在训练数据中很突出、你认为没有专用连接器的系统。在脚本运行前就宣布“系统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.sh
    ,通过
    pick_connector.sh
    记录,随后由
    commit_connectors.sh
    升级到
    tmp/connector-versions/
    。这同样适用于
    dx mule project create --dependencies
    pom.xml
    <dependency>
    块以及版本出现的所有其他地方。
  • 不要
    references/connector-catalog.md
    粘贴版本。目录的存在是为了帮助识别使用哪个连接器(资产ID、用途);其版本号是尽力而为的快照,会随时间变化。
  • 不要从记忆中发明版本或通过推断生成。Exchange上的版本号不可预测。
  • 不要在对应的
    tmp/connector-choices/<name>.json
    草稿存在于磁盘之前写入版本。

Move 1 — list

第一步 — 列出

One invocation per named system. The nickname is how the draft and later pin will be named, so pick something short and use it consistently:
bash
bash scripts/get_latest_connector.sh mule-salesforce-connector sfdc
Stdout shape, one GAV per line, ranked best-guess-first:
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
Exit 0 means at least one row was returned; exit 1 means no Mule 4 extension matched and you need to treat this system as HTTP-fallback territory (see the workflow-wide discipline).
每个命名系统调用一次。昵称是草稿和后续固定项的名称,因此选择简短且一致的名称:
bash
bash scripts/get_latest_connector.sh mule-salesforce-connector sfdc
Stdout形状,每行一个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回退场景(请参阅工作流规范)。

Move 2 — decide & confirm

第二步 — 决策与确认

Read the list. Two cases, in order:
Case A — one row. Only one Mule 4 extension matches. Acknowledge the choice inline in one sentence ("Only match for HTTP:
org.mule.connectors:mule-http-connector:1.11.1
") and go to Move 3.
Case B — multiple rows that are real variants of the same system family. Rows whose assetId does not plausibly name the user's system are noise — filter them out logically. If more than one row/choice remains after the logical filtering for the user's system (e.g., Salesforce query returned
mule-salesforce-connector
plus three Salesforce sub-products for Analytics / Composite / Marketing Cloud etc.), always escalate via
AskUserQuestion
. Some common variant families that requires user confirmation:
  • slack
    mule-slack-connector
    (community, v4.x) vs
    mule4-slack-connector
    (premium MuleSoft, v2.x). Different OAuth shapes, different operations — neither is a "newer version" of the other.
  • ftp
    mule-ftp-connector
    (plain) vs
    mule-ftps-connector
    (TLS). Always confirm and suggest FTPS for best security practices.
  • ibm-mq
    vs
    solace
    vs
    kafka
    vs
    jms
    — a destination name in the prompt (e.g.
    foo.queue
    ,
    bar.topic
    ) does NOT identify the backend. Unless the prompt explicitly names the broker technology, escalate with these as options;
    mule-jms-connector
    is the generic-protocol fallback when the user declines to pick.
  • microsoft-dynamics-*
    — 365, GP, NAV, 365-Business-Central, CRM are different products the user may have meant interchangeably.
  • oracle-ebs
    vs
    oracle-ebs-122
    — alternate EBS major versions.
  • Any pair whose assetIds share the target system's name and differ only in a variant suffix, protocol marker, major-version marker, or the
    mule4-
    vs
    mule-
    prefix.
  • A public connector + a private (UUID-groupId) connector for the same system family — e.g.
    com.mulesoft.connectors:mule4-snowflake-connector
    and
    <uuid>:mule-plugin-snowflake-sys-api-json
    both showing up in a
    snowflake
    search. The org-published asset usually exists for a reason (custom auth, internal endpoints, a wrapper around the public connector), so NEVER silently pick the public one when a private alternative is in the same ranked list — surface both with
    AskUserQuestion
    and let the user choose.
Prompt shape when you escalate:
xml
<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>
When more than one choice exists, confirm with user; a silent wrong variant costs a Phase-2 rewrite.
If the user's prompt explicitly names one variant ("use the Dynamics 365 connector, not BC", "the premium Slack connector only"), that pins the choice and you proceed without asking.
读取列表。分两种情况,按顺序处理:
情况A — 一行。只有一个Mule 4扩展匹配。在响应中用一句话确认选择(“HTTP的唯一匹配:
org.mule.connectors:mule-http-connector:1.11.1
”),然后进入第三步。
情况B — 多行且是同一系统家族的真实变体。assetId似乎与用户系统无关的行是干扰项——逻辑过滤掉它们。如果逻辑过滤后仍有多个行/选择(例如,Salesforce查询返回
mule-salesforce-connector
以及三个Salesforce子产品:Analytics/Composite/Marketing Cloud等),必须通过
AskUserQuestion
升级。一些需要用户确认的常见变体家族:
  • slack
    mule-slack-connector
    (社区版,v4.x)vs
    mule4-slack-connector
    (MuleSoft高级版,v2.x)。OAuth形状不同,操作不同——两者都不是对方的“新版本”。
  • ftp
    mule-ftp-connector
    (普通版)vs
    mule-ftps-connector
    (TLS版)。始终确认并建议使用FTPS以遵循最佳安全实践。
  • ibm-mq
    vs
    solace
    vs
    kafka
    vs
    jms
    ——提示中的目标名称(例如
    foo.queue
    bar.topic
    不能识别后端。除非提示明确命名代理技术,否则将这些作为选项升级;当用户拒绝选择时,
    mule-jms-connector
    是通用协议回退选项。
  • microsoft-dynamics-*
    ——365、GP、NAV、365-Business-Central、CRM是不同的产品,用户可能交替使用。
  • oracle-ebs
    vs
    oracle-ebs-122
    ——EBS的不同主版本。
  • 任何assetId包含目标系统名称,仅在变体后缀、协议标记、主版本标记或
    mule4-
    mule-
    前缀上不同的配对。
  • 同一系统家族的公共连接器+私有(UUID-groupId)连接器——例如,
    snowflake
    搜索中同时出现
    com.mulesoft.connectors:mule4-snowflake-connector
    <uuid>:mule-plugin-snowflake-sys-api-json
    。组织发布的资产通常有其原因(自定义身份验证、内部端点、公共连接器的包装器),因此绝不要在同一排名列表中存在私有替代方案时静默选择公共连接器——通过
    AskUserQuestion
    展示两者,让用户选择。
升级时的提示形状:
xml
<ask_followup_question>
<question>Exchange上存在两个Mule 4 Slack连接器。此集成应使用哪一个?</question>
<options>[
  "org.mule.connectors:mule-slack-connector:4.3.2 — 社区版Slack连接器",
  "com.mulesoft.connectors:mule4-slack-connector:2.0.1 — MuleSoft高级版Slack连接器",
  "其他 — 描述你需要的Slack变体"
]</options>
</ask_followup_question>
当有多个选择时,请与用户确认;静默选择错误变体需要重写第二阶段。
如果用户的提示明确命名了一个变体(“使用Dynamics 365连接器,而非BC”、“仅使用高级版Slack连接器”),则固定选择,无需询问即可继续。

Move 3 — draft

第三步 — 记录草稿

Record the chosen GAV as a draft. Idempotent — you can re-run with a different GAV if Step 4 or Step 5 metadata reveals a better fit:
bash
bash scripts/pick_connector.sh sfdc   com.mulesoft.connectors:mule-salesforce-connector:11.1.0
bash scripts/pick_connector.sh slack  com.mulesoft.connectors:mule4-slack-connector:2.0.1
bash scripts/pick_connector.sh jms    org.mule.connectors:mule-jms-connector:1.10.3
将所选GAV记录为草稿。幂等操作——如果步骤4或步骤5的元数据显示更好的选择,可以使用不同的GAV重新运行:
bash
bash scripts/pick_connector.sh sfdc   com.mulesoft.connectors:mule-salesforce-connector:11.1.0
bash scripts/pick_connector.sh slack  com.mulesoft.connectors:mule4-slack-connector:2.0.1
bash scripts/pick_connector.sh jms    org.mule.connectors:mule-jms-connector:1.10.3

→ tmp/connector-choices/{sfdc,slack,jms}.json now each contain {groupId, assetId, version}

→ tmp/connector-choices/{sfdc,slack,jms}.json现在各包含{groupId, assetId, version}


If you realize after Step 4's metadata digest that the draft is wrong, re-run `pick_connector.sh` with the corrected GAV. Drafts remain mutable until Step 8's `commit_connectors.sh` promotes them.

**Why drafts instead of pins during Phase 1.** If a connector choice bakes into `tmp/connector-versions/` before the user has seen the Technical Design Summary, the agent — and every downstream check — treats it as settled. Holding the choice as a draft until TDD approval keeps the whole design reversible, and lets the approval gate be the real commitment point.

如果在步骤4的元数据摘要后意识到草稿错误,使用正确的GAV重新运行`pick_connector.sh`。草稿在步骤8的`commit_connectors.sh`升级前保持可变。

**为什么在第一阶段使用草稿而非固定项**。如果在用户看到技术设计摘要前,连接器选择就被写入`tmp/connector-versions/`,代理——以及所有下游检查——会将其视为已确定。在TDD批准前将选择保留为草稿,使整个设计保持可逆,并让批准关卡成为真正的承诺点。

Selection rules the script applies internally

脚本内部应用的选择规则

(Reference only — you don't need to reimplement them. The list is ordered best-guess-first; treat ordering as a soft hint, not a directive.)
  • Only
    "type": "extension"
    (Mule 4 compatible) — Mule 3
    type=connector
    assets, templates, examples, and rest-apis are filtered out.
  • Any groupId whose asset is
    type="extension"
    is admissible; ranking keeps first-party connectors on top via a 3-tier preference (
    com.mulesoft.connectors
    >
    org.mule.connectors
    > other).
  • Latest semantic version within each
    (groupId, assetId)
    group.
  • Token-overlap scoring with the search term; the premium groupId and shorter assetId break ties. Scores are used for ordering only and are never emitted.
  • Two pages fetched in parallel (offsets 0 and 200) so broad searches like
    salesforce
    don't drop candidates off a single page.
(仅供参考——你无需重新实现。列表按最佳猜测排序;将排序视为软提示,而非指令。)
  • 仅选择
    "type": "extension"
    (兼容Mule 4)——过滤掉Mule 3的
    type=connector
    资产、模板、示例和rest-apis。
  • 任何资产为
    type="extension"
    的groupId都可接受;排序通过三层偏好将第一方连接器放在顶部(
    com.mulesoft.connectors
    >
    org.mule.connectors
    > 其他)。
  • 每个
    (groupId, assetId)
    组内的最新语义版本。
  • 与搜索词的标记重叠评分;高级groupId和更短的assetId用于打破平局。分数仅用于排序,不会输出。
  • 并行获取两页(偏移量0和200),因此像
    salesforce
    这样的宽泛搜索不会遗漏单页之外的候选。

Common search terms

常见搜索术语

These are starting hints for well-known systems. For anything else — and especially for any system you suspect the user's org may have a private connector for — search by system name first.
SystemSearch term
Salesforce
salesforce
Database
database
HTTP
http
NetSuite
netsuite
ServiceNow
servicenow
Amazon S3
amazon
JMS
jms
Slack
slack
(returns both Slack variants — this is the ambiguous case above)
For any system not in the list, search dynamically with the system name (e.g.
stripe
,
shopify
,
hubspot
,
databricks
,
snowflake
) — don't assume naming patterns and don't assume the system has no connector. Broader system-name terms also surface any private (UUID-groupId) connectors the user's organization has published in the same domain, which a narrow
mule-<name>-connector
term tends to miss.

这些是知名系统的起始提示。对于其他任何系统——尤其是你怀疑用户组织可能有私有连接器的系统——先按系统名称搜索。
系统搜索术语
Salesforce
salesforce
Database
database
HTTP
http
NetSuite
netsuite
ServiceNow
servicenow
Amazon S3
amazon
JMS
jms
Slack
slack
(返回两个Slack变体——这是上面提到的模糊情况)
对于列表中未包含的任何系统,动态使用系统名称搜索(例如
stripe
shopify
hubspot
databricks
snowflake
)——不要假设命名模式,不要假设系统没有连接器。更宽泛的系统名称术语还会显示用户组织在同一领域发布的任何私有(UUID-groupId)连接器,而窄的
mule-<name>-connector
术语往往会错过这些。

Step 4: Describe Connectors

步骤4:描述连接器

For each connector resolved in Step 3, retrieve its full metadata and read the digest that the wrapper script prints. Use
describe_connector.sh
rather than writing the
describe-connector
pipeline by hand — the wrapper resolves the probe path, saves the full JSON to disk for later steps, AND echoes
sources[]
and
configs[]
to stdout so you see what Step 5 will need:
bash
bash scripts/describe_connector.sh sfdc       # nickname from Step 3
bash scripts/describe_connector.sh stripe
bash scripts/describe_connector.sh http
Each invocation writes
tmp/connector-metadata/<nickname>.json
(full response, consumed again by Phase 2) and prints a digest shaped like this:
json
{
  "namespace_prefix": "stripe",
  "sources": [
    "on-canceled-subscription-listener",
    "on-new-charge-listener",
    ...
  ],
  "configs": [
    { "name": "config", "providers": ["api-key"] }
  ],
  "operations_count": 335,
  "operations_sample": ["createV13dSecure", "..."]
}
Read the
sources[]
array that comes back.
That list is the set of real native triggers the connector supports; it is what Step 5 branches on. Do not skip past the digest straight to the next
describe_connector.sh
call — Step 5's trigger decision depends on you knowing which sources each connector exposes, and the digest is the cheapest place to get that information.
The script also writes
tmp/connector-errors/<nickname>.json
with the connector-wide error-type whitelist. The Step 16 validator reads this directory; you don't need to invoke it manually.
If you ever need the full response (e.g. to introspect
childElements[]
for
oauth-callback-config
), read
tmp/connector-metadata/<nickname>.json
directly.
Manual fallback — if for some reason the wrapper is unavailable, you can reproduce it by hand. In Phase 1 the draft file is authoritative;
build_gav.sh
accepts either location:
bash
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(bash scripts/build_gav.sh 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
But in the common case prefer the wrapper — it is one line instead of three and it makes the sources list visible in your turn's tool output without a follow-up call.

对于步骤3中解析的每个连接器,检索其完整元数据并读取包装脚本打印的摘要。使用
describe_connector.sh
而非手动编写
describe-connector
管道——包装脚本会解析探测路径,将完整JSON保存到磁盘供后续步骤使用,并将
sources[]
configs[]
输出到stdout,以便你看到步骤5需要的内容:
bash
bash scripts/describe_connector.sh sfdc       # 步骤3中的昵称
bash scripts/describe_connector.sh stripe
bash scripts/describe_connector.sh http
每次调用都会将完整响应写入
tmp/connector-metadata/<nickname>.json
(第二阶段会再次使用),并打印如下形状的摘要:
json
{
  "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.sh
调用——步骤5的触发器决策依赖于你知道每个连接器暴露的源,而摘要是获取该信息最便捷的地方。
脚本还会将连接器范围的错误类型白名单写入
tmp/connector-errors/<nickname>.json
。步骤16的验证器会读取此目录;你无需手动调用。
如果需要完整响应(例如,内省
childElements[]
中的
oauth-callback-config
),直接读取
tmp/connector-metadata/<nickname>.json
即可。
手动回退——如果包装脚本因某种原因不可用,你可以手动重现。在第一阶段,草稿文件是权威的;
build_gav.sh
接受任一位置:
bash
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(bash scripts/build_gav.sh 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
但在常见情况下,优先使用包装脚本——它只需一行,而非三行,并且无需后续调用即可在你的回合工具输出中显示源列表。

Step 5: Select Trigger

步骤5:选择触发器

Every top-level flow (that is not a sub-flow or a
<flow-ref>
target) needs exactly ONE trigger. Step 5 decides which trigger by letting connector metadata drive the choice — not prompt-text intuition.
[BLOCKER] Explore-before-decide gate. Before committing to a trigger — whether inline or via
AskUserQuestion
— both of the following must be true for EVERY named system from Step 2's Systems list:
  1. tmp/connector-choices/<nick>.json
    exists on disk (Step 3, via
    pick_connector.sh
    ).
  2. tmp/connector-metadata/<nick>.json
    exists on disk (Step 4).
AND you must have the
sources[]
content in view. Run:
bash
for f in tmp/connector-metadata/*.json; do
  echo "--- $f ---"
  jq '{namespace: .namespace.prefix, sources}' "$f"
done
in the response that begins Step 5, and read the output. This is the same data the
describe_connector.sh
digest already showed per connector in Step 4, but re-echoing it here puts every connector's sources side-by-side in one place right before the decision. Do not commit to a trigger without having those lists in the current tool output — past turns scroll out of context quickly.
Why this gate exists: if the agent commits to a trigger before reading
sources[]
, the usual failure mode is to default to
http:listener
(treating the prompt as a webhook) and silently ignore a real connector source that Step 4 just fetched. A connector's
sources[]
array is the authoritative list of triggers it supports; Step 5 must branch from that list, not from prompt-text intuition.
每个顶级流(不是子流或
<flow-ref>
目标)恰好需要一个触发器。步骤5通过让连接器元数据驱动选择来决定使用哪个触发器——而非提示文本直觉。
[阻塞点] 先探索后决策关卡。在提交触发器选择之前——无论是内联还是通过
AskUserQuestion
——步骤2系统列表中的每个命名系统必须满足以下两个条件:
  1. tmp/connector-choices/<nick>.json
    存在于磁盘上(步骤3,通过
    pick_connector.sh
    )。
  2. tmp/connector-metadata/<nick>.json
    存在于磁盘上(步骤4)。
并且你必须查看
sources[]
内容。运行:
bash
for f in tmp/connector-metadata/*.json; do
  echo "--- $f ---"
  jq '{namespace: .namespace.prefix, sources}' "$f"
done
在开始步骤5的响应中运行此命令,并读取输出。这与步骤4中
describe_connector.sh
摘要为每个连接器显示的数据相同,但在此处重新输出会将每个连接器的源并排放在一个地方,就在决策之前。不要在当前工具输出中没有这些列表的情况下提交触发器选择——之前的回合会很快滚出上下文。
此关卡存在的原因:如果代理在读取
sources[]
之前就提交触发器选择,通常的失败模式是默认使用
http:listener
(将提示视为Webhook),并静默忽略步骤4刚刚获取的真实连接器源。连接器的
sources[]
数组是其支持的触发器的权威列表;步骤5必须从此列表分支,而非提示文本直觉。

Decision ladder (evaluate in order)

决策阶梯(按顺序评估)

Work through the rungs below in order. Each rung is one of the possible paths — there is no "fallback" ranking; the first path whose preconditions all match is the one you take.
按顺序处理以下阶梯。每个阶梯是一种可能的路径——没有“回退”排名;第一个所有前提条件都匹配的路径就是你要采取的路径。

Rung 1 — Connector-source path

阶梯1 — 连接器源路径

For each connector in scope, examine
sources[]
from the digest. For any source whose name plausibly relates to the user's stated need (noun match: "product", "order", "charge", "customer"; AND verb-prefix consistency:
on-new-*
,
on-updated-*
,
on-modified-*
,
on-*-arrived
,
poll-*
,
*-listener
,
*-trigger
), inspect its shape via the unified
describe-connector
command with
--type source
:
bash
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(bash scripts/build_gav.sh tmp/connector-choices/<nick>.json)" \
  --type source \
  --name <source-name> \
  --output json
Do not call
source-detail
on every source — only on those whose name plausibly fits the user's intent per the noun+verb-prefix check above. On rich connectors this is the difference between 1–2 CLI calls and 7+ (Shopify, Salesforce, etc.).
Compare the returned shapes — not the names alone — to the user's intent:
  • If the source's
    childElements[]
    includes a
    scheduling-strategy
    element, this is a polling source — the connector itself handles the cadence natively. When the user's prompt names a cadence ("every N", "daily", "hourly") AND the matched source is a polling source, the cadence goes inside the source's
    <scheduling-strategy>
    child. Do NOT introduce a separate top-level
    <scheduler>
    alongside it.
    Example correct shape:
    xml
    <shopify:on-updated-product-trigger config-ref="shopifyConfig">
        <scheduling-strategy>
            <fixed-frequency frequency="3000" startDelay="5000"/>
        </scheduling-strategy>
    </shopify:on-updated-product-trigger>
  • If the source has no
    scheduling-strategy
    child and exposes an event (
    on-new-*
    ,
    on-modified-*
    ), it's an event source — use it directly; no top-level scheduler needed.
  • If the source's shape includes
    listenerConfig
    references, it's a webhook receiver — suitable for "receive callback at endpoint" prompts.
Commit inline to a connector source when exactly one source's shape fits the user's intent and the match is explainable in one sentence. State the choice with a one-line rationale citing the source name.
Prompt via
AskUserQuestion
when two or more sources both pass the shape check against the user's intent, OR when the user's language is genuinely ambiguous about which source semantics they want. Options list the real source names, not generic placeholders:
xml
<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>
If no source passes the shape check against any connector in scope, move to Rung 2.
对于每个范围内的连接器,检查摘要中的
sources[]
。对于任何名称似乎与用户需求相关的源(名词匹配:“product”、“order”、“charge”、“customer”;并且动词前缀一致:
on-new-*
on-updated-*
on-modified-*
on-*-arrived
poll-*
*-listener
*-trigger
),通过带有
--type source
的统一
describe-connector
命令检查其形状:
bash
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(bash scripts/build_gav.sh tmp/connector-choices/<nick>.json)" \
  --type source \
  --name <source-name> \
  --output json
不要对每个源调用
source-detail
——仅对那些名称根据名词+动词前缀检查似乎符合用户意图的源调用。对于丰富的连接器,这是1–2次CLI调用与7+次调用的区别(Shopify、Salesforce等)。
将返回的形状——而非仅名称——与用户意图进行比较:
  • 如果源的
    childElements[]
    包含
    scheduling-strategy
    元素,则这是一个轮询源——连接器本身原生处理节奏。当用户提示命名了节奏(“每N”、“每日”、“每小时”)且匹配的源是轮询源时,节奏应放在源的
    <scheduling-strategy>
    子元素内。不要单独引入顶级
    <scheduler>
    。正确示例形状:
    xml
    <shopify:on-updated-product-trigger config-ref="shopifyConfig">
        <scheduling-strategy>
            <fixed-frequency frequency="3000" startDelay="5000"/>
        </scheduling-strategy>
    </shopify:on-updated-product-trigger>
  • 如果源没有
    scheduling-strategy
    子元素且暴露事件(
    on-new-*
    on-modified-*
    ),则是事件源——直接使用;无需顶级调度器。
  • 如果源的形状包含
    listenerConfig
    引用,则是Webhook接收器——适用于“在端点接收回调”的提示。
内联提交连接器源:当恰好一个源的形状符合用户意图,且匹配可以用一句话解释时。用一句话说明选择,引用源名称。
通过
AskUserQuestion
提示
:当两个或多个源都通过形状检查符合用户意图,或者用户的语言确实对他们想要的源语义模糊时。选项列出真实的源名称,而非通用占位符:
xml
<ask_followup_question>
<question>此流应使用哪个Salesforce事件作为触发器?</question>
<options>[
  "salesforce:replay-topic-listener — 订阅Salesforce流主题",
  "salesforce:modified-object-listener — 当给定sObject的记录被修改时触发",
  "其他 — 选择不同的源"
]</options>
</ask_followup_question>
如果没有源通过范围内任何连接器的形状检查,则进入阶梯2。

Rung 2 — Generic scheduler path

阶梯2 — 通用调度器路径

Take this path when:
  • No connector source fits the user's intent (Rung 1 examined the candidates and none matched), AND
  • The prompt names a cadence ("every N", "daily", "hourly", "poll every…"), AND
  • The flow body will call connector operations (not event-driven).
Use
<scheduler>
with
<fixed-frequency>
or
<cron>
. State the choice inline.
Record the rejection. When taking this path, note in one sentence why Rung 1's sources were rejected — e.g. "Shopify's
on-updated-product-trigger
matches 'every 3 seconds' but the user wants to pull by custom date range not 'updated since last poll', so a generic
<scheduler>
+
shopify:product-list
is more appropriate." Step 7's TDD requires this list; capture it now while the reasoning is fresh.
在以下情况下采用此路径:
  • 没有连接器源符合用户意图(阶梯1检查了候选,但没有匹配),并且
  • 提示命名了节奏(“每N”、“每日”、“每小时”、“每隔…轮询”),并且
  • 流主体将调用连接器操作(非事件驱动)。
使用带有
<fixed-frequency>
<cron>
<scheduler>
。内联说明选择。
记录拒绝原因。采用此路径时,用一句话说明为什么阶梯1的源被拒绝——例如,“Shopify的
on-updated-product-trigger
匹配‘每3秒’的意图,但用户希望按自定义日期范围拉取,而非‘自上次轮询后更新’,因此通用
<scheduler>
+
shopify:product-list
更合适。”步骤7的TDD需要此列表;现在就记录,因为推理还清晰。

Rung 3 — HTTP Listener path

阶梯3 — HTTP监听器路径

Take this path when:
  • The prompt explicitly says "expose endpoint", "receive HTTP request", "provide REST API", "webhook at /path", AND
  • No connector source in scope is a webhook-style receiver.
Use
<http:listener>
. State the choice inline. Record which connector sources were considered and why they were rejected (see Step 7).
在以下情况下采用此路径:
  • 提示明确说“暴露端点”、“接收HTTP请求”、“提供REST API”、“在/path处设置Webhook”,并且
  • 范围内没有连接器源是Webhook风格的接收器。
使用
<http:listener>
。内联说明选择。记录考虑过的连接器源以及拒绝它们的原因(请参阅步骤7)。

Rung 4 — Ask the user

阶梯4 — 询问用户

Take this path when none of Rungs 1–3 clearly apply — e.g. the prompt is outbound-only ("makes a request", "fetches", "retrieves") with no cadence and no endpoint language.
AskUserQuestion
with options derived from the actual
sources[]
of connectors in scope PLUS Scheduler and HTTP Listener:
xml
<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>
在阶梯1–3均不明确适用时采用此路径——例如,提示仅描述出站操作(“发起请求”、“获取”、“检索”),无节奏和端点语言。使用
AskUserQuestion
,选项来自范围内连接器的实际
sources[]
加上调度器和HTTP监听器:
xml
<ask_followup_question>
<question>提示描述了出站调用,但未命名触发器。应如何启动此流?</question>
<options>[
  "调度器 — 按时间计划运行",
  "HTTP监听器 — 接收入站HTTP请求",
  "<connector>:<source-name> — 范围内某个连接器的原生事件(根据sources[]列出适用的)",
  "其他 — 请描述"
]</options>
</ask_followup_question>

After the decision

决策后

Record the selected trigger, its owning connector (if any), and — if the path is Rung 2 or Rung 3 — the list of connector sources that were considered and one-line reasons each was dismissed. Step 7's TDD surfaces this list; if it is missing, the TDD is incomplete and Phase 2 cannot start.
[BLOCKER] WAIT for the user's response before moving to Step 6 when this step prompts.

记录所选触发器、其所属连接器(如果有)——如果路径是阶梯2或阶梯3,记录考虑过的连接器源列表以及每个源被拒绝的一句话原因。步骤7的TDD会显示此列表;如果缺失,TDD不完整,第二阶段无法启动。
[阻塞点] 如果此步骤提示,请等待用户响应后再进入步骤6

Step 6: Select Connection Providers

步骤6:选择连接提供商

Ask the user only when there is an actual choice to make. For each connector, look at the
configs[]
metadata captured in Step 4 — specifically the
connectionProviders
list of the config that owns the operation you intend to call in Phase 2.
Decision rule:
  • Multiple configs or multiple providersMUST use
    AskUserQuestion
    . The user's choice determines both which
    config-name
    and which
    --connection-provider
    you pass to Step 11's
    config-detail
    call, and which XML structure you write in Step 12.
  • Exactly one config and exactly one providerDO NOT prompt. State the choice inline in one line ("Using
    s3:config[connection]
    — the only option provided by the connector") and proceed. Prompts that offer a single "option" look like pointless ceremony and waste a conversation turn.
Worked examples from live Exchange metadata:
Connector
configs
× providers
Action
S3 connector
config[connection]
Announce, do NOT prompt
VM connector
config[connection]
Announce, do NOT prompt
HTTP connector
listener-config[listener-connection]
,
request-config[request-connection]
Configs map 1:1 to listener vs request — determined by the flow shape (trigger vs outbound call), not a user preference. Announce, do NOT prompt.
A multi-config connector with stream vs. non-stream configs
config[basic, role]
,
streams-config[streams]
Prompt — pick the config whose operations match the use case, and if that config has >1 provider, pick a provider.
A connector offering basic / OAuth / JWT / client-credentials
config[basic, oauth-user-pass, jwt, oauth-client-credentials]
Prompt — real alternatives with different credential models.
Database connector
config[my-sql-connection, oracle-connection, data-source-connection, generic-connection, ...]
Prompt for the provider, then resolve the JDBC driver GAV in the same step (see "Step 6b — JDBC driver resolution" below). Step 9 is a mechanical
pom.xml
edit.
When you do prompt, present only the real alternatives (don't pad with "if unsure..." copy). Example:
This connector offers four connection providers. Which should this integration use?
  • basic
    — username + password + security token
  • oauth-user-pass
    — OAuth with user credentials
  • jwt
    — JWT bearer token (server-to-server)
  • oauth-client-credentials
    — OAuth client credentials
Do not offer a "recommendation" as one of the options if it's really the only option. If there is only one choice, do not ask.
Store the selected
(config-name, connection-provider)
pair for each connector, and persist the
describe-connector
connection-provider output
for Phase 2 so it doesn't have to re-invoke the CLI. Flag semantics note:
--name
carries the connection provider name,
--config-name
carries the config name — easy to get backwards:
bash
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(bash scripts/build_gav.sh tmp/connector-choices/sfdc.json)" \
  --type connection-provider \
  --name basic-connection \
  --config-name sfdc-config \
  --output json > tmp/connector-metadata/sfdc-config.json
仅当存在实际选择时才询问用户。对于每个连接器,查看步骤4中捕获的
configs[]
元数据——特别是你打算在第二阶段调用的操作所属配置的
connectionProviders
列表。
决策规则:
  • 多个配置或多个提供商必须使用
    AskUserQuestion
    。用户的选择决定了你在步骤11的
    config-detail
    调用中传递的
    config-name
    --connection-provider
    ,以及你在步骤12中编写的XML结构。
  • 恰好一个配置和恰好一个提供商不要提示。用一句话内联说明选择(“使用
    s3:config[connection]
    — 连接器提供的唯一选项”),然后继续。提供单一“选项”的提示看起来像无意义的仪式,会浪费对话回合。
来自实时Exchange元数据的示例:
连接器
configs
× providers
操作
S3连接器
config[connection]
宣布,不要提示
VM连接器
config[connection]
宣布,不要提示
HTTP连接器
listener-config[listener-connection]
,
request-config[request-connection]
配置与监听器vs请求流形状一一对应,而非用户偏好。宣布,不要提示
带有流与非流配置的多配置连接器
config[basic, role]
,
streams-config[streams]
提示 — 选择操作符合用例的配置;如果该配置有多个提供商,选择一个提供商。
提供基础/OAuth/JWT/客户端凭证的连接器
config[basic, oauth-user-pass, jwt, oauth-client-credentials]
提示 — 具有不同凭证模型的真实替代方案。
Database连接器
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承载令牌(服务器到服务器)
  • oauth-client-credentials
    — OAuth客户端凭证
如果实际上只有一个选项,不要将“推荐”作为选项之一。如果只有一个选择,不要询问。
存储每个连接器的选定
(config-name, connection-provider)
对,并持久化
describe-connector
连接提供商输出
供第二阶段使用,这样就无需重新调用CLI。标志语义注意:
--name
是连接提供商名称,
--config-name
是配置名称——很容易弄反:
bash
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(bash scripts/build_gav.sh tmp/connector-choices/sfdc.json)" \
  --type connection-provider \
  --name basic-connection \
  --config-name sfdc-config \
  --output json > tmp/connector-metadata/sfdc-config.json

Step 6b — JDBC driver resolution (only if
mule-db-connector
is in scope)

步骤6b — JDBC驱动解析(仅当
mule-db-connector
在范围内时)

mule-db-connector
is the one case where the Step-6 provider choice needs more clarification. The provider choice only handles the XML connection element (
<db:my-sql-connection>
,
<db:generic-connection>
, etc.), but the JDBC driver JAR is a separate Maven artifact that must ship alongside the connector via
<sharedLibrary>
. Resolve both in Step 6 so Step 7's design summary can show a full
groupId:artifactId:version
for the driver and Step 9 becomes a mechanical
pom.xml
edit with no further prompting.
Branch on the Step-6 provider answer:
Provider pickedDriver auto-pin from the canonical table (Step 9)Prompt?
my-sql
com.mysql:mysql-connector-j:8.4.0
(8.4 LTS; 9.x requires JDK 21)
No — announce inline
oracle
com.oracle.database.jdbc:ojdbc11:23.9.0.25.07
(Java 17+)
No — announce inline
mssql
com.microsoft.sqlserver:mssql-jdbc:13.4.0.jre11
(Java 17+)
No — announce inline
generic
and the target database is identifiable as PostgreSQL (user prompt names Postgres, a prior turn named Postgres, or a JDBC URL placeholder shows
jdbc:postgresql://
)
org.postgresql:postgresql:42.7.11
— the canonical
generic
pairing
No — announce inline
generic
and the target is something else or unspecified (H2, Snowflake, SAP HANA, Vertica, unknown, or the user didn't name a database)
Cannot auto-pin
generic-connection
accepts any JDBC URL and only the target database identifies the driver. IF user select generic, see
references/jdbc-drivers.md
for canonical options, then prompt with Postgres listed first (most common
generic
target).
Yes
data-source
Cannot auto-pin — driver is supplied by the container, or by an explicit
<sharedLibrary>
declaration. Prompt.
Yes
derby
Multi-artifact + JDK-dependent — If selected, see
references/jdbc-drivers.md
for the embedded vs network-client split and the Java 17 vs Java 8 version matrix. Prompt on
embedded
vs
client
.
Yes
The rule: if the target database is one of the first rows above (my-sql, oracle, mssql or generic with postgres) do not prompt — auto-pin and announce inline. Reserve the prompt for non-canonical
generic
targets and for the inherently multi-choice providers (
data-source
,
derby
).
The always-prompt branches all use the same prompt shape:
xml
<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>
What Step 6 must record for Step 9:
One or more
{groupId, artifactId, version}
tuples, plus the driver class, persisted so Step 9 can emit the
<dependency>
+
<sharedLibrary>
pairs without re-asking. Use a sidecar file next to the connector choice:
bash
undefined
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-sql
com.mysql:mysql-connector-j:8.4.0
(8.4 LTS;9.x需要JDK 21)
否 — 内联宣布
oracle
com.oracle.database.jdbc:ojdbc11:23.9.0.25.07
(Java 17+)
否 — 内联宣布
mssql
com.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
了解嵌入式vs网络客户端的拆分以及Java 17 vs Java 8版本矩阵。提示选择
embedded
vs
client
规则:如果目标数据库是上面前几行中的一个(my-sql、oracle、mssql或generic+postgres),不要提示——自动固定并内联宣布。仅对非规范
generic
目标和固有多选提供商(
data-source
derby
)保留提示。
所有需要提示的分支使用相同的提示形状:
xml
<ask_followup_question>
<question>你为<database>选择了<provider>;应将哪个JDBC驱动作为sharedLibrary部署?</question>
<options>[
  "<参考文件中的规范选项1><一句话用途>",
  "<规范选项2> — ...",
  "其他 — 我将提供groupId:artifactId:version"
]</options>
</ask_followup_question>
步骤6必须为步骤9记录的内容:
一个或多个
{groupId, artifactId, version}
元组,加上驱动类,持久化以便步骤9可以输出
<dependency>
+
<sharedLibrary>
对,无需重新询问。在连接器选择旁边使用辅助文件:
bash
undefined

example: after Step 6 picks my-sql

示例:步骤6选择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

For `derby:embedded` that file contains three entries; for `generic` with PostgreSQL it contains one. The Step-9 applier reads this file and applies every entry in it to `pom.xml`.

The driver choice will be part of the technical design. Step 7 shows it under "Build-time additions"; the user's approval at Step 7 is what authorizes Step 9 to edit `pom.xml`.
---
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`,该文件包含三个条目;对于`generic+PostgreSQL`,包含一个条目。步骤9的应用程序读取此文件,并将每个条目应用到`pom.xml`。

驱动选择将成为技术设计的一部分。步骤7在“构建时添加项”下显示它;用户在步骤7的批准授权步骤9编辑`pom.xml`。
---

Step 7: Present Technical Design Summary

步骤7:呈现技术设计摘要

[BLOCKER] Present ONLY after Steps 1–6 are complete. Every connector must have a drafted GAV (from
tmp/connector-choices/*.json
), every connector must have captured metadata (from
tmp/connector-metadata/*.json
), and every config must have a selected provider. If any of those is missing, go back to the relevant step — do not paper over with "TBD" in the summary.
**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
Then ask for explicit approval:
xml
<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] WAIT for explicit "Yes, proceed to build." before Step 8. On "No, I want to change the plan.", ask which part (trigger, connectors, providers) and loop back to the relevant step. On "No, cancel generation.", stop the workflow politely.
Why this gate matters: Phase 1 is the last chance to catch a silent HTTP fallback, a wrong connector variant, a wrong trigger, or a missing clarifying question. Once Phase 2 begins the project skeleton is on disk and rewinding is more expensive for everyone. The summary is the user's chance to correct course; respect "No, I want to change the plan." as a first-class outcome, not an exception.
After approval, the very first action of Step 8 is
commit_connectors.sh
— that is the script that promotes every Phase-1 draft in
tmp/connector-choices/
to the pinned
tmp/connector-versions/
directory that
dx mule project create
and
pom.xml
will read from. Do not skip it;
build_deps.sh
/
build_gav.sh
calls later in Phase 2 will fail if the pin files aren't there.
Output: User approval to proceed.

[阻塞点] 仅在步骤1–6完成后呈现。每个连接器必须有一个草稿GAV(来自
tmp/connector-choices/*.json
),每个连接器必须有捕获的元数据(来自
tmp/connector-metadata/*.json
),每个配置必须有选定的提供商。如果任何一项缺失,返回相关步骤——不要在摘要中用“TBD”掩盖。
**技术设计摘要**

**用户需求:** "<原始用户提示,原句>"

**项目上下文:**
- 项目目录:<第二阶段将创建项目的绝对路径>
- 工作类型:<新建/修改/脚手架后>
- Mule运行时:<来自tmp/mule-dev-env.json的mule_version>
- Java:<java_version>

**触发器:**
- 选定:<元素名称>来自<连接器或内置源>
  (例如:"shopify:on-updated-product-trigger,固定频率3000ms"或"salesforce:replay-topic-listener来自mule-salesforce-connector:10.15.7"或"内置调度器,每5分钟")
- 考虑的源:列出通过`source-detail`检查的`sources[]`条目(如果有),每个条目用一句话说明选择或拒绝的原因。如果选定的触发器是`<scheduler>`或`<http:listener>`,且范围内任何连接器有`sources[]`条目,则至少需要一条拒绝理由——如果“考虑的源”为空但存在连接器源,TDD不完整,第二阶段无法启动。
  示例:
  - `shopify:on-updated-product-trigger` — 已选择:带`scheduling-strategy`子元素的轮询源,匹配“每3秒”的意图。
  - `shopify:on-new-product-trigger` — 已拒绝:仅在创建时触发,用户需要更新。
  - `shopify:on-updated-customer-trigger` — 已拒绝:错误对象(产品,而非客户)。

**所需连接器:**
1. <系统名称>:<GAV> [com.mulesoft.connectors | org.mule.connectors | 第三方]
   - 用途:<一句话>
   - 配置:<config-name> | 提供商:<provider-name>
2. ...

**构建时添加项(自动):**
- `mule-http-connector` — 如果触发器是HTTP监听器,或任何步骤6提供商属于OAuth家族(回调监听器),则包含
- JDBC驱动 — 如果`mule-db-connector`在范围内,则包含。列出步骤6b中记录在`tmp/connector-choices/db-driver.json`中的**每个**`groupId:artifactId:version`,加上驱动类。此处不接受模糊表述,如“包含PostgreSQL JDBC驱动”——用户正在批准明确的构建编辑,需要确切的坐标。
  示例:
  - `org.postgresql:postgresql:42.7.11`(驱动类`org.postgresql.Driver`) — 在`pom.xml`中添加为`<dependency>`和`<sharedLibrary>`。

**预期的内置处理器(如适用):**
- DataWeave、Logger、错误处理器
然后请求明确批准:
xml
<ask_followup_question>
<question>请审阅上述技术设计。是否继续构建(第二阶段)?</question>
<options>[
  "是,继续构建。",
  "否,我想更改计划。",
  "否,取消生成。"
]</options>
</ask_followup_question>
[阻塞点] 在步骤8之前等待明确的“是,继续构建。”。如果用户选择“否,我想更改计划。”,询问要更改的部分(触发器、连接器、提供商),并返回相关步骤。如果选择“否,取消生成。”,礼貌地停止工作流。
此关卡重要的原因:第一阶段是最后一次机会,可以发现静默HTTP回退、错误的连接器变体、错误的触发器或缺失的澄清问题。一旦第二阶段开始,项目骨架已在磁盘上,回退对每个人来说成本更高。摘要是用户纠正路线的机会;将“否,我想更改计划。”视为一等结果,而非异常。
批准后,步骤8的第一个操作是
commit_connectors.sh
——此脚本将第一阶段
tmp/connector-choices/
中的所有草稿升级到固定的
tmp/connector-versions/
目录,
dx mule project create
pom.xml
将从此目录读取。不要跳过此步骤;如果固定文件不存在,第二阶段后续的
build_deps.sh
/
build_gav.sh
调用将失败。
输出: 用户批准继续。

Phase 2: Build

第二阶段:构建

Step 8: Create Project

步骤8:创建项目

First action — promote Phase 1 drafts to pinned versions. The user just approved the TDD, so every connector choice in
tmp/connector-choices/
is now official. Promote them in one shot:
bash
bash scripts/commit_connectors.sh
第一个操作 — 将第一阶段草稿升级为固定版本。用户刚刚批准了TDD,因此
tmp/connector-choices/
中的每个连接器选择现在都是正式的。一次性升级:
bash
bash scripts/commit_connectors.sh

→ copies every tmp/connector-choices/.json → tmp/connector-versions/.json

→ 将每个tmp/connector-choices/.json复制到tmp/connector-versions/.json

→ exits 1 if no drafts exist (means Step 3 was skipped for some system)

→ 如果没有草稿存在,退出码1(意味着某个系统跳过了步骤3)


Then read `tmp/mule-dev-env.json` for the Mule version and use `build_deps.sh` to emit the full `--dependencies` string from the pins on disk — do not retype GAVs from previous tool output, and do not inline `$(build_gav.sh ...)` once per connector:

```bash
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 "$(bash scripts/build_deps.sh)"
build_deps.sh
reads every
tmp/connector-versions/*.json
pin, filters out the Step 6b JDBC driver sidecar (
db-driver.json
), and prints a comma-joined GAV string. Any pin file in that directory is included automatically — including
http.json
if you added it via the rule below.
Why one wrapper instead of N inlined
$(build_gav.sh …)
substitutions:
with absolute script paths (per the invocation rule above) each inlined
$(…)
is ~165 characters, so a 4-connector project produces a 1000+ character
dx mule project create
command. The Dev Agent terminal harness loses its completion marker on very long commands and stalls the whole turn until the 2-minute timeout fires.
build_deps.sh
keeps the command under ~250 characters regardless of how many connectors are in scope.
Every connector that appears in the approved Technical Design Summary must have a pin in
tmp/connector-versions/
before
build_deps.sh
runs
commit_connectors.sh
already put them there. Two cases add an extra connector beyond the systems explicitly named in the TDD:
ConditionAdded connector
Step 5 selected trigger is HTTP Listener (flow contains
<http:listener>
)
mule-http-connector
Step 6 selected any OAuth-family provider (OAuth, JWT, auth-code)
mule-http-connector
(for OAuth callbacks)
Any event-listener source trigger (e.g.,
<s3:new-object-listener>
,
<salesforce:replay-topic-listener>
)
None beyond the connector that owns the source — it is already in the TDD
If either HTTP-trigger condition applies and you have not already picked HTTP in Step 3, run
get_latest_connector.sh mule-http-connector http
+
pick_connector.sh http <gav>
+
commit_connectors.sh
before
dx mule project create
so
tmp/connector-versions/http.json
exists when
build_deps.sh
scans the directory. Missing it causes a first-build failure like
Can't resolve http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
— self-healable, but it costs a turn.
Version source-of-truth (from Step 3): every GAV in
--dependencies
must come from a
tmp/connector-versions/*.json
file. Do not inline a literal version like
com.mulesoft.connectors:mule-amazon-s3-connector:6.6.0
in the
--dependencies
string — if you bypass
build_deps.sh
and the literal version differs from what the helper would have returned,
mvn clean package
will fail with a "not found" error, and the failure is often not self-healable because the version is fictional.
Project structure created:
  • pom.xml
    (Maven configuration with dependencies)
  • mule-artifact.json
    (artifact metadata with correct Java version)
  • src/main/mule/<project-name>.xml
    (flow definition)
  • src/main/resources/
    (configuration files)


然后从`tmp/mule-dev-env.json`读取Mule版本,并使用`build_deps.sh`从磁盘上的固定项输出完整的`--dependencies`字符串——不要从之前的工具输出重新输入GAV,也不要为每个连接器内联`$(build_gav.sh ...)`:

```bash
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 "$(bash scripts/build_deps.sh)"
build_deps.sh
读取每个
tmp/connector-versions/*.json
固定项,过滤掉步骤6b的JDBC驱动辅助文件(
db-driver.json
),并打印逗号分隔的GAV字符串。该目录中的任何固定文件都会自动包含——如果您根据以下规则添加了HTTP,也包括
http.json
为什么使用一个包装器而非N个内联
$(build_gav.sh …)
替换:
使用绝对脚本路径(根据上面的调用规则),每个内联
$(…)
约165个字符,因此一个4连接器项目会生成1000+字符的
dx mule project create
命令。Dev Agent终端工具在处理超长命令时会丢失完成标记,导致整个回合停滞直到2分钟超时。
build_deps.sh
无论范围内有多少连接器,都能将命令保持在~250字符以内。
每个出现在已批准技术设计摘要中的连接器,在
build_deps.sh
运行前必须在
tmp/connector-versions/
中有一个固定项
——
commit_connectors.sh
已经将它们放在那里。有两种情况会在TDD中明确命名的系统之外添加额外的连接器:
条件添加的连接器
步骤5选择的触发器是HTTP监听器(流包含
<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.sh mule-http-connector http
+
pick_connector.sh http <gav>
+
commit_connectors.sh
,以便
build_deps.sh
扫描目录时
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.sh
且文字版本与辅助脚本返回的版本不同,
mvn clean package
会因“未找到”错误失败,且失败通常无法自我修复,因为版本是虚构的。
创建的项目结构:
  • pom.xml
    (带依赖项的Maven配置)
  • mule-artifact.json
    (带正确Java版本的工件元数据)
  • src/main/mule/<project-name>.xml
    (流定义)
  • src/main/resources/
    (配置文件)

Step 9: Apply JDBC Driver to pom.xml

步骤9:将JDBC驱动应用到pom.xml

The driver GAVs were already chosen in Step 6b and approved by the user at Step 7. Step 9 reads
tmp/connector-choices/db-driver.json
and applies every entry to
<project-name>/pom.xml
— one entry produces one
<dependency>
block AND one
<sharedLibrary>
block. Skip this step entirely if
mule-db-connector
is not in scope.
If
tmp/connector-choices/db-driver.json
is missing but
mule-db-connector
is in scope, return to Step 6b — do NOT invent a driver here or assume prematurely.
For every entry in
db-driver.json
's
dependencies[]
array, add both:
  1. <dependency>
    inside
    <dependencies>
    , verbatim from the sidecar:
xml
<dependency>
    <groupId>{groupId}</groupId>
    <artifactId>{artifactId}</artifactId>
    <version>{version}</version>
</dependency>
  1. <sharedLibrary>
    inside the
    mule-maven-plugin
    <configuration><sharedLibraries>
    groupId
    /
    artifactId
    copied verbatim from the
    <dependency>
    above (no version here).
xml
<configuration>
    <sharedLibraries>
        <sharedLibrary>
            <groupId>{groupId}</groupId>
            <artifactId>{artifactId}</artifactId>
        </sharedLibrary>
        <!-- repeat for every entry in db-driver.json -->
    </sharedLibraries>
</configuration>

驱动GAV已在步骤6b中选择,并在步骤7中获得用户批准。步骤9读取
tmp/connector-choices/db-driver.json
,并将每个条目应用到
<project-name>/pom.xml
——一个条目生成一个
<dependency>
一个
<sharedLibrary>
块。如果
mule-db-connector
不在范围内,完全跳过此步骤。
如果
tmp/connector-choices/db-driver.json
缺失但
mule-db-connector
在范围内,返回步骤6b——不要在此处发明驱动或过早假设。
对于
db-driver.json
dependencies[]
数组中的每个条目,添加两者:
  1. <dependencies>
    内的
    <dependency>
    ,与辅助文件完全一致:
xml
<dependency>
    <groupId>{groupId}</groupId>
    <artifactId>{artifactId}</artifactId>
    <version>{version}</version>
</dependency>
  1. mule-maven-plugin
    <configuration><sharedLibraries>
    内的
    <sharedLibrary>
    groupId
    /
    artifactId
    与上面的
    <dependency>
    完全一致(此处无版本)。
xml
<configuration>
    <sharedLibraries>
        <sharedLibrary>
            <groupId>{groupId}</groupId>
            <artifactId>{artifactId}</artifactId>
        </sharedLibrary>
        <!-- 对db-driver.json中的每个条目重复 -->
    </sharedLibraries>
</configuration>

Step 10: Verify HTTP Connector (OAuth/HTTP-Listener defensive check)

步骤10:验证HTTP连接器(OAuth/HTTP监听器防御性检查)

In v7, Step 8's
--dependencies
already includes
mule-http-connector
when Step 5 chose HTTP Listener or Step 6 chose an OAuth-family provider — because Phase 1's approved TDD made that visible. This step is a defensive no-op check in the common case: run the helper in case the TDD missed the HTTP addition for some reason.
Skip this step entirely when none of the selected providers match
oauth|jwt|auth-code|authorization-code
AND the trigger is not HTTP Listener. Running it as a "just to be safe" consumes a turn.
For the OAuth / HTTP-Listener case, run the idempotent helper:
bash
bash <skill-dir>/scripts/maybe_add_http_connector.sh \
  --project ./<project-name> \
  "oauth-user-pass"    # one argument per Step-6 provider
<skill-dir>
is the absolute path you were given in the "skill is now active" message (the directory containing this
SKILL.md
). Using the absolute path avoids the "No such file or directory" errors that come from relative invocation.
If any provider argument matches
oauth
,
jwt
,
auth-code
, or
authorization-code
(case-insensitive), the script:
  1. Reuses
    <project-name>/tmp/connector-choices/http.json
    if the agent already picked HTTP in Step 3, otherwise runs
    get_latest_connector.sh mule-http-connector http
    and drafts the top row via
    pick_connector.sh http <gav>
    — HTTP is an unambiguous search, so no user prompt is needed.
  2. Inserts a
    <dependency>
    block before
    </dependencies>
    in
    <project-name>/pom.xml
    (with
    <classifier>mule-plugin</classifier>
    ).
  3. Is a no-op if the HTTP connector is already present.
Manual fallback — also add HTTP connector if: the connector documentation mentions "callback URL" or "redirect URI", or Step 11
config-detail
shows an
oauth-callback-config
child element. Without HTTP connector the build fails with:
The content of element '<connector>:<connection-provider>' is not complete
.

在v7中,当步骤5选择HTTP监听器或步骤6选择OAuth家族提供商时,步骤8的
--dependencies
已包含
mule-http-connector
——因为第一阶段已批准的TDD使其可见。此步骤在常见情况下是防御性无操作检查:运行辅助脚本,以防TDD因某种原因遗漏了HTTP添加。
完全跳过此步骤:当没有选定的提供商匹配
oauth|jwt|auth-code|authorization-code
,且触发器不是HTTP监听器时。作为“以防万一”运行它会浪费一个回合。
对于OAuth/HTTP监听器情况,运行幂等辅助脚本:
bash
bash <skill-dir>/scripts/maybe_add_http_connector.sh \
  --project ./<project-name> \
  "oauth-user-pass"    # 每个步骤6提供商一个参数
<skill-dir>
是“技能已激活”消息中提供的绝对路径(包含此
SKILL.md
的目录)。使用绝对路径避免相对调用导致的“找不到文件或目录”错误。
如果任何提供商参数匹配
oauth
jwt
auth-code
authorization-code
(不区分大小写),脚本:
  1. 如果代理已在步骤3中选择HTTP,重用
    <project-name>/tmp/connector-choices/http.json
    ;否则运行
    get_latest_connector.sh mule-http-connector http
    ,并通过
    pick_connector.sh http <gav>
    将顶部行记录为草稿——HTTP是明确的搜索,无需用户提示。
  2. <project-name>/pom.xml
    </dependencies>
    之前插入
    <dependency>
    块(带
    <classifier>mule-plugin</classifier>
    )。
  3. 如果HTTP连接器已存在,则无操作。
手动回退——如果连接器文档提到“callback URL”或“redirect URI”,或步骤11的
config-detail
显示
oauth-callback-config
子元素,也添加HTTP连接器。没有HTTP连接器,构建会失败,错误为:
The content of element '<connector>:<connection-provider>' is not complete

Step 11: Get Configuration Details

步骤11:获取配置详情

Phase 1 Step 6 already persisted
config-detail
output to
tmp/connector-metadata/<nickname>-config.json
. Read it from there:
bash
cat tmp/connector-metadata/sfdc-config.json
Only re-invoke the CLI if the cache file is missing (which should not happen if Phase 1 ran correctly). Flag semantics note:
--name
is the connection provider,
--config-name
is the config:
bash
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(bash scripts/build_gav.sh tmp/connector-versions/sfdc.json)" \
  --type connection-provider \
  --name basic-connection \
  --config-name sfdc-config \
  --output json
Response shape (same
attributes
+
childElements
pattern):
json
{
  "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" }
      ]
    }
  ]
}
Check BOTH attributes AND childElements of the selected connection provider:
bash
jq '.connectionProviders[0].childElements[] | select(.paramName == "oauthCallbackConfig")' tmp/connector-metadata/sfdc-config.json
Connection providers use one of two patterns:
  1. Attributes pattern (e.g., Salesforce basic-connection):
    xml
    <salesforce:basic-connection
        username="${salesforce.username}"
        password="${salesforce.password}"
        securityToken="${salesforce.securityToken}" />
  2. Child elements pattern (e.g., Slack OAuth):
    xml
    <slack:slack-auth-connection>
        <slack:oauth-authorization-code
            consumerKey="${slack.consumerKey}"
            consumerSecret="${slack.consumerSecret}" />
    </slack:slack-auth-connection>
When generating XML: if
attributes
has items, use attributes on the connection element; if
attributes
is empty but
childElements
has items, use nested child elements. Never hardcode structure — always use the metadata.

第一阶段步骤6已将
config-detail
输出持久化到
tmp/connector-metadata/<nickname>-config.json
。从那里读取:
bash
cat tmp/connector-metadata/sfdc-config.json
仅当缓存文件缺失时才重新调用CLI(如果第一阶段正确运行,这不应该发生)。标志语义注意:
--name
是连接提供商,
--config-name
是配置:
bash
NODE_NO_WARNINGS=1 anypoint-cli-v4 dx mule describe-connector \
  --connector "$(bash scripts/build_gav.sh tmp/connector-versions/sfdc.json)" \
  --type connection-provider \
  --name basic-connection \
  --config-name sfdc-config \
  --output json
响应形状(与
attributes
+
childElements
模式相同):
json
{
  "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
bash
jq '.connectionProviders[0].childElements[] | select(.paramName == "oauthCallbackConfig")' tmp/connector-metadata/sfdc-config.json
连接提供商使用两种模式之一:
  1. 属性模式(例如Salesforce basic-connection):
    xml
    <salesforce:basic-connection
        username="${salesforce.username}"
        password="${salesforce.password}"
        securityToken="${salesforce.securityToken}" />
  2. 子元素模式(例如Slack OAuth):
    xml
    <slack:slack-auth-connection>
        <slack:oauth-authorization-code
            consumerKey="${slack.consumerKey}"
            consumerSecret="${slack.consumerSecret}" />
    </slack:slack-auth-connection>
生成XML时: 如果
attributes
有项,在连接元素上使用属性;如果
attributes
为空但
childElements
有项,使用嵌套子元素。永远不要硬编码结构——始终使用元数据。

Step 12: Create Configuration Files

步骤12:创建配置文件

Based on Step 11 metadata, create configuration files.
src/main/resources/config.yaml
— extract required attributes and child-element parameters; emit placeholders:
yaml
salesforce:
  username: "user@example.com"
  password: "password"
  securityToken: "token"

slack:
  consumerKey: "your-consumer-key"
  consumerSecret: "your-consumer-secret"
Configuration XML — structure driven entirely by metadata:
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>
Generate ALL
childElements[]
entries from metadata
— the connection provider's
elementName
, the
attributes[]
array, and every
childElements[]
entry. For OAuth connectors,
oauth-callback-config
requires a
listenerConfig
attribute referencing an
http:listener-config
. Missing required childElements = build failure.

基于步骤11的元数据,创建配置文件。
src/main/resources/config.yaml
— 提取所需属性和子元素参数;输出占位符:
yaml
salesforce:
  username: "user@example.com"
  password: "password"
  securityToken: "token"

slack:
  consumerKey: "your-consumer-key"
  consumerSecret: "your-consumer-secret"
配置XML — 结构完全由元数据驱动:
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连接器,
oauth-callback-config
需要引用
http:listener-config
listenerConfig
属性。缺少所需的childElements会导致构建失败。

Step 13: Get Operation / Source Details

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

For each operation the flow will call, retrieve metadata:
bash
bash scripts/describe_connector.sh sfdc --type operation --name query
Response shape (same
attributes
+
childElements
pattern as
config-detail
):
json
{
  "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 }
  ]
}
For event-driven triggers (the Step 5 selected trigger is a connector source, not built-in Scheduler or generic HTTP Listener), also retrieve source details:
bash
bash scripts/describe_connector.sh sfdc --type source --name replay-topic-listener
Same
attributes
+
childElements
structure. Always include ALL
required: true
attributes and child elements.
The script also writes
tmp/connector-errors/<nick>.<op>.json
(per-op error subset) and the Step 4 invocation populated
tmp/connector-errors/<nick>.json
(connector-wide union). The error types in those files are the ONLY
NS:ID
strings allowed in
<on-error-propagate type="...">
and
<on-error-continue type="...">
at Step 14. Do NOT invent identifiers from connector vocabulary —
SFTP:FILE_NOT_FOUND
is not real; the actual name is
SFTP:FILE_DOESNT_EXIST
.
<raise-error>
has a stricter rule:
it can only throw
MULE:*
errors or custom namespaces like
APP:*
— never connector namespaces (e.g.
EC2:*
,
SFTP:*
,
SALESFORCE:*
). Connector error types can be caught in
<on-error-*>
handlers but not raised. If you need to raise a retry-exhausted error, use
<raise-error type="MULE:RETRY_EXHAUSTED">
, not
<raise-error type="EC2:RETRY_EXHAUSTED">
. The Step 16 validator enforces both rules mechanically.
Generate operation XML (example):
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>

对于流将调用的每个操作,检索元数据:
bash
bash scripts/describe_connector.sh sfdc --type operation --name query
响应形状(与
config-detail
attributes
+
childElements
模式相同):
json
{
  "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选择的触发器是连接器源,而非内置调度器或通用HTTP监听器),还需检索源详情:
bash
bash scripts/describe_connector.sh 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(示例):
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>

Step 14: Generate Complete Flow

步骤14:生成完整流

Generate the complete flow in
src/main/mule/<project-name>.xml
using metadata from Steps 10, 12. Do NOT use hardcoded structures.
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">
    <!-- One namespace URI + XSD URL pair per <dependency> in pom.xml. Do not include doc or xsi. -->

    <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
construction rule:
Include in
xsi:schemaLocation
exactly one entry for each module or connector namespace that has a matching
<dependency>
in
pom.xml
(core, ee/core, and each connector —
http
,
salesforce
,
db
,
anypoint-mq
, etc.). Each entry is the namespace URI followed by the URL of its XSD, separated by whitespace.
Namespaces that must NOT appear in
xsi:schemaLocation
(closed list):
Namespace
xmlns:*
declaration
Why it's excluded from
schemaLocation
doc
(
http://www.mulesoft.org/schema/mule/documentation
)
Required when any
doc:name
/
doc:description
is used (Step 15)
No XSD exists at that URL.
doc:*
attributes are accepted by
mule-core
via
anyAttribute
. Adding a schemaLocation entry makes
mvn clean package
fail at
process-classes
with
Can't resolve …/mule-documentation.xsd
.
xsi
(
http://www.w3.org/2001/XMLSchema-instance
)
Required to use the
xsi:schemaLocation
attribute itself
xsi
is a W3C standard namespace, not a Mule schema.
Namespace ↔ dependency parity: if a namespace is declared via
xmlns:X
but has no matching
<dependency>
in
pom.xml
, the correct fix is to add the dependency or remove the namespace — not to add a schemaLocation entry that points at a non-existent XSD. Same failure mode as
mule-documentation.xsd
above, different root cause; applies to
mule-scripting
,
mule-objectstore
,
mule-validation
,
mule-http
when those namespaces are declared without their connector dep.
Generation rules:
  • Use exact
    elementName
    from metadata for all tags
  • Use exact
    attributeName
    from metadata for all attributes
  • Include every
    required: true
    attribute and child element
  • Use the correct namespace prefix from metadata
  • Reference
    config-ref
    names from Step 12
  • Generate child elements in the exact order of the
    childElements[]
    array
    — XSD schemas enforce strict sequencing
  • Do not add wrapper elements that are not in metadata (e.g., use
    <reconnect>
    not
    <reconnection-strategy><reconnect>
    )
  • Place reconnection at the config connection level, not operation level (unless metadata explicitly includes it there)
  • Build
    xsi:schemaLocation
    from the module/connector
    <dependency>
    list in
    pom.xml
    ; never include
    doc
    or
    xsi
    . See the rule block above.

使用步骤10、12的元数据,在
src/main/mule/<project-name>.xml
中生成完整流。不要使用硬编码结构。
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">
    <!-- 每个pom.xml中的<dependency>对应一个命名空间URI + XSD URL对。不要包含doc或xsi。 -->

    <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
构建规则:
xsi:schemaLocation
中为每个
pom.xml
中有匹配
<dependency>
的模块或连接器命名空间
(core、ee/core以及每个连接器——
http
salesforce
db
anypoint-mq
等)添加恰好一个条目。每个条目是命名空间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
    。请参阅上面的规则块。

Step 15: Add
doc:name
and
doc:description
to Canvas-Visible Elements

步骤15:为画布可见元素添加
doc:name
doc:description

Every XML element that appears as a visible node on the flow canvas MUST have
doc:name
and
doc:description
attributes.
doc:description
is displayed as the label text on the canvas node (overriding
doc:name
when present), so keep it concise and meaningful.
Prerequisite — namespace declaration:
Any use of a
doc:*
attribute requires
xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
on the
<mule>
root (see Step 14). If it is missing, every
doc:name
triggers
The prefix "doc" for attribute "doc:name" associated with an element type "..." is not bound
. The fix is to add the
xmlns:doc
attribute — and, per Step 14's
xsi:schemaLocation
construction rule, not to add
mule-documentation
to
xsi:schemaLocation
.
Infer descriptions from the
description
field returned by
config-detail
,
operation-detail
, and
source-detail
metadata, the XML structure and element purpose, flow comments, and the overall integration context.
Rules:
  • Human-readable sentences that explain the element's purpose in this integration, not generic documentation
  • Max 125 characters — keeps labels readable on the canvas
  • Active voice; be specific about what the element does
  • Include relevant details: endpoints, object types, field names, scheduling intervals
  • Add
    doc:name
    as a short label alongside
    doc:description
Add
doc:name
and
doc:description
to these canvas-visible element types:
Element TypeExamples
Flows/sub-flows
<flow>
,
<sub-flow>
Sources/triggers
<http:listener>
,
<scheduler>
,
<salesforce:modified-object-listener>
Operations/processors
<logger>
,
<set-variable>
,
<set-payload>
,
<ee:transform>
,
<http:request>
,
<salesforce:query>
,
<flow-ref>
, any connector operation
Scopes/containers
<choice>
,
<scatter-gather>
,
<round-robin>
,
<first-successful>
,
<foreach>
,
<until-successful>
,
<parallel-foreach>
,
<try>
Branches/routes
<when>
,
<otherwise>
,
<route>
Global configs
<salesforce:sfdc-config>
,
<http:listener-config>
,
<configuration-properties>
Do NOT add
doc:description
to inner property elements
— these aren't rendered on the canvas:
  • <scheduling-strategy>
    ,
    <fixed-frequency>
    ,
    <cron>
  • <http:body>
    ,
    <http:headers>
    ,
    <http:query-params>
  • <reconnection>
    ,
    <reconnect>
  • <non-repeatable-stream>
    ,
    <repeatable-in-memory-stream>
  • Operation-specific child content elements (e.g.,
    <salesforce:salesforce-query>
    ,
    <slack:chatpost-message-content>
    )
  • Transform inner elements (
    <ee:message>
    ,
    <ee:set-payload>
    ,
    <ee:variables>
    ,
    <ee:set-variable>
    )
Example — after (with doc:name and doc:description):
xml
<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>

流画布上显示为可见节点的每个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:name
    作为短标签和
    doc:description
为以下画布可见元素类型添加
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):
xml
<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>

Step 16: Build and Verify

步骤16:构建与验证

Pre-build validation

预构建验证

As the FIRST tool call of the build response, run:
bash
bash <skill-dir>/scripts/validate_before_build.sh ./<project-name>
The validator runs three static checks (error-type whitelist, namespace↔dependency parity, canonical XSD URL shape) and exits 1 on any violation. If it fails, fix the reported issue (revisit Step 14), re-run the validator, and only proceed to
mvn clean package
after it exits 0. This preserves the one-command-per-response discipline — validator and mvn stay in separate responses.
bash
cd <project-name>
mvn clean package
Success:
target/<project-name>-1.0.0-SNAPSHOT-mule-application.jar
.
Build-then-verify protocol (do NOT skip steps):
  1. Emit
    mvn clean package
    as the only tool call in this response. Do not include a completion signal, a follow-up
    ls
    , or any other tool call alongside it. Stop and wait for the build output.
  2. You MUST wait until the mvn clean package succeeds. Do NOT make any assumptions about build completion. The terminal output is the ONLY source of truth.
  3. Read the output:
    • If the last line block contains
      BUILD SUCCESS
      , proceed to Step 17 (cleanup) in a new response.
    • If the last line block contains
      BUILD FAILURE
      , find the
      [ERROR]
      line beginning
      Failed to execute goal ...
      , diagnose the root cause, edit the offending file (revisiting the relevant earlier Step — e.g. Step 14 for XML structure, Step 15 for documentation attributes and namespaces), and return to step 1 of this protocol. MUST follow the "Best Practices" section for diagnosing the issue and applying the correct fix. Never assume a solution. ALWAYS reference the connector metadata sources and operations under tmp/ folder as your source of truth.
After any
<write_to_file>
or
<replace_in_file>
on
pom.xml
, on flow XML, or on config XML, you MUST re-run
mvn clean package
before declaring completion.
Editing without re-verifying silently ships a broken build.

作为构建响应的第一个工具调用,运行:
bash
bash <skill-dir>/scripts/validate_before_build.sh ./<project-name>
验证器运行三个静态检查(错误类型白名单、命名空间与依赖项一致性、规范XSD URL形状),任何违规时返回退出码1。如果失败,修复报告的问题(重新访问步骤14),重新运行验证器,仅在返回退出码0后才继续
mvn clean package
。这保持了每个响应一个命令的规范——验证器和mvn保持在不同的响应中。
bash
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
      ,找到以
      [ERROR]
      开头的行
      Failed to execute goal ...
      ,诊断根本原因,编辑有问题的文件(重新访问相关的早期步骤——例如步骤14处理XML结构,步骤15处理文档属性和命名空间),然后返回此协议的步骤1。必须遵循“最佳实践”部分诊断问题并应用正确的修复。永远不要假设解决方案。始终参考tmp/文件夹下的连接器元数据来源和操作作为真相来源。
在对
pom.xml
、流XML或配置XML进行任何
<write_to_file>
<replace_in_file>
后,必须重新运行
mvn clean package
才能宣布完成。
编辑后不重新验证会静默发布损坏的构建。

Step 17: Clean Up Workspace
tmp/

步骤17:清理工作区
tmp/

Pre-condition: The immediately preceding response must be a build response (per Step 16) whose returned output contains
BUILD SUCCESS
. If this is not true, do NOT enter Step 17 — go back to Step 16.
The workspace-relative
tmp/
directory carries Phase 1 → Phase 2 coordination state —
tmp/mule-dev-env.json
,
tmp/connector-choices/
,
tmp/connector-versions/
,
tmp/connector-metadata/
,
tmp/connector-errors/
, plus the
tmp/mule-dev-*-XXXXXX
scratch files written by
get_latest_connector.sh
and
describe_connector.sh
. Once the build passes those files have served their purpose — they exist only so steps can hand state to each other, not as artifacts the user needs.
Remove the directory in its own response, as the only tool call:
bash
rm -r tmp/
Discipline:
  • Delete only
    tmp/
    at the workspace root.

前置条件: 紧接的前一个响应必须是构建响应(根据步骤16),其返回的输出包含
BUILD SUCCESS
。如果不满足,不要进入步骤17——返回步骤16。无论最终尝试成功或失败,都必须清理tmp/文件夹。
工作区相对的
tmp/
目录承载第一阶段→第二阶段的协调状态——
tmp/mule-dev-env.json
tmp/connector-choices/
tmp/connector-versions/
tmp/connector-metadata/
tmp/connector-errors/
,以及
get_latest_connector.sh
describe_connector.sh
写入的
tmp/mule-dev-*-XXXXXX
临时文件。一旦构建通过,这些文件就完成了它们的用途——它们仅用于步骤之间传递状态,而非用户需要的工件。
在单独的响应中删除该目录,作为唯一的工具调用:
bash
rm -r tmp/
规范:
  • 仅删除工作区根目录下的
    tmp/

Step 18: Declare Completion

步骤18:宣布完成

Pre-condition: The two preceding responses must be (a) a build response (per Step 16) returning
BUILD SUCCESS
, immediately followed by (b) a cleanup response (per Step 17) that ran
rm -r tmp/
. If either is missing, do NOT enter Step 18 — go back to whichever step is missing. You HAVE to clean up the tmp/ folder no matter the attemps succeed or fail at the end.
Discipline:
  • The completion signal is the ONLY tool call in this response. Do not run
    mvn
    here. Do not add follow-up shell commands. The build was already executed and verified in the previous response.
  • Do not declare completion after a
    BUILD FAILURE
    , even if you believe the subsequent edit fixes it.
    Re-run
    mvn clean package
    in its own response (Step 16), observe
    BUILD SUCCESS
    , then declare completion in the next response.
  • Do not declare completion if the most recent build was never actually executed (e.g., the command was shown but no result came back). Re-run it in its own response and wait.
Completion message content — keep it tight. The user can see the files and the build log. The completion message is evidence that the build passed, not a marketing document. Include exactly these, and nothing else:
  1. The successful build artifact path:
    target/<project-name>-1.0.0-SNAPSHOT-mule-application.jar
  2. One sentence naming the integration (what it does, e.g. "Polls S3 every 5s and publishes new-object events to a JMS queue").
  3. The
    config.yaml
    keys the user still needs to fill in (credentials, bucket names, queue names) — as a short bullet list. This is the only information that is not already visible on disk.
Do not include: lengthy "Features Implemented" sections, redacted JSON payload examples, "Next Steps" (the user will deploy when they're ready), or recap tables.

前置条件: 前两个响应必须是(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. 一句话命名集成(它做什么,例如“每5秒轮询S3并将新对象事件发布到JMS队列”)。
  3. 用户仍需填写的
    config.yaml
    键(凭证、存储桶名称、队列名称)——作为简短的项目符号列表。这是磁盘上未显示的唯一信息。
不要包含:冗长的“已实现功能”部分、编辑后的JSON负载示例、“下一步”(用户准备好后会部署)或回顾表格。

Best Practices

最佳实践

1. Dynamic connector versions. See Step 3's "Version source-of-truth rule" for the full mandate.
  • ✅ Phase 1:
    get_latest_connector.sh mule-salesforce-connector sfdc
    → list →
    pick_connector.sh sfdc <gav>
    → draft at
    tmp/connector-choices/sfdc.json
  • ✅ Phase 2:
    commit_connectors.sh
    (first action of Step 8) →
    build_deps.sh
    for the
    dx mule project create --dependencies
    string, or
    build_gav.sh tmp/connector-versions/<name>.json
    for a single GAV elsewhere
  • ❌ Hardcoded literal:
    com.mulesoft.connectors:mule-salesforce-connector:10.20.0
  • ❌ Pasted from
    references/connector-catalog.md
    (snapshot only — drifts)
  • ❌ Search term alone as the GAV:
    salesforce
2. Metadata-driven XML generation. Never manually write XML. Always use metadata from
operation-detail
,
config-detail
,
source-detail
:
  • attributes
    → XML attributes on the tag (use
    attributeName
    verbatim)
  • childElements
    → nested XML elements (use
    prefix:elementName
    as the tag)
  • Always include every
    required: true
    attribute and child element
  • Optional entries may be omitted unless specifically needed
  • description
    → understand what the parameter does
  • allowedValues
    → constrain values
  • defaultValue
    → skip if acceptable
3. System-specific connectors first, every time. Always search Exchange for a dedicated connector before falling back to HTTP — and "before" means literally before, not "before I type my decision". The skill's discipline is that a Phase-1 draft in
tmp/connector-choices/
(or the post-commit pin in
tmp/connector-versions/
) is the only evidence that allows declaring "system X is covered"; the helper script having exited 1 is the only evidence that allows declaring "no dedicated connector, falling back to HTTP".
4. Validate metadata before generation. Use
describe-connector
for features,
config-detail
/
operation-detail
/
source-detail
for exact specs.

1. 动态连接器版本。请参阅步骤3的“版本来源规则”获取完整要求。
  • ✅ 第一阶段:
    get_latest_connector.sh mule-salesforce-connector sfdc
    → 列出 →
    pick_connector.sh sfdc <gav>
    → 草稿存储在
    tmp/connector-choices/sfdc.json
  • ✅ 第二阶段:
    commit_connectors.sh
    (步骤8的第一个操作) →
    build_deps.sh
    生成
    dx mule project create --dependencies
    字符串,或
    build_gav.sh 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/
中的第一阶段草稿(或提交后的固定项在
tmp/connector-versions/
)是宣布“系统X已覆盖”的唯一证据;辅助脚本返回退出码1是宣布“无专用连接器,回退到HTTP”的唯一证据。
4. 生成前验证元数据。使用
describe-connector
获取功能,使用
config-detail
/
operation-detail
/
source-detail
获取精确规格。

Common Integration Patterns

常见集成模式

#1 HTTP API → Query → Response: http:listener → salesforce:query → ee:transform → response (Components: HTTP + Salesforce/Database)
#2 Scheduled Sync → Query → Transform → Notification: scheduler → salesforce:query → ee:transform → slack:post-message (Components: Scheduler + Salesforce + Slack)
#3 Scheduled Sync → Query → Transform → Batch Insert: scheduler → query → ee:transform → foreach → db:insert (Components: Scheduler + Source System + Database)
#4 Event listener → Transform → Downstream system:
<connector>:<event-source>
→ ee:transform → target-connector operation (Components: Source System with native listener + Target System). This pattern shines when the source connector exposes a real event source in
sources[]
— use that instead of polling via Scheduler.

#1 HTTP API → 查询 → 响应: http:listener → salesforce:query → ee:transform → response(组件:HTTP + Salesforce/Database)
#2 调度同步 → 查询 → 转换 → 通知: scheduler → salesforce:query → ee:transform → slack:post-message(组件:调度器 + Salesforce + Slack)
#3 调度同步 → 查询 → 转换 → 批量插入: scheduler → query → ee:transform → foreach → db:insert(组件:调度器 + 源系统 + Database)
#4 事件监听器 → 转换 → 下游系统:
<connector>:<event-source>
→ ee:transform → 目标连接器操作(组件:带有原生监听器的源系统 + 目标系统)。当源连接器在
sources[]
中暴露真实事件源时,此模式效果最佳——使用它而非通过调度器轮询。

Troubleshooting

故障排除

JAVA_HOME not set:
export JAVA_HOME=$(/usr/libexec/java_home -v 11)
anypoint-cli-v4 not found:
npm install -g @mulesoft/anypoint-cli-v4
DX plugin not found:
npm install -g @salesforce/anypoint-cli-dx-mule-plugin
Connector not found: check spelling · try
mule-<name>-connector
and
mule4-<name>-connector
· verify Mule 4 compatibility.
Wrong connector selected: use specific search terms (
mule-http-connector
, not
http
).
scripts/get_latest_connector.sh
scores by token overlap and requires at least one token match — a bogus search will exit 1 rather than return a random result.
Runtime path required: first use of
dx mule describe-connector
or related commands prompts for runtime location. The path is saved to
~/.mule-dx/config.json
.
Database driver missing: if Step 6b didn't record
tmp/connector-choices/db-driver.json
, return to Step 6b to make the choice (with prompts where the provider is
generic
/
data-source
/
derby
). If the sidecar exists but
pom.xml
is missing the entries, return to Step 9 — it reads that file and applies every entry as a
<dependency>
+
<sharedLibrary>
pair. Do not invent a driver GAV at Step 9.
The mule application does not contain the following shared libraries: [<artifactId>:<groupId>]
:
the
<dependency>
block is present but the matching
<sharedLibrary>
block inside
mule-maven-plugin
is either missing or has a mismatched
groupId
/
artifactId
. Every driver dependency needs both; see Step 9.
Derby driver layout: see
references/jdbc-drivers.md
. Derby 10.15+ split into multiple artifacts (
derby
+
derbyshared
+
derbytools
for embedded;
derbyclient
+
derbyshared
for network client), so the
<sharedLibrary>
list has multiple entries, not one.

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.sh
按标记重叠评分,需要至少一个标记匹配——虚假搜索会返回退出码1,而非随机结果。
需要运行时路径: 首次使用
dx mule describe-connector
或相关命令时,会提示输入运行时位置。路径会保存到
~/.mule-dx/config.json
Database驱动缺失: 如果步骤6b未记录
tmp/connector-choices/db-driver.json
,返回步骤6b做出选择(提供商为
generic
/
data-source
/
derby
时提示)。如果辅助文件存在但
pom.xml
缺失条目,返回步骤9——它读取该文件并将每个条目应用为
<dependency>
+
<sharedLibrary>
对。不要在步骤9中发明驱动GAV。
The mule application does not contain the following shared libraries: [<artifactId>:<groupId>]
<dependency>
块存在,但
mule-maven-plugin
内的匹配
<sharedLibrary>
块缺失或
groupId
/
artifactId
不匹配。每个驱动依赖都需要两者;请参阅步骤9。
Derby驱动布局: 请参阅
references/jdbc-drivers.md
。Derby 10.15+拆分为多个工件(嵌入式为
derby
+
derbyshared
+
derbytools
;网络客户端为
derbyclient
+
derbyshared
),因此
<sharedLibrary>
列表有多个条目,而非一个。

Quick Reference

快速参考

<skill-dir>
below is the absolute path you were given in the "skill is now active" message. Use it consistently — do not construct relative
../scripts/...
paths.
bash
undefined
下面的
<skill-dir>
是“技能已激活”消息中提供的绝对路径。请一致使用——不要构造
../scripts/...
相对路径。
bash
undefined

Step 1: validate toolchain (CLI, DX plugin, Java 11+, Mule runtime presence) - Validation-only

步骤1:验证工具链(CLI、DX插件、Java 11+、Mule运行时是否存在) - 仅用于验证

bash <skill-dir>/scripts/validate_prerequisites.sh
bash <skill-dir>/scripts/validate_prerequisites.sh

Step 3: connector search — list, decide, draft (one loop per system; search

步骤3:连接器搜索 — 列出、决策、记录草稿(每个系统循环一次;搜索

EVERY named system including mid-market SaaS; don't pre-judge as "no connector")

每个命名系统,包括中型SaaS;不要预先判断“无连接器”)

bash <skill-dir>/scripts/get_latest_connector.sh <search-term> [<nickname>] # prints ranked GAVs to stdout
bash <skill-dir>/scripts/get_latest_connector.sh <search-term> [<nickname>] # 将排名GAV输出到stdout

... agent reads list, decides (or AskUserQuestion for real variant ambiguity), then:

... 代理读取列表,决策(或针对真实变体模糊性使用AskUserQuestion),然后:

bash <skill-dir>/scripts/pick_connector.sh <nickname> groupId:assetId:version # drafts to tmp/connector-choices/
bash <skill-dir>/scripts/pick_connector.sh <nickname> groupId:assetId:version # 草稿存储到tmp/connector-choices/

Step 4: describe connectors (Phase 1 — wrapper saves JSON + echoes sources[] digest)

步骤4:描述连接器(第一阶段 — 包装脚本保存JSON + 输出sources[]摘要)

bash <skill-dir>/scripts/describe_connector.sh <nickname> # one per connector
bash <skill-dir>/scripts/describe_connector.sh <nickname> # 每个连接器调用一次

Step 6: connection-provider detail (Phase 1 — also cached for Phase 2).

步骤6:连接提供商详情(第一阶段 — 也缓存供第二阶段使用)。

Flag semantics: --name = connection provider, --config-name = config name.

标志语义:--name = 连接提供商,--config-name = 配置名称。

GAV_A="$(bash <skill-dir>/scripts/build_gav.sh 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
GAV_A="$(bash <skill-dir>/scripts/build_gav.sh 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)

步骤8:将草稿升级为固定版本,然后创建真实项目(第二阶段)

bash <skill-dir>/scripts/commit_connectors.sh # 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 "$(bash <skill-dir>/scripts/build_deps.sh)" # reads every tmp/connector-versions/*.json pin
bash <skill-dir>/scripts/commit_connectors.sh # 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 "$(bash <skill-dir>/scripts/build_deps.sh)" # 读取每个tmp/connector-versions/*.json固定项

Step 10: OAuth → HTTP defensive check (only when Step 6 chose OAuth/JWT/auth-code

步骤10:OAuth → HTTP防御性检查(仅当步骤6选择了OAuth/JWT/auth-code

or the trigger is HTTP Listener)

或触发器是HTTP监听器时)

bash <skill-dir>/scripts/maybe_add_http_connector.sh --project ./<name> "<provider1>" "<provider2>"
bash <skill-dir>/scripts/maybe_add_http_connector.sh --project ./<name> "<provider1>" "<provider2>"

Step 13: operation / source details (Phase 2 — wrapper saves JSON + caches errorTypes)

步骤13:操作/源详情(第二阶段 — 包装脚本保存JSON + 缓存errorTypes)

bash <skill-dir>/scripts/describe_connector.sh <nickname> --type operation --name <op> bash <skill-dir>/scripts/describe_connector.sh <nickname> --type source --name <src>
bash <skill-dir>/scripts/describe_connector.sh <nickname> --type operation --name <op> bash <skill-dir>/scripts/describe_connector.sh <nickname> --type source --name <src>

Step 16: pre-mvn validation — error-type whitelist + namespace↔dep parity + XSD shape

步骤16:预mvn验证 — 错误类型白名单 + 命名空间与依赖项一致性 + XSD形状

bash <skill-dir>/scripts/validate_before_build.sh ./<project>

| Pre-mvn validation | `bash <skill-dir>/scripts/validate_before_build.sh ./<project>` |

---
bash <skill-dir>/scripts/validate_before_build.sh ./<project>

| 预mvn验证 | `bash <skill-dir>/scripts/validate_before_build.sh ./<project>` |

---