Skip to main content
Glama
Kelley-Austin

SObjectActions

sfka-invocableMCP

通用、与对象无关的可调用 Apex 操作(27 个 MCP 工具),涵盖发现(describe、picklists、list flows、access check、record summary)、读取(read、find、count、aggregate、search、related)、写入(create、upsert、update、delete、validate、dry-run、assign owner、change type)、意图快捷方式(log activity、close case、convert lead、post to Chatter、add note、attach file)和运行 Flow(runFlow),并以托管型 Salesforce MCP 服务器上的工具形式(McpServerDefinition = SObjectActions)对外提供。相同操作也可以通过 Flow、Agentforce 代理操作和 REST /services/data/vXX.X/actions/custom/apex/ 调用。

每个操作都接受一个 objectApiName 字符串以及 records 或 Ids,因此一次部署即可覆盖任何标准或自定义对象,无需额外编码。


快速上手(可重复)

scripts/setup.sh <org-alias> [--no-fixtures] [--no-eca] [--no-tests] [--no-smoke] [--assign user@example.com]

部署测试夹具、27 个操作 + echo flow、权限集、MCP 服务器定义和 External Client App;分配 SObjectActions_User 权限集;运行全部 45 个单元测试和 45 项必查检查的 REST 冒烟测试;打印 consumer key 以及剩余的两个手动步骤(在 Setup 中激活服务器,授予对象 CRUD/FLS 权限)。幂等;已在启用和不启用 Enhanced Notes / 记录类型的组织上验证通过。

备选方案:unlocked 包 SObject Actions MCP(Apex + Flow + 权限集;scripts/package.sh install <org> <04t> 还部署 mcp/eca/;Salesforce 不允许将两者打进包内)。此外还有 scripts/scratch.sh(创建全新 scratch 组织并执行完整安装)和 .github/workflows/validate.yml(PR 上执行仅检查的部署 + 测试 + 文档漂移检查;需要密钥 SF_AUTH_URL)。

仓库结构:force-app/(一些可下发的类、Flow、权限集)、mcp/(McpServerDefinition)、eca/(External Client App)、test-fixtures/(可选)。

Related MCP server: Salesforce-Hosted-Custom-Mcp-Server

文档大观

文档

用途

README.md(本文件)

使用指南:部署、启用、约定、工具级参考、安全、限流、错误、测试

docs/TOOLS.md

生成的功能清单:所有工具及其输入、输出、注解(由 scripts/gen-tool-manifest.py 生成)

docs/tools.json

同上,但为机器可读 JSON(用于 Agent Prompts、文档站点、CI diff)

docs/ARCHITECTURE.md

类如何组合、共享辅助函数、设计规范、如何新增工具

docs/CLIENT_AUTH.md

外部客户端如何进行身份认证(ECA、PKCE、refresh token、Postman、headless fallback)

docs/postman/SObjectActions.postman_collection.json

生成的 Postman collection:OAuth 预置请求、为每个工具提供 MCP + REST 请求

docs/DEMO_SCRIPT.md

30 分钟的演示脚本,逐页对照幻灯片 23 页的发烧_

docs/DEMO_SCRIPT_15MIN.md

同一场分享的 15 分钟精简版:9 张幻灯片,一个演示瞬间

docs/DEMO_NOTES.md

分享背后的参考资料:目录解读、问题库,以及不能做的声明

CHANGELOG.md

发布历史列表

目录

  1. 组件

  2. 部署

  3. 启用 MCP 服务器

  4. 通用说明

  5. 工具参考

  6. 调用:Flow

  7. 调用:REST

  8. 安全模型

  9. 限制和批量行为

  10. 错误目录

  11. 测试

  12. 扩展


工具家族

家族

工具

发现类

checkAccess, describeObject, picklistValues, listFlows, recordSummary

读取类

readRecords, findRecords, countRecords, aggregateRecords, searchRecords, relatedRecords

写入类

validateRecords(dry-run)、createRecords, upsertRecords, updateRecords, cloneRecords, assignOwner, changeRecordType, deleteRecords, undeleteRecords

意图快捷键

logActivity, closeCase, convertLead, postChatter, addNote, attachFile

自动化

runFlow

完整输入/输出参考,请见 docs/TOOLS.md

组件

所有源代码都位于 force-app/main/default/

路径

用途

classes/SObjectCreateAction.cls

可调用 Create Records (Generic) -> MCP 工具 createRecords

classes/SObjectReadAction.cls

可调用 Read Records (Generic) -> MCP 工具 readRecords

classes/SObjectUpdateAction.cls

可调用 Update Records (Generic) -> MCP 工具 updateRecords

classes/SObjectDeleteAction.cls

可调用 Delete Records (Generic) -> MCP 工具 deleteRecords

classes/SObjectUpsertAction.cls

可调用 Upsert Records (Generic) -> MCP 工具 upsertRecords

classes/SObjectFindAction.cls

可调用 Find Records (Generic) -> MCP 工具 findRecords

classes/SObjectDescribeAction.cls

可调用 Describe Object (Generic) -> MCP 工具 describeObject

classes/SObjectSearchAction.cls

可调用 Search Records (Generic) -> MCP 工具 searchRecords (SOSL)

classes/SObjectRelatedAction.cls

可调用 Get Related Records (Generic) -> MCP 工具 relatedRecords

classes/SObjectCountAction.cls

可调用 Count Records (Generic) -> MCP 工具 countRecords

classes/SObjectUndeleteAction.cls

可调用 Undelete Records (Generic) -> MCP 工具 undeleteRecords

classes/SObjectAccessAction.cls

可调用 Check Access (Generic) -> MCP 工具 checkAccess

classes/SObjectCloneAction.cls

可调用 Clone Records (Generic) -> MCP 工具 cloneRecords

classes/SObjectValidateAction.cls

可调用 Validate Records (Generic, dry run) -> MCP 工具 validateRecords

classes/SObjectAggregateAction.cls

可调用 Aggregate Records (Generic) -> MCP 工具 aggregateRecords

classes/SObjectRunFlowAction.cls

可调用 Run Flow (Generic) -> MCP 工具 runFlow

classes/SObjectAssignOwnerAction.cls

Assign Owner (Generic) -> assignOwner

classes/SObjectRecordTypeAction.cls

Change Record Type (Generic) -> changeRecordType

classes/SObjectPicklistAction.cls

Get Picklist Values (Generic) -> picklistValues

classes/SObjectLogActivityAction.cls

Log Activity -> logActivity

classes/SObjectCloseCaseAction.cls

Close Case -> closeCase

classes/SObjectConvertLeadAction.cls

Convert Lead -> convertLead

classes/SObjectPostChatterAction.cls

Post to Chatter -> postChatter

classes/SObjectAddNoteAction.cls

Add Note -> addNote

classes/SObjectAttachFileAction.cls

Attach File -> attachFile

classes/SObjectListFlowsAction.cls

List Flows -> listFlows

classes/SObjectSummaryAction.cls

Get Record Summary (Generic) -> recordSummary

flows/SObjectActions_EchoFlow.flow-meta.xml

用作 runFlow 测试夹具的小型自动启动流

test-fixtures/(独立包目录)

可选:SObjectActions_Fixture__c,包含 2 个记录类型 + 依赖选项列表 + 权限集;仅由测试使用,动态引用

classes/SObjectActionUtil.cls

共享辅助工具:类型解析、JSON -> SObject、字段验证、标签查找、防重复 DML

classes/SObjectActionsTest.cls

覆盖 create/read/update/delete 的 14 个测试

classes/SObjectActionsExtTest.cls

覆盖 upsert/find/describe 的 6 个测试

classes/SObjectActionsExt2Test.cls

覆盖 search/related/count/undelete/access 的 7 个测试

classes/SObjectActionsExt3Test.cls

覆盖 clone/validate/aggregate/runFlow/urls 的 7 个测试

classes/SObjectActionsExt4Test.cls

针对 intent/utility 工具的 11 个测试(总覆盖率约 97.5%)

mcp/main/default/mcpServerDefinitions/SObjectActions.mcpServerDefinition-meta.xml

MCP 服务器定义,将所有 27 个 Apex 操作映射到工具(自有包目录:不可打包)

scripts/smoke-test.sh

REST 端到端冒烟测试(45 项检查)

scripts/apex/smoke.apex

匿名 Apex 冒烟脚本

scripts/gen-tool-manifest.py

从源码重新生成 docs/TOOLS.mddocs/tools.json

scripts/gen-postman.py

docs/tools.json 重新生成 Postman 集合

scripts/setup.sh

一键安装并验证到任意 org

scripts/scratch.sh, scripts/package.sh

Scratch org 引导;unlocked package 创建/版本/安装

permissionsets/SObjectActions_User

对所有操作类的执行访问权限(无对象 CRUD)

eca/main/default/externalClientApps/SObjectActionsClient(+ OAuth 设置、全局 OAuth、策略)

MCP 的 OAuth 客户端:MCP + 刷新范围、PKCE、JWT 令牌、通用回调(自有包目录:全局 OAuth 设置不可打包)

.github/workflows/validate.yml

CI:仅检查部署并运行测试,文档漂移检查

API 版本:67.0 (sfdx-project.json)。需要一个已启用托管 MCP 服务器且 McpServerDefinition 元数据可用的组织(Summer ’26 或更高版本;请在最新发行说明中确认)。


部署

# deploy everything and run the test class
sf project deploy start -o <alias> -d force-app/main/default \
  -l RunSpecifiedTests -t SObjectActionsTest -t SObjectActionsExtTest -t SObjectActionsExt2Test -t SObjectActionsExt3Test -t SObjectActionsExt4Test

# Test fixtures (record types + dependent picklist used by two tests; deploy first for full per-class coverage)
sf project deploy start -o <alias> -d test-fixtures && sf org assign permset -n SObjectActions_Fixture -o <alias>
# MCP server definition and External Client App (separate dirs)
sf project deploy start -o <alias> -d mcp -d eca

# Apex only (skip the MCP definition)
sf project deploy start -o <alias> -d force-app/main/default/classes \
  -l RunSpecifiedTests -t SObjectActionsTest -t SObjectActionsExtTest -t SObjectActionsExt2Test -t SObjectActionsExt3Test -t SObjectActionsExt4Test

注意:

  • McpServerDefinition 的开发者名称必须为字母数字,以字母开头、长度为 2–40 个字符(不允许使用下划线)。其值为 SObjectActions

  • apiIdentifier 的值为 aa:apex-<ClassName>,类部署后会自动从 API Catalog 解析。部署时请将 Apex 类与定义一起部署,或先部署类、再部署定义。


启用 MCP 服务器

  1. 设置 > MCP 服务器 > SObject Actions > 关联外部客户端应用 SObject Actions MCP Client(由本仓库部署)> Activate

  2. 客户端身份验证说明,请参见 docs/CLIENT_AUTH.md

  3. 为用户分配权限集,使其具有对应 Apex 类的访问权限,并对要操作的对象拥有 CRUD/FLS 权限。这些工具以当前登录用户身份运行。

  4. 将 MCP 客户端(Claude、Agentforce、Cursor 等)指向“设置”中显示的服务器 URL。tools/list 返回 checkAccessdescribeObjectsearchRecordsfindRecordscountRecordsaggregateRecordsreadRecordsrelatedRecordsrecordSummarypicklistValuesvalidateRecordscreateRecordsupsertRecordsupdateRecordscloneRecordsassignOwnerchangeRecordTypedeleteRecordsundeleteRecordslogActivitycloseCaseconvertLeadpostChatteraddNoteattachFilelistFlowsrunFlow

在定义中设置的工具注解:

Tool

readOnly

destructive

idempotent

createRecords

false

false

false

readRecords

true

false

true

updateRecords

false

false

true

deleteRecords

false

true

false

upsertRecords

false

false

true

findRecords

true

false

true

describeObject

true

false

true

searchRecords

true

false

true

relatedRecords

true

false

true

countRecords

true

false

true

undeleteRecords

false

false

true

checkAccess

true

false

true

cloneRecords

false

false

false

validateRecords

true

false

true

aggregateRecords

true

false

true

runFlow

false

false

false

assignOwner

false

false

true

changeRecordType

false

false

true

picklistValues

true

false

true

logActivity

false

false

false

closeCase

false

false

true

convertLead

false

false

false

postChatter

false

false

false

addNote

false

false

false

attachFile

false

false

false

listFlows

true

false

true

recordSummary

true

false

true

典型推荐调用顺序:describeObject(获取准确的 API 名称)-> findRecords / readRecords -> createRecords / upsertRecords / updateRecords -> deleteRecords


常见约定

objectApiName

每个操作都要用到。API 名称不区分大小写:AccountCaseTaskMy_Object__c 等。

Activity(Task/Event 多态类型)

Activity 不能直接创建或查询,因此它被当作“Task 或 Event”的别名处理:

输入方式

具体类型的判定方式

record / records(SObject)

取 SObject 本身的实际类型;除 Task/Event 外的其他类型都会被拒绝

recordsJson

每个元素必须包含 {"attributes":{"type":"Task"}}"Event";缺少则报错

recordId / recordIds

根据 Id 前缀判定(00T = Task,00U = Event);其他前缀会被拒绝

因此,当 objectApiName = "Activity" 时,一次请求可以同时混合包含 Task 和 Event。

记录输入(create / update)

可以任意组合使用下面的字段,它们将按此顺序合并:

字段

类型

使用场景

说明

record

SObject

Flow 流程

类型必须与 objectApiName 匹配

records

List<SObject>

Flow 流程

objectApiName 匹配

recordsJson

String

MCP / REST

JSON 对象数组,元素为 {field: value} 映射

recordsJson 规则:

  • 字段名必须存在于对象上(可直接使用字段 API 名称,或使用相关字段名称,例如我们Iid:外部 Id upsert 时使用 Account)。未知名称会导致整个请求失败,并返回 Unknown field(s) on <Object>: <names>,而不是被自动丢弃。

  • 值的强制转换由平台 JSON 反序列化器处理:日期格式为 YYYY-MM-DD,日期时间为 ISO-8601(如 2030-01-01T10:00:00.000Z),布尔值为 true/false,数字不带引号。

  • 若提供 attributes.type,其值必须等于 objectApiName(当 objectApiNameActivity 时,取 Task 或 Event)。

Id 输入(read / delete)

  • recordId(仅用于 read)或 recordIds(用于 read + delete),也可同时提供。空白条目会被跳过。

  • 支持 15 位和 18 位 Id;无效的字符串会使请求失败(Invalid record Id: ...)。

  • 每个 Id 的类型必须与 objectApiName 匹配(Activity 对应 Task 或 Event)。

allOrNone(create / update / delete)

  • 默认 false:允许部分成功;每个失败的行会在 errors 中报告为 row <n>: <message>

  • 设为 true:任何一行失败都会导致整个请求全部回滚;failureCount 等于行数,errors 保存 DML 异常消息。

标签

recordLabelsrecordIds 按下标一一对应。标签的来源:

对象类别

标签说明

Case

CaseNumber - Subject(Subject 为空时省略)

其他类型

对象的 describe 名称字段(Task/Event 为 Subject,其余如 NameCaseNumber 等)

没有名称字段 / 无读取权限

记录 Id

create/update 的标签会在 DML 后重新查询(因此能取得 CaseNumber 等自动编号值)。delete 的标签会在删除获取。

通用输出

字段

类型

含义

isSuccess

Boolean

仅当请求中每一行都成功(读取时:每个 Id 均找到)时为 true

successCount / failureCount(CUD)

Integer

行数(成功 / 失败)

foundCount / notFoundCount(读取)

Integer

行数

recordIds

List<String>

受影响/找到的 Id,按输入顺序返回

recordLabels

List<String>

recordIds 一一对应

errors

List<String>

每行失败对应 row <n>: <msg>,或单个请求级错误

message

String

单行汇总,例如 2 created, 1 failed.

recordUrls

List<String>

recordIds 对应的 Lightning 记录 URL(create/read/update/upsert/clone/find/search/related 返回)

请求级验证错误(如对象名错误、JSON 格式错误等)会使 isSuccess=true? 不,是 isSuccess=falsefailureCount=1errors 中有一条记录;该请求不会执行任何 DML。同一批调用中的其他请求不受影响。


工具说明参考

createRecords / SObjectCreateAction

可调用名称:Create Records (Generic),类别:SObject Actions

输入:objectApiName(必填)、recordrecordsrecordsJsonallOrNone。 输出:isSuccesssuccessCountfailureCountrecordIdsrecordLabelserrorsmessage

MCP 调用示例:

{ "objectApiName": "Account",
  "recordsJson": "[{\"Name\":\"Acme\",\"Industry\":\"Energy\"},{\"Name\":\"Globex\"}]" }
{ "objectApiName": "Case",
  "recordsJson": "{\"Subject\":\"Printer on fire\",\"Status\":\"New\",\"Origin\":\"Web\",\"AccountId\":\"001...\"}" }

-> recordLabels: ["00001234 - Printer on fire"]

{ "objectApiName": "Activity",
  "recordsJson": "[{\"attributes\":{\"type\":\"Task\"},\"Subject\":\"Call\",\"WhatId\":\"001...\"},{\"attributes\":{\"type\":\"Event\"},\"Subject\":\"Meet\",\"DurationInMinutes\":30,\"ActivityDateTime\":\"2030-01-01T10:00:00.000Z\"}]" }

示例结果:

{ "isSuccess": true, "successCount": 2, "failureCount": 0,
  "recordIds": ["00T...", "00U..."], "recordLabels": ["Call", "Meet"],
  "errors": [], "message": "2 created, 0 failed." }

部分失败(allOrNone=false):

{ "isSuccess": false, "successCount": 1, "failureCount": 1,
  "recordIds": ["003..."], "recordLabels": ["Good"],
  "errors": ["row 2: Required fields are missing: [LastName] [LastName]"],
  "message": "1 created, 1 failed." }

readRecords / SObjectReadAction

可调用名称:Read Records (Generic)

输入:

字段

类型

说明

objectApiName

String(必填)

允许 Activity 以混合传入 Task/Event 的 Id

recordId

String

单个 Id、单条记录

recordIds

List<String>

批量传入;重复的 Id 会被合并

fields

List<String>

可选。字段 API 名称,支持关系路径(Owner.NameAccount.Name)。留空表示返回当前用户可访问的所有字段。始终会包含 Id 和标签字段。

输出:

字段

说明

isSuccess

如果每个请求的 Id 均找到且可见,则为 true

foundCount, notFoundCount

recordIds, recordLabels

找到的记录,按输入顺序

recordsJson

找到记录的 JSON 数组(序列化后的 SObject,包含 attributes.type)- 面向 MCP

record, records

第一个 / 全部找到的记录,以 SObject 形式返回 - 面向 Flow

notFoundIds

不存在或在共享规则下不可见的 Id

errors, message

示例:

{ "objectApiName": "Account", "recordId": "001..." }
{ "objectApiName": "Case", "recordIds": ["500...","500..."], "fields": ["Status","Priority","Account.Name"] }
{ "objectApiName": "Activity", "recordIds": ["00T...","00U..."], "fields": ["Subject","ActivityDate"] }

示例结果:

{ "isSuccess": true, "foundCount": 1, "notFoundCount": 0,
  "recordIds": ["500..."], "recordLabels": ["00001234 - Printer on fire"],
  "recordsJson": "[{\"attributes\":{\"type\":\"Case\"},\"Id\":\"500...\",\"CaseNumber\":\"00001234\",\"Subject\":\"Printer on fire\",\"Status\":\"New\",\"Account\":{\"attributes\":{\"type\":\"Account\"},\"Name\":\"Acme\"}}]",
  "notFoundIds": [], "errors": [], "message": "1 found, 0 not found." }

直接字段名会在查询前进行校验(Unknown field on Account: Foo__c)。 关系路径由 SOQL 本身进行校验;路径错误会在 errors 中体现为 QueryException。 用户不可见的记录会被直接列入 notFoundIds(不报错)。

updateRecords / SObjectUpdateAction

可调用标签:更新记录(通用)

输入:objectApiName(必填)、recordrecordsrecordsJsonallOrNone。 每一行必须包含 Id;缺少 Id 的行会使整个请求失败 (row <n>: Id is required for update.)。

输出:与 create 相同的结构(successCountrecordIdsrecordLabels、...)。 recordLabels 反映记录在本次调用中所有更新应用完成后的状态。

示例:

{ "objectApiName": "Account",
  "recordsJson": "[{\"Id\":\"001...\",\"Name\":\"Acme Corp\",\"Phone\":\"555-0100\"}]" }
{ "objectApiName": "Case", "recordsJson": "{\"Id\":\"500...\",\"Status\":\"Closed\"}" }
{ "objectApiName": "Activity",
  "recordsJson": "[{\"attributes\":{\"type\":\"Task\"},\"Id\":\"00T...\",\"Status\":\"Completed\"}]" }

要清空某个字段,传入 null{"Id":"001...","Description":null}

deleteRecords / SObjectDeleteAction

可调用标签:删除记录(通用)。工具被标记为 destructive

输入:objectApiName(必填)、recordIdsrecordrecordsallOrNone。 通过 SObject 形式传入的记录必须包含 Id

输出:isSuccesssuccessCountfailureCountrecordIdsrecordLabels (删除前捕获)、errorsmessage

示例:

{ "objectApiName": "Account", "recordIds": ["001...", "001..."] }
{ "objectApiName": "Activity", "recordIds": ["00T...", "00U..."] }

已删除的记录会进入回收站(标准 Database.delete 语义)。级联删除遵循平台的 master-detail / lookup 规则。

upsertRecords / SObjectUpsertAction

可调用标签:Upsert 记录(通用)

输入:objectApiName(必填)、externalIdField(可选,默认 Id)、recordrecordsrecordsJsonallOrNoneexternalIdField 必须是对象上的 external Id / idLookup 字段。支持 Activity(通过 attributes_ascription 区分 Task/Event);external Id 字段必须 存在于具体类型上。

输出:isSuccesssuccessCountfailureCountcreatedCountupdatedCountrecordIdsrecordLabelswasCreated[](与 recordIds 对齐)、errorsmessage

示例:

{ "objectApiName": "Account", "externalIdField": "External_Key__c",
  "recordsJson": "[{\"External_Key__c\":\"ERP-1001\",\"Name\":\"Acme\"},{\"External_Key__c\":\"ERP-1002\",\"Name\":\"Globex\"}]" }
{ "objectApiName": "Contact",
  "recordsJson": "[{\"Id\":\"003...\",\"Email\":\"a@b.com\"},{\"LastName\":\"New Person\"}]" }

-> wasCreated: [false, true], message: "1 created, 1 updated, 0 failed."

行按具体对象类型分组;每组对应一次 Database.upsert。 若 allOrNone=true,则使用保存点;任一组失败都会回滚整个请求。

findRecords / SObjectFindAction

可调用标签:按条件查找记录(通用)。仅支持结构化过滤条件;不接受原始 SOQL/WHERE

输入:

字段

说明

objectApiName(必填)

必须可查询。请使用 Task/Event,不要使用 Activity

filtersJson

JSON 数组(或单个对象)形式的 {"field","op"#"value"}。运算符:=!=<<=>>=LIKEINNOT INvalue 可与 =/!= 一起使用 nullIN/NOT IN 需要数组。过滤字段必须是直接的、可过滤的字段(无需关系路径)。

filterLogic

对所有过滤条件使用 AND(默认)或 OR

fields

额外返回的字段;允许关系路径(Owner.Name)。Id 和 label 字段始终包含。

orderBy

"Field""Field ASC|DESC"

limitCount

1-200,默认 50。

值在绑定前会根据字段类型进行强制转换(日期 YYYY-MM-DD、日期时间 ISO-8601、数字、布尔值、 Id)。所有值均通过绑定变量传入(Database.queryWithBinds、USER_MODE)。

输出:isSuccessresultCountrecordIdsrecordLabelsrecordsJsonrecords(Flow)、 soql(实际执行的查询,值以 :b0:b1... 形式隐藏)、errorsmessage (当 resultCount == limitCount 时返回 "... (limit reached)")。

示例:

{ "objectApiName": "Case",
  "filtersJson": "[{\"field\":\"Status\",\"op\":\"IN\",\"value\":[\"New\",\"Working\"]},{\"field\":\"CreatedDate\",\"op\":\">=\",\"value\":\"2026-08-01T00:00:00Z\"}]",
  "fields": ["Status","Priority","Account.Name"], "orderBy": "CreatedDate DESC", "limitCount": 25 }
{ "objectApiName": "Account",
  "filtersJson": "[{\"field\":\"Name\",\"op\":\"LIKE\",\"value\":\"Acme%\"},{\"field\":\"Industry\",\"op\":\"=\",\"value\":\"Energy\"}]",
  "filterLogic": "OR" }
{ "objectApiName": "Task",
  "filtersJson": "[{\"field\":\"WhatId\",\"op\":\"=\",\"value\":\"001...\"},{\"field\":\"IsClosed\",\"op\":\"=\",\"value\":false}]" }

describeObject / SObjectDescribeAction

可调用标签:描述对象(通用)。一个工具包含两种模式。

描述模式(已设置 objectApiName):

输入

说明

objectApiName

Activity 会描述 Task

includeFields

默认为 true

fieldNameContains

按字段 API 名称或标签进行不区分大小写的过滤

输出:objectApiNameobjectLabelkeyPrefixisCustomisCreateable/Updateable/Deletable/QueryablelabelFields(例如 Case 的 ["CaseNumber","Subject"])、requiredFields(可创建、必填、无默认值的字段)、 fieldsJson(仅可访问字段:apiName, label, type, required, createable, updateable, externalId, nameField, length, referenceTo[], relationshipName, picklistValues[])、 recordTypesJson(可用的非 master 记录类型:id, developerName, name, isDefault)、 childRelationshipsJsonrelationshipNamechildObjectfield)、resultCount(返回的字段数)。

列表模式objectApiName 留空):

输入

说明

objectNameContains

按 API 名称或标签进行不区分大小写的过滤

customOnly

默认为 false

输出:objectsJson(可访问对象的 apiName, label, keyPrefix, isCustom, createable, queryable;排除自定义设置和无 keyPrefix 的系统对象)、resultCount

示例:

{ "objectApiName": "Case", "fieldNameContains": "status" }
{ "objectApiName": "My_Object__c" }
{ "objectNameContains": "invoice", "customOnly": true }

searchRecords / SObjectSearchAction

可调用标签:搜索记录(通用)。SOSL 全文本搜索;搜索词以绑定形式传入(FIND :term)。

输入

说明

searchTerm(必填)

至少 2 个字符;允许使用 *? 通配符

objectApiNames

默认 Account, Contact, Lead, Opportunity, Order;每个都必须可搜索;不允许 Activity(请使用 Task/Event)

searchIn

ALL(默认)、NAMEEMAILPHONESIDEBAR

fields

额外字段,仅在该字段存在于对应对象时应用

limitCount

每个对象 1-200,默认 20

输出:resultCountrecordIdsrecordLabelsrecordObjectNames(对齐)、 recordsJsonrecords

{ "searchTerm": "acme*", "objectApiNames": ["Account","Contact"], "searchIn": "NAME", "fields": ["Phone","Email"] }

可调用标签:获取相关记录(通用)

输入

说明

parentRecordId(必填)

父对象根据 Id 推断

relationshipName(必填)

父对象上的子关系名称(ContactsCasesOpportunitiesTasksEventsMy_Children__r);不区分大小写;参见 describeObject.childRelationshipsJson

fieldsorderBylimitCount

与 findRecords 相同(limit 默认 50,最大 200)

输出:parentObjectApiNamechildObjectApiNameresultCountrecordIdsrecordLabelsrecordsJsonrecords

{ "parentRecordId": "001...", "relationshipName": "Cases", "fields": ["Status","Priority"], "orderBy": "CreatedDate DESC", "limitCount": 10 }

countRecords / SObjectCountAction

可调用标签:计数记录(通用)

输入

说明

objectApiName(必填)

Task/Event,不是 Activity

filtersJsonfilterLogic

与 findRecords 完全相同

groupByField

可选的可分组字段;最多 200 组,按计数值降序排列;null 组按 null 报告

输出:totalCountgroupValues[]groupCounts[](对齐)、groupsJson[{value,count}])、 soql

{ "objectApiName": "Case", "filtersJson": "[{\"field\":\"IsClosed\",\"op\":\"=\",\"value\":false}]", "groupByField": "Priority" }

undeleteRecords / SObjectUndeleteAction

可调用标签:取消删除记录(通用)。从回收站恢复记录。

输入:objectApiName(必填;Task/Event 混合时用 Activity)、recordIds(必填)、allOrNone。 输出:successCountfailureCountrecordIdsrecordLabels(还原后)、errorsmessage。 同一次请求中的重复 Id 会被合并。已还原或已彻底删除的记录则按行失败。

{ "objectApiName": "Account", "recordIds": ["001..."] }

checkAccess / SObjectAccessAction

可调用标签:检查访问(通用)。所有输入均可选;一个都不传时,相当于“我是谁”。

输入

说明

objectApiName

CRUD 检查(Activity 时视为 Task)

fields

需要 objectApiName;每个字段报告为 {apiName, exists, readable, editable, createable}

recordIds

最多 200 个;使用 UserRecordAccess -> {recordId, hasRead, hasEdit, hasDelete, hasTransfer, maxAccessLevel}(不可见/不存在时返回 None

输出:userIduserNameloginUsernameprofileIdprofileNameuserTypeorganizationIdtimeZoneobjectApiNamecanCreate/canRead/canUpdate/canDeletefieldAccessJsonrecordAccessJsonmessage

{ "objectApiName": "Opportunity", "fields": ["Amount","StageName"], "recordIds": ["006..."] }

cloneRecords / SObjectCloneAction

可调用标签:克隆记录(通用)

输入

说明

objectApiName(必填)

Task/Event 混合时用 Activity

recordIds(必填)

源记录;未找到或不可见的 Id 按行失败

overridesJson

应用于每个克隆记录的 JSON 对象(字段名已验证)

excludeFields

不复制的字段(例如 OwnerId、外部 Id)

allOrNone

会复制每个可创建、可读、非自动编号且非公式的字段。子记录不会被复制。输出:sourceRecordIds(与输入对齐)、recordIdsrecordLabelsrecordUrls、计数、errors

{ "objectApiName": "Opportunity", "recordIds": ["006..."], "overridesJson": "{\"Name\":\"Renewal 2027\",\"StageName\":\"Prospecting\"}", "excludeFields": ["OwnerId"] }

validateRecords / SObjectValidateAction

可调用标签:校验记录(通用,试运行)。在保存点(savepoint)内执行 insert/update,并且总是回滚;触发器、验证规则、必填字段、字段级安全(FLS)和共享规则都会真实执行。

输入:objectApiName(必填)、operation(默认 CREATE | UPDATE)、recordrecordsrecordsJson。 输出:isSuccess(所有行是否都能保存)、successCountfailureCountrowResultsJson[{row, valid, errors[]}])、errorsmessage... Nothing was saved.)。

注意:DML 仍然会计入系统限制,事务中消耗的自动编号不会再被复用。

{ "objectApiName": "Contact", "recordsJson": "[{\"LastName\":\"Ok\"},{\"FirstName\":\"No last name\"}]" }

aggregateRecords / SObjectAggregateAction

可调用标签:聚合记录(通用)

输入

说明

objectApiName(必填)

Task/Event,不能是 Activity

aggregationsJson(必填)

[{"function","field"}];函数 COUNT、COUNT_DISTINCT、SUM、AVG、MIN、MAX;SUM/AVG 需要数字字段;字段必须是可聚合的。

filtersJson`filterLogic``

与 findRecords 相同

groupByFields

0-3 个可分组字段

limitCount

仅用于分组,1-2000,默认 200(未分组的查询不能使用 LIMIT)

输出:resultCountrowsJson(每行返回分组字段的值 + FUNCTION_Field 键,例如 SUM_Amount)、soql

{ "objectApiName": "Opportunity",
  "aggregationsJson": "[{\"function\":\"SUM\",\"field\":\"Amount\"},{\"function\":\"COUNT\",\"field\":\"Id\"}]",
  "filtersJson": "[{\"field\":\"IsClosed\",\"op\":\"=\",\"value\":false}]",
  "groupByFields": ["StageName"] }

runFlow恒 / SObjectRunFlowAction

可调用标签:运行流程(通用)

输入

说明

flowApiName(必填)

已激活的自动启动流程

inputsJson

输入变量到值的 JSON 对象

outputVariableNames

标记为“可用于输出”并需要返回的变量名

输出:interviewIdoutputsJsonerrors(流程错误表现为 Could not start flow ... 或错误消息)、message。流程在相同的上下文中,按其声明的运行模式运行。

{ "flowApiName": "SObjectActions_EchoFlow", "inputsJson": "{\"inputText\":\"hi\",\"inputNumber\":1}", "outputVariableNames": ["outputText","outputNumber"] }

assignOwner / SObjectAssignOwnerAction

输入:objectApiNamerecordIdsnewOwnerId(005/00G) newOwnerName(精确的用户全名 / 用户名,或队列名 / developer name;名称不明确会被拒绝)、allOrNone。 输出:解析后的 ownerId/ownerName、按记录统计、recordIdsrecordLabelserrors

{ "objectApiName": "Case", "recordIds": ["500..."], "newOwnerName": "Tier 2 Support" }

changeRecordType / SObjectRecordTypeAction

输入:objectApiNamerecordIdsrecordType(Id、DeveloperName 或 Name)、allOrNone。错误信息会列出可用的开发者名称。输出:解析后的 recordTypeId/recordTypeName,每个记录的结果。

picklistValues / SObjectPicklistAction

输入:objectApiNamefieldApiNameincludeInactive。输出:values[]labels[]valuesJsonOf{value,label,active,default,validFor[]})、isRestrictedisDependentcontrollingFielddefaultValue。值反映组织级字段定义(不应用记录类型的值集)。

logAction / FasteningAction

往名为 Task 的对象写入一条已完成的记录。输入:subject(必填)、relatedRecordId(WhatId)、personRecordId(Contact/Lead 的 WhoId)、descriptionactivityDate(默认为今天)、activityTypestatus(默认第一个已关闭状态)、priorityownerIdextraFieldsJson。输出:recordIdrecordUrlstatus

{ "subject": "Call with CFO", "relatedRecordId": "001...", "personRecordId": "003...", "description": "Discussed renewal", "activityType": "Call" }

closeCase / SObjectCaseAction

输入:caseIdsstatus(必须是已关闭状态,默认为第一个已关闭状态)、comment(+ commentIsPublic)、extraFieldsJsonallOrNone。输出:status 实际使用状态、按 Case 计数的数量、recordLabelsCaseNumber - Subject)。

convertLead / SObjectConvertLeadAction

输入:leadIdconvertedStatus(默认第一个已转化状态)、accountId / contactId(合并到已有对象)、createOpportunity(默认 true)、opportunityNameownerIdsendEmailTaskToOwner。输出:accountIdcontactId、映射的 opportunityId + Error.

postChatter / SObjectPostChatterAction

输入:recordId(任何可 feed 的记录或 User),text(文本内容),mentionUserIds(提及的用户 Id)。输出:feedItemId。当对象未启用 feed tracking 时,返回明确错误信息。

addNote So / SObjectAddNoteAction

输入:titlebody(纯文本或简单 HTML)、recordIdsshareType(默认 V / I / C)。创建 ContentNoteContentDocumentLink;如果在组织中没有可用,则改为将备注存储为 .html 文件(storedAsFile=true)。输出:noteId、关联的 recordIdsstoredAsFile

attachFile / SObjectAttachFileAction

输入:fileNametextContent base64ContenttitlerecordIdsshareType。输出:contentVersionIdcontentDocumentId、关联的 recordIds。大 base64 有效内容会查询到堆内存限制(约 6 MB 同步)。

listFlows / SObjectListFlowsAction

列表模式:nameContainsprocessType(默认 AutoLaunchedFlow,可选值 ALL)、includeInactivelimitCount -> flowsJson。 详情模式:flowApiName -> variablesJsonapiNamedataTypeisInputisOutputisCollectionobjectTypedescription)。可与 runFlow 搭配使用。

recordSummary / SObjectSummaryAction

输入:recordIdfields(默认所有可访问字段)、relationshipNames(最多 10 个,默认为存在的常用字段)、recentActivityLimit(0-20,默认 5)。 输出:recordTyperecordLabelrecordUrlownerNamerecordJsonrelatedCountsJson{Contacts: 3, Cases: 1, ...})、recentActivityJson[{id,type,subject,date,status,ownerName}])。 成本:记录本身 1 次 SOQL + 每个关系 1 次 SOQL + 活动 2 次 SOQL。


在 Flow 中调用

添加一个 Action 元素,搜索 SObject Actions 类别,并选择操作。

  • Object API Name 设为字面量或文本变量。

  • 对于 create/update,将记录变量映射到 Record,或将记录集合映射到 Records。 Flow 的通用 SObject 输入要求您在操作元素上选择对象类型;该类型必须与 objectApiName 匹配(若是 Activity,则为 Task/Event)。

  • 将输出变量 Created Record Ids / Record / Records / Errors 写入 Flow 变量。

  • All or None 留空则允许部分成功;设置为 {!$GlobalConstant.True} 则会对所有记录执行回滚。

故障路径:请求级问题不会抛出异常;请检查 SuccessErrors。只有意外的平台异常(例如限制超限)才会进入 Flow 故障连接器。


在 REST 中调用

POST /services/data/v67.0/actions/custom/apex/SObjectCreateAction
Authorization: Bearer <token>
Content-Type: application/json

{ "inputs": [
  { "objectApiName": "Account", "recordsJson": "[{\"Name\":\"Acme\"}]" },
  { "objectApiName": "Contact", "recordsJson": "[{\"LastName\":\"Smith\",\"AccountId\":\"001...\"}]" }
] }

SObjectReadActionSObjectUpdateActionSObjectDeleteAction 的传入结构相同。inputs 中的每个元素对应响应 outputValues 中的一个元素。


安全模型

  • 所有 DML 和 SOQL 均以 AccessLevel.USER_MODE 执行:由平台强制执行对象的 CRUD、字段级安全性(FLS)和运行用户的共享规则。

    • 对不可写字段进行 create/update -> 平台返回行错误。

    • 对不可访问的对象/字段进行读取 -> 不会返回该对象/字段(默认字段列表只包含当前用户可用的字段;显式指定的不可访问字段会引发查询错误)。

    • 不在用户共享范围内的记录 -> 读取时返回 notFoundIds,更新/删除时返回行错误。

  • 这些类是 global with sharing(MCP 发现功能所需)。

  • 不会从原始调用输入构建 Database.query 字符串:对象名通过 describe 验证,字段名通过字段映射验证,ID 通过 Id.valueOf 验证;readRecords.fields 中的关系路径只会出现在 SELECT 列表中,绝不会用于 WHERE 子句。

  • 由于这些工具是通用的,因此请将数据库操作行为与权限集结合使用:定义哪些人可以调用 Apex 类,以及这些类能访问哪些对象和字段(CRUD/FLS)。对于禁止删除的场景,请考虑将 deleteRecords 从 agent 使用的服务器中移除。


– 全局限制与批量执行行为

  • Invocable 输入是批量的:一次调用中所有请求(records)会合并为尽可能少的数据库操作(正常情况每个调用一次 insert / update / delete)。

  • 重复 Id:多个请求中针对同一记录多次更新/删除时,会转换为顺序 DML 批次执行,而不是报错 Duplicate id in list

  • 标签查询:每次创建/更新/删除具体对象类型使用 1 次 SOQL;读取操作按每个具体类型每次请求使用 1 个 SOQL。

  • 每次事务的限制:100 次 SOQL、150 个 DML 语句、10,000 行 DML、6 MB 堆内存(同步)。非常大的 recordsJson 负载,或对较宽对象、数百个 Id 且未指定 fields 时,可能会接近堆/CPU 限制;对于批量读取,请显式传入 fields 列表。

  • action 参数中的对象类型可以不同,但将设置元数据(Setup)对象与非Setup 对象(如 User + Account)放在同一调用中,会受平台的混合 DML 规则限制。

  • 平台对单次 DML 语句中同时操作的 SObject 类型数有限制(10 种)。


错误目录

消息

原因

修复

objectApiName is required.

缺少输入

提供 API 名称

Unknown object API name: X

拼写错误 / 对象不可见

检查拼写和对象访问权限

Activity is polymorphic. ...

直接使用 Activity 但未解析成 Task 或 Event

使用 Task/Event,或在 JSON 每行的 attributes.type 中指定类型

Record of type X does not match objectApiName Y.

传入的 SObject 类型不正确

对齐输入类型

attributes.type X does not match objectApiName Y.

JSON 行中指定的类型不一致

删除或修正 attributes.type

recordsJson is not valid JSON: ...

字符串格式错误

确认它是一个合法转义后的 JSON 对象或数组

recordsJson must be a JSON object or array of objects.

传入标量/其他 JSON

{}[] 包裹

Unknown field(s) X: a, b

字段名不在该对象上

修正 API 名称(__c 后缀、命名空间)

Could not build X from JSON: ...

值类型强制转换失败(如日期格式错误)

使用 ISO-8601 日期/日期时间,并保持类型正确

No records supplied. ...

没有任何可操作的记录

提供 recordrecordsrecordsJson 或 Ids

Invalid record Id: ...

不是合法的 15/18 位 Id

修正 Id

Id X is a T and does not match objectApiName Y.

前缀错误

修正 objectApiName 或 Id

row n: Id is required for update.

更新行缺少 Id

添加 Id

Every record must include Id for delete.

删除时用的 SObject 输入缺少 Id

添加 Id

Unknown case on X: f(读取/查找)

fields/orderBy/externalIdField 中有错误条目

修正为正确的 API 名称

Field X on Y is not an external Id / idLookup field ...

externalIdField 配置错误

使用外部 Id 字段或 Id

Unknown filter field on X: f (relationship paths are not allowed in filters).

过滤字段错误或使用了带点号的关系路径

使用直接字段

Field X is not filterable.

长文本/加密字段等

改用其他字段过滤

Unsupported op "X"

op 错误

使用 =、!=、<、<=、>、>=、LIKE、IN、NOT IN

Value "x" is not valid for field F (TYPE)

类型转换失败

使用符合字段类型的值 / ISO 日期格式

limitCount must be between 1 and 200.

超出范围

调整取值范围

row n: <platform message> [Field]

DML 失败(校验规则、必填字段、FLS、共享设置)

修正数据或权限


测试

测试分三层,按成本从低到高:

# 1. Unit tests (Apex, ~98% coverage)
sf apex run test -o <alias> -n SObjectActionsTest -n SObjectActionsExtTest -n SObjectActionsExt2Test -n SObjectActionsExt3Test -n SObjectActionsExt4Test -r human -w 10 -c

# 2. Anonymous Apex smoke: create -> read -> update -> delete, prints every result
sf apex run -o <alias> -f scripts/apex/smoke.apex

# 3. REST smoke through the Invocable Actions API (the exact path MCP tools use).
#    45 assertions across all 27 tools incl. Case labels, Activity Task/Event mix,
#    validation errors. Creates and removes its own records. Needs jq.
scripts/smoke-test.sh <alias> [apiVersion]

要测试 MCP 层本身,先激活服务器(见上文),通过 External Client App 连接一个 MCP 客户端,运行 tools/list,并调用一次例如 readRecords {"objectApiName":"Account","recordId":"001..."}

测试覆盖:创建(SObject + JSON 输入、Case 标签、Activity 解析、校验矩阵、部分成功与 all-or-none)、读取(全部/显式字段、关系路径、Activity 混合、未找到、校验)、更新(JSON/SObject/Activity、缺少 Id、幽灵 Id、all-or-none 回滚、跨请求重复 Id)、删除(Ids/SObject/Activity 混合、Case 标签、校验、部分成功、all-or-none、重复 Id)以及工具(util)兜底。

测试根据记录 Id 断言,而不依赖记录数量计数,因此即使 org 中已有 Account 的触发器/自动化逻辑,测试也仍能通过。


扩展

原始设计清单中的内容都已实现。接下来自然的后续扩展包括:按记录类型感知的选择列表取值(通过命名凭据调用 UI API)、sendEmail(单封邮件/邮件模板)、审批提交/撤回、以及共享/取消共享(手动共享)操作。

  • 自定义标签规则:扩展 SObjectActionUtil.labelFields()(当前的例外是 Case)。

  • 在服务器上新增工具:在 mcp/main/default/mcpServerDefinitions/SObjectActions.mcpServerDefinition-meta.xml 中添加一个 <tools> 块,并使用 apiIdentifier = aa:apex-<ClassName> 重新部署;如果需要,在 Setup 中重新激活服务器。

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to interact with Salesforce organizations through natural language by exposing Salesforce APIs (REST, Bulk v2, GraphQL, Tooling, Auth) as MCP tools for querying data, managing records, and executing SOQL queries.
    12
    19
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to interact with Salesforce through MCP, supporting queries, records, metadata, and bulk operations with flexible OAuth authentication.
    MIT

View all related MCP servers

Related MCP Connectors

  • Search, document and execute authenticated API calls across 700+ apps via one MCP server

  • An AI concierge that turns static forms into adaptive AI conversations. From any MCP client.

  • Operator-as-agent MCP hub. 6 tools. First $5 free, then $0.001/call.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Kelley-Austin/sfka-mcp-demo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server