mcpField
P
platform-mcp-tool-widget-coordinate

platform-mcp-tool-widget-coordinate

github.com/forcedotcom设计与 UI1.1k2.4k 次安装1 天前更新
代码仓库

关于 platform-mcp-tool-widget-coordinate

用于协调自定义 MCP 工具输出与 Salesforce Lightning 组件渲染的技能,面向构建 Apex Invocable Action 可视化输出的开发者。它按依赖顺序调用生成技能,并通过审批与验证关卡串联两个 CLT 和 HXL widget。

主要功能

  1. 01发现工具响应字段结构
  2. 02协调两个对象型 CLT
  3. 03调用技能生成 HXL widget
  4. 04生成信封 CLT 默认渲染器
  5. 05设置审批与验证关卡

适用场景

  1. 01为 Apex Invocable Action 输出构建 widget
  2. 02将嵌套工具结果映射到平面字段
  3. 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.md
apex-invocable-source-prompt.md
nested-object-list-source-prompt.md
nested-object-single-source-prompt.md
pasted-tool-output-prompt.md
references/
build-plan-format.md
mcp-tool-output-discovery.md
two-clt-modeling.md
validation-gates.md

详细介绍

使用组件渲染自定义 MCP 工具输出

协调**两个基于对象的自定义 Lightning 类型(CLT)**和一个 HXL 组件,以渲染自定义 MCP 服务工具的输出,该工具由 Apex @InvocableMethod 实现。本技能不会直接编写内容,而是按依赖顺序加载并调用叶子技能,在获得用户批准后才继续,并在报告完成前运行验证关卡。

职责边界——本技能仅负责:

  1. MCP 工具用例——解析工具的输出形状,并按正确顺序编排叶子技能;以及
  2. 封套 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 名称(用于命名所有产物——请参阅下方的命名约定)。

来源优先级: 实时权威架构来源优先于本地类解析,本地类解析优先于粘贴的示例。顺序如下:

  1. 如果有操作 API 名称且有已通过身份验证的组织可用,则使用 action。Actions REST API 描述与平台自身公开的架构相同,因此无需请求/辅助程序筛选,并能提供真实的字段类型。
  2. 如果粘贴了运行时 JSON 示例,则使用 sample(运行时响应——明确且为最新)。
  3. 如果本地存在 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 不区分大小写地映射为 CLT lightning: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 的规范执行其工作流,验证输出,然后在继续下一项前创建检查点。

  1. 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>),而不是用包装类型——参见上文“嵌套对象列表”。
  2. Envelope CLT — 加载 platform-custom-lightning-type-generate。编写基于对象的 CLT <toolCLT>(惯例:<toolApiName>,即 envelope),并在根级设置 "lightning:tags": ["mcp"],且:

    • actionName → lightning__textType
    • isSuccess → lightning__booleanType
    • outputValues → c__<responseCLT>(引用 CLT 模式;必须先部署响应 CLT,再部署 envelope CLT)
  3. Widget — 加载 platform-widget-generate。编写一个扁平 widget,其 schema.json 属性为 payloadFields(名称 + 基本类型)——这是独立的 widget 契约,不从任何 Lightning Type 派生,也不与之耦合。它只渲染 outputValues 数据字段:绝不渲染 actionName/isSuccess(仅属于 envelope)。widget 正文通过 {!$attrs.<field>} 绑定每个字段——widget 与 envelope 无关,绝不直接引用 outputValues。

  4. 默认渲染器(在 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 负责,并在其自身验证中运行。

硬性 — 失败时阻止:

  1. clt-reference-integrity — 信封的 outputValues 类型为 c__<responseCLT>,响应 CLT 存在且两者都能解析;没有 $schema/items;每个非基本类型的响应属性都是 @apexClassType/... 引用(绝不能是裸对象或 CLT 级别的 lightning__listType)。
  2. renderer-wires-widget — bundle 根目录下的 renderer.json 连接 @widget/c/<widgetName>,并在 outputValues 下按照 describe 指定的深度绑定每个 widget 属性(顶层基本类型或列表输出为两个字段段;包装对象中的叶节点/列表为三个字段段)。必须双向校验:缺少或多余的绑定都会失败。
  3. nested-list-coverage — Phase 2 中发现的每个嵌套对象和列表字段都由 widget 呈现,并在正确深度绑定。“超出范围”/“beta 单响应”都不是有效的舍弃理由。

警告 — 仅供参考:

  1. 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>)>

硬性规则(始终适用)

  1. 先规划,再继续。 在写入任何文件前,打印完整的 Phase 3 构建计划。若收到明确拒绝或修改请求 → 停止并修订;否则继续。这里的硬性要求是计划必须显示在对话记录中,而不是需要人工交互式批准——这适用于手动聊天、Agent 间流程和单轮评测。
  2. 使用两个基于对象的 CLT,绝不只用一个。 信封和负载是两个独立的 CLT。信封的 outputValues 通过 c__<responseCLT> 指定类型,绝不能内联为嵌套的 lightning__objectType。两个 CLT 都必须在根级别包含 "lightning:tags": ["mcp"](参见 platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md)。
  3. 渲染器位于信封 CLT 中,且位于 bundle 根目录。 lightningTypes/<toolCLT>/renderer.json——默认渲染器,与 schema.json 同级。绝不能使用 lightningDesktopGenAi/renderer.json(那是 Agent 操作流程中针对特定界面的路径),也绝不能放在响应 CLT 中。
  4. 渲染器绑定必须嵌套。 每个 widget 属性都映射到 {!$attrs.outputValues.<field>},而不是 {!$attrs.<field>}。Widget schema 保持扁平,由渲染器负责衔接。
  5. Widget 以负载为依据,而不是信封。 Widget schema 的属性对应负载(outputValues)字段。Widget 绝不引用 actionName 或 isSuccess——这些仅是信封 CLT 中的信封字段。
  6. 字段来源取决于类的角色。 顶层响应类由 @InvocableVariable 限定——describe 只会列出这些字段,因此,顶层字段若只带有 @AuraEnabled(或没有注解),就会正确地不被视为输出;枚举顶层响应类时,只纳入带有 @InvocableVariable 的字段,并排除请求类和私有辅助类。通过 @apexClassType/<ns>__<Outer>$<Inner> 引用的内部类/引用类则按其公开成员 / @AuraEnabled 成员枚举——其叶子字段不会出现在 describe 中,而且这里不会使用 @InvocableVariable,因此对其搜索 @InvocableVariable 会得到零个叶子字段(空 CLT + widget)。
  7. 绝不虚构字段。 Widget schema(以及响应 CLT)必须是工具实际暴露字段的子集——顶层 @InvocableVariable 输出,以及任何被引用内部类的公开成员 / @AuraEnabled 叶子字段——绝不能包含任何类都未暴露的属性。field-trace 会打印这两组字段。
  8. Beta 版只返回单个响应。 将一个结果对象建模,而不是 content[] 批量包装对象。
  9. 生成前务必加载叶子技能。 不要凭记忆编写。
  10. 运行门禁,不要只描述门禁。 未实际执行门禁却报告 pass 属于严重违规;应报告 not run。
  11. 不得使用会触发 Vibes 安全 Shell 筛选器的 Shell 元字符。 在此编排器及其调用的任何叶子技能发出的每个 Bash 工具调用中,都不得使用命令替换($(…) 或反引号)、进程替换(<(…)、>(…))、大括号展开({a,b,c} 或 {1..N})或 eval / exec。即使处于 Bypass 模式,这些用法也会强制要求人工批准并导致评测停滞。请运行独立命令(mkdir -p a && mkdir -p b),用各自的命令打印每个中间值并据此判断结果;需要重复使用某个值时,请使用普通 Shell 变量(X=literal)或 here-string。
  12. 只能通过 Actions REST API describe 解析 action schema,绝不能使用原始 HTTP 请求访问 MCP 端点或提取凭据。 使用 sf api request rest 请求组织的 Actions REST API(该 API 使用现有的 sf 组织身份验证)。绝不能读取 a4d_mcp_settings.json 或任何 MCP 设置文件,绝不能提取组织访问令牌,也绝不能直接 curl MCP 服务端点——当前会话没有所需凭据,而且运行时并不会以这种方式暴露该 URL。
  13. 绝不调用 MCP 工具来探查其输出结构。 描述负载时绝不能执行底层操作。应通过对底层操作执行 Actions REST API describe 来解析 schema——绝不能使用示例/猜测的输入调用工具,以观察响应。如果无法解析操作名称,请让用户粘贴 sample,而不是调用任何内容。
  14. 另一个 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 — 顶层列表及包装器内的列表。

评论

支持 Markdown