@salesforce/afv-skills 1.67.0 → 1.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -5
- package/package.json +1 -1
- package/skills/agentforce-offline-experimentation-run/SKILL.md +14 -22
- package/skills/agentforce-offline-experimentation-run/scripts/bt_compare.py +81 -0
- package/skills/agentforce-offline-experimentation-run/scripts/bt_fetch.py +358 -0
- package/skills/agentforce-offline-experimentation-run/scripts/bt_patch_agent.py +110 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/__init__.py +34 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/agentpatch.py +483 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/agentsrc.py +565 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/cache.py +95 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/compare.py +617 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/fetch.py +1012 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/judge.py +148 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/messages.py +92 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/patch.py +327 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/redact.py +40 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/results.py +288 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/scoring.py +340 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/sfap.py +238 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/splits.py +136 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/stdm.py +98 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/tokens.py +173 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/units.py +199 -0
- package/skills/agentforce-offline-experimentation-run/scripts/btlib/versions.py +194 -0
- package/skills/agentforce-offline-experimentation-run/scripts/build_report.py +366 -378
- package/skills/agentforce-offline-experimentation-run/scripts/resolve-token.sh +14 -4
- package/skills/agentforce-offline-experimentation-run/scripts/sfap_replay.py +472 -796
- package/skills/dx-lifecycle-center/SKILL.md +5 -2
- package/skills/dx-lifecycle-center/assets/widgets/connect-connected/connect-connected.json +65 -0
- package/skills/dx-lifecycle-center/assets/widgets/connect-connected/schema.json +32 -0
- package/skills/dx-lifecycle-center/assets/widgets/connect-sign-in/connect-sign-in.json +57 -0
- package/skills/dx-lifecycle-center/assets/widgets/connect-sign-in/schema.json +21 -0
- package/skills/dx-lifecycle-center/examples/workflows.md +20 -6
- package/skills/dx-lifecycle-center/references/connect-org.md +21 -2
- package/skills/dx-lifecycle-center/references/deploy.md +2 -2
- package/skills/dx-lifecycle-center/references/list-orgs.md +1 -1
- package/skills/dx-lifecycle-center/scripts/card-policy.jq +71 -0
- package/skills/dx-lifecycle-center/scripts/fill-widget.jq +29 -0
- package/skills/dx-lifecycle-center/scripts/widget.sh +28 -0
- package/skills/dx-org-devhub-configure/SKILL.md +85 -84
- package/skills/dx-org-devhub-configure/examples/status-output.md +25 -2
- package/skills/dx-org-devhub-configure/scripts/devhub.sh +265 -140
- package/skills/dx-org-devhub-configure/scripts/test-devhub.sh +100 -15
- package/skills/dx-packaging-move-to-2gp-migrate/SKILL.md +89 -0
- package/skills/dx-packaging-move-to-2gp-migrate/references/api-operations.md +164 -0
- package/skills/dx-scratch-def-configure/SKILL.md +163 -0
- package/skills/dx-scratch-def-configure/examples/confirm-card.md +53 -0
- package/skills/dx-scratch-def-configure/examples/report-recipes.md +100 -0
- package/skills/dx-scratch-def-configure/references/features.json +3270 -0
- package/skills/dx-scratch-def-configure/references/project-scratch-def.schema.json +1841 -0
- package/skills/dx-scratch-def-configure/references/settings-map.json +682 -0
- package/skills/dx-scratch-def-configure/scripts/lib_scratch_def.py +1306 -0
- package/skills/dx-scratch-def-configure/scripts/requirements.txt +3 -0
- package/skills/dx-scratch-def-configure/scripts/scrape_config_values.py +848 -0
- package/skills/education-cloud-domain-configure/SKILL.md +1 -1
- package/skills/education-cloud-multi-campus-configure/SKILL.md +1 -1
- package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +1 -1
- package/skills/experience-design-validate/SKILL.md +1 -1
- package/skills/experience-search-coordinate/SKILL.md +18 -12
- package/skills/experience-search-coordinate/references/content-route.md +79 -1
- package/skills/field-service-data-capture-migrate/SKILL.md +1 -1
- package/skills/platform-apex-generate/SKILL.md +14 -11
- package/skills/platform-apex-test-generate/SKILL.md +2 -1
- package/skills/platform-datamask-run/SKILL.md +52 -71
- package/skills/platform-datamask-run/references/api-surface.md +10 -4
- package/skills/platform-datamask-run/references/policy-authoring.md +56 -7
- package/skills/platform-datamask-run/references/run-and-abort.md +17 -10
- package/skills/platform-datamask-run/scripts/poll-job.sh +15 -25
- package/skills/platform-dataspace-access-configure/SKILL.md +19 -1
- package/skills/platform-metadata-discovery-get/SKILL.md +185 -0
- package/skills/platform-metadata-discovery-get/references/api-map.md +25 -0
- package/skills/platform-metadata-discovery-get/references/h360-routes.md +30 -0
- package/skills/platform-metadata-discovery-get/references/scoped-soql.md +52 -0
- package/skills/platform-metadata-grounding-get/SKILL.md +170 -0
- package/skills/platform-policy-rule-generate/SKILL.md +84 -232
- package/skills/platform-policy-rule-generate/references/condition-model.md +47 -0
- package/skills/platform-policy-rule-generate/references/condition-templates.md +89 -0
- package/skills/platform-policy-rule-generate/references/conjunction-compiler.md +25 -0
- package/skills/platform-policy-rule-generate/references/conjunction-expression.md +22 -0
- package/skills/platform-policy-rule-generate/references/consumer-all.md +9 -0
- package/skills/platform-policy-rule-generate/references/consumer-datacloud-ui.md +91 -0
- package/skills/platform-policy-rule-generate/references/deploy-errors.md +24 -36
- package/skills/platform-policy-rule-generate/references/examples/ExistsJoinRLS/package.xml +12 -0
- package/skills/platform-policy-rule-generate/references/examples/ExistsJoinRLS/policyRuleDefinitionSets/ExistsJoinRLS.policyRuleDefinitionSet +6 -0
- package/skills/platform-policy-rule-generate/references/examples/ExistsJoinRLS/policyRuleDefinitions/ExistsJoinRLS_Rule_1.policyRuleDefinition +91 -0
- package/skills/platform-policy-rule-generate/references/examples/FieldReadDeny/package.xml +12 -0
- package/skills/platform-policy-rule-generate/references/examples/FieldReadDeny/policyRuleDefinitionSets/FieldReadDeny.policyRuleDefinitionSet +6 -0
- package/skills/platform-policy-rule-generate/references/examples/FieldReadDeny/policyRuleDefinitions/FieldReadDeny_Rule_1.policyRuleDefinition +72 -0
- package/skills/platform-policy-rule-generate/references/examples/LastCharactersFieldMasking/package.xml +12 -0
- package/skills/platform-policy-rule-generate/references/examples/LastCharactersFieldMasking/policyRuleDefinitionSets/LastCharactersFieldMasking.policyRuleDefinitionSet +6 -0
- package/skills/platform-policy-rule-generate/references/examples/LastCharactersFieldMasking/policyRuleDefinitions/LastCharactersFieldMasking_Rule_1.policyRuleDefinition +77 -0
- package/skills/platform-policy-rule-generate/references/examples/PrincipalFieldMatchRLS/package.xml +12 -0
- package/skills/platform-policy-rule-generate/references/examples/PrincipalFieldMatchRLS/policyRuleDefinitionSets/PrincipalFieldMatchRLS.policyRuleDefinitionSet +6 -0
- package/skills/platform-policy-rule-generate/references/examples/PrincipalFieldMatchRLS/policyRuleDefinitions/PrincipalFieldMatchRLS_Rule_1.policyRuleDefinition +77 -0
- package/skills/platform-policy-rule-generate/references/examples/TaggedObjectAccess/package.xml +12 -0
- package/skills/platform-policy-rule-generate/references/examples/TaggedObjectAccess/policyRuleDefinitionSets/TaggedObjectAccess.policyRuleDefinitionSet +4 -0
- package/skills/platform-policy-rule-generate/references/examples/TaggedObjectAccess/policyRuleDefinitions/TaggedObjectAccess_Rule_1.policyRuleDefinition +65 -0
- package/skills/platform-policy-rule-generate/references/headless-transport.md +87 -0
- package/skills/platform-policy-rule-generate/references/policy-schema-full.md +17 -10
- package/skills/platform-policy-rule-generate/references/policy-shapes.md +74 -0
- package/skills/platform-policy-rule-generate/references/principal-expression.md +31 -0
- package/skills/platform-policy-rule-generate/references/reference-resolution.md +51 -0
- package/skills/platform-policy-rule-generate/references/resource-expression.md +48 -0
- package/skills/platform-policy-rule-generate/references/runtime-context.md +9 -0
- package/skills/platform-policy-rule-generate/references/templates-access.md +11 -9
- package/skills/platform-policy-rule-generate/references/templates-advanced.md +4 -2
- package/skills/platform-policy-rule-generate/references/templates-record.md +37 -22
- package/skills/platform-policy-rule-generate/references/templates-transform.md +10 -8
- package/skills/platform-policy-rule-generate/references/templates.md +2 -2
- package/skills/platform-policy-rule-generate/references/ui-compatibility.md +35 -17
- package/skills/platform-report-generate/SKILL.md +9 -4
- package/skills/platform-report-generate/references/chart-types.md +26 -2
- package/skills/platform-report-generate/references/column-names.md +43 -0
- package/skills/platform-report-generate/references/errors-and-troubleshooting.md +1 -1
- package/skills/platform-report-generate/references/verification-checklist.md +10 -2
- package/skills/service-digital-engagement-channel-configure/SKILL.md +7 -5
- package/skills/service-digital-engagement-channel-configure/assets/messaging_channel_template.xml +33 -0
- package/skills/service-digital-engagement-channel-configure/references/channel_settings.md +110 -14
|
@@ -91,13 +91,62 @@ Each `DataMaskPolicyField` carries a `MaskingCategory` plus a `MaskValue` — th
|
|
|
91
91
|
`MaskingRuleType` column** (older docs claiming values like `RandomEmail` / `RandomPhoneNumber`
|
|
92
92
|
were wrong; that column does not exist).
|
|
93
93
|
|
|
94
|
-
- `MaskingCategory` — `library` (pick a value from the built-in library, the common case)
|
|
95
|
-
`replaceRandom` (random replacement).
|
|
96
|
-
- `MaskValue`
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
- `MaskingCategory` — `library` (pick a value from the built-in library, the common case),
|
|
95
|
+
`characters` (random characters), or `replaceRandom` (random replacement).
|
|
96
|
+
- `MaskValue` depends on the category:
|
|
97
|
+
- `library` — a snake_case library token identifying the value set. Verified tokens include:
|
|
98
|
+
`first_name`, `last_name`, `email`, `phone`, `street`, `city`, `state`, `postal_code`,
|
|
99
|
+
`country`, `account_name`, `URL`.
|
|
100
|
+
- `characters` and `replaceRandom` — the field's Salesforce type (for example `Text`, `Date`).
|
|
101
|
+
Left empty, the run fails with `UNKNOWN_EXCEPTION` ("Mask Value is required for Replace Random
|
|
102
|
+
field").
|
|
103
|
+
|
|
104
|
+
## Choosing the fields from Core's classification
|
|
105
|
+
|
|
106
|
+
The Data Mask UI's "Automatic PII rules" are driven by a per-field `recommendedPiiType` that Core's
|
|
107
|
+
classifier computes. Use the same source here so the skill and the UI recommend the same fields. Do
|
|
108
|
+
this once per target object, before inserting any `DataMaskPolicyField` row.
|
|
109
|
+
|
|
110
|
+
1. **Call the classification operation** through the `project-codey` server's `dispatch_readonly`
|
|
111
|
+
tool. It takes a `path`, not an operationId: `path`
|
|
112
|
+
`/headless/invoke/platform/datamask/data-mask-policy/get-object-fields-with-masking-info`, `method`
|
|
113
|
+
`GET`, and `query_params` `{objectApiName: <object>, includePicklistValues: false,
|
|
114
|
+
includeFilterOnlyFields: false}` (for example `objectApiName: "Contact"`). Omit `policyObjectId` (a
|
|
115
|
+
new object has no `DataMaskPolicyObject` row yet). If this path 404s, re-resolve it with `find_route`
|
|
116
|
+
rather than guessing. The `.../data-mask-policy-builder/...` route serves the same operation but does
|
|
117
|
+
not take the two picklist/filter params.
|
|
118
|
+
2. **Read the response.** The result is `{status_code, body}`, where `body` is an array with one entry
|
|
119
|
+
per field. Each entry carries `apiName`, `label`, `type`, and `recommendedPiiType` (`null` when Core
|
|
120
|
+
has no recommendation). Any other shape counts as a failed call (step 4).
|
|
121
|
+
3. **Map each recommendation to a treatment.** Core returns tokens such as `last_name`, `street`,
|
|
122
|
+
`phone`, `characters`, and `replaceRandom`. Compare `recommendedPiiType` case-insensitively with the
|
|
123
|
+
verified `MaskValue` tokens in "Field treatment columns" above. `characters` and `text` are the same
|
|
124
|
+
category.
|
|
125
|
+
- A verified token → `MaskingCategory=library`, `MaskValue=<that token>`.
|
|
126
|
+
- `characters`/`text` → `MaskingCategory=characters`. `replaceRandom` → `MaskingCategory=replaceRandom`.
|
|
127
|
+
Both also require `MaskValue` = the field's Salesforce type as Core reports it in `type` (for example
|
|
128
|
+
`Text`, `Date`). The UI's "Mask With" menu offers `Random Characters` for `characters`.
|
|
129
|
+
- Any other recommendation (for example `title`, `full_name`, `full_address`, `credit_card`, `ssn`,
|
|
130
|
+
`password`, `date`, `numberRange`) has no `MaskingCategory` or verified token here. **Report it as
|
|
131
|
+
unmapped. Do not guess a `MaskValue`.**
|
|
132
|
+
- A field with no `recommendedPiiType` is not masked. List it in the report.
|
|
133
|
+
4. **Fall back only when the call fails** (refused, errored, the `project-codey` server is unavailable,
|
|
134
|
+
or the response shape is unrecognized). Then use the static table below and state in the report that
|
|
135
|
+
the field list came from the static table, not from Core. When the call succeeds, Core's answer is
|
|
136
|
+
authoritative. Do not add fields from the static table.
|
|
137
|
+
|
|
138
|
+
## Fallback treatments (static table)
|
|
139
|
+
|
|
140
|
+
Use this table **only** when the classification call in "Choosing the fields from Core's classification"
|
|
141
|
+
fails. When the call succeeds, use Core's `recommendedPiiType` and skip this table. The table is narrower than Core:
|
|
142
|
+
|
|
143
|
+
- It covers only the standard fields listed below. It does not apply Core's label, type, or encryption
|
|
144
|
+
rules, so it misses fields Core does recommend (for Contact: the Other* address fields, `Fax`,
|
|
145
|
+
`HomePhone`, `OtherPhone`, `AssistantPhone`).
|
|
146
|
+
- Core's type-based fallback classifies some text fields as `characters` (for Contact, `AssistantName`)
|
|
147
|
+
and some date fields as `replaceRandom` (for Contact, `Birthdate`). Both map to their `MaskingCategory`
|
|
148
|
+
value with `MaskValue` set to the field's Salesforce type (`Text` for AssistantName, `Date` for
|
|
149
|
+
Birthdate), so they are masked, not reported as unmapped.
|
|
101
150
|
|
|
102
151
|
Match the `MaskValue` to the field's semantics — do **not** apply one blanket replacement:
|
|
103
152
|
|
|
@@ -48,28 +48,35 @@ Capture `jobRunId`. A `409`/`CONFLICT` means a run is already active for this po
|
|
|
48
48
|
## 3. Poll to terminal
|
|
49
49
|
```bash
|
|
50
50
|
sf data query --target-org <alias> \
|
|
51
|
-
--query "SELECT Id, Status, Type
|
|
51
|
+
--query "SELECT Id, Status, Type FROM DataMaskPolicyJobRun WHERE Id = '<jobRunId>'"
|
|
52
52
|
```
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
`DataMaskPolicyJobRun` has **no `TotalRecords` field** — there is no record-count column on the
|
|
54
|
+
parent; the masked count lives on the child detail rows (step 4). Repeat the status query on a
|
|
55
|
+
bounded interval until `Status` is one of `completed`, `completed_with_errors`, `failed`.
|
|
56
|
+
`scheduled`/`running` are NOT terminal. `scripts/poll-job.sh` automates this with a timeout.
|
|
56
57
|
|
|
57
58
|
## 4. Report from the detail object
|
|
58
|
-
Per-object masked results live on the child, not the parent
|
|
59
|
+
Per-object masked results live on the child, not the parent. `DataMaskPolicyJobRunDtl` has **no
|
|
60
|
+
`Status` column** — the status lives in `Type`/`Subtype`, with the count itself in `Value` (a
|
|
61
|
+
string):
|
|
59
62
|
```bash
|
|
60
63
|
sf data query --target-org <alias> \
|
|
61
|
-
--query "SELECT
|
|
64
|
+
--query "SELECT ObjectReference, Type, Subtype, Value FROM DataMaskPolicyJobRunDtl WHERE DataMaskPolicyJobRunId = '<jobRunId>' ORDER BY Timestamp"
|
|
62
65
|
```
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
Expect one `status_update` row per `Subtype` (`loaded`, `completed`, `total_records_masked`) for
|
|
67
|
+
each target object; `total_records_masked`'s `Value` is the masked count. Report the concrete
|
|
68
|
+
masked-record count and per-object success/failure from these rows. Do not report a number you did
|
|
69
|
+
not read from here.
|
|
65
70
|
|
|
66
71
|
## 5. Abort the currently-running job — ONLY when the user asked to cancel
|
|
67
72
|
Steps 5–7 are the **abort** flow. Run them **only when the prompt explicitly asks to cancel/abort**
|
|
68
73
|
a run — a "create and run" or "edit and run" task is complete after step 4.
|
|
69
74
|
|
|
70
75
|
Aborting is an **on-demand action against a job that is already in progress**: take that job's
|
|
71
|
-
`jobRunId` (and note its
|
|
72
|
-
missed
|
|
76
|
+
`jobRunId` (and note its policy Id — you need it to start a replacement run if the window is
|
|
77
|
+
missed; `DataMaskPolicyJobRun` has no direct policy lookup field, so resolve it from the job's
|
|
78
|
+
`PolicyName` via `SELECT Id FROM DataMaskPolicy WHERE MasterLabel = '<PolicyName>'` against the
|
|
79
|
+
Tooling API), wait for `DataMaskPolicyJobRun.Status = running`, and go straight to the abort (step 6).
|
|
73
80
|
You can only abort a running job. Do **not** use the terminal poll from step 3 here — it waits until
|
|
74
81
|
the job is *done*. Use the poller's `running` mode, which returns the instant the job is abortable:
|
|
75
82
|
```bash
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
# so the default cap is 600s (10 min) with a 20s interval to keep the query volume low. Do NOT poll
|
|
18
18
|
# on a tight (sub-10s) interval — it just burns tool calls against a job that cannot finish sooner.
|
|
19
19
|
#
|
|
20
|
-
# TERMINAL MODE:
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
20
|
+
# TERMINAL MODE: exits only on a terminal PARENT status. A `total_records_masked` detail row is NOT a
|
|
21
|
+
# completion signal: it can already exist, reading `0`, while the parent is still `running`, so
|
|
22
|
+
# stopping on the first row reports a count for a job that has not finished. Read the detail rows
|
|
23
|
+
# only AFTER this script exits.
|
|
24
24
|
#
|
|
25
25
|
# RUNNING MODE (for abort): exits the instant the parent status reads `running` — the only state in
|
|
26
26
|
# which the abort endpoint accepts the request (a `pending`/`scheduled` job 409s). Because the pool
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
# start a fresh run rather than abort a job that already finished.
|
|
31
31
|
#
|
|
32
32
|
# Exit codes:
|
|
33
|
-
# terminal mode: 0 = terminal status
|
|
33
|
+
# terminal mode: 0 = terminal parent status observed (printed on stdout);
|
|
34
34
|
# 1 = timed out; 2 = bad args.
|
|
35
35
|
# running mode: 0 = `running` observed (prints `running`); 1 = timed out; 2 = bad args;
|
|
36
36
|
# 3 = job reached a terminal state before `running` was seen (prints that status).
|
|
@@ -63,12 +63,15 @@ is_terminal() {
|
|
|
63
63
|
|
|
64
64
|
elapsed=0
|
|
65
65
|
while (( elapsed < MAX_SECONDS )); do
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
# CSV is a header line then one value per row, so bash can read it with no pipe and no
|
|
67
|
+
# post-processor. stderr is left attached so an auth or alias failure is visible; a failed
|
|
68
|
+
# query only skips this tick, it does not end the poll.
|
|
69
|
+
csv="$(sf data query --target-org "$ORG" \
|
|
70
|
+
--query "SELECT Status FROM DataMaskPolicyJobRun WHERE Id = '${JOB_ID}'" \
|
|
71
|
+
--result-format csv)" || csv=""
|
|
72
|
+
status=""
|
|
73
|
+
{ IFS= read -r _; IFS= read -r status; } <<< "$csv" || true
|
|
74
|
+
status="${status:-UNKNOWN}"
|
|
72
75
|
echo "[poll-job] ${JOB_ID} status=${status} (${elapsed}s/${MAX_SECONDS}s) mode=${POLL_MODE}"
|
|
73
76
|
|
|
74
77
|
if [[ "$POLL_MODE" == "running" ]]; then
|
|
@@ -88,19 +91,6 @@ while (( elapsed < MAX_SECONDS )); do
|
|
|
88
91
|
echo "$status"
|
|
89
92
|
exit 0
|
|
90
93
|
fi
|
|
91
|
-
|
|
92
|
-
# Ground-truth check: a masked-count detail row means the job finished even if the parent lags.
|
|
93
|
-
masked="$(
|
|
94
|
-
sf data query --target-org "$ORG" \
|
|
95
|
-
--query "SELECT Value FROM DataMaskPolicyJobRunDtl WHERE DataMaskPolicyJobRunId = '${JOB_ID}' AND Subtype = 'total_records_masked'" \
|
|
96
|
-
--json 2>/dev/null \
|
|
97
|
-
| python3 -c "import json,sys; r=json.load(sys.stdin).get('result',{}).get('records',[]); print(r[0]['Value'] if r else '')"
|
|
98
|
-
)"
|
|
99
|
-
if [[ -n "$masked" ]]; then
|
|
100
|
-
echo "[poll-job] detail row total_records_masked=${masked} — job effectively complete (parent status=${status})"
|
|
101
|
-
echo "completed"
|
|
102
|
-
exit 0
|
|
103
|
-
fi
|
|
104
94
|
fi
|
|
105
95
|
|
|
106
96
|
sleep "$INTERVAL"
|
|
@@ -110,6 +100,6 @@ done
|
|
|
110
100
|
if [[ "$POLL_MODE" == "running" ]]; then
|
|
111
101
|
echo "[poll-job] TIMEOUT after ${MAX_SECONDS}s — job never reached 'running'" >&2
|
|
112
102
|
else
|
|
113
|
-
echo "[poll-job] TIMEOUT after ${MAX_SECONDS}s —
|
|
103
|
+
echo "[poll-job] TIMEOUT after ${MAX_SECONDS}s — job never reached a terminal status" >&2
|
|
114
104
|
fi
|
|
115
105
|
exit 1
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-dataspace-access-configure
|
|
3
|
-
description: "
|
|
3
|
+
description: "THE skill for granting Data Cloud data-space access and the PERMISSION-SET / RBAC way of granting object access. Grants data-space-level access via MDAPI PermissionSet XML with dataspaceScopes, grants object-level access to DMO/DLO/CIO objects via the Object Access Grants Connect API (RBAC object grants), and inspects existing scopes read-only. TRIGGER when the user needs to grant access to a data space / dataspace, create/update a permission set with DataSpace access, list/configure scopes, or add/list/remove RBAC object access grants — 'grant access to a data space' routes here. DO NOT TRIGGER for a generic permission set (use platform-permission-set-generate), data ingestion/streams, creating dataspaces themselves, or an access POLICY / policy rule / tag/classification-based access (use platform-policy-rule-generate). For a vague object-access ask, surface both skills and ASK the permission-set way vs the policy way; if unsure the object is Data Cloud, ask which first."
|
|
4
4
|
metadata:
|
|
5
5
|
relatedSkills:
|
|
6
6
|
- "platform-permission-set-generate"
|
|
7
|
+
- "platform-policy-rule-generate"
|
|
7
8
|
version: "1.0"
|
|
8
9
|
domains: ["Platform", "Data 360"]
|
|
9
10
|
minApiVersion: "67.0"
|
|
@@ -27,6 +28,23 @@ The MDAPI layer is required to establish the PermissionSet → DataSpace linkage
|
|
|
27
28
|
|
|
28
29
|
---
|
|
29
30
|
|
|
31
|
+
## When to use this skill (routing)
|
|
32
|
+
|
|
33
|
+
This is the **permission-set / RBAC** path to Data Cloud access:
|
|
34
|
+
|
|
35
|
+
- **"Grant access to a data space" / dataspace access** → this skill (Layer 1, `dataspaceScopes` on a permission set).
|
|
36
|
+
- **Grant object access via a permission set / RBAC object grant** → this skill (Layer 2, Object Access Grants Connect API).
|
|
37
|
+
|
|
38
|
+
Object access has a second, separate path — a **data-governance access policy** (a tag/OLS `PolicyRuleDefinition`), owned by **`platform-policy-rule-generate`**. Infer the mechanism from context; ask only when the request is vague:
|
|
39
|
+
|
|
40
|
+
- The user is **driving from a permission set** (mentions a permission set, RBAC, or an object grant on a permset) → object access grant → **this skill**, no need to ask.
|
|
41
|
+
- The user **frames it as a policy** (create a policy / access policy / policy rule, OLS/FLS/RLS, tag/classification-based) → route to **`platform-policy-rule-generate`**.
|
|
42
|
+
- The request is **genuinely vague** — "grant object access to X" with no mechanism and no clue → **ASK**: the **permission-set way** (this skill) or the **policy way** (`platform-policy-rule-generate`)? If it is also unclear whether the object is a Data Cloud object, ask which object first.
|
|
43
|
+
|
|
44
|
+
"Access to a **data space**" is always this skill (the permission-set / `dataspaceScopes` path) — no disambiguation needed.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
30
48
|
## Decide the Case First
|
|
31
49
|
|
|
32
50
|
Pick exactly one case from the table below before writing any files. Each case has a different output shape.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: platform-metadata-discovery-get
|
|
3
|
+
description: "Use before creating, changing, or writing code against Salesforce metadata, to find out what already exists in the org with the right tool. Routes each question to metadata grounding, scoped SOQL with sf data query, sf sobject list or describe, sf org list metadata, source retrieval, or local files, and covers orgs and setups where grounding is not available. Use to check whether an object, field, picklist, permission set, page, app, or flow already exists before creating one, confirm field names, types, and picklist values before writing Apex, LWC, SOQL, or Flow logic, list every field on an object or every member of a metadata type, find automation that runs on an object, or explain how something in the org is configured and who can access it. DO NOT TRIGGER for whole-org overviews, org comparison, or drift (use dx-org-analyze), metadata file structure or XML shape (platform-metadata-api-context-get), or Data Cloud DLO and DMO schema (data360-code-extension-generate)."
|
|
4
|
+
metadata:
|
|
5
|
+
relatedSkills:
|
|
6
|
+
- data360-code-extension-generate
|
|
7
|
+
- dx-org-analyze
|
|
8
|
+
- platform-metadata-api-context-get
|
|
9
|
+
- platform-metadata-grounding-get
|
|
10
|
+
- platform-metadata-retrieve
|
|
11
|
+
version: "1.3"
|
|
12
|
+
domains:
|
|
13
|
+
- "Platform"
|
|
14
|
+
cliTools:
|
|
15
|
+
- tool:
|
|
16
|
+
- "sf"
|
|
17
|
+
semver: ">=2.0.0"
|
|
18
|
+
mcpTools:
|
|
19
|
+
metadata-grounding:
|
|
20
|
+
tools:
|
|
21
|
+
- "search_metadata"
|
|
22
|
+
- "query_metadata"
|
|
23
|
+
- "describe_metadata"
|
|
24
|
+
semver: ">=1.0.0"
|
|
25
|
+
headless-360:
|
|
26
|
+
tools:
|
|
27
|
+
- "describe"
|
|
28
|
+
- "dispatch"
|
|
29
|
+
- "dispatch_readonly"
|
|
30
|
+
semver: ">=1.0.0"
|
|
31
|
+
id: "platform-metadata-discovery-get"
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# Salesforce Metadata Discovery
|
|
35
|
+
|
|
36
|
+
Before you create metadata, change it, or write code that references it, know what already exists in the org. This skill picks the tool for each question. Metadata grounding finds metadata by meaning when you do not know its name. It needs Salesforce Catalog and a connected grounding server, and many orgs and setups have neither. Every other route works with the `sf` CLI alone.
|
|
37
|
+
|
|
38
|
+
Skip discovery when the request only changes local files and adds no new references to org metadata.
|
|
39
|
+
|
|
40
|
+
## Check What You Have
|
|
41
|
+
|
|
42
|
+
Do this once per session, before the first lookup.
|
|
43
|
+
|
|
44
|
+
1. **The CLI and its org.** Run `sf org display --json` to see the default org and its id. If no org is authenticated, ask the user to run `sf org login web` and wait. If you cannot run commands at all, read `references/h360-routes.md`.
|
|
45
|
+
2. **Grounding servers.** Note whether the Metadata Grounding MCP (tools `search_metadata`, `query_metadata`, `describe_metadata`) or the Salesforce MCP is connected. The Salesforce MCP is shown in Setup today as the Headless360 (H360) MCP Server. Its tools are `describe`, `dispatch`, and `dispatch_readonly`, which are different from the Metadata Grounding MCP tools.
|
|
46
|
+
3. **One org.** Grounding identifiers start with the 18-character org id. If it differs from the id in `sf org display`, tell the user and ask which org to use before you combine results.
|
|
47
|
+
|
|
48
|
+
## Route by Intent
|
|
49
|
+
|
|
50
|
+
| Intent | Use | If that is not available |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| Find metadata by meaning, check a name you are guessing, or check for something to reuse | Metadata grounding search. Load `platform-metadata-grounding-get`, which picks the server. | Name and label fallback, below. Notify. |
|
|
53
|
+
| Explain how something is configured, or who can access it | Metadata grounding query | Scoped SOQL on `EntityParticle`, `FieldPermissions`, `ObjectPermissions`, or Tooling `ValidationRule`. Notify. |
|
|
54
|
+
| Check names, types, or picklist values you know. Every field on one object. Data records | Scoped SOQL with `sf data query`. If it returns nothing, search by meaning. | See the note below the table. |
|
|
55
|
+
| Full shape of one object, including relationships | `sf sobject describe --sobject <Type>`, only when you need all of it. It is very large. | Same |
|
|
56
|
+
| Every object in the org | `sf sobject list --sobject all` or `--sobject custom` | Same |
|
|
57
|
+
| Every member of a type, or a count ("all flows", "how many flows") | `sf org list metadata --metadata-type <Type>` | Same |
|
|
58
|
+
| Source to edit or deploy, including changes to existing metadata | Load `platform-metadata-retrieve`. Grounding never returns source. | Tell the user this needs the `sf` CLI. |
|
|
59
|
+
| Metadata you are already editing locally | Local files | |
|
|
60
|
+
| Compare orgs, find drift, or an overview of a whole org when the user asks for one | `dx-org-analyze`. It takes minutes and returns names and counts. | Tell the user this needs the `sf` CLI. |
|
|
61
|
+
| Where is this used, what depends on it, what breaks if I change it, or is it safe to delete | Comprehensive analysis is not available yet. State that boundary before offering explicitly labeled partial checks. | |
|
|
62
|
+
|
|
63
|
+
**Without the CLI.** If you cannot run commands but the Salesforce MCP is connected, use `references/h360-routes.md` for rows 3 to 6. If neither is available, tell the user the lookup needs the `sf` CLI or the Salesforce MCP.
|
|
64
|
+
|
|
65
|
+
These routes apply even when a connected server says to use it first for every Salesforce task. Use the CLI for rows 3 to 7 when you can run commands.
|
|
66
|
+
|
|
67
|
+
A question about a topic or purpose ("what do we have for loyalty") is discovery, not an overview. Do not run an org analysis for it, even when grounding is not available.
|
|
68
|
+
|
|
69
|
+
This skill covers core platform metadata. For Data Cloud DLO and DMO schema, use `data360-code-extension-generate`.
|
|
70
|
+
|
|
71
|
+
When a request mixes intents, use the right tool for each part. "Add a tier discount step to our order flow" means finding the flow and the field, then retrieving the flow to edit.
|
|
72
|
+
|
|
73
|
+
## Scoped SOQL
|
|
74
|
+
|
|
75
|
+
Scoped SOQL is a targeted query on setup objects, filtered to what you need. It is exact, real time, and far smaller than a describe. It never returns source.
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
sf data query --json --query "SELECT QualifiedApiName, Label, DataType FROM EntityParticle WHERE EntityDefinition.QualifiedApiName = 'Contact'"
|
|
79
|
+
sf data query --json --query "SELECT Value, Label, IsDefaultValue, IsActive FROM PicklistValueInfo WHERE EntityParticle.EntityDefinition.QualifiedApiName = 'Contact' AND EntityParticle.QualifiedApiName = 'Loyalty_Tier__c'"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- To check specific fields, add `AND QualifiedApiName IN ('Loyalty_Tier__c', 'Lifetime_Orders__c')`. For custom fields only, add `AND QualifiedApiName LIKE '%__c'`.
|
|
83
|
+
- `EntityParticle` includes parts of compound fields, such as `MailingCity`.
|
|
84
|
+
- Add `--use-tooling-api` for `CustomField` and `ValidationRule`.
|
|
85
|
+
- `EntityDefinition`, `EntityParticle`, and `FieldDefinition` do not accept `OR`. Run one scoped query per condition instead of broadening the query or removing its object/type filters.
|
|
86
|
+
|
|
87
|
+
More patterns, including access and automation, are in `references/scoped-soql.md`.
|
|
88
|
+
|
|
89
|
+
## When Grounding Is Not Available
|
|
90
|
+
|
|
91
|
+
Say the line below once per session, in your answer, next to the results of the name and label search. Do not announce the fallback before you search, and do not repeat it later in the session. Use the line that matches the cause, word for word, including the last sentence. Do not shorten or rephrase it. If you stop before you can report results, say it in that message instead.
|
|
92
|
+
|
|
93
|
+
| Cause | Say |
|
|
94
|
+
|---|---|
|
|
95
|
+
| No grounding server connected | "Metadata Grounding isn't connected, so I matched by name and label, which can miss metadata named differently. Connect the Metadata Grounding MCP to search by meaning." |
|
|
96
|
+
| Catalog not enabled | "Salesforce Catalog isn't turned on in this org, so I matched by name and label, which can miss metadata named differently. An admin can turn on Salesforce Catalog through Feature Manager in Data Cloud Setup." |
|
|
97
|
+
| Permission missing | "Catalog search needs the Customize Application permission, so I matched by name and label, which can miss metadata named differently." |
|
|
98
|
+
|
|
99
|
+
Then search names and labels. Try two or three likely words, one `LIKE` per query.
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
sf data query --json --query "SELECT QualifiedApiName, Label FROM EntityDefinition WHERE Label LIKE '%loyalty%'"
|
|
103
|
+
sf data query --json --query "SELECT QualifiedApiName, Label, DataType FROM EntityParticle WHERE EntityDefinition.QualifiedApiName = 'Contact' AND Label LIKE '%tier%'"
|
|
104
|
+
sf data query --json --use-tooling-api --query "SELECT DeveloperName, EntityDefinition.QualifiedApiName FROM CustomField WHERE DeveloperName LIKE '%tier%' AND NamespacePrefix = null"
|
|
105
|
+
sf data query --json --query "SELECT ApiName, Label, ProcessType, IsActive FROM FlowDefinitionView WHERE Label LIKE '%order%'"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- The `CustomField` query searches custom fields on every object. `DeveloperName` has no `__c`. Drop rows with no object name. They are Data Cloud fields.
|
|
109
|
+
- For other types, and for types the catalog does not cover, list names with `sf org list metadata` and scan them.
|
|
110
|
+
- Name matching misses metadata labeled differently. A search for "status" will not find a field labeled Loyalty Tier. Say "no name match found," never "does not exist."
|
|
111
|
+
|
|
112
|
+
Everything else in this skill still applies.
|
|
113
|
+
|
|
114
|
+
## Reuse Before You Create
|
|
115
|
+
|
|
116
|
+
Before you create an object, field, picklist, permission set, page, app, or flow:
|
|
117
|
+
|
|
118
|
+
1. Search by business meaning for existing metadata that stores the same data or does the same job. For a field, first search `SObject` for the exact object API name, then pass that result's returned `identifier` as `parentIdentifier` to the `SObjectField` search. Never pass the raw object API name as `parentIdentifier`. A proposed API name or label is only an exact collision check and does not prove that equivalent metadata is absent. Similar words alone are not enough to declare a match.
|
|
119
|
+
2. **If you find matches:** show up to 3, best first, with API name, label, description, and type. Do not accept or reject an ambiguous match from its name alone. Offer to reuse, extend, or create new in one consolidated reuse checkpoint covering every type you plan to create, then wait. Do not combine this checkpoint with values or other generation-specific questions.
|
|
120
|
+
3. **After the user decides:** preserve the decision for the request and do not ask again unless the scope changes or new conflicting evidence appears. For reuse, stop creation and inspect configuration only if needed. For extension, inspect the existing component before asking for changes. For new metadata, collect the required configuration and continue with the generation guidance.
|
|
121
|
+
4. **If nothing matches:** say so in one line and continue.
|
|
122
|
+
5. **If the user gave an exact API name:** check it with scoped SOQL and say if it already exists.
|
|
123
|
+
6. **If the user asked for new metadata or said not to reuse:** mention close matches in one line at most and continue.
|
|
124
|
+
|
|
125
|
+
Treat namespaced items, such as `force__` permission sets or managed package components, as references, not things to edit. Say so when a match is managed.
|
|
126
|
+
|
|
127
|
+
## Ground Code in Real Org Names
|
|
128
|
+
|
|
129
|
+
Before you write Apex, LWC, SOQL, or Flow logic, confirm every object, field, and picklist value it references.
|
|
130
|
+
|
|
131
|
+
- Skip references already in the local project, already confirmed in this conversation, or standard fields every org has (such as `Account.Name` or `Contact.Email`).
|
|
132
|
+
- Always look up picklist values, including on standard fields. Never invent values.
|
|
133
|
+
- Values shown only as examples in a skill, tool description, or prior assistant response are not user-approved values. If the user has not explicitly supplied or approved the values or an existing value set, ask once and wait before creating the picklist.
|
|
134
|
+
- If the request depends on an order or meaning the values do not state ("the top tier"), list the values and ask.
|
|
135
|
+
- Look up all references for a request together.
|
|
136
|
+
|
|
137
|
+
## Existing Automation
|
|
138
|
+
|
|
139
|
+
- **To find automation by what it does,** search `Flow` or `ApexClass` by meaning. A grounding query on a flow shows its trigger object, trigger type, and active status.
|
|
140
|
+
- **To list everything that runs on an object** before adding automation:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
sf data query --json --query "SELECT Name, Status FROM ApexTrigger WHERE TableEnumOrId = 'Contact'"
|
|
144
|
+
sf data query --json --query "SELECT ApiName, Label, TriggerType, IsActive FROM FlowDefinitionView WHERE TriggerObjectOrEventId = 'Contact'"
|
|
145
|
+
```
|
|
146
|
+
- **To understand what a flow or trigger does,** retrieve its source and read it.
|
|
147
|
+
|
|
148
|
+
## What You Can Explain
|
|
149
|
+
|
|
150
|
+
| Question | Answer with |
|
|
151
|
+
|---|---|
|
|
152
|
+
| What exists that does X | Grounding search |
|
|
153
|
+
| How a field, object, or validation rule is configured | Grounding query, or `EntityParticle` and Tooling `ValidationRule` |
|
|
154
|
+
| Who can read or edit a field or object | Grounding on `FieldPermissions`, or SOQL on `FieldPermissions` and `ObjectPermissions` for a complete list. These records prove configured access, not actual use; a permission container's name does not prove an integration or dependency is active. |
|
|
155
|
+
| Sharing rules, roles, groups, and which package a component came from | Grounding |
|
|
156
|
+
| What a flow is | Grounding gives the header only (type, trigger, active). For its steps, retrieve the source. |
|
|
157
|
+
| What an Apex class does | Grounding gives signatures only. Retrieve the source. |
|
|
158
|
+
| Why a save failed | A partial answer. Check validation rules on the object, then retrieve the automation that runs on it. |
|
|
159
|
+
| Page layouts, reports, custom metadata types, custom settings | Not in the catalog. Use `sf org list metadata` and retrieve. |
|
|
160
|
+
| Where something is used, what depends on it, what breaks if it changes, or whether it is safe to delete | Comprehensive analysis is not available yet. Say so before any checks. You may offer narrow checks, but label each one as partial and never present their union as complete or conclude that the change is safe. |
|
|
161
|
+
|
|
162
|
+
## Working With Skills
|
|
163
|
+
|
|
164
|
+
- This skill decides what already exists. Build skills decide how to create it. Run discovery before any skill writes files.
|
|
165
|
+
- If a loaded skill has its own equivalent semantic reuse check, use it instead of the matching step here. Do not run both. An exact-name or exact-label collision check is not equivalent and does not replace semantic reuse discovery.
|
|
166
|
+
- Two exceptions, even when a skill says otherwise: always look up picklist values, and check names, types, or picklist values you know with scoped SOQL instead of a full object describe.
|
|
167
|
+
- If a skill's discovery step fails or its tool is not available, use the routes in this skill and tell the user once.
|
|
168
|
+
|
|
169
|
+
## Freshness and Local Files
|
|
170
|
+
|
|
171
|
+
- Reuse discovery results for the rest of the conversation unless that item has changed.
|
|
172
|
+
- The catalog can take several minutes to reflect deploys and other org changes. For anything changed in this session, trust the local source and deploy results, and confirm with SOQL, which is real time. If the user reports a change SOQL does not show, ask them to confirm the name and org before you write code that uses it.
|
|
173
|
+
- When the metadata you need is not in the local project, retrieve it before editing, and tell the user you did.
|
|
174
|
+
- If you learn the org version differs from local in ways you did not make, for example from a retrieve or a deploy conflict, show the difference and ask before you overwrite either one.
|
|
175
|
+
- When scanning local source for references, read the package directories from `sfdx-project.json` and search those directories. Exclude response exports, logs, generated output, caches, and agent configuration unless the user explicitly asks to inspect them.
|
|
176
|
+
- Searching local files finds exact text only. Do not use it to find org metadata by meaning.
|
|
177
|
+
|
|
178
|
+
## Never
|
|
179
|
+
|
|
180
|
+
- Write or deploy files built from grounding query output. It returns properties, not source.
|
|
181
|
+
- Treat grounding results as a complete list. They are ranked and capped.
|
|
182
|
+
- Say metadata does not exist because a search or a name match found nothing.
|
|
183
|
+
- Combine results from two different orgs.
|
|
184
|
+
- Present partial dependency or reference checks as a complete impact analysis, or conclude from them that metadata is safe to change or delete.
|
|
185
|
+
- Treat illustrative values from examples, skills, or tool descriptions as user-approved metadata.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# What Each Route Calls
|
|
2
|
+
|
|
3
|
+
For engineers and reviewers. The skill names tools, not APIs, because the agent picks tools. Read from sf CLI 2.150.6 and jsforce source, and checked live on 2026-10-07.
|
|
4
|
+
|
|
5
|
+
| Route | API call |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `sf sobject list` | REST `describeGlobal` (`GET /services/data/vXX.0/sobjects`), filtered to custom or all on the client |
|
|
8
|
+
| `sf sobject describe` | REST describe SObject (`GET /services/data/vXX.0/sobjects/<Type>/describe`). Tooling describe with `--use-tooling-api` |
|
|
9
|
+
| `sf org list metadata` | Metadata API `listMetadata` (SOAP) |
|
|
10
|
+
| `sf data query` | REST query resource (`GET /services/data/vXX.0/query`). Tooling query resource with `--use-tooling-api` |
|
|
11
|
+
| `sf project retrieve start` | Metadata API `retrieve` (SOAP) |
|
|
12
|
+
| Metadata grounding search | `GET /services/data/vXX.0/metadata-grounding/search` |
|
|
13
|
+
| Metadata grounding describe | `POST /services/data/vXX.0/metadata-grounding/schema` |
|
|
14
|
+
| Metadata grounding query | `POST /services/data/vXX.0/metadata-grounding/query` |
|
|
15
|
+
|
|
16
|
+
## Which Surface Reaches Which Call
|
|
17
|
+
|
|
18
|
+
| Call | `sf` CLI | Metadata Grounding MCP | Salesforce MCP |
|
|
19
|
+
|---|---|---|---|
|
|
20
|
+
| REST and Tooling query | Yes | No | Yes, `dispatch_readonly` |
|
|
21
|
+
| REST describe and object list | Yes | No | Yes, `dispatch_readonly` |
|
|
22
|
+
| Metadata API `listMetadata` and `retrieve` | Yes | No | No |
|
|
23
|
+
| Metadata grounding | No. The grounding paths are reachable only through an MCP server, so a direct REST call returns not found | Yes | Yes, after `describe` on the operation id |
|
|
24
|
+
|
|
25
|
+
The skills do not use the Enterprise API, the SOAP API with the org-specific WSDL. None of these tools call it.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Routes Through the Salesforce MCP
|
|
2
|
+
|
|
3
|
+
Use this file when you reach the org through the Salesforce MCP. If you are in a terminal or IDE and can run the `sf` CLI, the CLI routes work as well.
|
|
4
|
+
|
|
5
|
+
Grounding itself is covered in `platform-metadata-grounding-get`. This file covers the routes the skill body sends to the CLI.
|
|
6
|
+
|
|
7
|
+
## How to Call
|
|
8
|
+
|
|
9
|
+
Use `dispatch_readonly` with `method: GET`. Pass the path as `url` and the SOQL as the `q` query parameter, not appended to the URL.
|
|
10
|
+
|
|
11
|
+
| Route | `url` | Query parameters |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Standard SOQL | `/services/data/v66.0/query` | `q`: the SOQL |
|
|
14
|
+
| Tooling SOQL | `/services/data/v66.0/tooling/query` | `q`: the SOQL |
|
|
15
|
+
| Describe one object | `/services/data/v66.0/sobjects/<Type>/describe` | None |
|
|
16
|
+
|
|
17
|
+
The rows are in `body.records`. For a `COUNT()` query the number is in `body.totalSize` and `records` is empty. Every pattern in `scoped-soql.md` works through these paths.
|
|
18
|
+
|
|
19
|
+
## Route by Intent
|
|
20
|
+
|
|
21
|
+
| Intent | Through this server |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Check names, types, or picklist values you know. Every field on one object. Data records | Standard SOQL. Use Tooling SOQL for `CustomField` and `ValidationRule`. |
|
|
24
|
+
| Full shape of one object, only when you need all of it | Describe one object. It is very large. |
|
|
25
|
+
| Every object in the org | Standard SOQL on `EntityDefinition`, for example `SELECT QualifiedApiName, Label FROM EntityDefinition WHERE QualifiedApiName LIKE '%__c'` for custom objects. Never call `/sobjects` for the full object list. It returned about 1.8 million characters in one test org. |
|
|
26
|
+
| Every member of a type, or a count | Tooling SOQL for Tooling types, for example `SELECT COUNT() FROM FlowDefinition`, or `ApexClass` and `ValidationRule`. To list flows with labels, use Standard SOQL on `FlowDefinitionView`. Other types cannot be listed through this server. Tell the user it needs the `sf` CLI in a terminal or IDE. |
|
|
27
|
+
| Source to edit or deploy | Not available through this server. Load `platform-metadata-retrieve` if it is available. Otherwise tell the user it needs the `sf` CLI in a terminal or IDE. |
|
|
28
|
+
| Compare orgs, find drift, or a whole-org overview | Not available through this server. Tell the user it needs the `sf` CLI. |
|
|
29
|
+
|
|
30
|
+
If a call fails with an authentication or session error, tell the user to reconnect the Salesforce MCP. If it fails as not found or not allowed, say so, and tell the user the lookup may need the `sf` CLI in a terminal or IDE.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Scoped SOQL Patterns
|
|
2
|
+
|
|
3
|
+
Run each query through the Salesforce MCP with `dispatch_readonly`, as Standard or Tooling SOQL per the table. The paths are in `ref:platform-metadata-discovery-get/references/h360-routes`. In a terminal or IDE, `sf data query --json --query "<SOQL>"` works too. Add `--use-tooling-api` where the table says Tooling, and `--target-org <alias>` when the user named an org other than the default.
|
|
4
|
+
|
|
5
|
+
## Objects and Fields
|
|
6
|
+
|
|
7
|
+
| Need | Query | API |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Does this object exist | `SELECT QualifiedApiName, Label FROM EntityDefinition WHERE QualifiedApiName = 'Loyalty_Transaction__c'` | Standard |
|
|
10
|
+
| Several objects at once | `SELECT QualifiedApiName, Label FROM EntityDefinition WHERE QualifiedApiName IN ('Menu__c', 'Promotion__c')` | Standard |
|
|
11
|
+
| Every field on one object | `SELECT QualifiedApiName, Label, DataType FROM EntityParticle WHERE EntityDefinition.QualifiedApiName = 'Contact'` | Standard |
|
|
12
|
+
| Custom fields only | Add `AND QualifiedApiName LIKE '%__c'` to the query above | Standard |
|
|
13
|
+
| Check specific fields | Add `AND QualifiedApiName IN ('Loyalty_Tier__c', 'Email')` | Standard |
|
|
14
|
+
| Picklist values | `SELECT Value, Label, IsDefaultValue, IsActive FROM PicklistValueInfo WHERE EntityParticle.EntityDefinition.QualifiedApiName = 'Contact' AND EntityParticle.QualifiedApiName = 'Loyalty_Tier__c'` | Standard |
|
|
15
|
+
| Record types on an object | `SELECT Name, DeveloperName, IsActive FROM RecordType WHERE SobjectType = 'Contact'` | Standard |
|
|
16
|
+
|
|
17
|
+
`EntityParticle` includes parts of compound fields, such as `MailingCity`. A full field list for Contact is about a tenth the size of a describe.
|
|
18
|
+
|
|
19
|
+
## Name and Label Fallback
|
|
20
|
+
|
|
21
|
+
Use these only when grounding is not available. Try two or three likely words.
|
|
22
|
+
|
|
23
|
+
| Need | Query | API |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| Objects by label | `SELECT QualifiedApiName, Label FROM EntityDefinition WHERE Label LIKE '%loyalty%'` | Standard |
|
|
26
|
+
| Fields on one object by label | `SELECT QualifiedApiName, Label, DataType FROM EntityParticle WHERE EntityDefinition.QualifiedApiName = 'Contact' AND Label LIKE '%tier%'` | Standard |
|
|
27
|
+
| Custom fields on every object by name | `SELECT DeveloperName, EntityDefinition.QualifiedApiName FROM CustomField WHERE DeveloperName LIKE '%tier%' AND NamespacePrefix = null` | Tooling |
|
|
28
|
+
| Flows by label | `SELECT ApiName, Label, ProcessType, IsActive FROM FlowDefinitionView WHERE Label LIKE '%order%'` | Standard |
|
|
29
|
+
| Permission sets by label | `SELECT Name, Label FROM PermissionSet WHERE IsOwnedByProfile = false AND Label LIKE '%loyalty%'` | Standard |
|
|
30
|
+
|
|
31
|
+
- `EntityDefinition`, `EntityParticle`, and `FieldDefinition` do not accept `OR`. The query fails with "Disjunctions not supported." Run one query per word.
|
|
32
|
+
- `CustomField` accepts `OR`. `DeveloperName` has no `__c`. Drop rows with no object name. They are Data Cloud fields.
|
|
33
|
+
|
|
34
|
+
## Access
|
|
35
|
+
|
|
36
|
+
| Need | Query | API |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| Who can read or edit a field | `SELECT Parent.Label, Parent.IsOwnedByProfile, Parent.Profile.Name, PermissionsRead, PermissionsEdit FROM FieldPermissions WHERE SobjectType = 'Contact' AND Field = 'Contact.Loyalty_Tier__c'` | Standard |
|
|
39
|
+
| Who can use an object | `SELECT Parent.Label, Parent.IsOwnedByProfile, Parent.Profile.Name, PermissionsRead, PermissionsCreate, PermissionsEdit, PermissionsDelete FROM ObjectPermissions WHERE SobjectType = 'Loyalty_Transaction__c'` | Standard |
|
|
40
|
+
|
|
41
|
+
When `Parent.IsOwnedByProfile` is true, the row belongs to a profile. Read the name from `Parent.Profile.Name`, because `Parent.Label` is an id for those rows. A field or object with no row for a profile or permission set has no access through it.
|
|
42
|
+
|
|
43
|
+
## Automation and Rules
|
|
44
|
+
|
|
45
|
+
| Need | Query | API |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| Triggers on an object | `SELECT Name, Status FROM ApexTrigger WHERE TableEnumOrId = 'Contact'` | Standard |
|
|
48
|
+
| Flows that run on an object | `SELECT ApiName, Label, TriggerType, IsActive FROM FlowDefinitionView WHERE TriggerObjectOrEventId = 'Contact'` | Standard |
|
|
49
|
+
| Validation rules on an object | `SELECT ValidationName, Active, ErrorMessage FROM ValidationRule WHERE EntityDefinition.QualifiedApiName = 'Contact'` | Tooling |
|
|
50
|
+
| Count of flows | `SELECT COUNT() FROM FlowDefinition` | Tooling |
|
|
51
|
+
|
|
52
|
+
These return names and status, not logic. To read what a flow, trigger, or rule does, retrieve its source.
|