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
文档大观
文档 | 用途 |
| 使用指南:部署、启用、约定、工具级参考、安全、限流、错误、测试 |
| 生成的功能清单:所有工具及其输入、输出、注解(由 |
| 同上,但为机器可读 JSON(用于 Agent Prompts、文档站点、CI diff) |
| 类如何组合、共享辅助函数、设计规范、如何新增工具 |
| 外部客户端如何进行身份认证(ECA、PKCE、refresh token、Postman、headless fallback) |
| 生成的 Postman collection:OAuth 预置请求、为每个工具提供 MCP + REST 请求 |
| 30 分钟的演示脚本,逐页对照幻灯片 23 页的发烧_ |
| 同一场分享的 15 分钟精简版:9 张幻灯片,一个演示瞬间 |
| 分享背后的参考资料:目录解读、问题库,以及不能做的声明 |
| 发布历史列表 |
目录
工具家族
家族 | 工具 |
发现类 |
|
读取类 |
|
写入类 |
|
意图快捷键 |
|
自动化 |
|
完整输入/输出参考,请见 docs/TOOLS.md。
组件
所有源代码都位于 force-app/main/default/。
路径 | 用途 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
| 可调用 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 用作 runFlow 测试夹具的小型自动启动流 |
| 可选: |
| 共享辅助工具:类型解析、JSON -> SObject、字段验证、标签查找、防重复 DML |
| 覆盖 create/read/update/delete 的 14 个测试 |
| 覆盖 upsert/find/describe 的 6 个测试 |
| 覆盖 search/related/count/undelete/access 的 7 个测试 |
| 覆盖 clone/validate/aggregate/runFlow/urls 的 7 个测试 |
| 针对 intent/utility 工具的 11 个测试(总覆盖率约 97.5%) |
| MCP 服务器定义,将所有 27 个 Apex 操作映射到工具(自有包目录:不可打包) |
| REST 端到端冒烟测试(45 项检查) |
| 匿名 Apex 冒烟脚本 |
| 从源码重新生成 |
| 从 |
| 一键安装并验证到任意 org |
| Scratch org 引导;unlocked package 创建/版本/安装 |
| 对所有操作类的执行访问权限(无对象 CRUD) |
| MCP 的 OAuth 客户端: |
| 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 服务器
设置 > MCP 服务器 >
SObject Actions> 关联外部客户端应用 SObject Actions MCP Client(由本仓库部署)> Activate。客户端身份验证说明,请参见
docs/CLIENT_AUTH.md。为用户分配权限集,使其具有对应 Apex 类的访问权限,并对要操作的对象拥有 CRUD/FLS 权限。这些工具以当前登录用户身份运行。
将 MCP 客户端(Claude、Agentforce、Cursor 等)指向“设置”中显示的服务器 URL。
tools/list返回checkAccess、describeObject、searchRecords、findRecords、countRecords、aggregateRecords、readRecords、relatedRecords、recordSummary、picklistValues、validateRecords、createRecords、upsertRecords、updateRecords、cloneRecords、assignOwner、changeRecordType、deleteRecords、undeleteRecords、logActivity、closeCase、convertLead、postChatter、addNote、attachFile、listFlows、runFlow。
在定义中设置的工具注解:
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 名称不区分大小写:Account、Case、Task、My_Object__c 等。
Activity(Task/Event 多态类型)
Activity 不能直接创建或查询,因此它被当作“Task 或 Event”的别名处理:
输入方式 | 具体类型的判定方式 |
| 取 SObject 本身的实际类型;除 Task/Event 外的其他类型都会被拒绝 |
| 每个元素必须包含 |
| 根据 Id 前缀判定( |
因此,当 objectApiName = "Activity" 时,一次请求可以同时混合包含 Task 和 Event。
记录输入(create / update)
可以任意组合使用下面的字段,它们将按此顺序合并:
字段 | 类型 | 使用场景 | 说明 |
| SObject | Flow 流程 | 类型必须与 |
| List<SObject> | Flow 流程 | 与 |
| String | MCP / REST | JSON 对象或数组,元素为 |
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(当objectApiName为Activity时,取 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 异常消息。
标签
recordLabels 与 recordIds 按下标一一对应。标签的来源:
对象类别 | 标签说明 |
Case |
|
其他类型 | 对象的 describe 名称字段(Task/Event 为 |
没有名称字段 / 无读取权限 | 记录 Id |
create/update 的标签会在 DML 后重新查询(因此能取得 CaseNumber 等自动编号值)。delete 的标签会在删除前获取。
通用输出
字段 | 类型 | 含义 |
| Boolean | 仅当请求中每一行都成功(读取时:每个 Id 均找到)时为 true |
| Integer | 行数(成功 / 失败) |
| Integer | 行数 |
| List<String> | 受影响/找到的 Id,按输入顺序返回 |
| List<String> | 与 |
| List<String> | 每行失败对应 |
| String | 单行汇总,例如 |
| List<String> | 与 |
请求级验证错误(如对象名错误、JSON 格式错误等)会使 isSuccess=true? 不,是 isSuccess=false、failureCount=1、errors 中有一条记录;该请求不会执行任何 DML。同一批调用中的其他请求不受影响。
工具说明参考
createRecords / SObjectCreateAction
可调用名称:Create Records (Generic),类别:SObject Actions。
输入:objectApiName(必填)、record、records、recordsJson、allOrNone。
输出:isSuccess、successCount、failureCount、recordIds、recordLabels、errors、message。
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)。
输入:
字段 | 类型 | 说明 |
| String(必填) | 允许 |
| String | 单个 Id、单条记录 |
| List<String> | 批量传入;重复的 Id 会被合并 |
| List<String> | 可选。字段 API 名称,支持关系路径( |
输出:
字段 | 说明 |
| 如果每个请求的 Id 均找到且可见,则为 true |
| |
| 找到的记录,按输入顺序 |
| 找到记录的 JSON 数组(序列化后的 SObject,包含 |
| 第一个 / 全部找到的记录,以 SObject 形式返回 - 面向 Flow |
| 不存在或在共享规则下不可见的 Id |
|
示例:
{ "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(必填)、record、records、recordsJson、allOrNone。
每一行必须包含 Id;缺少 Id 的行会使整个请求失败
(row <n>: Id is required for update.)。
输出:与 create 相同的结构(successCount、recordIds、recordLabels、...)。
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(必填)、recordIds、record、records、allOrNone。
通过 SObject 形式传入的记录必须包含 Id。
输出:isSuccess、successCount、failureCount、recordIds、recordLabels
(删除前捕获)、errors、message。
示例:
{ "objectApiName": "Account", "recordIds": ["001...", "001..."] }{ "objectApiName": "Activity", "recordIds": ["00T...", "00U..."] }已删除的记录会进入回收站(标准 Database.delete 语义)。级联删除遵循平台的
master-detail / lookup 规则。
upsertRecords / SObjectUpsertAction
可调用标签:Upsert 记录(通用)。
输入:objectApiName(必填)、externalIdField(可选,默认 Id)、record、
records、recordsJson、allOrNone。externalIdField 必须是对象上的 external Id / idLookup
字段。支持 Activity(通过 attributes_ascription 区分 Task/Event);external Id 字段必须
存在于具体类型上。
输出:isSuccess、successCount、failureCount、createdCount、updatedCount、
recordIds、recordLabels、wasCreated[](与 recordIds 对齐)、errors、message。
示例:
{ "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。
输入:
字段 | 说明 |
| 必须可查询。请使用 |
| JSON 数组(或单个对象)形式的 |
| 对所有过滤条件使用 |
| 额外返回的字段;允许关系路径( |
|
|
| 1-200,默认 50。 |
值在绑定前会根据字段类型进行强制转换(日期 YYYY-MM-DD、日期时间 ISO-8601、数字、布尔值、
Id)。所有值均通过绑定变量传入(Database.queryWithBinds、USER_MODE)。
输出:isSuccess、resultCount、recordIds、recordLabels、recordsJson、records(Flow)、
soql(实际执行的查询,值以 :b0、:b1... 形式隐藏)、errors、message
(当 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):
输入 | 说明 |
|
|
| 默认为 true |
| 按字段 API 名称或标签进行不区分大小写的过滤 |
输出:objectApiName、objectLabel、keyPrefix、isCustom、
isCreateable/Updateable/Deletable/Queryable、labelFields(例如 Case 的
["CaseNumber","Subject"])、requiredFields(可创建、必填、无默认值的字段)、
fieldsJson(仅可访问字段:apiName, label, type, required, createable, updateable, externalId, nameField, length, referenceTo[], relationshipName, picklistValues[])、
recordTypesJson(可用的非 master 记录类型:id, developerName, name, isDefault)、
childRelationshipsJson(relationshipName、childObject、field)、resultCount(返回的字段数)。
列表模式(objectApiName 留空):
输入 | 说明 |
| 按 API 名称或标签进行不区分大小写的过滤 |
| 默认为 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)。
输入 | 说明 |
| 至少 2 个字符;允许使用 |
| 默认 |
|
|
| 额外字段,仅在该字段存在于对应对象时应用 |
| 每个对象 1-200,默认 20 |
输出:resultCount、recordIds、recordLabels、recordObjectNames(对齐)、
recordsJson、records。
{ "searchTerm": "acme*", "objectApiNames": ["Account","Contact"], "searchIn": "NAME", "fields": ["Phone","Email"] }relatedRecords / SObjectRelatedAction
可调用标签:获取相关记录(通用)。
输入 | 说明 |
| 父对象根据 Id 推断 |
| 父对象上的子关系名称( |
| 与 findRecords 相同(limit 默认 50,最大 200) |
输出:parentObjectApiName、childObjectApiName、resultCount、recordIds、
recordLabels、recordsJson、records。
{ "parentRecordId": "001...", "relationshipName": "Cases", "fields": ["Status","Priority"], "orderBy": "CreatedDate DESC", "limitCount": 10 }countRecords / SObjectCountAction
可调用标签:计数记录(通用)。
输入 | 说明 |
| Task/Event,不是 Activity |
| 与 findRecords 完全相同 |
| 可选的可分组字段;最多 200 组,按计数值降序排列;null 组按 |
输出:totalCount、groupValues[]、groupCounts[](对齐)、groupsJson([{value,count}])、
soql。
{ "objectApiName": "Case", "filtersJson": "[{\"field\":\"IsClosed\",\"op\":\"=\",\"value\":false}]", "groupByField": "Priority" }undeleteRecords / SObjectUndeleteAction
可调用标签:取消删除记录(通用)。从回收站恢复记录。
输入:objectApiName(必填;Task/Event 混合时用 Activity)、recordIds(必填)、allOrNone。
输出:successCount、failureCount、recordIds、recordLabels(还原后)、errors、message。
同一次请求中的重复 Id 会被合并。已还原或已彻底删除的记录则按行失败。
{ "objectApiName": "Account", "recordIds": ["001..."] }checkAccess / SObjectAccessAction
可调用标签:检查访问(通用)。所有输入均可选;一个都不传时,相当于“我是谁”。
输入 | 说明 |
| CRUD 检查( |
| 需要 |
| 最多 200 个;使用 |
输出:userId、userName、loginUsername、profileId、profileName、userType、organizationId、timeZone、objectApiName、canCreate/canRead/canUpdate/canDelete、fieldAccessJson、recordAccessJson、message。
{ "objectApiName": "Opportunity", "fields": ["Amount","StageName"], "recordIds": ["006..."] }cloneRecords / SObjectCloneAction
可调用标签:克隆记录(通用)。
输入 | 说明 |
| Task/Event 混合时用 |
| 源记录;未找到或不可见的 Id 按行失败 |
| 应用于每个克隆记录的 JSON 对象(字段名已验证) |
| 不复制的字段(例如 |
|
会复制每个可创建、可读、非自动编号且非公式的字段。子记录不会被复制。输出:sourceRecordIds(与输入对齐)、recordIds、recordLabels、recordUrls、计数、errors。
{ "objectApiName": "Opportunity", "recordIds": ["006..."], "overridesJson": "{\"Name\":\"Renewal 2027\",\"StageName\":\"Prospecting\"}", "excludeFields": ["OwnerId"] }validateRecords / SObjectValidateAction
可调用标签:校验记录(通用,试运行)。在保存点(savepoint)内执行 insert/update,并且总是回滚;触发器、验证规则、必填字段、字段级安全(FLS)和共享规则都会真实执行。
输入:objectApiName(必填)、operation(默认 CREATE | UPDATE)、record、records、recordsJson。
输出:isSuccess(所有行是否都能保存)、successCount、failureCount、rowResultsJson([{row, valid, errors[]}])、errors、message(... Nothing was saved.)。
注意:DML 仍然会计入系统限制,事务中消耗的自动编号不会再被复用。
{ "objectApiName": "Contact", "recordsJson": "[{\"LastName\":\"Ok\"},{\"FirstName\":\"No last name\"}]" }aggregateRecords / SObjectAggregateAction
可调用标签:聚合记录(通用)。
输入 | 说明 |
| Task/Event,不能是 Activity |
|
|
| 与 findRecords 相同 |
| 0-3 个可分组字段 |
| 仅用于分组,1-2000,默认 200(未分组的查询不能使用 LIMIT) |
输出:resultCount、rowsJson(每行返回分组字段的值 + 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
可调用标签:运行流程(通用)。
输入 | 说明 |
| 已激活的自动启动流程 |
| 输入变量到值的 JSON 对象 |
| 标记为“可用于输出”并需要返回的变量名 |
输出:interviewId、outputsJson、errors(流程错误表现为 Could not start flow ... 或错误消息)、message。流程在相同的上下文中,按其声明的运行模式运行。
{ "flowApiName": "SObjectActions_EchoFlow", "inputsJson": "{\"inputText\":\"hi\",\"inputNumber\":1}", "outputVariableNames": ["outputText","outputNumber"] }assignOwner / SObjectAssignOwnerAction
输入:objectApiName、recordIds、newOwnerId(005/00G)或 newOwnerName(精确的用户全名 / 用户名,或队列名 / developer name;名称不明确会被拒绝)、allOrNone。
输出:解析后的 ownerId/ownerName、按记录统计、recordIds、recordLabels、errors。
{ "objectApiName": "Case", "recordIds": ["500..."], "newOwnerName": "Tier 2 Support" }changeRecordType / SObjectRecordTypeAction
输入:objectApiName、recordIds、recordType(Id、DeveloperName 或 Name)、allOrNone。错误信息会列出可用的开发者名称。输出:解析后的 recordTypeId/recordTypeName,每个记录的结果。
picklistValues / SObjectPicklistAction
输入:objectApiName、fieldApiName、includeInactive。输出:values[]、labels[]、valuesJsonOf({value,label,active,default,validFor[]})、isRestricted、isDependent、controllingField、defaultValue。值反映组织级字段定义(不应用记录类型的值集)。
logAction / FasteningAction
往名为 Task 的对象写入一条已完成的记录。输入:subject(必填)、relatedRecordId(WhatId)、personRecordId(Contact/Lead 的 WhoId)、description、activityDate(默认为今天)、activityType、status(默认第一个已关闭状态)、priority、ownerId、extraFieldsJson。输出:recordId、recordUrl、status。
{ "subject": "Call with CFO", "relatedRecordId": "001...", "personRecordId": "003...", "description": "Discussed renewal", "activityType": "Call" }closeCase / SObjectCaseAction
输入:caseIds、status(必须是已关闭状态,默认为第一个已关闭状态)、comment(+ commentIsPublic)、extraFieldsJson、allOrNone。输出:status 实际使用状态、按 Case 计数的数量、recordLabels(CaseNumber - Subject)。
convertLead / SObjectConvertLeadAction
输入:leadId、convertedStatus(默认第一个已转化状态)、accountId / contactId(合并到已有对象)、createOpportunity(默认 true)、opportunityName、ownerId、sendEmailTaskToOwner。输出:accountId、contactId、映射的 opportunityId + Error.
postChatter / SObjectPostChatterAction
输入:recordId(任何可 feed 的记录或 User),text(文本内容),mentionUserIds(提及的用户 Id)。输出:feedItemId。当对象未启用 feed tracking 时,返回明确错误信息。
addNote So / SObjectAddNoteAction
输入:title、body(纯文本或简单 HTML)、recordIds、shareType(默认 V / I / C)。创建 ContentNote 和 ContentDocumentLink;如果在组织中没有可用,则改为将备注存储为 .html 文件(storedAsFile=true)。输出:noteId、关联的 recordIds、storedAsFile。
attachFile / SObjectAttachFileAction
输入:fileName、textContent 或 base64Content、title、recordIds、shareType。输出:contentVersionId、contentDocumentId、关联的 recordIds。大 base64 有效内容会查询到堆内存限制(约 6 MB 同步)。
listFlows / SObjectListFlowsAction
列表模式:nameContains、processType(默认 AutoLaunchedFlow,可选值 ALL)、includeInactive、 limitCount -> flowsJson。
详情模式:flowApiName -> variablesJson(apiName、dataType、isInput、isOutput、isCollection、objectType、description)。可与 runFlow 搭配使用。
recordSummary / SObjectSummaryAction
输入:recordId、fields(默认所有可访问字段)、relationshipNames(最多 10 个,默认为存在的常用字段)、recentActivityLimit(0-20,默认 5)。
输出:recordType、recordLabel、recordUrl、ownerName、recordJson、relatedCountsJson({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}则会对所有记录执行回滚。
故障路径:请求级问题不会抛出异常;请检查 Success 和 Errors。只有意外的平台异常(例如限制超限)才会进入 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...\"}]" }
] }SObjectReadAction、SObjectUpdateAction、SObjectDeleteAction 的传入结构相同。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 种)。
错误目录
消息 | 原因 | 修复 |
| 缺少输入 | 提供 API 名称 |
| 拼写错误 / 对象不可见 | 检查拼写和对象访问权限 |
| 直接使用 | 使用 |
| 传入的 SObject 类型不正确 | 对齐输入类型 |
| JSON 行中指定的类型不一致 | 删除或修正 |
| 字符串格式错误 | 确认它是一个合法转义后的 JSON 对象或数组 |
| 传入标量/其他 JSON | 用 |
| 字段名不在该对象上 | 修正 API 名称( |
| 值类型强制转换失败(如日期格式错误) | 使用 ISO-8601 日期/日期时间,并保持类型正确 |
| 没有任何可操作的记录 | 提供 |
| 不是合法的 15/18 位 Id | 修正 Id |
| 前缀错误 | 修正 objectApiName 或 Id |
| 更新行缺少 Id | 添加 |
| 删除时用的 SObject 输入缺少 Id | 添加 |
|
| 修正为正确的 API 名称 |
|
| 使用外部 Id 字段或 |
| 过滤字段错误或使用了带点号的关系路径 | 使用直接字段 |
| 长文本/加密字段等 | 改用其他字段过滤 |
|
| 使用 =、!=、<、<=、>、>=、LIKE、IN、NOT IN |
| 类型转换失败 | 使用符合字段类型的值 / ISO 日期格式 |
| 超出范围 | 调整取值范围 |
| 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 中重新激活服务器。
This server cannot be installed
Maintenance
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
- AlicenseNot gradedqualityDmaintenanceEnables 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.1219MIT
- FlicenseNot gradedqualityBmaintenanceEnables interaction with Salesforce data and services via custom MCP tools, including account analytics, opportunity queries, case creation, and AI agent invocation.4
- AlicenseNot gradedqualityBmaintenanceProvides live-org context for AI assistants with tools to search skills, agents, templates, decision trees, and query Salesforce metadata (Apex, LWC, objects, fields, etc.) via MCP.15Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with Salesforce through MCP, supporting queries, records, metadata, and bulk operations with flexible OAuth authentication.MIT
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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