salesforce-metadata-mcp 2.8.0 → 2.11.1
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/CHANGELOG.md +673 -0
- package/README.md +5 -3
- package/TOOLS.md +2 -2
- package/dist/index.js +19 -5
- package/dist/index.js.map +1 -1
- package/dist/schemas/index.d.ts +222 -69
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +93 -18
- package/dist/schemas/index.js.map +1 -1
- package/dist/services/deployment.d.ts +49 -1
- package/dist/services/deployment.d.ts.map +1 -1
- package/dist/services/deployment.js +108 -8
- package/dist/services/deployment.js.map +1 -1
- package/dist/services/mcpgen.d.ts.map +1 -1
- package/dist/services/mcpgen.js +14 -4
- package/dist/services/mcpgen.js.map +1 -1
- package/dist/services/salesforce.d.ts +80 -13
- package/dist/services/salesforce.d.ts.map +1 -1
- package/dist/services/salesforce.js +832 -265
- package/dist/services/salesforce.js.map +1 -1
- package/dist/tools/agentforce.d.ts.map +1 -1
- package/dist/tools/agentforce.js +187 -44
- package/dist/tools/agentforce.js.map +1 -1
- package/dist/tools/automation.js +6 -6
- package/dist/tools/automation.js.map +1 -1
- package/dist/tools/data.d.ts.map +1 -1
- package/dist/tools/data.js +3 -1
- package/dist/tools/data.js.map +1 -1
- package/dist/tools/deployment.d.ts.map +1 -1
- package/dist/tools/deployment.js +29 -5
- package/dist/tools/deployment.js.map +1 -1
- package/dist/tools/flows.d.ts.map +1 -1
- package/dist/tools/flows.js +7 -3
- package/dist/tools/flows.js.map +1 -1
- package/dist/tools/integrations.d.ts.map +1 -1
- package/dist/tools/integrations.js +27 -2
- package/dist/tools/integrations.js.map +1 -1
- package/dist/tools/metadata.d.ts.map +1 -1
- package/dist/tools/metadata.js +13 -1
- package/dist/tools/metadata.js.map +1 -1
- package/dist/tools/objects.js +1 -1
- package/dist/tools/objects.js.map +1 -1
- package/dist/tools/ui.d.ts.map +1 -1
- package/dist/tools/ui.js +8 -8
- package/dist/tools/ui.js.map +1 -1
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,678 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.11.1] - 2026-08-03
|
|
4
|
+
|
|
5
|
+
### Fixed — `sf_retrieve_metadata` reported success on zero-result retrieves, plus two dead parameters found while investigating
|
|
6
|
+
|
|
7
|
+
Ajay's follow-up report confirmed v2.11.0's `sf_create_agent_action`/`sf_describe_object` fixes were
|
|
8
|
+
correct — his contradicting evidence turned out to be tested against a stale Claude Desktop MCP
|
|
9
|
+
subprocess predating the rebuild (a recurring gotcha this project has hit before: a rebuilt `dist/`
|
|
10
|
+
only takes effect after a full restart). His genuinely new finding, independent of that mix-up, was
|
|
11
|
+
real: `sf_retrieve_metadata` returned `success: true` and `"Retrieved 1 file(s)."` for a retrieve that
|
|
12
|
+
found **zero** of the requested components — because Salesforce's retrieve zip always includes
|
|
13
|
+
`package.xml` as its own manifest even when every requested member comes back empty, and the file count
|
|
14
|
+
included it. Verified with his exact repro (4 nonexistent `GenAiFunction` names) against a control
|
|
15
|
+
retrieve of a real component.
|
|
16
|
+
|
|
17
|
+
Fixed in `retrieveMetadataAndWait`: the reported count now excludes `package.xml`, and when components
|
|
18
|
+
were requested by name but none came back, the result is `success: false` with an explicit message
|
|
19
|
+
listing what was requested — instead of a misleadingly cheerful count of a manifest nobody asked for.
|
|
20
|
+
|
|
21
|
+
**Investigating this surfaced two more, more severe versions of the same underlying problem**, both
|
|
22
|
+
previously untested: the tool's schema has advertised `metadataType`+`componentName` (single-component
|
|
23
|
+
shortcut) and `packageXml` (raw manifest) as alternatives to `components` since this tool existed, but
|
|
24
|
+
the handler only ever read `params.components` — the other two were silently no-ops. A call using
|
|
25
|
+
either form retrieved **nothing at all** (an empty `<unpackaged>` body) while still reporting a
|
|
26
|
+
"successful" retrieve of the manifest-only zip, even when the requested component genuinely existed.
|
|
27
|
+
Wired up both: `metadataType`/`componentName` now builds a single-item `components` array, and
|
|
28
|
+
`packageXml` now extracts the `<types>`/`<version>` content from the supplied document and uses it
|
|
29
|
+
directly as the retrieve request body (needs the `met:` namespace prefix added to every element, since
|
|
30
|
+
the SOAP body has no default namespace, unlike a standalone package.xml). Verified live for all three
|
|
31
|
+
paths: the shortcut form now retrieves a real flow's XML, the zero-match array form now fails honestly,
|
|
32
|
+
and a hand-written raw `packageXml` retrieve returns real content.
|
|
33
|
+
|
|
34
|
+
No regressions: `test-suite.mjs` 209/212 (2 pre-existing unrelated failures, same as before).
|
|
35
|
+
|
|
36
|
+
## [2.11.0] - 2026-08-03
|
|
37
|
+
|
|
38
|
+
### Fixed — `sf_create_agent_action` was fundamentally broken for Flow/ApexClass-backed actions; the misdiagnosis pointed users at their org's licensing instead of the real bug
|
|
39
|
+
|
|
40
|
+
Ajay reported that `sf_create_agent_action` failed for `type=Flow` even against a confirmed-Active
|
|
41
|
+
flow, and that the failure message wrongly concluded custom agent actions were unsupported in the org
|
|
42
|
+
("check your Agentforce/Einstein licensing") — disprovable because he had just created five
|
|
43
|
+
Flow-backed actions successfully through Salesforce's own Agentforce Builder UI, in the same org, same
|
|
44
|
+
user, same flows.
|
|
45
|
+
|
|
46
|
+
**Root cause, found by following Ajay's own recommended debugging path (diff a UI-created action's
|
|
47
|
+
real XML against what the tool generates) and going further once retrieval came back empty:**
|
|
48
|
+
`sf_create_agent_action` built a classic Metadata API `.genAiFunction` deploy with
|
|
49
|
+
`<invocationTarget>` set to the flow/class **API name** — but UI-created actions, read back via the
|
|
50
|
+
Tooling API's `GenAiFunctionDefinition` object (the classic Metadata API's `GenAiFunction` type turned
|
|
51
|
+
out not to expose these records at all — confirmed by a wildcard retrieve finding zero results even
|
|
52
|
+
for the five UI-created ones), showed `InvocationTarget` holding an 18-character **record ID**
|
|
53
|
+
(`FlowDefinition.Id` / `ApexClass.Id`), not a name. The generic "Specify a valid invocationTarget and
|
|
54
|
+
invocationTargetType" error — previously treated as a reliable org-capability signal after testing in
|
|
55
|
+
an unrelated org — turned out to be Salesforce's error for *this exact payload bug* in an org that
|
|
56
|
+
supports the feature just fine.
|
|
57
|
+
|
|
58
|
+
Fixed by switching `sf_create_agent_action` from the classic Metadata API deploy to a Tooling API
|
|
59
|
+
`GenAiFunctionDefinition` upsert — the same mechanism Agentforce Builder itself uses — resolving
|
|
60
|
+
`reference` to the right record ID first (`FlowDefinition.DeveloperName` → `Id` for Flow,
|
|
61
|
+
`ApexClass.Name` → `Id` for ApexClass). Verified live end-to-end, not just unit-tested: created a real
|
|
62
|
+
action against Ajay's own reported flow through the actual registered tool handler (reproducing his
|
|
63
|
+
repro exactly), confirmed the fix also works for ApexClass, and confirmed a Tooling-API-created action
|
|
64
|
+
is correctly picked up by a topic (`GenAiPlugin`) deployed the normal Metadata-API way — the two APIs
|
|
65
|
+
share the same underlying records, so mixing them across the 5-step agent sequence is safe.
|
|
66
|
+
`PromptTemplate`/`DataCategoryGroup`/`ExternalService` are now honestly refused (clear "not yet
|
|
67
|
+
verified" message) rather than silently deploying an unverified, likely-broken payload — matching this
|
|
68
|
+
project's standing rule not to ship unverified metadata behavior.
|
|
69
|
+
|
|
70
|
+
**`sf_create_agent`'s pre-flight capability probe (added in v2.10.0)** used the same broken deploy
|
|
71
|
+
path internally, so it always failed and hard-blocked agent creation even in orgs — like this one —
|
|
72
|
+
where it works fine. Rewritten to match: it now attempts a real Tooling API insert with a
|
|
73
|
+
syntactically-valid-but-nonexistent target ID. This fails fast on a specific field-validation error in
|
|
74
|
+
any org where the object exists (verified live: `INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST`), and since
|
|
75
|
+
nothing is ever actually written, there's no cleanup step either — simpler than the old probe, which
|
|
76
|
+
had to deploy-then-delete a throwaway. Verified live: `sf_create_agent` now succeeds normally in this
|
|
77
|
+
org, both with and without `skipActionCapabilityCheck`.
|
|
78
|
+
|
|
79
|
+
Also fixed: `sf_describe_object` returned a bare `NOT_FOUND` 404 for standard objects whose *feature*
|
|
80
|
+
is disabled (Ajay's repro: `Quote`, when the Quotes feature is off in Setup) — indistinguishable from a
|
|
81
|
+
typo'd or nonexistent object name. Added a hint for a hardcoded list of commonly feature-gated standard
|
|
82
|
+
objects (Quote, Contract, Order, Campaign, Territory2, WorkOrder, ServiceAppointment, etc.) pointing at
|
|
83
|
+
Setup instead of sending the user hunting for a naming mistake that isn't there.
|
|
84
|
+
|
|
85
|
+
**`qa-agentforce.mjs` needed a substantial rewrite, not just the tool code.** The suite's own
|
|
86
|
+
assertions were written under the same now-disproven assumption ("demo-org cannot create GenAiFunction
|
|
87
|
+
actions") baked into skip logic throughout — so the action/topic verification paths had never actually
|
|
88
|
+
executed in any prior session, and once they finally ran (because the underlying bug they were gated
|
|
89
|
+
behind is now fixed), they surfaced a second, independent, pre-existing gap: `existsInOrg`'s plain REST
|
|
90
|
+
`queryRecords` can't see `GenAiFunction`/`GenAiFunctionDefinition` (Tooling-API-only, confirmed) or
|
|
91
|
+
`GenAiPlugin` (no SOQL interface at all, confirmed both plain REST and Tooling — only readable via
|
|
92
|
+
classic Metadata API `readMetadata`) — it happened to work before only because those checks were never
|
|
93
|
+
reached. Rewrote `existsInOrg` to dispatch to the right API per type, and rewrote the action-type
|
|
94
|
+
matrix check to read `GenAiFunctionDefinition.InvocationTargetType` back via Tooling API instead of a
|
|
95
|
+
`retrieveMetadata` call that (per the finding above) was always going to come back empty. Suite went
|
|
96
|
+
from 33 passed/15 failed (pre-fix) to 35 passed/2 failed against a live org.
|
|
97
|
+
|
|
98
|
+
**Known open item, not a code defect:** after extensive live testing today (mine plus Ajay's own
|
|
99
|
+
UI-based testing), further `GenAiFunctionDefinition` inserts in `demo-org` started failing with
|
|
100
|
+
`DUPLICATE_DEVELOPER_NAME` ("...already exists or has been previously used") even against
|
|
101
|
+
brand-new, never-used developer names and master labels — isolated by testing fresh-name/reused-label
|
|
102
|
+
and fresh-everything/reused-target combinations independently; neither explains it, and the condition
|
|
103
|
+
persisted for several minutes and across unrelated work. Most likely an org-side rate limit or cooldown
|
|
104
|
+
on this object triggered by the volume of creates/deletes during today's testing, not a regression in
|
|
105
|
+
this fix — the two remaining `qa-agentforce.mjs` failures are this condition surfacing honestly through
|
|
106
|
+
the tool's real error message, not a masked defect. Re-run the suite after some time has passed to
|
|
107
|
+
confirm it clears; if it doesn't, this needs further investigation as a genuine platform limit.
|
|
108
|
+
|
|
109
|
+
## [2.10.0] - 2026-08-01
|
|
110
|
+
|
|
111
|
+
### Fixed/Added — 4 more findings from an extended manual test session (Bugs 6-8, and a sharper Bug 3), plus 2 new tools
|
|
112
|
+
|
|
113
|
+
Ajay's follow-up report superseded the previous one. Bugs 1, 2, 4, 5 from that report were already
|
|
114
|
+
fixed in v2.9.0 (confirmed still present in this session, not re-fixed) — the report describing them
|
|
115
|
+
as unfixed was written from a Claude Desktop session whose MCP server subprocess had been running
|
|
116
|
+
since before v2.9.0 landed; restarting Claude Desktop picks up a rebuilt `dist/`, an already-spawned
|
|
117
|
+
subprocess does not. New findings below.
|
|
118
|
+
|
|
119
|
+
**Bug 3, revisited with much stronger evidence.** The previous release's conclusion (org-side schema
|
|
120
|
+
propagation lag) held, but Ajay's new evidence sharpened *what kind* of lag it is: persisted 2+ hours
|
|
121
|
+
in his session (ruling out ordinary propagation delay), and — critically — REST describe worked fine
|
|
122
|
+
on **pre-existing** custom fields on standard objects (Opportunity's own custom fields) while failing
|
|
123
|
+
on everything newly created, which looked like it might be specific to brand-new custom *objects*
|
|
124
|
+
rather than fields in general. Tested that distinction directly and decisively: created a field on the
|
|
125
|
+
standard `Account` object and a field on a brand-new custom object side by side, then polled both for
|
|
126
|
+
2 full minutes. **Both were equally stuck** — this rules out "new object vs. existing object" as the
|
|
127
|
+
differentiator. The real explanation: Ajay's "working" standard-object fields were older, already-
|
|
128
|
+
propagated fields from earlier sessions, not freshly created ones — not evidence that new fields on
|
|
129
|
+
standard objects propagate faster. Also directly tested Ajay's identity-mismatch hypothesis (REST and
|
|
130
|
+
Metadata clients authenticating as different users): confirmed live that both use the exact same
|
|
131
|
+
`auth.accessToken`, resolving to the same user (`semwalajaydevorg@agentforce.com`) and same org ID —
|
|
132
|
+
ruled out. Revised, more precise conclusion: this org has a severe (multi-hour-observed), general
|
|
133
|
+
schema-cache propagation lag affecting all newly created fields, regardless of parent object type —
|
|
134
|
+
still not something any client-side code change can fix.
|
|
135
|
+
|
|
136
|
+
**Bug 6: `sf_create_agent` now probes GenAiFunction (custom action) support before creating a Bot
|
|
137
|
+
shell.** Reproduced Ajay's exact finding: active Agentforce permission set licenses are not sufficient
|
|
138
|
+
evidence that custom actions work — confirmed by deploying a `GenAiFunction` aimed at a deliberately
|
|
139
|
+
nonexistent target and getting the identical generic error a real target would get, meaning the org
|
|
140
|
+
never even attempts target resolution. Added a pre-flight probe (a real, throwaway `GenAiFunction`
|
|
141
|
+
deploy, cleaned up automatically) that runs before the Bot shell is created on the first call in the
|
|
142
|
+
5-step sequence: if actions aren't supported, the call fails immediately with no shell created, instead
|
|
143
|
+
of leaving an orphaned Bot with no planner/topic/action once step 2 turns out to be unreachable. Added
|
|
144
|
+
`skipActionCapabilityCheck` for topics-only agents that don't need custom actions. Verified live both
|
|
145
|
+
ways: the probe correctly blocks with no shell created, and the skip flag correctly bypasses it.
|
|
146
|
+
|
|
147
|
+
**Bug 7: `sf_create_agent_topic` now validates that every referenced action exists before deploying.**
|
|
148
|
+
Previously, a topic referencing a missing action deployed and failed with an opaque Salesforce support
|
|
149
|
+
ErrorId naming nothing. `GenAiFunction` isn't SOQL/Tooling-queryable in every org (confirmed: it isn't
|
|
150
|
+
in `demo-org`), so existence is checked via Metadata API `readMetadata` instead, which works regardless
|
|
151
|
+
of SOQL support for the type. Verified live: a topic referencing a nonexistent action is now rejected
|
|
152
|
+
before ever reaching Salesforce, naming the specific missing action.
|
|
153
|
+
|
|
154
|
+
**Bug 8: added `sf_delete_metadata`.** There was previously no way to remove anything deployed by this
|
|
155
|
+
MCP server — `sf_deploy_metadata` has no `destructiveChanges` support, and diagnostic/orphaned metadata
|
|
156
|
+
had nowhere to go (confirmed: Ajay's stranded `AccountOpportunityAgent` Bot shell was still in the org,
|
|
157
|
+
alongside 13 similar leftovers from earlier sessions). Wraps the `deleteMetadata` SOAP call, which
|
|
158
|
+
already existed as an internal service function but was never exposed as a tool. Verified live with a
|
|
159
|
+
real round-trip (create → delete → confirm gone) and by actually attempting cleanup of the accumulated
|
|
160
|
+
org backlog: correctly surfaced Salesforce's own 10-record-per-call limit (batched around it), and
|
|
161
|
+
correctly surfaced a real dependency-order error when a `GenAiPlannerBundle` was still referenced by a
|
|
162
|
+
Bot. The specific stuck Bot records from earlier sessions remain undeletable — a pre-existing, already-
|
|
163
|
+
documented Salesforce-side "unexpected error" on those particular records, not something this tool or
|
|
164
|
+
any client-side retry can work around.
|
|
165
|
+
|
|
166
|
+
**Documented, not a bug**: Flow `textTemplates` strip leading/trailing whitespace on deploy while
|
|
167
|
+
preserving internal newlines — concatenating per-iteration templates in a loop without an internal
|
|
168
|
+
separator runs lines together. Added to the `sf_create_flow` schema description.
|
|
169
|
+
|
|
170
|
+
**Confirmed already fixed, not re-touched**: Bugs 1 (duplicate Decision rule names), 2 (filter value
|
|
171
|
+
typing), 4 (FLS warning), 5 (raw-XML pre-validation) — all still present and correct in this codebase,
|
|
172
|
+
verified by direct inspection before assuming anything needed re-fixing.
|
|
173
|
+
|
|
174
|
+
Tool count 222→223 (`sf_delete_metadata`). Regression: `qa-agentforce.mjs` gained 5 new checks for
|
|
175
|
+
Bugs 6/7 (all existing `sf_create_agent` calls updated with `skipActionCapabilityCheck` so the new
|
|
176
|
+
probe doesn't change what those pre-existing tests were actually testing); `test-suite.mjs` gained a
|
|
177
|
+
real create→delete→confirm-gone round-trip test for `sf_delete_metadata`.
|
|
178
|
+
|
|
179
|
+
## [2.9.0] - 2026-08-01
|
|
180
|
+
|
|
181
|
+
### Fixed — 5 real bugs from a genuine manual test session in Claude Desktop, all reproduced and re-verified live
|
|
182
|
+
|
|
183
|
+
Ajay tested v2.8.9 by hand in Claude Desktop against a real Developer Edition org — created a custom
|
|
184
|
+
object, six custom fields, and two Flows built against them — and reported five specific issues found
|
|
185
|
+
along the way. Every one below was reproduced first (not assumed from the report), root-caused in the
|
|
186
|
+
source, fixed, and re-verified against `demo-org` before being called done.
|
|
187
|
+
|
|
188
|
+
**`sf_create_flow` emits duplicate rule developer names for 2+ Decision elements (blocking).**
|
|
189
|
+
Reproduced exactly: `Deployment failed: Duplicate developer name: Rule_1`. Root cause: each Decision's
|
|
190
|
+
rule `<name>` was `Rule_1`, `Rule_2`, ... scoped per-decision, but Salesforce requires uniqueness across
|
|
191
|
+
the whole Flow. Fixed in both XML generators by namespacing with the parent Decision's own name
|
|
192
|
+
(`Decision_One_Rule_1`), verified unique in the deployed XML afterward. Fixing this surfaced a second,
|
|
193
|
+
related bug in the same code path: `<defaultConnectorLabel>` was only emitted when `defaultConnector`
|
|
194
|
+
was set, but Salesforce requires it unconditionally (it labels the implicit "no rule matched" branch,
|
|
195
|
+
which exists whether or not it connects anywhere) — every existing Decision test in this repo's own
|
|
196
|
+
suite happened to set `defaultConnector` explicitly, which is exactly why this had never been caught.
|
|
197
|
+
Fixed in both generators; added a permanent regression test with two Decisions, the second deliberately
|
|
198
|
+
relying on implicit fall-through. Flow suite: 142→144 checks, 144/144 passing.
|
|
199
|
+
|
|
200
|
+
**`sf_create_flow`'s filter/assignment values only accepted strings, and GetRecords filters silently
|
|
201
|
+
mistyped numbers.** The schema rejected a native boolean (`value: true`) with a bare zod error; the
|
|
202
|
+
reported workaround (`value: "true"`) did work correctly (booleans were already string-sniffed
|
|
203
|
+
correctly), but investigating this surfaced a real, more serious, previously-unreported bug in the same
|
|
204
|
+
code: GetRecords filter values had NO numeric detection at all — unlike Decision/CreateRecords/
|
|
205
|
+
UpdateRecords, which all correctly detect numeric-looking values, GetRecords filters emitted
|
|
206
|
+
`<stringValue>100</stringValue>` for every non-boolean value including numbers, which risks incorrect
|
|
207
|
+
or no matches when filtering Number/Currency/Percent fields with comparison operators. Fixed the missing
|
|
208
|
+
numeric detection in both generators, and widened `rightValue`/`filterValue`/`filters[].value`/
|
|
209
|
+
`inputAssignments[].value`/`assignments[].value` to accept string, number, or boolean directly (coerced
|
|
210
|
+
to string before the existing, now-correct typed-XML logic) so the natural call shape works without a
|
|
211
|
+
string-coercion workaround. Verified live: a native boolean and a native number filter both produce the
|
|
212
|
+
correct typed XML element in the deployed flow.
|
|
213
|
+
|
|
214
|
+
**REST describe / SOQL not seeing custom fields that the Metadata API and Tooling API confirm exist.**
|
|
215
|
+
Reproduced exactly — `describe field count: 10` (only standard fields), matching the report precisely,
|
|
216
|
+
persisting well past 60 seconds and after granting FLS (also reproduced: 0 grants by default). Ruled
|
|
217
|
+
out an in-process cache (there isn't one — `sf_describe_object` makes a fresh HTTP call every time,
|
|
218
|
+
confirmed by reading the code) and ruled out XML element ordering in the picklist-value generator via a
|
|
219
|
+
controlled live A/B test (an initial hypothesis that looked promising but didn't hold up once tested
|
|
220
|
+
head-to-head). What's actually happening: Salesforce's own REST describe/SOQL schema cache lagged
|
|
221
|
+
behind the Metadata API and (intermittently, itself) the Tooling API by 10+ minutes on this org during
|
|
222
|
+
testing — a genuine platform-side propagation characteristic, not something any client-side code
|
|
223
|
+
change can fix. Implemented the mitigation Ajay suggested regardless: `sf_describe_object` gained
|
|
224
|
+
`waitForFields`/`timeoutSeconds` to poll until named fields appear (or say clearly that they still
|
|
225
|
+
haven't, with a pointer to the Tooling API to confirm the field truly exists), so callers don't have to
|
|
226
|
+
hand-roll retry loops. Documented plainly in both the tool description and the CHANGELOG that this is
|
|
227
|
+
an org-side lag, not a bug in this server — don't re-diagnose it as one without new evidence.
|
|
228
|
+
|
|
229
|
+
**`sf_create_custom_field` leaves every new field with zero FLS grants and no indication a follow-up
|
|
230
|
+
call is needed.** Reproduced: `sf_get_field_permissions` returns 0 grants immediately after a
|
|
231
|
+
`success: true` field creation, for every profile including System Administrator. Fixed by checking FLS
|
|
232
|
+
right after creation and appending an explicit warning to the response when none exist, naming the
|
|
233
|
+
exact fix (`sf_create_field_level_security`) — chose this over auto-granting FLS by default since that
|
|
234
|
+
would silently change existing behavior; surfacing the truth is a strictly additive fix.
|
|
235
|
+
|
|
236
|
+
**`sf_create_flow_from_xml` surfaced raw, unhelpful Salesforce schema errors for 3 common mistakes.**
|
|
237
|
+
Added `validateAndNormalizeFlowXml()`: a bounded, depth-tracking tokenizer (not a full XML parser —
|
|
238
|
+
sufficient for direct children of the root `<Flow>` element) that catches, before ever deploying: (1)
|
|
239
|
+
non-contiguous top-level elements of the same type (interleaved `<assignments>`/`<decisions>` etc.,
|
|
240
|
+
which Salesforce reports as a baffling "Element X is duplicated at this location in type Flow" instead
|
|
241
|
+
of naming the real issue — grouping), (2) missing `<start>` `locationX`/`locationY`, auto-defaulted
|
|
242
|
+
rather than just flagged since there's no ambiguity about a safe default, (3) an SObject-typed
|
|
243
|
+
`<variables>` entry missing `<objectType>`, naming the specific variable (Salesforce's own error for
|
|
244
|
+
this one was already good — replicated its style for consistency, and to catch it pre-deploy instead of
|
|
245
|
+
after). Verified live: interleaved elements and a missing objectType are both now rejected pre-deploy
|
|
246
|
+
with a specific, actionable message and never reach Salesforce at all; missing start coordinates are
|
|
247
|
+
silently defaulted and the flow deploys successfully.
|
|
248
|
+
|
|
249
|
+
**Full regression, both before committing and after every individual fix**: `qa-flow-comprehensive.mjs`
|
|
250
|
+
142→144/144 (2 new checks from the Bug 1 regression test, all passing). `test-suite.mjs` and the
|
|
251
|
+
Agentforce suites held their existing pass rates — none of these five fixes touch Agentforce code paths.
|
|
252
|
+
|
|
253
|
+
## [2.8.9] - 2026-07-31
|
|
254
|
+
|
|
255
|
+
### Fixed — the v2.8.8 command-injection guard was over-broad; narrowed after a second round of live testing
|
|
256
|
+
|
|
257
|
+
Asked for a second round of testing before publishing v2.8.8, rather than treating the first pass as
|
|
258
|
+
done. Good call: it surfaced a real false-positive regression in the fix itself. The original guard
|
|
259
|
+
rejected any `sf` CLI argument containing `` " ` $ & | ; < > ^ `` — but a systematic, one-character-
|
|
260
|
+
at-a-time live test (each character alone, then each paired with a quote) showed every one of those
|
|
261
|
+
metacharacters is completely inert on its own through cross-spawn; **only a literal `"` combined with
|
|
262
|
+
a following metacharacter reaches a live shell.** A quote alone never did either. That means the
|
|
263
|
+
original guard would have rejected entirely legitimate values this codebase's own tools pass
|
|
264
|
+
routinely — a package description like `"Sales & Service Tools"`, a company name like
|
|
265
|
+
`"O'Brien Industries"` — with zero actual security benefit, since none of those ever reach a shell
|
|
266
|
+
regardless. Narrowed to reject only `"` (plus raw newlines, never legitimate in a single CLI arg
|
|
267
|
+
either). Re-verified live, both directions: the original exploit payload (`"x & echo ... & echo x"`)
|
|
268
|
+
is still rejected; a package description containing `&` and `'` now reaches the real `sf` CLI instead
|
|
269
|
+
of being rejected by the guard.
|
|
270
|
+
|
|
271
|
+
### Reviewed in depth on request — Flow builder and Agentforce agent/topic/action/planner creation
|
|
272
|
+
|
|
273
|
+
Ajay asked specifically for closer attention here. Read both Flow XML generators
|
|
274
|
+
(`buildFlowXml`/SOAP and `buildFlowDeployXml`/ZIP, ~800 lines combined) end to end and all four
|
|
275
|
+
Agentforce tools (`sf_create_agent`, `sf_create_agent_topic`, `sf_create_agent_action`,
|
|
276
|
+
`sf_create_agent_planner`) line by line, not just grep-sampled. Result: both Flow builders escape
|
|
277
|
+
every free-text value correctly and consistently (verified their `buildFilterValue`/
|
|
278
|
+
`typedResourceValue` helpers apply `x()` at every actual string-literal insertion point); the
|
|
279
|
+
remaining raw interpolations are all zod-enum-constrained `dataType` fields or booleans, not user
|
|
280
|
+
text, so no injection surface. Agentforce's XML construction is equally clean throughout.
|
|
281
|
+
|
|
282
|
+
**Did find one real, separate bug while reading this closely, unrelated to injection**:
|
|
283
|
+
`sf_create_agent_action`'s `inputs` parameter (input parameter mappings — `[{name, value}]`) is
|
|
284
|
+
accepted by the schema and documented, but was never wired into the generated `GenAiFunction` XML at
|
|
285
|
+
all — confirmed by grep, not a one-off oversight in this pass. Any caller who passed `inputs` had them
|
|
286
|
+
silently discarded, with no error and no indication in the response. This went uncaught because
|
|
287
|
+
`demo-org` cannot create `GenAiFunction` actions at all (a pre-existing, documented org-licensing
|
|
288
|
+
limit), so this parameter has never been exercised against a live org — and this project's own rule is
|
|
289
|
+
not to ship metadata XML shapes that haven't been verified that way. Rather than guess at the correct
|
|
290
|
+
XML (a real risk of shipping a second, differently-wrong bug), the tool's success message now says
|
|
291
|
+
explicitly when `inputs` was provided but not applied, so callers aren't silently misled. Implementing
|
|
292
|
+
it for real is still blocked on the same thing blocking the rest of GenAiFunction action testing: an
|
|
293
|
+
org where custom agent actions are actually licensed.
|
|
294
|
+
|
|
295
|
+
**Full regression**, both directions of this round: `test-suite.mjs` 207/211 passed — the one new
|
|
296
|
+
"failure" beyond the existing 2 pre-existing ones (`sf_share_report_folder`) is test-state
|
|
297
|
+
accumulation (it selects an existing report folder from the org, and today's own repeated
|
|
298
|
+
`test-suite.mjs` runs have created enough of them that one now has a too-long derived name), not a
|
|
299
|
+
code regression — unrelated to any change in this release. `qa-agentforce.mjs`,
|
|
300
|
+
`qa-agentforce-adjacent.mjs`, and `qa-flow-comprehensive.mjs` (142/142) all held their existing rates.
|
|
301
|
+
|
|
302
|
+
## [2.8.8] - 2026-07-31
|
|
303
|
+
|
|
304
|
+
### Security audit — one confirmed, exploitable command-injection vulnerability fixed, plus 13 more real findings across SOQL, generated-code, and credential-handling surfaces
|
|
305
|
+
|
|
306
|
+
Ajay asked for a standing security review of this project, not tied to any specific bug report. Went
|
|
307
|
+
through the codebase systematically rather than waiting for a report, and proved every finding below
|
|
308
|
+
by actually attempting the attack through the real, compiled MCP tool handler — not just reading code
|
|
309
|
+
and guessing. Everything here was found live against `demo-org`, the same standard this project holds
|
|
310
|
+
itself to for functional bugs.
|
|
311
|
+
|
|
312
|
+
**Command injection — CRITICAL, confirmed live-exploitable, now fixed.** Every `sf` CLI invocation in
|
|
313
|
+
this codebase built a shell command by joining an args array with spaces and running it through
|
|
314
|
+
`execSync`. Six tools passed unvalidated string parameters straight into that array:
|
|
315
|
+
`sf_create_scratch_org`, `sf_delete_scratch_org`, `sf_create_package`, `sf_create_package_version`,
|
|
316
|
+
`sf_install_package`, `sf_uninstall_package`, `sf_run_code_scanner`, `sf_scan_apex_antipatterns`.
|
|
317
|
+
Proof of concept: a `devHubAlias` of `DevHub & echo INJECTED > marker.txt & echo x` actually created
|
|
318
|
+
the marker file when run through `sf_create_scratch_org`'s real handler. Fixed by replacing every
|
|
319
|
+
`execSync`/`exec` call with `cross-spawn`, which passes each argument as a genuine argv entry instead
|
|
320
|
+
of shell text — re-verified the same payload no longer executes. **cross-spawn alone was not enough**:
|
|
321
|
+
an argument combining a literal `"` with a shell metacharacter (e.g. `"x & ... & echo x"`) still
|
|
322
|
+
reached a live shell even through cross-spawn 7.0.6's own escaping, because `sf` is a `.cmd` batch
|
|
323
|
+
shim on Windows and its internal `%*` argument-forwarding to node.exe is a second, uncontrolled
|
|
324
|
+
re-parsing step outside cross-spawn's reach — also proven live before being closed with an explicit
|
|
325
|
+
character allowlist (`runSfCli` now rejects any argument containing `"`, `` ` ``, `$`, `&`, `|`, `;`,
|
|
326
|
+
`<`, `>`, `^`, or a newline before it ever reaches cross-spawn; none of this codebase's CLI arguments —
|
|
327
|
+
aliases, IDs, paths, keys, rule selectors — ever legitimately need one). Also deleted `createScratchOrg`,
|
|
328
|
+
a second, entirely dead implementation of scratch-org creation with the same injection flaw plus
|
|
329
|
+
broken shell quoting that would have failed on both POSIX and Windows — confirmed zero references
|
|
330
|
+
anywhere before removing it.
|
|
331
|
+
|
|
332
|
+
**SOQL injection — real, live, found across 9 call sites.** `sf_get_field_history` (`recordId` and
|
|
333
|
+
`objectApiName`, the latter landing unquoted in a FROM-clause identifier position — no amount of
|
|
334
|
+
quote-escaping fixes that, only an identifier allowlist does), `sf_get_setup_audit_trail` /
|
|
335
|
+
`sf_get_login_history` / `sf_get_event_logs` (`startDate`/`endDate`, interpolated unquoted with zero
|
|
336
|
+
format validation), `sf_get_apex_test_results` (`testRunId`), `sf_detect_devops_merge_conflict` /
|
|
337
|
+
`sf_check_devops_commit_status` / `sf_list_devops_work_items` (`workItemId`/`projectId`/`stageId`),
|
|
338
|
+
and `sf_create_field_dependency` (`objectName`/`dependentField` in a Tooling API query). Fixed with a
|
|
339
|
+
new `soqlEscape()` helper (formalizing the backslash-quote pattern already used correctly at ~15 other
|
|
340
|
+
call sites in this file) and a new `assertSoqlDateLiteral()` validator for the unquoted date-literal
|
|
341
|
+
positions. `sf_query_records`'s raw `soql`/`whereClause`/`orderBy` and `sf_create_apex_batch`'s
|
|
342
|
+
`queryFilter` were deliberately left alone — both are documented, intentional raw-SOQL-fragment
|
|
343
|
+
parameters, the entire point of those tools, not an oversight.
|
|
344
|
+
|
|
345
|
+
**Code injection into generated, later-executed source — found in both Apex and TypeScript
|
|
346
|
+
generation.** `sf_create_invocable_action`'s `label`/`description` and its input/output variable
|
|
347
|
+
labels landed unescaped inside `@InvocableMethod`/`@InvocableVariable` annotation string literals in
|
|
348
|
+
generated Apex — an unescaped `'` breaks out of the literal and injects arbitrary class members into
|
|
349
|
+
a class that gets deployed and can execute. Same pattern in the (dead, unwired — see below)
|
|
350
|
+
`createApexScheduler`'s doc comment (a literal `*/` closes the comment early, turning the rest of the
|
|
351
|
+
generated file back into live code) and `createRestResource`'s `urlMapping`. Fixed with the same
|
|
352
|
+
`soqlEscape()` helper (Apex and SOQL use identical backslash-quote string-literal escaping) plus a
|
|
353
|
+
comment-safe `*/`-stripping helper for the comment-only cases. **More seriously, this same class of
|
|
354
|
+
bug was live and reachable** in `sf_create_mcp_server` and `sf_create_mcp_tool` (the tools that
|
|
355
|
+
scaffold a brand-new MCP server project on disk): `serverName` and `toolName` were spliced unescaped
|
|
356
|
+
into generated `.ts` source as string literals, and `sf_create_mcp_tool`'s `inputSchema` keys —
|
|
357
|
+
`z.record(z.unknown())`, so literally any string — were spliced in as raw, unquoted object-property
|
|
358
|
+
names with zero validation. Proof of concept: an `inputSchema` key of
|
|
359
|
+
`x(){require("fs").writeFileSync("pwn2","x");return z.string()` was accepted by the old code and would
|
|
360
|
+
have become live, executable code in the generated project the moment it was built and run. Fixed:
|
|
361
|
+
`serverName`/`toolName` now go through `JSON.stringify()` (matching how `toolDescription` was already
|
|
362
|
+
correctly handled in the same function — this was an inconsistency, not a from-scratch gap), and
|
|
363
|
+
`inputSchema` keys are now validated against a strict identifier regex before generation, with a clear
|
|
364
|
+
rejection instead of silent code smuggling.
|
|
365
|
+
|
|
366
|
+
**Credential leak in the codebase's own default error-sanitization path — real, high-impact, silently
|
|
367
|
+
broken since introduction.** `sanitizeError()` is called in essentially every one of this codebase's
|
|
368
|
+
~100 catch blocks — it's what stands between an internal error and what gets returned to the calling
|
|
369
|
+
LLM/user. Its only token-redaction rule was `[A-Fa-f0-9]{40,}` (pure hex, 40+ chars). Real Salesforce
|
|
370
|
+
access tokens/session IDs look like `00Dxxxxxxxxxxxx!AQEAQ...` — base64url-ish with a literal `!`
|
|
371
|
+
separator, never pure hex — so a real token embedded in any error message (a network client echoing a
|
|
372
|
+
request URL, an API error quoting back session context) would have passed straight through
|
|
373
|
+
completely unredacted. Proved this live with a realistic fake token before and after the fix. There
|
|
374
|
+
was a second function, `redactSensitive()`, that had the right idea (a `Bearer` pattern, a `00D...`
|
|
375
|
+
pattern) but its own character classes stopped at the first `!` or `.`, so it only partially redacted
|
|
376
|
+
even the cases it targeted, and — separately — it was only ever called from 2 of the ~100 relevant
|
|
377
|
+
sites, so almost nothing benefited from it regardless. Fixed by broadening the character classes to
|
|
378
|
+
cover the actual token shape and having `sanitizeError()` call `redactSensitive()` internally, so
|
|
379
|
+
every existing caller gets the protection retroactively with no call-site changes needed.
|
|
380
|
+
|
|
381
|
+
**HTTP transport mode has no authentication and bound to all network interfaces by default.** This
|
|
382
|
+
project supports `TRANSPORT=http` as an alternative to the default stdio transport. Its `/mcp` endpoint
|
|
383
|
+
has no auth of its own — any request that reaches it executes MCP tools using whatever Salesforce
|
|
384
|
+
credentials the server process is configured with — and `http.Server.listen(port)` with no explicit
|
|
385
|
+
host binds to every interface, not just localhost, by Node's own default. Fixed: defaults to
|
|
386
|
+
`127.0.0.1` now; set `HOST` explicitly (e.g. behind a reverse proxy that adds real auth) to opt into
|
|
387
|
+
broader exposure instead of getting it by accident. This mode is opt-in and off by default (stdio is
|
|
388
|
+
the default transport), so this was a foot-gun for anyone who did enable it, not an always-on gap.
|
|
389
|
+
|
|
390
|
+
**Audited and confirmed clean, no action needed:** XML metadata builders (spot-checked ~60 candidate
|
|
391
|
+
interpolation sites; every one was already either correctly wrapped in the existing `x()` escaper or a
|
|
392
|
+
boolean/number field with no injection surface at all — the real gaps here were already closed in
|
|
393
|
+
v2.8.2). `npm audit`: still exactly the 2 moderate findings already documented and deliberately left
|
|
394
|
+
(a path-traversal bug in `@hono/node-server`'s `serve-static`, a transitive dependency of the MCP SDK
|
|
395
|
+
that this project's own HTTP transport — a from-scratch, two-route implementation — never calls into
|
|
396
|
+
regardless of transport mode; confirmed again this session, not just carried forward from memory).
|
|
397
|
+
Secrets/credential files: confirmed `.gitignore` still covers `.env`, `.env.local`, `*.key`, and
|
|
398
|
+
`CLAUDE.local.md`, and confirmed via `git log --all` that nothing secret-shaped has ever been committed.
|
|
399
|
+
|
|
400
|
+
**Also found, not fixed (dead code, zero live exposure, hardened anyway for defense-in-depth):**
|
|
401
|
+
`createInvocableAction`, `createApexScheduler`, and `createRestResource` are all exported from
|
|
402
|
+
`services/salesforce.ts` but never imported by any tool file — confirmed via full-codebase grep before
|
|
403
|
+
and after — so their injection fixes above protect nothing reachable today. Left in place rather than
|
|
404
|
+
deleted (unlike `createScratchOrg`, which was both dead *and* had no legitimate salvage value) since
|
|
405
|
+
these look like intended-but-never-wired functionality; flagging here so a future session that wires
|
|
406
|
+
them up inherits the fix rather than reintroducing the bug.
|
|
407
|
+
|
|
408
|
+
**Full regression confirmation:** `test-suite.mjs` (211 tests) — 208 passed, 2 failed (both pre-existing,
|
|
409
|
+
unrelated, intentional negative-test probes), 1 skipped — no change from the pre-audit baseline.
|
|
410
|
+
`qa-agentforce.mjs`, `qa-agentforce-adjacent.mjs`, and `qa-flow-comprehensive.mjs` all held their
|
|
411
|
+
existing pass rates. Every fix above was also independently proven by re-attempting its specific
|
|
412
|
+
exploit through the real, compiled tool handler and confirming it now fails cleanly instead of
|
|
413
|
+
executing.
|
|
414
|
+
|
|
415
|
+
## [2.8.7] - 2026-07-31
|
|
416
|
+
|
|
417
|
+
### Added — `sf_create_external_client_app`, and it closes a gap `sf_create_connected_app` structurally cannot
|
|
418
|
+
|
|
419
|
+
Asked why the previous release reached for a Connected App at all when External Client Apps (ECAs) are
|
|
420
|
+
Salesforce's newer, recommended replacement, the honest answer was: the codebase simply had no ECA
|
|
421
|
+
tool, so the fix used what existed. Built the missing tool instead of leaving that as a permanent
|
|
422
|
+
excuse — and it turns out ECAs solve a real limitation, not just a modernization nicety.
|
|
423
|
+
|
|
424
|
+
`ExternalClientApplication` decomposes into three independent metadata types
|
|
425
|
+
(`ExternalClientApplication`, `ExtlClntAppOauthSettings`, `ExtlClntAppOauthConfigurablePolicies`),
|
|
426
|
+
none of which are documented with a field-level reference on Salesforce's own metadata pages for the
|
|
427
|
+
parts that matter most. Every field below — including the full 36-value OAuth scope enum, which the
|
|
428
|
+
official docs don't list at all — was discovered by deploying deliberately-invalid values against a
|
|
429
|
+
live org and reading Salesforce's own rejection messages (the scope enum came back complete, unprompted,
|
|
430
|
+
in a single error string), then confirmed correct by deploying the real values and reading the records
|
|
431
|
+
back. `sf_create_external_client_app` deploys all three components in one call.
|
|
432
|
+
|
|
433
|
+
**The actual reason to prefer this over `sf_create_connected_app`:** on a classic Connected App,
|
|
434
|
+
Client Credentials Flow's "Run As" user can only ever be picked in Setup UI — confirmed in the
|
|
435
|
+
previous release, and still true, there is no metadata or REST field for it. On an External Client
|
|
436
|
+
App, `ExtlClntAppOauthConfigurablePolicies.clientCredentialsFlowUser` sets it directly, verified live:
|
|
437
|
+
create the app, enable the flow, name the user, done — no human required for that part. The one thing
|
|
438
|
+
that's still Setup-UI-only for *either* app type is viewing the Consumer Key/Secret — Salesforce does
|
|
439
|
+
not expose it via any API, full stop, and the tool's success message says so rather than implying the
|
|
440
|
+
app is immediately usable.
|
|
441
|
+
|
|
442
|
+
**Also fixed the same "cannot fail" defect in `sf_create_connected_app`'s own regression test** — it
|
|
443
|
+
called the underlying function with a wrong parameter name (`appName` instead of `fullName`) and
|
|
444
|
+
omitted the two required fields (`callbackUrls`, `scopes`) entirely, so it always threw internally and
|
|
445
|
+
passed via `orgLimitFallback`'s catch. This is very likely *how* the scope-enum bug fixed in the
|
|
446
|
+
previous release went unnoticed for as long as it did: the one test that should have caught it never
|
|
447
|
+
actually ran the real code path. Fixed with real required parameters; added a matching real test for
|
|
448
|
+
the new tool.
|
|
449
|
+
|
|
450
|
+
Also updated the tool count (221→222) across `package.json`, `server.json`, `README.md`, `TOOLS.md`,
|
|
451
|
+
and `CLAUDE.md`.
|
|
452
|
+
|
|
453
|
+
## [2.8.6] - 2026-07-31
|
|
454
|
+
|
|
455
|
+
### Fixed — `sf_create_connected_app` could never successfully grant any OAuth scope
|
|
456
|
+
|
|
457
|
+
Asked to prove an Agentforce agent can hold a live conversation, the trail led to the connected app
|
|
458
|
+
required for the Agent API's client-credentials flow — and that tool turned out to be broken end to
|
|
459
|
+
end, independent of anything Agentforce-specific. Every scope value in the schema (`api`, `web`,
|
|
460
|
+
`chatter_api`, `offline_access`, ...) is lowercase snake_case, matching the OAuth2 `scope`
|
|
461
|
+
query-parameter convention developers know. The `ConnectedApp` Metadata API's scope enum
|
|
462
|
+
(`ConnectedAppOauthAccessScope`) is a completely different, undocumented set of PascalCase literals
|
|
463
|
+
that has no relationship to that convention — `'api' is not a valid value for the enum
|
|
464
|
+
'ConnectedAppOauthAccessScope'`. Every one of the tool's own scope values was therefore guaranteed to
|
|
465
|
+
fail deployment, 100% of the time, for every caller who ever used it. Fixed with a mapping table where
|
|
466
|
+
each entry was individually verified by deploying it against a live org and reading Salesforce's own
|
|
467
|
+
accept/reject response — not guessed from docs: `api`→`Api`, `web`→`Web`, `full`→`Full`,
|
|
468
|
+
`chatter_api`→`Chatter`, `wave_api`→`Wave`, `eclair_api`→`Eclair`, `content`→`Content`,
|
|
469
|
+
`openid`→`OpenID`, `profile`→`Profile`, `email`→`Email`, `address`→`Address`, `phone`→`Phone`,
|
|
470
|
+
`offline_access`→`RefreshToken`, `custom_permissions`→`CustomPermissions`, `pardot_api`→`Pardot`.
|
|
471
|
+
Also added `chatbot_api`→`Chatbot` (verified live) — needed by any external caller of a Salesforce
|
|
472
|
+
bot/Agentforce agent, previously missing from the enum entirely. `visualforce` has **no** confirmed
|
|
473
|
+
working literal — `Visualforce`, `VisualForce`, `Vf`, and `ViewVisualforce` were all tried and
|
|
474
|
+
rejected; it is passed through unmapped and documented as a known open gap rather than guessed.
|
|
475
|
+
|
|
476
|
+
Also added `enableClientCredentialsFlow`, which sets `isClientCredentialEnabled`/`isAdminApproved` on
|
|
477
|
+
the deployed app (both verified accepted by the Metadata API). This does **not** make Client
|
|
478
|
+
Credentials Flow usable by itself: Salesforce still requires a human in Setup → App Manager → Edit
|
|
479
|
+
Policies to pick the flow's "Run As" user, and the Consumer Secret can only ever be viewed/copied from
|
|
480
|
+
that same Setup UI — neither is exposed by any API, Connect resource, or Tooling endpoint found during
|
|
481
|
+
a deliberate search. The tool's success message now says this explicitly instead of implying the app
|
|
482
|
+
is ready to use.
|
|
483
|
+
|
|
484
|
+
### Fixed — `sf_create_agent_action`'s `label` parameter was required but the code already treated it as optional
|
|
485
|
+
|
|
486
|
+
The handler has always used `params.label ?? params.actionName` as a fallback, but the zod schema
|
|
487
|
+
marked `label` as required — so the fallback code was dead and every caller who reasonably omitted a
|
|
488
|
+
redundant label got a hard validation rejection. Schema now matches the code's actual behavior.
|
|
489
|
+
|
|
490
|
+
### Fixed — `activateAgent`/`deactivateAgent` claimed success on total failure
|
|
491
|
+
|
|
492
|
+
Not wired to any MCP tool (dead code, found during this session), but worth fixing since it embodies a
|
|
493
|
+
pattern already flagged elsewhere in this codebase: its final fallback returned `success: true` even
|
|
494
|
+
when every attempt inside the function had failed. Verified live: the `/connect/einstein/copilot/
|
|
495
|
+
{name}/activate` Connect resource 404s ("resource does not exist"), and both `BotDefinition.Status`
|
|
496
|
+
and `BotVersion.Status` are read-only via REST ("No such column" / "Unable to create/update fields:
|
|
497
|
+
Status... check security settings" — Salesforce's own read-only-field wording, not a permissions gap).
|
|
498
|
+
No working programmatic activation path was found despite testing roughly ten REST/Connect/Tooling
|
|
499
|
+
endpoint candidates. The function now reports failure honestly instead of guessing success; Agent
|
|
500
|
+
activation (Setup → Agent Builder → Activate) appears to be Setup-UI-only as of API v66.0.
|
|
501
|
+
|
|
502
|
+
### Fixed — `test-suite.mjs` had 5 Agentforce/Einstein tests that could never fail (not 3, as
|
|
503
|
+
### previously scoped)
|
|
504
|
+
|
|
505
|
+
`sf_create_einstein_bot`, `sf_create_einstein_prediction`, `sf_create_agent`, `sf_create_agent_topic`,
|
|
506
|
+
and `sf_create_agent_action` were each wrapped in try/catch returning `success: true`, then passed
|
|
507
|
+
through `orgLimitFallback` (which also counts HTTP 500/404/NOT_FOUND as a pass). The agent/topic tests
|
|
508
|
+
imported `createAgent`/`createAgentTopic` from `services/salesforce.js`, which no MCP tool calls —
|
|
509
|
+
`src/tools/agentforce.ts` builds its own XML inline — so they exercised orphaned code while the shipped
|
|
510
|
+
handlers had zero coverage from this suite. The action test hand-rolled `GenAiFunction` XML with a
|
|
511
|
+
`type`/`functionRef` shape that doesn't match what the real tool deploys
|
|
512
|
+
(`invocationTarget`/`invocationTargetType`), aimed at a flow named `NonExistentFlow`. All five removed;
|
|
513
|
+
real, live-org-verified coverage for these five tools plus `sf_create_agent_planner`,
|
|
514
|
+
`sf_create_bot_routing`, and `sf_assign_skill_to_agent` (none of which had any test before) lives in
|
|
515
|
+
`qa-agentforce.mjs` (47 checks) and `qa-agentforce-adjacent.mjs` (11 checks). `test-suite.mjs` is now
|
|
516
|
+
210 tests (was 215); re-run against `demo-org` after the changes above: 206 passed, 3 failed (all
|
|
517
|
+
pre-existing and unrelated — `sf_share_report_folder` field-length validation,
|
|
518
|
+
`sf_delete_scratch_org`/`sf_install_package` both probing intentionally-nonexistent targets), 1 skipped.
|
|
519
|
+
|
|
520
|
+
### Investigated — the Agentforce conversation test is still unproven, now for precisely two reasons
|
|
521
|
+
|
|
522
|
+
Went further than any previous session on both blockers named in the last release:
|
|
523
|
+
|
|
524
|
+
**Custom agent actions (`GenAiFunction`) are conclusively an org licensing ceiling, not a code
|
|
525
|
+
defect.** Beyond re-confirming every `invocationTargetType` is still rejected against a fresh
|
|
526
|
+
confirmed-Active flow, this session found and tried the specific fix: `demo-org` has two relevant
|
|
527
|
+
Permission Set Licenses sitting unused (`Agentforce Service Agent User` — 200 seats, 0 assigned;
|
|
528
|
+
`Agent platform builder` — 5 seats, 0 assigned). Assigning `Agent Platform Builder`'s permission set
|
|
529
|
+
succeeded but changed nothing. Assigning the one that actually gates this
|
|
530
|
+
(`AgentforceServiceAgentUser`, and its `AgentforceServiceAgentBase` companion) was rejected outright by
|
|
531
|
+
Salesforce: *"Ajay Semwal can't be assigned the Agentforce Service Agent User permission set license,
|
|
532
|
+
because Ajay Semwal's user license doesn't support it."* That's a base User License / edition ceiling,
|
|
533
|
+
not a permission set gap — nothing reachable from this MCP server's tools can change it.
|
|
534
|
+
|
|
535
|
+
**A live conversation turn needs a Setup UI session, and none exists in this MCP server by design.**
|
|
536
|
+
The Agent API requires an ACTIVE agent and a connected app's Consumer Key/Secret. Both dead ends above
|
|
537
|
+
converge here: agent activation has no API path (see the `activateAgent` fix above), and Consumer
|
|
538
|
+
Secret can only be viewed once from Setup → App Manager, never via any API. Getting either therefore
|
|
539
|
+
requires a human (or a browser-automation session with its own explicit device-confirmation step) in
|
|
540
|
+
Salesforce Setup — outside what a stdio metadata MCP server does. Recorded as a precise, actionable
|
|
541
|
+
handoff rather than an unexplained skip.
|
|
542
|
+
|
|
543
|
+
**Also tried and reverted:** attempting to pre-stage a working connected app for that eventual Setup UI
|
|
544
|
+
session surfaced a third, unresolved anomaly — connected apps created via `upsertMetadata` (Salesforce's
|
|
545
|
+
own SOAP response says `success: true`) became unretrievable via `readMetadata`/`listMetadataType`/SOQL
|
|
546
|
+
within roughly 10–20 minutes, while apps checked within seconds of creation persisted and deleted
|
|
547
|
+
cleanly. Root cause not identified (not a scope-literal issue — reproduced with the corrected literals
|
|
548
|
+
above). Flagged for follow-up, not fixed; treat any connected app in `demo-org` as unverified until
|
|
549
|
+
checked shortly after creation.
|
|
550
|
+
|
|
551
|
+
**QA housekeeping:** cascade-deleted 15 of the 22 leftover `QA*`-prefixed Agentforce agents (plus their
|
|
552
|
+
topics/planners/actions) accumulated across this and prior sessions, via the previously-unwired
|
|
553
|
+
`deleteAgent` service function. 8 remain — Salesforce's Metadata API delete rejects them with a generic
|
|
554
|
+
internal-server error (`ErrorId ...`, not an application-level error), on both batch and individual
|
|
555
|
+
retry; not something a client-side retry fixes. Today's own flow/Apex test artifacts were left in place
|
|
556
|
+
(flows are Active and must be deactivated before delete; Apex class deletion via this org's Metadata
|
|
557
|
+
API delete path is rejected as "not available for this organization") — low-risk, consistent with this
|
|
558
|
+
project's existing no-teardown QA methodology. The broader "few hundred QA artifacts, no teardown
|
|
559
|
+
suite" backlog noted previously is real, unaddressed, and grew further from this session's full
|
|
560
|
+
`test-suite.mjs` run (required to confirm no regressions) — still a separate, unstarted engineering task.
|
|
561
|
+
|
|
562
|
+
## [2.8.5] - 2026-07-30
|
|
563
|
+
|
|
564
|
+
### Fixed — 8 more bugs, from widening Agentforce QA past the four headline tools
|
|
565
|
+
|
|
566
|
+
Asked whether the v2.8.4 Agentforce testing was thorough, the honest answer was no: 4 of 8 agent-related tools, no runtime assertions, no content-level verification, only 2 of 5 action types, and a parameter (`actionNames`) that shipped without ever being exercised. Closing those gaps found eight more real bugs.
|
|
567
|
+
|
|
568
|
+
**`sf_retrieve_metadata` could never return metadata to anyone.** It started an async retrieve, handed back a job id, and the package contained no tool that calls `checkRetrieveStatus` — while the tool's own description told callers the zip was "available via Metadata API checkRetrieveStatus". Added `pollRetrieveStatus`/`retrieveMetadataAndWait`: the tool now waits for the job, unpacks the zip, and returns each file's path and source. This also unblocked every content-level assertion in the QA suites, which is how several bugs below were confirmed.
|
|
569
|
+
|
|
570
|
+
**`sf_create_einstein_bot` was non-functional — five separate defects, each hidden behind the previous one:**
|
|
571
|
+
- `<defaultLocale>` is not a `Bot` field ("Element defaultLocale invalid at this location").
|
|
572
|
+
- The ML domain element is `<botMlDomain>` with a `<name>` child, not `<mlDomain>` with `<developerName>`.
|
|
573
|
+
- The Bot and its BotVersion were upserted in two separate calls, but a Bot alone is rejected with "Bot needs at least one Bot version" — the sequence could never succeed. The version is now embedded as `<botVersions>` in the same payload.
|
|
574
|
+
- `BotStep` takes `<type>`, not `<conversationStepType>`, and its text belongs in `<botMessages><message>`, not a flat `<botMessage>`.
|
|
575
|
+
- `<isGoalStep>` is not a `BotDialog` field; it is `isPlaceholderDialog`.
|
|
576
|
+
|
|
577
|
+
**`sf_create_einstein_prediction` was also non-functional, and its XML is now correct element-by-element:** `<label>` → `<masterLabel>`; `<predictionType>` → `<type>`, whose `AIPredictionType` enum accepts only `BinaryClassification` and `Regression` (`Classification` and `Numeric` are rejected outright, so the schema's friendly `Classification` alias is now mapped before it reaches the XML); `<developerName>` and `<aiApplicationDeveloperName>` are both required, the latter naming an existing AIApplication; `<predictionField>` is a plain string, not a `fieldName`/`objectName` pair; and `<positiveLabel>`, `<negativeLabel>` and `<active>` are not elements of this type at all. `targetField` was a **required** schema parameter the XML never used — it is the predicted field and now populates `<predictionField>`.
|
|
578
|
+
|
|
579
|
+
**Verification widened, not just repeated:**
|
|
580
|
+
- `qa-agentforce.mjs` grew from 27 to 47 checks: content-level assertions that the deployed Bot really carries `persona` as `<role>`, `company`/`toneType`, the org-valid `agentType`/`type` enums and **no** `systemPrompt`; that the deployed planner carries every requested topic, `AiCopilot__ReAct` and a description; all five action types asserting their own `invocationTargetType` mapping; the previously-untested `actionNames`; planner replace semantics (verified by deploying a second action-free topic, so it no longer depends on actions the org cannot create); and a runtime section asserting the agent→planner link survives into deployed metadata and reading real `BotVersion` status from the org.
|
|
581
|
+
- New `qa-agentforce-adjacent.mjs` covers the four tools nothing had ever driven: `sf_create_einstein_bot`, `sf_create_bot_routing`, `sf_create_einstein_prediction`, `sf_assign_skill_to_agent`. `sf_assign_skill_to_agent` passes fully, including its negative cases.
|
|
582
|
+
|
|
583
|
+
**Results against `demo-org`:** `qa-agentforce.mjs` 30 passed / 1 failed / 16 skipped (the failure a transient `fetch failed`, not a defect); `qa-agentforce-adjacent.mjs` 6 passed / 0 failed / 5 skipped.
|
|
584
|
+
|
|
585
|
+
**Honest limits, unchanged and still not papered over.** No conversation test exists: the Agent API needs an ACTIVE agent plus a connected app with client credentials, and creating a connected app is a change to the org this suite should not make unasked — so "agent answers a real conversation turn" is a recorded skip. `demo-org` cannot create GenAiFunction actions, cannot deploy classic Bots ("You don't have access to bots of type Bot" — it licenses Agentforce agents instead), has no Queue for bot routing, and has no AIApplication for predictions. Those are org boundaries, recorded as skips and never as passes. The corrected Einstein Bot and Prediction XML is therefore structurally verified element-by-element against a live org, but neither has completed a successful end-to-end deploy anywhere.
|
|
586
|
+
|
|
587
|
+
## [2.8.4] - 2026-07-30
|
|
588
|
+
|
|
589
|
+
### Fixed — 6 Agentforce bugs; agent creation was broken end to end
|
|
590
|
+
|
|
591
|
+
Agentforce had three tests in `test-suite.mjs` and none of them could fail. They imported `createAgent`/`createAgentTopic` from `services/salesforce.js`, which **no MCP tool calls** — `src/tools/agentforce.ts` builds its own XML inline, so the tests covered an orphaned parallel implementation. Each was also wrapped in `try/catch` returning `success: true`, with results passed through `orgLimitFallback()`, which counts HTTP 500 / 404 / NOT_FOUND as a pass. `sf_create_agent_action`'s test never called the tool at all — it hand-rolled XML aimed at a flow named `NonExistentFlow`. And `sf_create_agent_planner`, the step without which an agent routes nothing, had no test whatsoever.
|
|
592
|
+
|
|
593
|
+
New `qa-agentforce.mjs` registers the four real tool handlers against a stub server, validates arguments through each tool's own zod schema, invokes the real handler, and verifies results with SOQL. Bugs it found:
|
|
594
|
+
|
|
595
|
+
- **`sf_create_agent_planner` deployed `GenAiPlanner`, a metadata type that no longer exists.** Salesforce superseded it with `GenAiPlannerBundle` and rejects the old type with "Not available for deploy for this API version". Step 4 of 4 therefore failed 100% of the time. Rewritten against the live org: `description`, `masterLabel` and `plannerType` are all required, `AiCopilot__ReAct` is the only accepted `PlannerType` (`ReAct`, `Standard` and `AiCopilot__Standard` are all rejected by the enum), topics belong in `<genAiPlugins><genAiPluginName>`, and `<botName>` is invalid on the bundle entirely.
|
|
596
|
+
- **The agent→planner link was never written, by any tool.** Because `<botName>` isn't valid on the planner, nothing connected the Bot to it — so even a successful planner left the agent inert. The link lives on the Bot, as `<conversationDefinitionPlanners><genAiPlannerName>`, verified accepted against the org. `sf_create_agent` gained an optional `plannerName` to write it; since that tool is idempotent, the sequence is now 5 steps, ending in a second `sf_create_agent` call. Its success message says so explicitly instead of claiming the agent is "fully wired".
|
|
597
|
+
- **`sf_create_agent` broke outright whenever `instructions` was passed.** It emitted `<systemPrompt>` inside `<botVersions>`, which Salesforce rejects: "Element systemPrompt invalid at this location in type BotVersion". Isolated by deploying one agent per optional parameter — minimal, `+description`, `+company`, `+persona` and `+tone` all succeed; only `+instructions` fails. `BotVersion` has no system-prompt field (confirmed by retrieving real Bot/BotVersion metadata from the org), so the parameter was removed and its description now points callers at `sf_create_agent_topic`'s `instructions`, which is where agent guidance actually belongs. CLAUDE.md's field-placement note listed `systemPrompt` as valid and has been corrected.
|
|
598
|
+
- **`sf_create_agent`'s `type` parameter was a no-op whose only two allowed values were both invalid.** It was schema-validated and passed to `buildBotDeployZip`, which ignores it — the XML hardcodes `agentType`/`type`. Its enum offered `Default` and `EinsteinCopilot`, which the code's own comment records as rejected by Salesforce. Removed.
|
|
599
|
+
- **`sf_create_agent_action` required two parameters it never reads.** `agentName` and `topicName` were mandatory while the schema's own descriptions called them "informational only, NOT written to the action XML". A caller correctly reasoning that the action XML doesn't need them got a hard zod rejection. Both are now optional.
|
|
600
|
+
- **`sf_create_agent_action`'s failure message sent callers to re-check a target that was already fine.** "Specify a valid invocationTarget and invocationTargetType" is also what Salesforce returns when custom agent actions aren't enabled in the org at all. The message now says so when it sees that specific error.
|
|
601
|
+
|
|
602
|
+
**Verified against `demo-org`:** agent deploys with every remaining optional field, both removed parameters are now rejected by the strict schema, the planner deploys as a `GenAiPlannerBundle` and appears in `GenAiPlannerDefinition`, a planner naming a nonexistent topic is still rejected, and the agent re-deploys with the planner link and reports it.
|
|
603
|
+
|
|
604
|
+
**Explicitly not fixed, because it is not a defect:** `sf_create_agent_action` cannot create actions in `demo-org`, but the cause is the org, not the tool. Hand-deploying spec-compliant `GenAiFunction` XML outside the suite — flat and bundle layouts, and every documented `invocationTargetType` (`flow`, `apex`, `standardInvocableAction`, `customInvocableAction`, `flowService`, plus capitalised variants) against a confirmed-Active flow, a confirmed-Active `@InvocableMethod` class, and standard invocable actions — was rejected identically every time. An earlier draft of this work called that a product bug; it isn't, and the suite now detects the condition and skips the dependent checks rather than reporting nine false failures. `sf_create_agent_topic` is likewise uninvolved: a zero-action topic deploys clean, and its failures here were downstream of actions that could not be created.
|
|
605
|
+
|
|
606
|
+
Also corrected in this release's own record: an earlier commit claimed `demo-org` had no Agentforce at all, based on a `sf org list metadata-types` call that returned 261 types without `Bot`. The org reports 266 types with `Bot` present — that probe ran while the org was still provisioning.
|
|
607
|
+
|
|
608
|
+
## [2.8.3] - 2026-07-30
|
|
609
|
+
|
|
610
|
+
### Fixed — 10 Flow bugs, found by testing the SOAP builder for the first time
|
|
611
|
+
|
|
612
|
+
v2.8.2 reported "30/30 Flow scenarios passing," and that was true, but it hid a structural gap: `sf_create_flow` builds XML with `buildFlowXml` (SOAP/upsertMetadata) while `sf_create_flow_from_xml` and every existing suite use `buildFlowDeployXml` (ZIP/deploy). These are two independent ~300-line generators, and **no suite had ever exercised the SOAP one**. Neither suite passed `triggerObject`/`triggerType` either — grep returns zero hits — so record-triggered flows, the single most common thing an admin builds, were never tested at all.
|
|
613
|
+
|
|
614
|
+
Wrote `qa-flow-comprehensive.mjs` to close both gaps: 46 flow definitions run through **both** builders, plus runtime verification that creates real records and asserts the flow actually fired. 142 checks. Bugs it found:
|
|
615
|
+
|
|
616
|
+
- **`flowType` was written straight into `<processType>`, so every record-triggered and scheduled flow failed to deploy** — `FlowProcessType` has no `RecordTriggeredFlow` or `ScheduledFlow` member; both are `AutoLaunchedFlow` at the process level, distinguished only by the `<start>` element's `triggerType`. Added `toProcessType()` in both builders.
|
|
617
|
+
- **Scheduled flows had no schedule at all** — the start element only handled record triggers, so a `ScheduledFlow` deployed as a plain autolaunched flow that never ran. Added `<schedule>` with new `scheduleFrequency`/`scheduleStartDate`/`scheduleStartTime` params.
|
|
618
|
+
- **`recordTriggerType` was hardcoded to `CreateAndUpdate`** — create-only and update-only triggers were unreachable. Now a parameter, and omitted entirely for `RecordBeforeDelete`, which Salesforce rejects it on.
|
|
619
|
+
- **No Update Records element existed** — Get/Create/Delete were present but not Update, so the most common admin pattern ("when the Opportunity closes, update its Account") could not be built. Added in both addressing modes, with a guardrail on the `inputReference` + `inputAssignments` combination Salesforce forbids.
|
|
620
|
+
- **No Formula, Constant, or Text Template resources existed.** Added to both builders; constants emit type-correct value tags instead of coercing everything to `stringValue`.
|
|
621
|
+
- **`IsNotNull` was rejected by Salesforce everywhere it appeared** — `FlowComparisonOperator` has no such member ("is not null" is `IsNull` compared against `false`), yet the schema advertises it. Now translated in all six sites that accept an operator. The first fix attempt landed in the SOAP builder only and inverted the boolean without changing the operator name in the ZIP builder; the new suite caught that on its first run, and the filter paths in *both* builders turned out to have the same untranslated operator. All six now share one `nullOperatorXml()` helper so this cannot land one-sided again.
|
|
622
|
+
- **Null checks in `GetRecords`/`UpdateRecords` filters emitted an empty `<stringValue>`** where Salesforce requires a `booleanValue`.
|
|
623
|
+
- **`recordUpdates` emitted `<inputReference>{!$Record}</inputReference>`** — merge syntax belongs in formula and text contexts only; an `inputReference` takes the bare element name, so this resolved to nothing.
|
|
624
|
+
- **A `fieldUpdates` formula was inlined as `<formula>` inside `<inputAssignments><value>`**, but `FlowElementReferenceOrValue` has no `formula` child. It is now hoisted into a real `<formulas>` resource the assignment references by name. New `fieldUpdates.formulaDataType` controls its return type — previously formulas were always `String`, and the field was read by the service but missing from the strict zod schema, so no caller could set it.
|
|
625
|
+
- **Latent ordering bug:** the approval-submit `actionCall` and the `fieldUpdates` `recordUpdate` were emitted outside the type-grouping logic, so combining `submitForApprovalProcessName` with an `ApexAction` or `SendEmailAlert` produced non-contiguous `<actionCalls>`, which MDAPI rejects. Both are now merged into their type groups.
|
|
626
|
+
- The `elements` description still advertised `Wait` and `PlatformEvent`, which v2.5.x removed from the enum — an LLM reading it would emit elements zod rejects.
|
|
627
|
+
|
|
628
|
+
**Final run: 142 passed, 0 failed** against `demo-org` — 10 flow types/structures, 20 record-trigger configurations, 22 variable/resource cases, 68 element cases, 12 connector and guardrail cases, 5 lifecycle (activate/versions/deactivate), and 5 runtime assertions proving before-save stamping, after-save related-record creation, entry-criteria suppression, Loop iteration, and Update Records all actually execute in the org.
|
|
629
|
+
|
|
630
|
+
One suite failure was a test bug, not a product defect, and is recorded as such: the entry-criteria check compared the `Industry` picklist with a bare `=`, which Salesforce rejects in favour of `ISPICKVAL`. The tool passed the caller's formula through verbatim and surfaced Salesforce's real reason — correct behavior, so the test was fixed rather than the code.
|
|
631
|
+
|
|
632
|
+
`server.json`'s description still claimed 219 tools; corrected to 221.
|
|
633
|
+
|
|
634
|
+
## [2.8.2] - 2026-07-30
|
|
635
|
+
|
|
636
|
+
### Fixed — 5 more real bugs found by full regression testing across all 221 tools
|
|
637
|
+
|
|
638
|
+
Ajay asked for thorough regression testing against every tool with edge cases, not just the tools touched in v2.8.0/2.8.1. Ran the repo's full `test-suite.mjs` (215 tests, all 221 tools either directly exercised or reviewed) against `demo-org`, plus the dedicated Flow QA suite (33 scenarios). Found and fixed:
|
|
639
|
+
|
|
640
|
+
- **`sf_create_global_value_set`** — schema didn't enforce or document the required `__gvs` fullName suffix; Salesforce rejects GlobalValueSet without it. Added regex validation matching the existing `__c`/`__mdt` pattern used by other metadata types, plus a description note.
|
|
641
|
+
- **`sf_create_business_process`** — XML included a `<label>` element that doesn't exist in the `BusinessProcess` Metadata API type at all (Salesforce: "label invalid at this location"). Removed it; `label` still feeds the `description` fallback.
|
|
642
|
+
- **`sf_create_dashboard`** — built XML inline without escaping (`ui.ts`), unlike every other metadata builder in the codebase. A `&` in the title broke the deploy outright. Applied the `x()` escaping helper to `fullName`, `title`, `description`, `runningUser`, and component `reportApiName`/`header`/`footer`.
|
|
643
|
+
- **`sf_create_escalation_rule`** — same missing-escaping issue (`automation.ts`) on `formula`, `assignedTo`, `notifyTo`, `template`, `ruleName`. `formula` is especially exposed since Salesforce formulas commonly contain `<`, `>`, `&&`.
|
|
644
|
+
- **`sf_create_field_dependency`** — two-layer bug. (1) Wrapped `controllingField`/`valueSettings` in a `<fieldDependency>` element that doesn't exist on `CustomField` (Salesforce: "fieldDependency invalid at this location") — they belong directly inside `<valueSet>`. (2) Even after that fix, a from-scratch SOAP update carrying only the new dependency info failed with "Could not resolve standard field's name" — Salesforce needs the field's full existing metadata (label, type, current values) present in the same update. Rewrote using the same Tooling-API read-merge-PATCH pattern already proven by `addPicklistValues`, instead of hand-building SOAP XML.
|
|
645
|
+
|
|
646
|
+
Also fixed **`getFreshTokenFromCLI`** (JWT/CLI auth path): didn't check `err.stdout` on a non-zero exit, so a transient CLI hiccup (e.g. an update-nag banner polluting stdout) discarded an otherwise-valid access token. Same root cause as the `execSfCli` fix in 2.8.1, applied to the one auth code path that predates it.
|
|
647
|
+
|
|
648
|
+
**Test coverage added:** `test-suite.mjs` gained a `sf_create_field_dependency` test (previously imported but never exercised) and its `sf_create_global_value_set`/`sf_delete_scratch_org` test data bugs were fixed (wrong fullName suffix, wrong parameter name — test bugs, not product bugs). `sf_update_dashboard`'s test no longer assumes a folder literally named "Dashboards" exists (org-specific sample-data assumption); it now discovers a real Dashboard folder first.
|
|
649
|
+
|
|
650
|
+
**Final confirmation run: 212 passed, 2 expected negative-test outcomes (deleting a nonexistent scratch org / installing a fake package ID — both now correctly surface the real Salesforce rejection reason instead of a generic error, proof the 2.8.1 error-handling fix works), 1 skipped (OmniStudio, not enabled in this org), 215 total.**
|
|
651
|
+
|
|
652
|
+
**Flow QA suite (33 scenarios, `qa-flow-test.mjs`) — fully resolved, zero code bugs found:** Initial run showed 18 passed / 15 failed. Root-caused every failure instead of assuming they were code defects: 11 traced to the test assuming specific Account/Opportunity records ("Apex Technologies", "Opp-Acct1-1") existed in the org — leftover from whatever org this suite was originally built against, absent in `demo-org` (which only has standard DE sample data like "Edge Communications"). Created the missing seed records; those 11 passed immediately with no code changes. 1 (T01) was pure infra flakiness — a Windows `cmd.exe` spawn timeout unrelated to Salesforce, passed clean on retry. The remaining 3 (`PROD_Get_Account_Overview` etc.) test pre-existing production flows this suite never creates — they don't exist in any org this suite would be pointed at fresh, confirmed out of scope. **Final: 30/30 real scenarios passing.** This also means the "Known Bugs Pending Fix" list in CLAUDE.md (Loop elements, cross-variable filters, Decision XML, GetRecords gaps, missing inline-XML support, missing `sf_create_flow_from_xml`) was entirely stale — none of it reproduced. Retired that list in CLAUDE.md with a dated note explaining why, rather than leaving inaccurate bug reports in the project's own docs.
|
|
653
|
+
|
|
654
|
+
Also fixed `qa-flow-test.mjs`'s own test harness bug: hardcoded `--target-org secondorg` in its Apex-verification helper, now reads `SF_ALIAS` with a fallback.
|
|
655
|
+
|
|
656
|
+
## [2.8.1] - 2026-07-29
|
|
657
|
+
|
|
658
|
+
### Fixed — 5 real bugs found by thorough live-org testing of v2.8.0, same day
|
|
659
|
+
|
|
660
|
+
Ajay pointed live testing at a working CLI-authenticated org (`demo-org`) after v2.8.0 shipped without it, then asked for thorough testing before this patch went out. Testing surfaced genuine defects:
|
|
661
|
+
|
|
662
|
+
- **`sf_run_code_scanner` returned 404 against every org** — its Tooling API query path double-prefixed `/services/data/v66.0` (the HTTP client's base URL already includes it). Copied this bug from `sf_scan_apex_antipatterns`.
|
|
663
|
+
- **`sf_scan_apex_antipatterns` has had the identical 404 bug since it shipped in v2.7.0 (2026-07-23)** — it was never tested against a live org before now, so this went undetected for 6 days across two releases. Fixed alongside the copy.
|
|
664
|
+
- **`sf_run_code_scanner`'s Java-fallback was cosmetic, not functional** — restricting `--rule-selector` to Java-free engines doesn't stop `code-analyzer` from still trying to *instantiate* the PMD/CPD/SFGE engines first, so it threw 3 "Critical" `UninstantiableEngineError` pseudo-violations even while the response's own `note` field claimed those engines "were skipped." Fixed by generating a `code-analyzer.yml` that explicitly sets `disable_engine: true` on each when Java isn't detected — verified clean (0 fake violations) against `demo-org`.
|
|
665
|
+
- **Every SF-CLI-wrapping function (`installPackage`, `uninstallPackage`, `createPackage`, `createPackageVersion`, `deleteScratchOrg`, `createNewScratchOrg`) swallowed the actual Salesforce error on failure** — Node's `execSync` puts the CLI's `--json` error payload on `err.stdout`, not `err.message`, so callers only ever saw a generic "Command failed: sf package ..." with no reason. Extracted a shared `execSfCli()` helper that parses `err.stdout` first; verified live by attempting to uninstall `demo-org`'s installed package and correctly receiving the real Salesforce reason ("Unable to delete custom app. Profiles are using this custom app as default...") instead of the useless wrapper text.
|
|
666
|
+
- **`sf_install_package` (pre-existing, not something this session touched originally) hung/force-failed on any package requesting third-party site access** — `sf package install` shows an interactive "Grant access?" confirmation for Remote Site Settings/CSP, which `execSync`'s non-interactive session can never answer, throwing `ExitPromptError`. Fixed by always passing `--no-prompt` (there's no scenario where an MCP tool call can answer an interactive prompt, so this is correct unconditionally, not just a default).
|
|
667
|
+
|
|
668
|
+
**All fixes verified against `demo-org` with real success AND failure paths, not just one or the other:**
|
|
669
|
+
- `sf_run_code_scanner` / `sf_scan_apex_antipatterns`: real findings returned (4 classes scanned, 0 violations, accurate `note`)
|
|
670
|
+
- `sf_uninstall_package`: failure path confirmed — real multi-component Salesforce rejection reason now surfaces correctly
|
|
671
|
+
- `sf_install_package`: **full success path confirmed** — real install against `demo-org` returned `Status: "SUCCESS"` end to end, only possible after the `--no-prompt` fix
|
|
672
|
+
- Broader regression sanity: unrelated, untouched tools (`sf_query_records`, `sf_describe_object`) spot-checked against the same org to confirm the shared auth/HTTP client plumbing wasn't affected
|
|
673
|
+
|
|
674
|
+
**Known remaining gap, documented not hidden:** `createNewScratchOrg`/`deleteScratchOrg`/`createPackage`/`createPackageVersion` could not be exercised live — `demo-org` is not Dev Hub-enabled, and creating one is out of scope for a same-day bug-fix pass. Their `execSfCli` usage is mechanically identical to `installPackage`'s (now proven), so risk is low, but this is explicitly unverified, not silently assumed fine.
|
|
675
|
+
|
|
3
676
|
## [2.8.0] - 2026-07-29
|
|
4
677
|
|
|
5
678
|
### Added — 2 new tools closing gaps found in a fresh competitive scan
|