salesforce-metadata-mcp 2.8.7 → 2.11.2
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 +464 -0
- package/README.md +4 -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 +52 -33
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +23 -6
- package/dist/schemas/index.js.map +1 -1
- package/dist/services/deployment.d.ts +28 -3
- package/dist/services/deployment.d.ts.map +1 -1
- package/dist/services/deployment.js +58 -12
- 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 +37 -3
- package/dist/services/salesforce.d.ts.map +1 -1
- package/dist/services/salesforce.js +319 -126
- package/dist/services/salesforce.js.map +1 -1
- package/dist/tools/agentforce.d.ts.map +1 -1
- package/dist/tools/agentforce.js +155 -33
- package/dist/tools/agentforce.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/metadata.d.ts.map +1 -1
- package/dist/tools/metadata.js +13 -1
- package/dist/tools/metadata.js.map +1 -1
- package/package.json +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,469 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.11.2] - 2026-08-06
|
|
4
|
+
|
|
5
|
+
### Security — resolved all 5 production-dependency advisories (2 high, 3 moderate)
|
|
6
|
+
|
|
7
|
+
`@modelcontextprotocol/sdk` bumped `1.29.0` → `1.30.0` (minor, non-breaking). Every advisory traced
|
|
8
|
+
back to that one dependency:
|
|
9
|
+
|
|
10
|
+
| Package | Advisory | Severity |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| `@modelcontextprotocol/sdk` | vulnerable range 1.25.0–1.29.0 | moderate |
|
|
13
|
+
| `@hono/node-server` | path traversal in `serve-static` on Windows via encoded backslash (`%5C`) | moderate |
|
|
14
|
+
| `hono` | ReDoS in CORS middleware via `Access-Control-Request-Headers` | moderate |
|
|
15
|
+
| `ip-address` | leading-zero octet / CIDR-suffix / IPv4-mapped-IPv6 parsing bugs enabling SSRF and trust-boundary bypass (3 advisories) | high |
|
|
16
|
+
| `fast-uri` | host confusion via backslash authority introducer | high |
|
|
17
|
+
|
|
18
|
+
Only two of these actually required a change here. `hono`, `fast-uri`, and `ip-address` sit under
|
|
19
|
+
parent ranges (`^4.11.4`, `^3.0.1`, `^10.2.0`) that already admit their patched versions, so any fresh
|
|
20
|
+
install resolves them safely regardless of what this repo's lockfile happens to pin. `@hono/node-server`
|
|
21
|
+
did not: SDK 1.29.0 constrained it to `^1.19.9`, which cannot reach the patched 2.0.5. SDK 1.30.0
|
|
22
|
+
widens that to `^1.19.9 || ^2.0.5`. The SDK's own advisory needed the bump too.
|
|
23
|
+
|
|
24
|
+
**Note on why the lockfile is not the fix.** This package is a library — consumers resolve from the
|
|
25
|
+
`dependencies` ranges in `package.json` at install time and never see `package-lock.json`. Running
|
|
26
|
+
`npm audit fix` alone would have cleaned the local `npm audit` output while changing nothing for
|
|
27
|
+
anyone installing from npm. The dependency bump is what actually reaches users, and only once
|
|
28
|
+
published.
|
|
29
|
+
|
|
30
|
+
Resolved after the bump: `@modelcontextprotocol/sdk` 1.30.0, `@hono/node-server` 2.1.0, `hono` 4.13.0,
|
|
31
|
+
`fast-uri` 3.1.5, `ip-address` 10.4.0. `npm audit` reports 0 vulnerabilities for both production and
|
|
32
|
+
full trees.
|
|
33
|
+
|
|
34
|
+
Verified beyond the audit output, since the SDK is the protocol layer and a clean `tsc` proves nothing
|
|
35
|
+
about runtime: **stdio** transport handshakes and lists 223 tools; **HTTP** transport (`TRANSPORT=http`)
|
|
36
|
+
serves `/health`, completes an `initialize` over `/mcp`, 404s unknown paths, and still binds
|
|
37
|
+
`127.0.0.1` by default (the v2.8.8 hardening is intact — this mattered to check, as the changed
|
|
38
|
+
packages are exactly the ones behind that transport); and a real `tools/call` round-trip dispatches
|
|
39
|
+
through zod validation into the handler, failing only at the expected credentials boundary.
|
|
40
|
+
|
|
41
|
+
### Fixed — server advertised a stale hardcoded version to MCP clients
|
|
42
|
+
|
|
43
|
+
`src/index.ts` hardcoded the version in four places (`McpServer` init, both startup banners, the
|
|
44
|
+
`/health` payload) and was never bumped for v2.11.0 or v2.11.1, so the server reported **2.10.0** in
|
|
45
|
+
its `initialize` response while `package.json` said 2.11.1. Now read from `package.json` at runtime via
|
|
46
|
+
`createRequire(import.meta.url)`, so it cannot drift again. A direct JSON import is not viable here:
|
|
47
|
+
`package.json` sits outside `tsconfig`'s `rootDir` (`./src`), so importing it would restructure `dist/`.
|
|
48
|
+
|
|
49
|
+
### Docs
|
|
50
|
+
|
|
51
|
+
Corrected the tool count in `QUICKSTART.md` (212 → 223) and in the GitHub repository description
|
|
52
|
+
(212 → 223). Both had drifted for the same reason: neither ships in the npm tarball, so publishing
|
|
53
|
+
never surfaces them the way it does `README.md`/`TOOLS.md`/`package.json`.
|
|
54
|
+
|
|
55
|
+
## [2.11.1] - 2026-08-03
|
|
56
|
+
|
|
57
|
+
### Fixed — `sf_retrieve_metadata` reported success on zero-result retrieves, plus two dead parameters found while investigating
|
|
58
|
+
|
|
59
|
+
Ajay's follow-up report confirmed v2.11.0's `sf_create_agent_action`/`sf_describe_object` fixes were
|
|
60
|
+
correct — his contradicting evidence turned out to be tested against a stale Claude Desktop MCP
|
|
61
|
+
subprocess predating the rebuild (a recurring gotcha this project has hit before: a rebuilt `dist/`
|
|
62
|
+
only takes effect after a full restart). His genuinely new finding, independent of that mix-up, was
|
|
63
|
+
real: `sf_retrieve_metadata` returned `success: true` and `"Retrieved 1 file(s)."` for a retrieve that
|
|
64
|
+
found **zero** of the requested components — because Salesforce's retrieve zip always includes
|
|
65
|
+
`package.xml` as its own manifest even when every requested member comes back empty, and the file count
|
|
66
|
+
included it. Verified with his exact repro (4 nonexistent `GenAiFunction` names) against a control
|
|
67
|
+
retrieve of a real component.
|
|
68
|
+
|
|
69
|
+
Fixed in `retrieveMetadataAndWait`: the reported count now excludes `package.xml`, and when components
|
|
70
|
+
were requested by name but none came back, the result is `success: false` with an explicit message
|
|
71
|
+
listing what was requested — instead of a misleadingly cheerful count of a manifest nobody asked for.
|
|
72
|
+
|
|
73
|
+
**Investigating this surfaced two more, more severe versions of the same underlying problem**, both
|
|
74
|
+
previously untested: the tool's schema has advertised `metadataType`+`componentName` (single-component
|
|
75
|
+
shortcut) and `packageXml` (raw manifest) as alternatives to `components` since this tool existed, but
|
|
76
|
+
the handler only ever read `params.components` — the other two were silently no-ops. A call using
|
|
77
|
+
either form retrieved **nothing at all** (an empty `<unpackaged>` body) while still reporting a
|
|
78
|
+
"successful" retrieve of the manifest-only zip, even when the requested component genuinely existed.
|
|
79
|
+
Wired up both: `metadataType`/`componentName` now builds a single-item `components` array, and
|
|
80
|
+
`packageXml` now extracts the `<types>`/`<version>` content from the supplied document and uses it
|
|
81
|
+
directly as the retrieve request body (needs the `met:` namespace prefix added to every element, since
|
|
82
|
+
the SOAP body has no default namespace, unlike a standalone package.xml). Verified live for all three
|
|
83
|
+
paths: the shortcut form now retrieves a real flow's XML, the zero-match array form now fails honestly,
|
|
84
|
+
and a hand-written raw `packageXml` retrieve returns real content.
|
|
85
|
+
|
|
86
|
+
No regressions: `test-suite.mjs` 209/212 (2 pre-existing unrelated failures, same as before).
|
|
87
|
+
|
|
88
|
+
## [2.11.0] - 2026-08-03
|
|
89
|
+
|
|
90
|
+
### 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
|
|
91
|
+
|
|
92
|
+
Ajay reported that `sf_create_agent_action` failed for `type=Flow` even against a confirmed-Active
|
|
93
|
+
flow, and that the failure message wrongly concluded custom agent actions were unsupported in the org
|
|
94
|
+
("check your Agentforce/Einstein licensing") — disprovable because he had just created five
|
|
95
|
+
Flow-backed actions successfully through Salesforce's own Agentforce Builder UI, in the same org, same
|
|
96
|
+
user, same flows.
|
|
97
|
+
|
|
98
|
+
**Root cause, found by following Ajay's own recommended debugging path (diff a UI-created action's
|
|
99
|
+
real XML against what the tool generates) and going further once retrieval came back empty:**
|
|
100
|
+
`sf_create_agent_action` built a classic Metadata API `.genAiFunction` deploy with
|
|
101
|
+
`<invocationTarget>` set to the flow/class **API name** — but UI-created actions, read back via the
|
|
102
|
+
Tooling API's `GenAiFunctionDefinition` object (the classic Metadata API's `GenAiFunction` type turned
|
|
103
|
+
out not to expose these records at all — confirmed by a wildcard retrieve finding zero results even
|
|
104
|
+
for the five UI-created ones), showed `InvocationTarget` holding an 18-character **record ID**
|
|
105
|
+
(`FlowDefinition.Id` / `ApexClass.Id`), not a name. The generic "Specify a valid invocationTarget and
|
|
106
|
+
invocationTargetType" error — previously treated as a reliable org-capability signal after testing in
|
|
107
|
+
an unrelated org — turned out to be Salesforce's error for *this exact payload bug* in an org that
|
|
108
|
+
supports the feature just fine.
|
|
109
|
+
|
|
110
|
+
Fixed by switching `sf_create_agent_action` from the classic Metadata API deploy to a Tooling API
|
|
111
|
+
`GenAiFunctionDefinition` upsert — the same mechanism Agentforce Builder itself uses — resolving
|
|
112
|
+
`reference` to the right record ID first (`FlowDefinition.DeveloperName` → `Id` for Flow,
|
|
113
|
+
`ApexClass.Name` → `Id` for ApexClass). Verified live end-to-end, not just unit-tested: created a real
|
|
114
|
+
action against Ajay's own reported flow through the actual registered tool handler (reproducing his
|
|
115
|
+
repro exactly), confirmed the fix also works for ApexClass, and confirmed a Tooling-API-created action
|
|
116
|
+
is correctly picked up by a topic (`GenAiPlugin`) deployed the normal Metadata-API way — the two APIs
|
|
117
|
+
share the same underlying records, so mixing them across the 5-step agent sequence is safe.
|
|
118
|
+
`PromptTemplate`/`DataCategoryGroup`/`ExternalService` are now honestly refused (clear "not yet
|
|
119
|
+
verified" message) rather than silently deploying an unverified, likely-broken payload — matching this
|
|
120
|
+
project's standing rule not to ship unverified metadata behavior.
|
|
121
|
+
|
|
122
|
+
**`sf_create_agent`'s pre-flight capability probe (added in v2.10.0)** used the same broken deploy
|
|
123
|
+
path internally, so it always failed and hard-blocked agent creation even in orgs — like this one —
|
|
124
|
+
where it works fine. Rewritten to match: it now attempts a real Tooling API insert with a
|
|
125
|
+
syntactically-valid-but-nonexistent target ID. This fails fast on a specific field-validation error in
|
|
126
|
+
any org where the object exists (verified live: `INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST`), and since
|
|
127
|
+
nothing is ever actually written, there's no cleanup step either — simpler than the old probe, which
|
|
128
|
+
had to deploy-then-delete a throwaway. Verified live: `sf_create_agent` now succeeds normally in this
|
|
129
|
+
org, both with and without `skipActionCapabilityCheck`.
|
|
130
|
+
|
|
131
|
+
Also fixed: `sf_describe_object` returned a bare `NOT_FOUND` 404 for standard objects whose *feature*
|
|
132
|
+
is disabled (Ajay's repro: `Quote`, when the Quotes feature is off in Setup) — indistinguishable from a
|
|
133
|
+
typo'd or nonexistent object name. Added a hint for a hardcoded list of commonly feature-gated standard
|
|
134
|
+
objects (Quote, Contract, Order, Campaign, Territory2, WorkOrder, ServiceAppointment, etc.) pointing at
|
|
135
|
+
Setup instead of sending the user hunting for a naming mistake that isn't there.
|
|
136
|
+
|
|
137
|
+
**`qa-agentforce.mjs` needed a substantial rewrite, not just the tool code.** The suite's own
|
|
138
|
+
assertions were written under the same now-disproven assumption ("demo-org cannot create GenAiFunction
|
|
139
|
+
actions") baked into skip logic throughout — so the action/topic verification paths had never actually
|
|
140
|
+
executed in any prior session, and once they finally ran (because the underlying bug they were gated
|
|
141
|
+
behind is now fixed), they surfaced a second, independent, pre-existing gap: `existsInOrg`'s plain REST
|
|
142
|
+
`queryRecords` can't see `GenAiFunction`/`GenAiFunctionDefinition` (Tooling-API-only, confirmed) or
|
|
143
|
+
`GenAiPlugin` (no SOQL interface at all, confirmed both plain REST and Tooling — only readable via
|
|
144
|
+
classic Metadata API `readMetadata`) — it happened to work before only because those checks were never
|
|
145
|
+
reached. Rewrote `existsInOrg` to dispatch to the right API per type, and rewrote the action-type
|
|
146
|
+
matrix check to read `GenAiFunctionDefinition.InvocationTargetType` back via Tooling API instead of a
|
|
147
|
+
`retrieveMetadata` call that (per the finding above) was always going to come back empty. Suite went
|
|
148
|
+
from 33 passed/15 failed (pre-fix) to 35 passed/2 failed against a live org.
|
|
149
|
+
|
|
150
|
+
**Known open item, not a code defect:** after extensive live testing today (mine plus Ajay's own
|
|
151
|
+
UI-based testing), further `GenAiFunctionDefinition` inserts in `demo-org` started failing with
|
|
152
|
+
`DUPLICATE_DEVELOPER_NAME` ("...already exists or has been previously used") even against
|
|
153
|
+
brand-new, never-used developer names and master labels — isolated by testing fresh-name/reused-label
|
|
154
|
+
and fresh-everything/reused-target combinations independently; neither explains it, and the condition
|
|
155
|
+
persisted for several minutes and across unrelated work. Most likely an org-side rate limit or cooldown
|
|
156
|
+
on this object triggered by the volume of creates/deletes during today's testing, not a regression in
|
|
157
|
+
this fix — the two remaining `qa-agentforce.mjs` failures are this condition surfacing honestly through
|
|
158
|
+
the tool's real error message, not a masked defect. Re-run the suite after some time has passed to
|
|
159
|
+
confirm it clears; if it doesn't, this needs further investigation as a genuine platform limit.
|
|
160
|
+
|
|
161
|
+
## [2.10.0] - 2026-08-01
|
|
162
|
+
|
|
163
|
+
### Fixed/Added — 4 more findings from an extended manual test session (Bugs 6-8, and a sharper Bug 3), plus 2 new tools
|
|
164
|
+
|
|
165
|
+
Ajay's follow-up report superseded the previous one. Bugs 1, 2, 4, 5 from that report were already
|
|
166
|
+
fixed in v2.9.0 (confirmed still present in this session, not re-fixed) — the report describing them
|
|
167
|
+
as unfixed was written from a Claude Desktop session whose MCP server subprocess had been running
|
|
168
|
+
since before v2.9.0 landed; restarting Claude Desktop picks up a rebuilt `dist/`, an already-spawned
|
|
169
|
+
subprocess does not. New findings below.
|
|
170
|
+
|
|
171
|
+
**Bug 3, revisited with much stronger evidence.** The previous release's conclusion (org-side schema
|
|
172
|
+
propagation lag) held, but Ajay's new evidence sharpened *what kind* of lag it is: persisted 2+ hours
|
|
173
|
+
in his session (ruling out ordinary propagation delay), and — critically — REST describe worked fine
|
|
174
|
+
on **pre-existing** custom fields on standard objects (Opportunity's own custom fields) while failing
|
|
175
|
+
on everything newly created, which looked like it might be specific to brand-new custom *objects*
|
|
176
|
+
rather than fields in general. Tested that distinction directly and decisively: created a field on the
|
|
177
|
+
standard `Account` object and a field on a brand-new custom object side by side, then polled both for
|
|
178
|
+
2 full minutes. **Both were equally stuck** — this rules out "new object vs. existing object" as the
|
|
179
|
+
differentiator. The real explanation: Ajay's "working" standard-object fields were older, already-
|
|
180
|
+
propagated fields from earlier sessions, not freshly created ones — not evidence that new fields on
|
|
181
|
+
standard objects propagate faster. Also directly tested Ajay's identity-mismatch hypothesis (REST and
|
|
182
|
+
Metadata clients authenticating as different users): confirmed live that both use the exact same
|
|
183
|
+
`auth.accessToken`, resolving to the same user (`semwalajaydevorg@agentforce.com`) and same org ID —
|
|
184
|
+
ruled out. Revised, more precise conclusion: this org has a severe (multi-hour-observed), general
|
|
185
|
+
schema-cache propagation lag affecting all newly created fields, regardless of parent object type —
|
|
186
|
+
still not something any client-side code change can fix.
|
|
187
|
+
|
|
188
|
+
**Bug 6: `sf_create_agent` now probes GenAiFunction (custom action) support before creating a Bot
|
|
189
|
+
shell.** Reproduced Ajay's exact finding: active Agentforce permission set licenses are not sufficient
|
|
190
|
+
evidence that custom actions work — confirmed by deploying a `GenAiFunction` aimed at a deliberately
|
|
191
|
+
nonexistent target and getting the identical generic error a real target would get, meaning the org
|
|
192
|
+
never even attempts target resolution. Added a pre-flight probe (a real, throwaway `GenAiFunction`
|
|
193
|
+
deploy, cleaned up automatically) that runs before the Bot shell is created on the first call in the
|
|
194
|
+
5-step sequence: if actions aren't supported, the call fails immediately with no shell created, instead
|
|
195
|
+
of leaving an orphaned Bot with no planner/topic/action once step 2 turns out to be unreachable. Added
|
|
196
|
+
`skipActionCapabilityCheck` for topics-only agents that don't need custom actions. Verified live both
|
|
197
|
+
ways: the probe correctly blocks with no shell created, and the skip flag correctly bypasses it.
|
|
198
|
+
|
|
199
|
+
**Bug 7: `sf_create_agent_topic` now validates that every referenced action exists before deploying.**
|
|
200
|
+
Previously, a topic referencing a missing action deployed and failed with an opaque Salesforce support
|
|
201
|
+
ErrorId naming nothing. `GenAiFunction` isn't SOQL/Tooling-queryable in every org (confirmed: it isn't
|
|
202
|
+
in `demo-org`), so existence is checked via Metadata API `readMetadata` instead, which works regardless
|
|
203
|
+
of SOQL support for the type. Verified live: a topic referencing a nonexistent action is now rejected
|
|
204
|
+
before ever reaching Salesforce, naming the specific missing action.
|
|
205
|
+
|
|
206
|
+
**Bug 8: added `sf_delete_metadata`.** There was previously no way to remove anything deployed by this
|
|
207
|
+
MCP server — `sf_deploy_metadata` has no `destructiveChanges` support, and diagnostic/orphaned metadata
|
|
208
|
+
had nowhere to go (confirmed: Ajay's stranded `AccountOpportunityAgent` Bot shell was still in the org,
|
|
209
|
+
alongside 13 similar leftovers from earlier sessions). Wraps the `deleteMetadata` SOAP call, which
|
|
210
|
+
already existed as an internal service function but was never exposed as a tool. Verified live with a
|
|
211
|
+
real round-trip (create → delete → confirm gone) and by actually attempting cleanup of the accumulated
|
|
212
|
+
org backlog: correctly surfaced Salesforce's own 10-record-per-call limit (batched around it), and
|
|
213
|
+
correctly surfaced a real dependency-order error when a `GenAiPlannerBundle` was still referenced by a
|
|
214
|
+
Bot. The specific stuck Bot records from earlier sessions remain undeletable — a pre-existing, already-
|
|
215
|
+
documented Salesforce-side "unexpected error" on those particular records, not something this tool or
|
|
216
|
+
any client-side retry can work around.
|
|
217
|
+
|
|
218
|
+
**Documented, not a bug**: Flow `textTemplates` strip leading/trailing whitespace on deploy while
|
|
219
|
+
preserving internal newlines — concatenating per-iteration templates in a loop without an internal
|
|
220
|
+
separator runs lines together. Added to the `sf_create_flow` schema description.
|
|
221
|
+
|
|
222
|
+
**Confirmed already fixed, not re-touched**: Bugs 1 (duplicate Decision rule names), 2 (filter value
|
|
223
|
+
typing), 4 (FLS warning), 5 (raw-XML pre-validation) — all still present and correct in this codebase,
|
|
224
|
+
verified by direct inspection before assuming anything needed re-fixing.
|
|
225
|
+
|
|
226
|
+
Tool count 222→223 (`sf_delete_metadata`). Regression: `qa-agentforce.mjs` gained 5 new checks for
|
|
227
|
+
Bugs 6/7 (all existing `sf_create_agent` calls updated with `skipActionCapabilityCheck` so the new
|
|
228
|
+
probe doesn't change what those pre-existing tests were actually testing); `test-suite.mjs` gained a
|
|
229
|
+
real create→delete→confirm-gone round-trip test for `sf_delete_metadata`.
|
|
230
|
+
|
|
231
|
+
## [2.9.0] - 2026-08-01
|
|
232
|
+
|
|
233
|
+
### Fixed — 5 real bugs from a genuine manual test session in Claude Desktop, all reproduced and re-verified live
|
|
234
|
+
|
|
235
|
+
Ajay tested v2.8.9 by hand in Claude Desktop against a real Developer Edition org — created a custom
|
|
236
|
+
object, six custom fields, and two Flows built against them — and reported five specific issues found
|
|
237
|
+
along the way. Every one below was reproduced first (not assumed from the report), root-caused in the
|
|
238
|
+
source, fixed, and re-verified against `demo-org` before being called done.
|
|
239
|
+
|
|
240
|
+
**`sf_create_flow` emits duplicate rule developer names for 2+ Decision elements (blocking).**
|
|
241
|
+
Reproduced exactly: `Deployment failed: Duplicate developer name: Rule_1`. Root cause: each Decision's
|
|
242
|
+
rule `<name>` was `Rule_1`, `Rule_2`, ... scoped per-decision, but Salesforce requires uniqueness across
|
|
243
|
+
the whole Flow. Fixed in both XML generators by namespacing with the parent Decision's own name
|
|
244
|
+
(`Decision_One_Rule_1`), verified unique in the deployed XML afterward. Fixing this surfaced a second,
|
|
245
|
+
related bug in the same code path: `<defaultConnectorLabel>` was only emitted when `defaultConnector`
|
|
246
|
+
was set, but Salesforce requires it unconditionally (it labels the implicit "no rule matched" branch,
|
|
247
|
+
which exists whether or not it connects anywhere) — every existing Decision test in this repo's own
|
|
248
|
+
suite happened to set `defaultConnector` explicitly, which is exactly why this had never been caught.
|
|
249
|
+
Fixed in both generators; added a permanent regression test with two Decisions, the second deliberately
|
|
250
|
+
relying on implicit fall-through. Flow suite: 142→144 checks, 144/144 passing.
|
|
251
|
+
|
|
252
|
+
**`sf_create_flow`'s filter/assignment values only accepted strings, and GetRecords filters silently
|
|
253
|
+
mistyped numbers.** The schema rejected a native boolean (`value: true`) with a bare zod error; the
|
|
254
|
+
reported workaround (`value: "true"`) did work correctly (booleans were already string-sniffed
|
|
255
|
+
correctly), but investigating this surfaced a real, more serious, previously-unreported bug in the same
|
|
256
|
+
code: GetRecords filter values had NO numeric detection at all — unlike Decision/CreateRecords/
|
|
257
|
+
UpdateRecords, which all correctly detect numeric-looking values, GetRecords filters emitted
|
|
258
|
+
`<stringValue>100</stringValue>` for every non-boolean value including numbers, which risks incorrect
|
|
259
|
+
or no matches when filtering Number/Currency/Percent fields with comparison operators. Fixed the missing
|
|
260
|
+
numeric detection in both generators, and widened `rightValue`/`filterValue`/`filters[].value`/
|
|
261
|
+
`inputAssignments[].value`/`assignments[].value` to accept string, number, or boolean directly (coerced
|
|
262
|
+
to string before the existing, now-correct typed-XML logic) so the natural call shape works without a
|
|
263
|
+
string-coercion workaround. Verified live: a native boolean and a native number filter both produce the
|
|
264
|
+
correct typed XML element in the deployed flow.
|
|
265
|
+
|
|
266
|
+
**REST describe / SOQL not seeing custom fields that the Metadata API and Tooling API confirm exist.**
|
|
267
|
+
Reproduced exactly — `describe field count: 10` (only standard fields), matching the report precisely,
|
|
268
|
+
persisting well past 60 seconds and after granting FLS (also reproduced: 0 grants by default). Ruled
|
|
269
|
+
out an in-process cache (there isn't one — `sf_describe_object` makes a fresh HTTP call every time,
|
|
270
|
+
confirmed by reading the code) and ruled out XML element ordering in the picklist-value generator via a
|
|
271
|
+
controlled live A/B test (an initial hypothesis that looked promising but didn't hold up once tested
|
|
272
|
+
head-to-head). What's actually happening: Salesforce's own REST describe/SOQL schema cache lagged
|
|
273
|
+
behind the Metadata API and (intermittently, itself) the Tooling API by 10+ minutes on this org during
|
|
274
|
+
testing — a genuine platform-side propagation characteristic, not something any client-side code
|
|
275
|
+
change can fix. Implemented the mitigation Ajay suggested regardless: `sf_describe_object` gained
|
|
276
|
+
`waitForFields`/`timeoutSeconds` to poll until named fields appear (or say clearly that they still
|
|
277
|
+
haven't, with a pointer to the Tooling API to confirm the field truly exists), so callers don't have to
|
|
278
|
+
hand-roll retry loops. Documented plainly in both the tool description and the CHANGELOG that this is
|
|
279
|
+
an org-side lag, not a bug in this server — don't re-diagnose it as one without new evidence.
|
|
280
|
+
|
|
281
|
+
**`sf_create_custom_field` leaves every new field with zero FLS grants and no indication a follow-up
|
|
282
|
+
call is needed.** Reproduced: `sf_get_field_permissions` returns 0 grants immediately after a
|
|
283
|
+
`success: true` field creation, for every profile including System Administrator. Fixed by checking FLS
|
|
284
|
+
right after creation and appending an explicit warning to the response when none exist, naming the
|
|
285
|
+
exact fix (`sf_create_field_level_security`) — chose this over auto-granting FLS by default since that
|
|
286
|
+
would silently change existing behavior; surfacing the truth is a strictly additive fix.
|
|
287
|
+
|
|
288
|
+
**`sf_create_flow_from_xml` surfaced raw, unhelpful Salesforce schema errors for 3 common mistakes.**
|
|
289
|
+
Added `validateAndNormalizeFlowXml()`: a bounded, depth-tracking tokenizer (not a full XML parser —
|
|
290
|
+
sufficient for direct children of the root `<Flow>` element) that catches, before ever deploying: (1)
|
|
291
|
+
non-contiguous top-level elements of the same type (interleaved `<assignments>`/`<decisions>` etc.,
|
|
292
|
+
which Salesforce reports as a baffling "Element X is duplicated at this location in type Flow" instead
|
|
293
|
+
of naming the real issue — grouping), (2) missing `<start>` `locationX`/`locationY`, auto-defaulted
|
|
294
|
+
rather than just flagged since there's no ambiguity about a safe default, (3) an SObject-typed
|
|
295
|
+
`<variables>` entry missing `<objectType>`, naming the specific variable (Salesforce's own error for
|
|
296
|
+
this one was already good — replicated its style for consistency, and to catch it pre-deploy instead of
|
|
297
|
+
after). Verified live: interleaved elements and a missing objectType are both now rejected pre-deploy
|
|
298
|
+
with a specific, actionable message and never reach Salesforce at all; missing start coordinates are
|
|
299
|
+
silently defaulted and the flow deploys successfully.
|
|
300
|
+
|
|
301
|
+
**Full regression, both before committing and after every individual fix**: `qa-flow-comprehensive.mjs`
|
|
302
|
+
142→144/144 (2 new checks from the Bug 1 regression test, all passing). `test-suite.mjs` and the
|
|
303
|
+
Agentforce suites held their existing pass rates — none of these five fixes touch Agentforce code paths.
|
|
304
|
+
|
|
305
|
+
## [2.8.9] - 2026-07-31
|
|
306
|
+
|
|
307
|
+
### Fixed — the v2.8.8 command-injection guard was over-broad; narrowed after a second round of live testing
|
|
308
|
+
|
|
309
|
+
Asked for a second round of testing before publishing v2.8.8, rather than treating the first pass as
|
|
310
|
+
done. Good call: it surfaced a real false-positive regression in the fix itself. The original guard
|
|
311
|
+
rejected any `sf` CLI argument containing `` " ` $ & | ; < > ^ `` — but a systematic, one-character-
|
|
312
|
+
at-a-time live test (each character alone, then each paired with a quote) showed every one of those
|
|
313
|
+
metacharacters is completely inert on its own through cross-spawn; **only a literal `"` combined with
|
|
314
|
+
a following metacharacter reaches a live shell.** A quote alone never did either. That means the
|
|
315
|
+
original guard would have rejected entirely legitimate values this codebase's own tools pass
|
|
316
|
+
routinely — a package description like `"Sales & Service Tools"`, a company name like
|
|
317
|
+
`"O'Brien Industries"` — with zero actual security benefit, since none of those ever reach a shell
|
|
318
|
+
regardless. Narrowed to reject only `"` (plus raw newlines, never legitimate in a single CLI arg
|
|
319
|
+
either). Re-verified live, both directions: the original exploit payload (`"x & echo ... & echo x"`)
|
|
320
|
+
is still rejected; a package description containing `&` and `'` now reaches the real `sf` CLI instead
|
|
321
|
+
of being rejected by the guard.
|
|
322
|
+
|
|
323
|
+
### Reviewed in depth on request — Flow builder and Agentforce agent/topic/action/planner creation
|
|
324
|
+
|
|
325
|
+
Ajay asked specifically for closer attention here. Read both Flow XML generators
|
|
326
|
+
(`buildFlowXml`/SOAP and `buildFlowDeployXml`/ZIP, ~800 lines combined) end to end and all four
|
|
327
|
+
Agentforce tools (`sf_create_agent`, `sf_create_agent_topic`, `sf_create_agent_action`,
|
|
328
|
+
`sf_create_agent_planner`) line by line, not just grep-sampled. Result: both Flow builders escape
|
|
329
|
+
every free-text value correctly and consistently (verified their `buildFilterValue`/
|
|
330
|
+
`typedResourceValue` helpers apply `x()` at every actual string-literal insertion point); the
|
|
331
|
+
remaining raw interpolations are all zod-enum-constrained `dataType` fields or booleans, not user
|
|
332
|
+
text, so no injection surface. Agentforce's XML construction is equally clean throughout.
|
|
333
|
+
|
|
334
|
+
**Did find one real, separate bug while reading this closely, unrelated to injection**:
|
|
335
|
+
`sf_create_agent_action`'s `inputs` parameter (input parameter mappings — `[{name, value}]`) is
|
|
336
|
+
accepted by the schema and documented, but was never wired into the generated `GenAiFunction` XML at
|
|
337
|
+
all — confirmed by grep, not a one-off oversight in this pass. Any caller who passed `inputs` had them
|
|
338
|
+
silently discarded, with no error and no indication in the response. This went uncaught because
|
|
339
|
+
`demo-org` cannot create `GenAiFunction` actions at all (a pre-existing, documented org-licensing
|
|
340
|
+
limit), so this parameter has never been exercised against a live org — and this project's own rule is
|
|
341
|
+
not to ship metadata XML shapes that haven't been verified that way. Rather than guess at the correct
|
|
342
|
+
XML (a real risk of shipping a second, differently-wrong bug), the tool's success message now says
|
|
343
|
+
explicitly when `inputs` was provided but not applied, so callers aren't silently misled. Implementing
|
|
344
|
+
it for real is still blocked on the same thing blocking the rest of GenAiFunction action testing: an
|
|
345
|
+
org where custom agent actions are actually licensed.
|
|
346
|
+
|
|
347
|
+
**Full regression**, both directions of this round: `test-suite.mjs` 207/211 passed — the one new
|
|
348
|
+
"failure" beyond the existing 2 pre-existing ones (`sf_share_report_folder`) is test-state
|
|
349
|
+
accumulation (it selects an existing report folder from the org, and today's own repeated
|
|
350
|
+
`test-suite.mjs` runs have created enough of them that one now has a too-long derived name), not a
|
|
351
|
+
code regression — unrelated to any change in this release. `qa-agentforce.mjs`,
|
|
352
|
+
`qa-agentforce-adjacent.mjs`, and `qa-flow-comprehensive.mjs` (142/142) all held their existing rates.
|
|
353
|
+
|
|
354
|
+
## [2.8.8] - 2026-07-31
|
|
355
|
+
|
|
356
|
+
### Security audit — one confirmed, exploitable command-injection vulnerability fixed, plus 13 more real findings across SOQL, generated-code, and credential-handling surfaces
|
|
357
|
+
|
|
358
|
+
Ajay asked for a standing security review of this project, not tied to any specific bug report. Went
|
|
359
|
+
through the codebase systematically rather than waiting for a report, and proved every finding below
|
|
360
|
+
by actually attempting the attack through the real, compiled MCP tool handler — not just reading code
|
|
361
|
+
and guessing. Everything here was found live against `demo-org`, the same standard this project holds
|
|
362
|
+
itself to for functional bugs.
|
|
363
|
+
|
|
364
|
+
**Command injection — CRITICAL, confirmed live-exploitable, now fixed.** Every `sf` CLI invocation in
|
|
365
|
+
this codebase built a shell command by joining an args array with spaces and running it through
|
|
366
|
+
`execSync`. Six tools passed unvalidated string parameters straight into that array:
|
|
367
|
+
`sf_create_scratch_org`, `sf_delete_scratch_org`, `sf_create_package`, `sf_create_package_version`,
|
|
368
|
+
`sf_install_package`, `sf_uninstall_package`, `sf_run_code_scanner`, `sf_scan_apex_antipatterns`.
|
|
369
|
+
Proof of concept: a `devHubAlias` of `DevHub & echo INJECTED > marker.txt & echo x` actually created
|
|
370
|
+
the marker file when run through `sf_create_scratch_org`'s real handler. Fixed by replacing every
|
|
371
|
+
`execSync`/`exec` call with `cross-spawn`, which passes each argument as a genuine argv entry instead
|
|
372
|
+
of shell text — re-verified the same payload no longer executes. **cross-spawn alone was not enough**:
|
|
373
|
+
an argument combining a literal `"` with a shell metacharacter (e.g. `"x & ... & echo x"`) still
|
|
374
|
+
reached a live shell even through cross-spawn 7.0.6's own escaping, because `sf` is a `.cmd` batch
|
|
375
|
+
shim on Windows and its internal `%*` argument-forwarding to node.exe is a second, uncontrolled
|
|
376
|
+
re-parsing step outside cross-spawn's reach — also proven live before being closed with an explicit
|
|
377
|
+
character allowlist (`runSfCli` now rejects any argument containing `"`, `` ` ``, `$`, `&`, `|`, `;`,
|
|
378
|
+
`<`, `>`, `^`, or a newline before it ever reaches cross-spawn; none of this codebase's CLI arguments —
|
|
379
|
+
aliases, IDs, paths, keys, rule selectors — ever legitimately need one). Also deleted `createScratchOrg`,
|
|
380
|
+
a second, entirely dead implementation of scratch-org creation with the same injection flaw plus
|
|
381
|
+
broken shell quoting that would have failed on both POSIX and Windows — confirmed zero references
|
|
382
|
+
anywhere before removing it.
|
|
383
|
+
|
|
384
|
+
**SOQL injection — real, live, found across 9 call sites.** `sf_get_field_history` (`recordId` and
|
|
385
|
+
`objectApiName`, the latter landing unquoted in a FROM-clause identifier position — no amount of
|
|
386
|
+
quote-escaping fixes that, only an identifier allowlist does), `sf_get_setup_audit_trail` /
|
|
387
|
+
`sf_get_login_history` / `sf_get_event_logs` (`startDate`/`endDate`, interpolated unquoted with zero
|
|
388
|
+
format validation), `sf_get_apex_test_results` (`testRunId`), `sf_detect_devops_merge_conflict` /
|
|
389
|
+
`sf_check_devops_commit_status` / `sf_list_devops_work_items` (`workItemId`/`projectId`/`stageId`),
|
|
390
|
+
and `sf_create_field_dependency` (`objectName`/`dependentField` in a Tooling API query). Fixed with a
|
|
391
|
+
new `soqlEscape()` helper (formalizing the backslash-quote pattern already used correctly at ~15 other
|
|
392
|
+
call sites in this file) and a new `assertSoqlDateLiteral()` validator for the unquoted date-literal
|
|
393
|
+
positions. `sf_query_records`'s raw `soql`/`whereClause`/`orderBy` and `sf_create_apex_batch`'s
|
|
394
|
+
`queryFilter` were deliberately left alone — both are documented, intentional raw-SOQL-fragment
|
|
395
|
+
parameters, the entire point of those tools, not an oversight.
|
|
396
|
+
|
|
397
|
+
**Code injection into generated, later-executed source — found in both Apex and TypeScript
|
|
398
|
+
generation.** `sf_create_invocable_action`'s `label`/`description` and its input/output variable
|
|
399
|
+
labels landed unescaped inside `@InvocableMethod`/`@InvocableVariable` annotation string literals in
|
|
400
|
+
generated Apex — an unescaped `'` breaks out of the literal and injects arbitrary class members into
|
|
401
|
+
a class that gets deployed and can execute. Same pattern in the (dead, unwired — see below)
|
|
402
|
+
`createApexScheduler`'s doc comment (a literal `*/` closes the comment early, turning the rest of the
|
|
403
|
+
generated file back into live code) and `createRestResource`'s `urlMapping`. Fixed with the same
|
|
404
|
+
`soqlEscape()` helper (Apex and SOQL use identical backslash-quote string-literal escaping) plus a
|
|
405
|
+
comment-safe `*/`-stripping helper for the comment-only cases. **More seriously, this same class of
|
|
406
|
+
bug was live and reachable** in `sf_create_mcp_server` and `sf_create_mcp_tool` (the tools that
|
|
407
|
+
scaffold a brand-new MCP server project on disk): `serverName` and `toolName` were spliced unescaped
|
|
408
|
+
into generated `.ts` source as string literals, and `sf_create_mcp_tool`'s `inputSchema` keys —
|
|
409
|
+
`z.record(z.unknown())`, so literally any string — were spliced in as raw, unquoted object-property
|
|
410
|
+
names with zero validation. Proof of concept: an `inputSchema` key of
|
|
411
|
+
`x(){require("fs").writeFileSync("pwn2","x");return z.string()` was accepted by the old code and would
|
|
412
|
+
have become live, executable code in the generated project the moment it was built and run. Fixed:
|
|
413
|
+
`serverName`/`toolName` now go through `JSON.stringify()` (matching how `toolDescription` was already
|
|
414
|
+
correctly handled in the same function — this was an inconsistency, not a from-scratch gap), and
|
|
415
|
+
`inputSchema` keys are now validated against a strict identifier regex before generation, with a clear
|
|
416
|
+
rejection instead of silent code smuggling.
|
|
417
|
+
|
|
418
|
+
**Credential leak in the codebase's own default error-sanitization path — real, high-impact, silently
|
|
419
|
+
broken since introduction.** `sanitizeError()` is called in essentially every one of this codebase's
|
|
420
|
+
~100 catch blocks — it's what stands between an internal error and what gets returned to the calling
|
|
421
|
+
LLM/user. Its only token-redaction rule was `[A-Fa-f0-9]{40,}` (pure hex, 40+ chars). Real Salesforce
|
|
422
|
+
access tokens/session IDs look like `00Dxxxxxxxxxxxx!AQEAQ...` — base64url-ish with a literal `!`
|
|
423
|
+
separator, never pure hex — so a real token embedded in any error message (a network client echoing a
|
|
424
|
+
request URL, an API error quoting back session context) would have passed straight through
|
|
425
|
+
completely unredacted. Proved this live with a realistic fake token before and after the fix. There
|
|
426
|
+
was a second function, `redactSensitive()`, that had the right idea (a `Bearer` pattern, a `00D...`
|
|
427
|
+
pattern) but its own character classes stopped at the first `!` or `.`, so it only partially redacted
|
|
428
|
+
even the cases it targeted, and — separately — it was only ever called from 2 of the ~100 relevant
|
|
429
|
+
sites, so almost nothing benefited from it regardless. Fixed by broadening the character classes to
|
|
430
|
+
cover the actual token shape and having `sanitizeError()` call `redactSensitive()` internally, so
|
|
431
|
+
every existing caller gets the protection retroactively with no call-site changes needed.
|
|
432
|
+
|
|
433
|
+
**HTTP transport mode has no authentication and bound to all network interfaces by default.** This
|
|
434
|
+
project supports `TRANSPORT=http` as an alternative to the default stdio transport. Its `/mcp` endpoint
|
|
435
|
+
has no auth of its own — any request that reaches it executes MCP tools using whatever Salesforce
|
|
436
|
+
credentials the server process is configured with — and `http.Server.listen(port)` with no explicit
|
|
437
|
+
host binds to every interface, not just localhost, by Node's own default. Fixed: defaults to
|
|
438
|
+
`127.0.0.1` now; set `HOST` explicitly (e.g. behind a reverse proxy that adds real auth) to opt into
|
|
439
|
+
broader exposure instead of getting it by accident. This mode is opt-in and off by default (stdio is
|
|
440
|
+
the default transport), so this was a foot-gun for anyone who did enable it, not an always-on gap.
|
|
441
|
+
|
|
442
|
+
**Audited and confirmed clean, no action needed:** XML metadata builders (spot-checked ~60 candidate
|
|
443
|
+
interpolation sites; every one was already either correctly wrapped in the existing `x()` escaper or a
|
|
444
|
+
boolean/number field with no injection surface at all — the real gaps here were already closed in
|
|
445
|
+
v2.8.2). `npm audit`: still exactly the 2 moderate findings already documented and deliberately left
|
|
446
|
+
(a path-traversal bug in `@hono/node-server`'s `serve-static`, a transitive dependency of the MCP SDK
|
|
447
|
+
that this project's own HTTP transport — a from-scratch, two-route implementation — never calls into
|
|
448
|
+
regardless of transport mode; confirmed again this session, not just carried forward from memory).
|
|
449
|
+
Secrets/credential files: confirmed `.gitignore` still covers `.env`, `.env.local`, `*.key`, and
|
|
450
|
+
`CLAUDE.local.md`, and confirmed via `git log --all` that nothing secret-shaped has ever been committed.
|
|
451
|
+
|
|
452
|
+
**Also found, not fixed (dead code, zero live exposure, hardened anyway for defense-in-depth):**
|
|
453
|
+
`createInvocableAction`, `createApexScheduler`, and `createRestResource` are all exported from
|
|
454
|
+
`services/salesforce.ts` but never imported by any tool file — confirmed via full-codebase grep before
|
|
455
|
+
and after — so their injection fixes above protect nothing reachable today. Left in place rather than
|
|
456
|
+
deleted (unlike `createScratchOrg`, which was both dead *and* had no legitimate salvage value) since
|
|
457
|
+
these look like intended-but-never-wired functionality; flagging here so a future session that wires
|
|
458
|
+
them up inherits the fix rather than reintroducing the bug.
|
|
459
|
+
|
|
460
|
+
**Full regression confirmation:** `test-suite.mjs` (211 tests) — 208 passed, 2 failed (both pre-existing,
|
|
461
|
+
unrelated, intentional negative-test probes), 1 skipped — no change from the pre-audit baseline.
|
|
462
|
+
`qa-agentforce.mjs`, `qa-agentforce-adjacent.mjs`, and `qa-flow-comprehensive.mjs` all held their
|
|
463
|
+
existing pass rates. Every fix above was also independently proven by re-attempting its specific
|
|
464
|
+
exploit through the real, compiled tool handler and confirming it now fails cleanly instead of
|
|
465
|
+
executing.
|
|
466
|
+
|
|
3
467
|
## [2.8.7] - 2026-07-31
|
|
4
468
|
|
|
5
469
|
### Added — `sf_create_external_client_app`, and it closes a gap `sf_create_connected_app` structurally cannot
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://modelcontextprotocol.io)
|
|
7
7
|
|
|
8
|
-
**The only Salesforce MCP server that builds Agentforce agents, OmniStudio components, and DevOps Center pipelines** — alongside a complete daily developer loop (schema describe, Apex read, debug logs) and
|
|
8
|
+
**The only Salesforce MCP server that builds Agentforce agents, OmniStudio components, and DevOps Center pipelines** — alongside a complete daily developer loop (schema describe, Apex read, debug logs) and 223 tools total for building, configuring, and automating Salesforce orgs directly from Claude or any MCP client.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -47,7 +47,7 @@ See [SETUP.md](SETUP.md) for all authentication methods and detailed setup instr
|
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
|
50
|
-
## Tools —
|
|
50
|
+
## Tools — 223 total
|
|
51
51
|
|
|
52
52
|
Highlights below; see [TOOLS.md](TOOLS.md) for the complete reference with parameters and example prompts.
|
|
53
53
|
|
|
@@ -163,6 +163,7 @@ Highlights below; see [TOOLS.md](TOOLS.md) for the complete reference with param
|
|
|
163
163
|
| `sf_deploy_metadata` | Deploy metadata via Metadata API (supports `testLevel`, inline XML) |
|
|
164
164
|
| `sf_check_deploy_status` | Check deployment job status |
|
|
165
165
|
| `sf_retrieve_metadata` | Retrieve metadata from the org |
|
|
166
|
+
| `sf_delete_metadata` | Permanently delete metadata components (CustomObject, CustomField, Flow, GenAiFunction, Bot, etc.) |
|
|
166
167
|
|
|
167
168
|
### MCP Server Management
|
|
168
169
|
| Tool | Description |
|
|
@@ -210,7 +211,7 @@ Highlights below; see [TOOLS.md](TOOLS.md) for the complete reference with param
|
|
|
210
211
|
## Documentation
|
|
211
212
|
|
|
212
213
|
- [SETUP.md](SETUP.md) — Prerequisites, authentication, Claude configuration
|
|
213
|
-
- [TOOLS.md](TOOLS.md) — All
|
|
214
|
+
- [TOOLS.md](TOOLS.md) — All 223 tools with full parameter documentation
|
|
214
215
|
- [AGENTFORCE.md](AGENTFORCE.md) — Agentforce agent creation guide
|
|
215
216
|
- [APEX_LWC.md](APEX_LWC.md) — Apex and LWC development guide
|
|
216
217
|
- [CHANGELOG.md](CHANGELOG.md) — Version history
|
package/TOOLS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Tools Reference
|
|
2
2
|
|
|
3
|
-
Complete documentation for all
|
|
3
|
+
Complete documentation for all 223 tools in `salesforce-metadata-mcp`. (Highlights below cover the most commonly used tools in depth; see the README's full 223-tool table for every tool name.)
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -323,4 +323,4 @@ Reads the current field-level security grants for a field across all Profiles an
|
|
|
323
323
|
|
|
324
324
|
---
|
|
325
325
|
|
|
326
|
-
*For the complete list of all
|
|
326
|
+
*For the complete list of all 223 tools, see [README.md](README.md).*
|
package/dist/index.js
CHANGED
|
@@ -3,17 +3,24 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
5
|
import { createServer } from "http";
|
|
6
|
+
import { createRequire } from "module";
|
|
6
7
|
import { registerTools } from "./tools/index.js";
|
|
8
|
+
// Single source of truth for the version. Hardcoding it here drifted from package.json across
|
|
9
|
+
// v2.11.0/v2.11.1 (the server advertised 2.10.0 to clients while the package said 2.11.1), so read
|
|
10
|
+
// it at runtime instead. dist/index.js → ../package.json resolves correctly both in the repo and
|
|
11
|
+
// when installed as node_modules/salesforce-metadata-mcp/dist/index.js.
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const { version: VERSION } = require("../package.json");
|
|
7
14
|
const server = new McpServer({
|
|
8
15
|
name: "salesforce-metadata-mcp",
|
|
9
|
-
version:
|
|
16
|
+
version: VERSION,
|
|
10
17
|
});
|
|
11
18
|
registerTools(server);
|
|
12
19
|
// ─── Transport: stdio ─────────────────────────────────────────────────────────
|
|
13
20
|
async function runStdio() {
|
|
14
21
|
const transport = new StdioServerTransport();
|
|
15
22
|
await server.connect(transport);
|
|
16
|
-
console.error(
|
|
23
|
+
console.error(`Salesforce Metadata MCP server v${VERSION} running on stdio`);
|
|
17
24
|
}
|
|
18
25
|
// ─── Transport: HTTP ──────────────────────────────────────────────────────────
|
|
19
26
|
function readBody(req) {
|
|
@@ -34,11 +41,18 @@ function readBody(req) {
|
|
|
34
41
|
}
|
|
35
42
|
async function runHTTP() {
|
|
36
43
|
const port = parseInt(process.env["PORT"] ?? "3000", 10);
|
|
44
|
+
// Defaults to localhost-only: this endpoint has no authentication of its own (any request that
|
|
45
|
+
// reaches /mcp executes tools using whatever Salesforce credentials this process is configured
|
|
46
|
+
// with), and Node's http.Server.listen(port) with no host binds to ALL interfaces by default —
|
|
47
|
+
// found during the 2026-07-31 security audit. Set HOST explicitly (e.g. "0.0.0.0") to opt into
|
|
48
|
+
// wider exposure — e.g. behind a reverse proxy that adds its own auth — rather than exposing this
|
|
49
|
+
// unauthenticated by accident.
|
|
50
|
+
const host = process.env["HOST"] ?? "127.0.0.1";
|
|
37
51
|
const httpServer = createServer(async (req, res) => {
|
|
38
52
|
const url = req.url ?? "/";
|
|
39
53
|
if (url === "/health" && req.method === "GET") {
|
|
40
54
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
41
|
-
res.end(JSON.stringify({ status: "ok", server: "salesforce-metadata-mcp", version:
|
|
55
|
+
res.end(JSON.stringify({ status: "ok", server: "salesforce-metadata-mcp", version: VERSION }));
|
|
42
56
|
return;
|
|
43
57
|
}
|
|
44
58
|
if (url === "/mcp" && req.method === "POST") {
|
|
@@ -63,8 +77,8 @@ async function runHTTP() {
|
|
|
63
77
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
64
78
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
65
79
|
});
|
|
66
|
-
httpServer.listen(port, () => {
|
|
67
|
-
console.error(`Salesforce Metadata MCP server
|
|
80
|
+
httpServer.listen(port, host, () => {
|
|
81
|
+
console.error(`Salesforce Metadata MCP server v${VERSION} running on http://${host}:${port}/mcp`);
|
|
68
82
|
});
|
|
69
83
|
}
|
|
70
84
|
// ─── Entry point ──────────────────────────────────────────────────────────────
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,YAAY,EAA6C,MAAM,MAAM,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,yBAAyB;IAC/B,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,aAAa,CAAC,MAAM,CAAC,CAAC;AAEtB,iFAAiF;AAEjF,KAAK,UAAU,QAAQ;IACrB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|