关于 platform-mcp-tool-widget-coordinate
用于协调自定义 MCP 工具输出与 Salesforce Lightning 组件渲染的技能,面向构建 Apex Invocable Action 可视化输出的开发者。它按依赖顺序调用生成技能,并通过审批与验证关卡串联两个 CLT 和 HXL widget。
主要功能
- 01发现工具响应字段结构
- 02协调两个对象型 CLT
- 03调用技能生成 HXL widget
- 04生成信封 CLT 默认渲染器
- 05设置审批与验证关卡
适用场景
- 01为 Apex Invocable Action 输出构建 widget
- 02将嵌套工具结果映射到平面字段
- 03按依赖顺序生成并验证相关文件
MCP 相关
该技能针对自定义 MCP 服务工具,处理其返回的 invocable-action 结果信封,并协调将输出呈现为 widget。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill platform-mcp-tool-widget-coordinate安装后在 Claude Code 中输入/platform-mcp-tool-widget-coordinate确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/action-name-source-prompt.mdapex-invocable-source-prompt.mdnested-object-list-source-prompt.mdnested-object-single-source-prompt.mdpasted-tool-output-prompt.mdreferences/build-plan-format.mdmcp-tool-output-discovery.mdtwo-clt-modeling.mdvalidation-gates.md详细介绍
使用组件渲染自定义 MCP 工具输出
协调**两个基于对象的自定义 Lightning 类型(CLT)**和一个 HXL 组件,以渲染自定义 MCP 服务工具的输出,该工具由 Apex @InvocableMethod 实现。本技能不会直接编写内容,而是按依赖顺序加载并调用叶子技能,在获得用户批准后才继续,并在报告完成前运行验证关卡。
职责边界——本技能仅负责:
- MCP 工具用例——解析工具的输出形状,并按正确顺序编排叶子技能;以及
- 封套 CLT 的默认
renderer.json——本技能内联编写的唯一构件,用于将封套连接到组件。
CLT由 platform-custom-lightning-type-generate 编写,所有组件元数据(schema + body + .uiwidget-meta.xml)均由 platform-widget-generate 编写并验证。本技能不会编写组件元数据,也不会修改 Apex 类,而是向各叶子技能提供输入并将结果连接起来。
范围
仅适用于由 Apex Invocable Action 支持的自定义 MCP 服务工具。MCP 工具返回平台的invocable-action 结果封套——一个包含 actionName、isSuccess 和 outputValues 节点的对象,该节点承载工具的实际负载。要使用组件渲染此封套,请将其建模为两个地位相同的基于对象的 CLT(lightning__objectType)。之所以需要两个 CLT,唯一原因是其中一个必须通过名称引用另一个(CLT 不能引用自身),因此它们需要使用不同的部署名称。请根据每个 CLT 实际建模的内容为其命名并编写说明——切勿使用「Payload CLT」/「Envelope CLT」或「Inner CLT」/「Outer CLT」之类虚构的角色名称组合:
- 模仿工具结果封套的 CLT,命名为
<toolApiName>。其outputValues属性通过c__<responseCLT>(CLT 引用前缀,参见下方的命名空间说明)使用另一个 CLT 作为类型。 - 与 Invocable Action 响应的形状完全一致的 CLT(即
@InvocableMethod响应类上的@InvocableVariable字段),命名为<toolApiName>Response——「Response」不是虚构的角色名称,而是 Apex 源代码中该类本身使用的名称。
组件基于响应字段(扁平结构)进行构建,封套 CLT 中的**默认 renderer.json**则通过 {!$attrs.outputValues.<field>} 将封套中的嵌套结构连接到扁平的组件。
命名空间前缀的来源(
c__/@apexClassType/<ns>__…): Actions REST describe 返回的是不带前缀的类名(Outer$Inner),且不包含命名空间——前缀是该 org 的 CLT 命名空间,由本技能添加。无命名空间的 org 使用c__(最常见情况,本文档中均按字面使用);打包的 org 使用包命名空间<ns>__(从类的NamespacePrefix读取;仅在 org 没有命名空间时才默认使用c)。下文中的每个c__都是此处所述前缀。
不在范围内,请转至其他技能处理:
- 自定义基于 Apex 的 Agent 操作输出(基于 Apex 的 CLT
@apexClassType/...、单个 CLT、特定表面的渲染器)→platform-lightning-type-widget-coordinate。 - 没有关联 MCP 工具 / Lightning Type 的独立组件 →
platform-widget-generate。 - 仅编写 CLT 或仅编写 Apex 类 →
platform-custom-lightning-type-generate/platform-apex-generate。
Beta 数量规则: invocable-action 结果是一个批量数组(
content[])。在 beta 版本中,本技能会建模并渲染单个响应,即content[]的第一个元素。CLT 封套建模的是一个结果对象,而不是content[]包装器。
与 platform-lightning-type-widget-coordinate 的区别
| 维度 | Agent 操作流(...lightning-type-widget-coordinate) | 此 MCP 工具流 |
|---|---|---|
| CLT 类型 | 基于 Apex(@apexClassType/...) | 基于对象(lightning__objectType) |
| CLT 数量 | 一个 | 两个(信封 + 响应) |
| 字段来源 | @AuraEnabled | 顶层响应类上的 @InvocableVariable(引用的内部类:public/@AuraEnabled — 请参阅硬性规则 6) |
| 渲染器位置 | lightningTypes/<T>/lightningDesktopGenAi/renderer.json(特定界面) | lightningTypes/<toolCLT>/renderer.json(默认,与 schema.json 并列) |
| 渲染器绑定 | 扁平的 {!$attrs.<field>} | 嵌套的 {!$attrs.outputValues.<field>} |
阶段图
| 阶段 | 目的 | 输出 |
|---|---|---|
| 1 — 输入选择 | 确定负载来源:可调用操作 API 名称(首选)、Apex Invocable 类,或粘贴的工具输出 JSON 示例。 | source(action | apex | sample)、工具 API 名称 |
| 2 — 负载发现 | 通过 Actions REST API 描述可调用操作并读取其类型化的 outputs(或从源代码解析响应类,或从示例中读取 outputValues)。 | payloadFields(名称 + lightning:type) |
| 3 — 构建计划 | 完整打印计划;除非下一条回复明确提出异议,否则继续执行。 | 已打印的计划 |
| 4 — 生成 | 加载并调用叶级技能:响应 CLT → 信封 CLT → 小组件 → 信封 CLT 中的内联默认渲染器。 | 已写入的文件 |
| 5 — 验证 | 运行硬性门槛(阻止)和警告门槛(提示)。 | 门槛报告 |
| 6 — 总结 | 文件、验证、部署顺序、预览就绪状态。 | 总结 |
各阶段模式: 重新加载技能 → 执行其工作流 → 验证输出 → 在进入下一阶段前设置检查点。即使你记得某个叶级技能的内容,技能也会不断演进——务必重新加载。
阶段 1 — 输入选择
确定负载结构的来源。按以下从上到下的顺序优先选择:
| 来源 | 触发条件 | 阶段 2 操作 |
|---|---|---|
action | 提示中提供了可调用操作 API 名称——直接提供,或通过可解析为该名称的 Apex 类名提供——且有已通过身份验证的组织可用。首选。 | 通过 Actions REST API 描述该操作,并读取其类型化的 outputs。 |
sample | 没有可访问的组织(或描述请求返回 404),但有粘贴的工具输出 JSON 示例。 | 从示例中解析 outputValues 对象。 |
apex | 只有 Apex 类可用(无法解析操作名称、没有可访问的组织、也没有示例)——仅作为后备方案使用,内容可能与已部署版本不同步。 | 解析响应类并枚举 @InvocableVariable 字段。 |
记录工具 API 名称(用于命名所有产物——请参阅下方的命名约定)。
来源优先级: 实时权威架构来源优先于本地类解析,本地类解析优先于粘贴的示例。顺序如下:
- 如果有操作 API 名称且有已通过身份验证的组织可用,则使用
action。Actions REST API 描述与平台自身公开的架构相同,因此无需请求/辅助程序筛选,并能提供真实的字段类型。 - 如果粘贴了运行时 JSON 示例,则使用
sample(运行时响应——明确且为最新)。 - 如果本地存在 Apex 类,且以上条件均不适用,则使用
apex(仅作为后备方案——可能与操作实际部署的版本不同步)。
如果以上来源都不可用,请停止并向用户索要操作名称、类、示例或架构。
阶段 2 — 载荷发现
首先阅读 references/mcp-tool-output-discovery.md(必需 — 不要仅根据本摘要执行阶段 2),然后执行所选来源对应的流程。该参考文档是完整的各来源流程、字段类型映射表以及嵌套/列表处理方式的权威依据;以下指引仅用于定位:
action(首选): 使用sf api request rest '/services/data/v<APIVER>/actions/custom/apex/<ActionApiName>' -o <org>进行描述;仅使用outputs数组(忽略inputs— 它是请求包装器);将每个type不区分大小写地映射为 CLTlightning:type。如果某个条目的"type": null,且带有"apexClass": "<Outer>$<Inner>"键,则该字段是 Apex 类类型字段(不是描述缺失):maxOccurs: 1→ 单个嵌套对象,maxOccurs > 1→ 顶层列表。如果描述请求返回 404,则依次回退到sample,然后是apex。apex(回退): 定位该类,识别响应类(@InvocableMethod返回的List<...>的元素类型),枚举其@InvocableVariable字段(排除请求类和private辅助成员),将 Apex 类型映射为 CLT 类型。sample(回退): 解析outputValues对象;根据 JSON 值推断每个字段的lightning:type。
嵌套对象和列表字段(适用于所有来源,作为扁平基本类型情形的补充): 类型为另一个 Apex 类的字段 — 单个对象(maxOccurs: 1)、顶层列表(maxOccurs > 1),或包装器对象内部的列表(描述返回一个 maxOccurs: 1 的 apexClass 输出,并隐藏内部列表)— 均在范围内,且绝不能建模为裸的 {"type":"object"} 或内联的 lightning__objectType。在响应 CLT 中将其类型设为 @apexClassType/c__<Outer>$<Inner>(绝不使用 CLT 级别的 lightning__listType/items),通过被引用/内部类的**公共 / @AuraEnabled**成员枚举字段(硬性规则 6),如果叶子字段本身是类,则递归处理。各类形状对应的 CLT 类型和渲染器绑定深度见 references/mcp-tool-output-discovery.md 和 references/two-clt-modeling.md(“顶层列表与包装器内列表”)— 同时参阅硬性规则 4、6、14 — 以及 examples/nested-object-*-source-prompt.md 中的演练示例。
记录 payloadFields — 定义响应 CLT 和 widget 架构的有序 { name, title, lightning:type } 列表。在构建计划中注明生成该列表的来源。
过时数据:不要维护跨会话缓存。每个会话都要重新读取本地项目,并从组织中重新获取数据。
阶段 3 — 构建计划 + 审批关卡
使用 references/build-plan-format.md 中的模板打印构建计划。计划必须列出:
- 面向开发者的一行摘要(
PLAN:行)。 - 工具 API 名称和响应类 FQN(或“来自粘贴的示例”)。
- 两个 CLT 名称(信封 + 载荷)和 widget 名称,并附上绝对路径。
- 信封 CLT 仅包含
actionName(文本)、isSuccess(布尔值)和outputValues(类型为c__<responseCLT>,这是渲染器桥接所依赖的字段)—actionName/isSuccess仅属于信封,绝不会出现在 widget 上(硬性规则 5)— 以及响应 CLT 和 widget 将承载的响应字段。 - 生成后将运行的验证。
完整打印计划,然后继续,除非用户的下一条回复明确表示反对。 明确表示反对包括 no、stop、wait、change X、use Y instead,或等效的拒绝/修改请求。明确批准当然欢迎,但并非必需 — 沉默、无关的后续消息,或单轮评测中的自然继续,都视为隐式批准。关键要求是计划必须在对话记录中可见。如果用户表示反对,则修改并重新打印计划,然后再继续。
阶段 4 — 生成
按此顺序加载并调用叶子技能。对每项:加载技能,根据阶段 3 的规范执行其工作流,验证输出,然后在继续下一项前创建检查点。
-
Response CLT — 加载
platform-custom-lightning-type-generate。编写基于对象的 CLT<responseCLT>(惯例:<toolApiName>Response),其properties为阶段 2 的payloadFields。根类型为lightning__objectType,并在根级设置"lightning:tags": ["mcp"]。- 响应 CLT 的顶层属性必须与 describe 的
outputs[]名称一一对应(或者,对于apex/sample,与响应类的@InvocableVariable字段对应——也就是 describe 会展示的同一组字段)。有 N 个同级输出的 describe → N 个扁平属性;只有 一个输出的 describe → 一个以该输出命名的属性。只有当 describe 本身返回一个输出时,单属性 CLT 才是正确的。 - 绝不要将多个同级输出合并到一个虚构的包装属性中。 如果给这个属性命名以承载多个输出,就会凭空引入一个不属于任何 describe 输出的键;在
action来源下无法解析该键——响应类名称永远不会出现在outputs[]中。每个响应 CLT 属性名称都必须能追溯到一个 describe 输出名称(参见field-trace/clt-reference-integrity)。 - 顶层列表输出应直接指定其元素类类型(
@apexClassType/c__<Outer>$<ElementClass>),而不是用包装类型——参见上文“嵌套对象列表”。
- 响应 CLT 的顶层属性必须与 describe 的
-
Envelope CLT — 加载
platform-custom-lightning-type-generate。编写基于对象的 CLT<toolCLT>(惯例:<toolApiName>,即 envelope),并在根级设置"lightning:tags": ["mcp"],且:actionName→lightning__textTypeisSuccess→lightning__booleanTypeoutputValues→c__<responseCLT>(引用 CLT 模式;必须先部署响应 CLT,再部署 envelope CLT)
-
Widget — 加载
platform-widget-generate。编写一个扁平 widget,其schema.json属性为payloadFields(名称 + 基本类型)——这是独立的 widget 契约,不从任何 Lightning Type 派生,也不与之耦合。它只渲染outputValues数据字段:绝不渲染actionName/isSuccess(仅属于 envelope)。widget 正文通过{!$attrs.<field>}绑定每个字段——widget 与 envelope 无关,绝不直接引用outputValues。 -
默认渲染器(在 ENVELOPE CLT 中内联编写——绝不能省略)。 首先阅读
platform-custom-lightning-type-generate/references/widget-rendition.md(必读——不得凭记忆编写或复制现有示例,因为示例可能使用已弃用的格式)。然后编写<pkgDir>/lightningTypes/<toolCLT>/renderer.json——默认渲染器位于 bundle 根目录,与schema.json同级(不得放在lightningDesktopGenAi/下)。其renderer.componentOverrides["$"]将definition设置为@widget/c/<widgetName>,并将每个 widget schema 属性映射到 envelope 的outputValues节点下对应的 payload 字段,使用{!$attrs.outputValues.<payloadField>}——这一嵌套绑定可将 envelope CLT 连接到扁平 widget。不要在renderer.json中复制 widget 正文。完整 JSON 示例见references/two-clt-modeling.md。
已有渲染器的处理方式: 如果目标路径下已存在 renderer.json,先读取它。如果它引用了相同的 widget,且绑定也相同,则保持不变。如果它引用了不同的 widget,或使用自定义 LWC 根覆盖(c/<component>),则停止并报告冲突,切勿覆盖。
第 5 阶段 — 验证关卡
阅读 references/validation-gates.md(必需 — 其中包含各关卡的运行流程和精确的通过/失败判定条件),并运行所有关卡。Widget 包内部检查(架构解析、根键、叶节点类型、{!$attrs.X} 解析、.uiwidget-meta.xml 格式正确性)由 platform-widget-generate 负责,并在其自身验证中运行。
硬性 — 失败时阻止:
clt-reference-integrity— 信封的outputValues类型为c__<responseCLT>,响应 CLT 存在且两者都能解析;没有$schema/items;每个非基本类型的响应属性都是@apexClassType/...引用(绝不能是裸对象或 CLT 级别的lightning__listType)。renderer-wires-widget— bundle 根目录下的renderer.json连接@widget/c/<widgetName>,并在outputValues下按照 describe 指定的深度绑定每个 widget 属性(顶层基本类型或列表输出为两个字段段;包装对象中的叶节点/列表为三个字段段)。必须双向校验:缺少或多余的绑定都会失败。nested-list-coverage— Phase 2 中发现的每个嵌套对象和列表字段都由 widget 呈现,并在正确深度绑定。“超出范围”/“beta 单响应”都不是有效的舍弃理由。
警告 — 仅供参考:
field-trace— 运行references/validation-gates.md中的跟踪流程:枚举响应字段,用jq查询 widget 架构键,并将其归类为 INVENTED 或 OMITTED。Widget 中自创的字段判定为失败;遗漏的响应数据字段发出警告。
在 Phase 6 中按名称报告每个关卡结果(pass、fail (<reason>)、warn (<reason>)、not run)。不要概括为“全部通过”。此技能仅生成元数据 — 不会执行部署;部署由调用方负责。
第 6 阶段 — 总结
MCP Tool Widget Build Complete: <widgetName>
FILES GENERATED:
Response CLT:
<pkgDir>/lightningTypes/<responseCLT>/schema.json
Envelope CLT:
<pkgDir>/lightningTypes/<toolCLT>/schema.json
<pkgDir>/lightningTypes/<toolCLT>/renderer.json # default renderer — wires the widget
Widget bundle:
<pkgDir>/uiWidgets/<widgetName>/<widgetName>.json
<pkgDir>/uiWidgets/<widgetName>/schema.json
<pkgDir>/uiWidgets/<widgetName>/<widgetName>.uiwidget-meta.xml
VALIDATIONS:
widget self-validation (platform-widget-generate gates): <pass | fail — see sub-skill report>
clt-reference-integrity (envelope.outputValues → c__<responseCLT>; nested → @apexClassType): <pass | fail (<reason>)>
renderer-wires-widget (nested {!$attrs.outputValues.X} bindings): <pass | fail (<reason>)>
nested-list-coverage (every discovered nested/list field rendered): <pass | fail (<reason>)>
field-trace (INVENTED + OMITTED lists printed): <pass | warn (<reason>) | fail (invented: <list>)>
硬性规则(始终适用)
- 先规划,再继续。 在写入任何文件前,打印完整的 Phase 3 构建计划。若收到明确拒绝或修改请求 → 停止并修订;否则继续。这里的硬性要求是计划必须显示在对话记录中,而不是需要人工交互式批准——这适用于手动聊天、Agent 间流程和单轮评测。
- 使用两个基于对象的 CLT,绝不只用一个。 信封和负载是两个独立的 CLT。信封的
outputValues通过c__<responseCLT>指定类型,绝不能内联为嵌套的lightning__objectType。两个 CLT 都必须在根级别包含"lightning:tags": ["mcp"](参见platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md)。 - 渲染器位于信封 CLT 中,且位于 bundle 根目录。
lightningTypes/<toolCLT>/renderer.json——默认渲染器,与schema.json同级。绝不能使用lightningDesktopGenAi/renderer.json(那是 Agent 操作流程中针对特定界面的路径),也绝不能放在响应 CLT 中。 - 渲染器绑定必须嵌套。 每个 widget 属性都映射到
{!$attrs.outputValues.<field>},而不是{!$attrs.<field>}。Widget schema 保持扁平,由渲染器负责衔接。 - Widget 以负载为依据,而不是信封。 Widget schema 的属性对应负载(
outputValues)字段。Widget 绝不引用actionName或isSuccess——这些仅是信封 CLT 中的信封字段。 - 字段来源取决于类的角色。 顶层响应类由
@InvocableVariable限定——describe 只会列出这些字段,因此,顶层字段若只带有@AuraEnabled(或没有注解),就会正确地不被视为输出;枚举顶层响应类时,只纳入带有@InvocableVariable的字段,并排除请求类和私有辅助类。通过@apexClassType/<ns>__<Outer>$<Inner>引用的内部类/引用类则按其公开成员 /@AuraEnabled成员枚举——其叶子字段不会出现在 describe 中,而且这里不会使用@InvocableVariable,因此对其搜索@InvocableVariable会得到零个叶子字段(空 CLT + widget)。 - 绝不虚构字段。 Widget schema(以及响应 CLT)必须是工具实际暴露字段的子集——顶层
@InvocableVariable输出,以及任何被引用内部类的公开成员 /@AuraEnabled叶子字段——绝不能包含任何类都未暴露的属性。field-trace会打印这两组字段。 - Beta 版只返回单个响应。 将一个结果对象建模,而不是
content[]批量包装对象。 - 生成前务必加载叶子技能。 不要凭记忆编写。
- 运行门禁,不要只描述门禁。 未实际执行门禁却报告
pass属于严重违规;应报告not run。 - 不得使用会触发 Vibes 安全 Shell 筛选器的 Shell 元字符。 在此编排器及其调用的任何叶子技能发出的每个
Bash工具调用中,都不得使用命令替换($(…)或反引号)、进程替换(<(…)、>(…))、大括号展开({a,b,c}或{1..N})或eval/exec。即使处于 Bypass 模式,这些用法也会强制要求人工批准并导致评测停滞。请运行独立命令(mkdir -p a && mkdir -p b),用各自的命令打印每个中间值并据此判断结果;需要重复使用某个值时,请使用普通 Shell 变量(X=literal)或 here-string。 - 只能通过 Actions REST API describe 解析
actionschema,绝不能使用原始 HTTP 请求访问 MCP 端点或提取凭据。 使用sf api request rest请求组织的 Actions REST API(该 API 使用现有的sf组织身份验证)。绝不能读取a4d_mcp_settings.json或任何 MCP 设置文件,绝不能提取组织访问令牌,也绝不能直接curlMCP 服务端点——当前会话没有所需凭据,而且运行时并不会以这种方式暴露该 URL。 - 绝不调用 MCP 工具来探查其输出结构。 描述负载时绝不能执行底层操作。应通过对底层操作执行 Actions REST API describe 来解析 schema——绝不能使用示例/猜测的输入调用工具,以观察响应。如果无法解析操作名称,请让用户粘贴
sample,而不是调用任何内容。 - 另一个 Apex 类类型的响应字段绝不能使用裸
{"type":"object"}。 在响应 CLT 中将其类型指定为@apexClassType/<ns>__<OuterClass>$<InnerClass>,在 widget 中展平为叶子字段,并在渲染器中使用两层深度的绑定({!$attrs.outputValues.<objectField>.<leaf>})。这是对扁平基本类型情形(硬性规则 5)的补充,而不是替代——参见references/two-clt-modeling.md。
参考文件索引
| 文件 | 阅读时机 |
|---|---|
references/mcp-tool-output-discovery.md | 阶段 2 — 三个来源、字段枚举和类型映射。 |
references/two-clt-modeling.md | 阶段 4 — 信封和响应 CLT、嵌套渲染器绑定、命名约定及嵌套对象处理。 |
references/build-plan-format.md | 阶段 3 — 计划模板。 |
references/validation-gates.md | 阶段 5 — 完整的硬性 / 警告门槛表及 RUN 流程。 |
examples/action-name-source-prompt.md | 阶段 3 — 从可调用的操作 API 名称开始的演练(首选)。 |
examples/apex-invocable-source-prompt.md | 阶段 3 — 从 Apex Invocable 类开始的演练(备选)。 |
examples/pasted-tool-output-prompt.md | 阶段 3 — 从粘贴的工具输出示例开始的演练(备选)。 |
examples/nested-object-single-source-prompt.md | 阶段 3 — 单个 Apex 类引用载荷字段。 |
examples/nested-object-list-source-prompt.md | 阶段 3 — 顶层列表及包装器内的列表。 |