salesforce-metadata-mcp 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +167 -1
- package/README.md +25 -5
- package/TOOLS.md +326 -326
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/schemas/index.d.ts +494 -130
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +122 -29
- package/dist/schemas/index.js.map +1 -1
- package/dist/services/deployment.d.ts.map +1 -1
- package/dist/services/deployment.js +19 -3
- package/dist/services/deployment.js.map +1 -1
- package/dist/services/impact.d.ts.map +1 -1
- package/dist/services/impact.js +9 -1
- package/dist/services/impact.js.map +1 -1
- package/dist/services/salesforce.d.ts +29 -0
- package/dist/services/salesforce.d.ts.map +1 -1
- package/dist/services/salesforce.js +1342 -323
- package/dist/services/salesforce.js.map +1 -1
- package/dist/services/tooling.d.ts.map +1 -1
- package/dist/services/tooling.js +28 -3
- package/dist/services/tooling.js.map +1 -1
- package/dist/tools/automation.d.ts.map +1 -1
- package/dist/tools/automation.js +18 -6
- package/dist/tools/automation.js.map +1 -1
- package/dist/tools/data.d.ts.map +1 -1
- package/dist/tools/data.js +4 -2
- package/dist/tools/data.js.map +1 -1
- package/dist/tools/experience.d.ts.map +1 -1
- package/dist/tools/experience.js +8 -1
- package/dist/tools/experience.js.map +1 -1
- package/dist/tools/ui.d.ts.map +1 -1
- package/dist/tools/ui.js +16 -4
- package/dist/tools/ui.js.map +1 -1
- package/dist/toolsets.d.ts +101 -2
- package/dist/toolsets.d.ts.map +1 -1
- package/dist/toolsets.js +360 -25
- package/dist/toolsets.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,172 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [3.2.0] - 2026-09-23
|
|
4
|
+
|
|
5
|
+
A full-surface QA release. Every one of the 233 tools was called individually over real MCP traffic
|
|
6
|
+
(`tools/call` → schema → service → org) against a Developer Edition org and a scratch org with
|
|
7
|
+
Communities, Service Cloud, Knowledge and Omni-Channel enabled, and each result was checked in the
|
|
8
|
+
org with the sf CLI — never by the server under test. **3.1.0 and 3.1.1 were never published to npm,
|
|
9
|
+
so this release also carries them** (lazy-toolset fallback, `sf_query_records` limit, `sf_find_tool`
|
|
10
|
+
search — see below).
|
|
11
|
+
|
|
12
|
+
Result on the scratch org: **170 PASS / 41 feature-unavailable / 4 org-limit / 17 open**, up from a
|
|
13
|
+
baseline of 100 PASS / 114 failing on the first sweep. None of the 17 open items is a tool sending a
|
|
14
|
+
wrong payload for a feature the org has: they are cascades (OmniStudio not installed, an org cap
|
|
15
|
+
reached earlier in the run), unlicensed features (Agentforce, Einstein, Digital Engagement) and
|
|
16
|
+
fixture gaps. **No tool claims success without doing the work.**
|
|
17
|
+
|
|
18
|
+
### Fixed: tools that silently replaced a whole component
|
|
19
|
+
`upsertMetadata` replaces what it names. Eight tools sent only the part they were changing, so they
|
|
20
|
+
either failed or would have wiped the rest: compact layouts / list views / search layouts, queue
|
|
21
|
+
routing config, connected-app OAuth policy, the five Workflow children (a `Workflow` upsert replaces
|
|
22
|
+
an object's entire workflow — the old 500s were the only reason nothing was lost), business hours,
|
|
23
|
+
holidays, LWC Jest tests, and forecasting settings. Each now addresses its component directly or
|
|
24
|
+
reads, modifies and writes it back.
|
|
25
|
+
|
|
26
|
+
### Fixed: tools that could not succeed at all
|
|
27
|
+
Around 50 tools, including: custom tabs, custom notification types, roles (access-level enum and
|
|
28
|
+
forecast-manager default), FlexiPages, SAML SSO, change data capture, quick/global actions, auth
|
|
29
|
+
providers with custom endpoints, sharing rules, dashboards, path assistants, scheduled flows,
|
|
30
|
+
duplicate rules (a bare Allow is rejected — Allow now carries Report), data category groups
|
|
31
|
+
(`KnowledgeArticleVersion`), service territories (operating hours), Experience sites (now created
|
|
32
|
+
through the Connect API from a real template), and package versions (`--installation-key-bypass`).
|
|
33
|
+
The full create → version → install → uninstall packaging chain is verified end to end.
|
|
34
|
+
|
|
35
|
+
### Fixed: errors nobody could act on
|
|
36
|
+
Bare 404s and 500s now name the cause: sandboxes and change sets on editions without them, DevOps
|
|
37
|
+
Center not installed, a connected app on an org that only allows External Client Apps, an Experience
|
|
38
|
+
page (Salesforce has no REST endpoint for creating one — the tool now says so), scheduled-job compile
|
|
39
|
+
errors, and upserts whose external-ID field is invisible to the running user.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
- `sf_create_forecast_hierarchy`: `forecastingType` is now a string checked against the org (the old
|
|
43
|
+
enum offered names that do not exist). Unknown names return the org's list.
|
|
44
|
+
- `sf_create_experience_site` returns the site URL, Builder link and the Site API names Salesforce
|
|
45
|
+
chose (it derives them from the label).
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
- `qa-full-sweep.mjs` and friends: the full-surface sweep, with a four-way verdict
|
|
49
|
+
(PASS / UNAVAIL / LIMIT / BUG). `QA_DEVHUB=<alias>` enables the real packaging chain.
|
|
50
|
+
- `qa-workflow-children.mjs`, `qa-translation-merge.mjs`, `qa-scratch-fixes.mjs`: pins for the
|
|
51
|
+
fixes above.
|
|
52
|
+
|
|
53
|
+
## [3.1.1] - 2026-09-09
|
|
54
|
+
|
|
55
|
+
Three bugs reported against a Claude Desktop session. **None were introduced by 3.1.0.** Two date to
|
|
56
|
+
the first public release (`f808b50`, v2.6.5) and one to the lazy-toolsets change in 3.0.0
|
|
57
|
+
(`479a40d`) — traced with `git log -S`/`-L`, not assumed.
|
|
58
|
+
|
|
59
|
+
### Fixed: `sf_query_records` ignored its own `limit` (since v2.6.5)
|
|
60
|
+
|
|
61
|
+
`limit` was only interpolated into the SOQL `queryRecords` *builds* from `objectApiName`/`fields`.
|
|
62
|
+
Every call through `sf_query_records` passes a `query` string, which takes the other branch, so the
|
|
63
|
+
parameter — and its documented default of 200 — did nothing. A caller setting `limit: 100` to stay
|
|
64
|
+
small got 1,323 rows and no warning; the failure was silent and failed open.
|
|
65
|
+
|
|
66
|
+
The cap is now applied by rewriting the outgoing SOQL, so it reduces what Salesforce sends rather
|
|
67
|
+
than only what the tool returns. **Precedence is `min(parameter, LIMIT in the query)`**: the
|
|
68
|
+
parameter is a guardrail, so a `LIMIT` in the query string may tighten it but never raise it, while
|
|
69
|
+
a caller who explicitly wrote `LIMIT 5` still gets 5.
|
|
70
|
+
|
|
71
|
+
A cap that is applied but not reported would just be a silent truncation — the same bug wearing a
|
|
72
|
+
different hat — so the response now carries `appliedLimit`, `limitSource` and `truncated`, and the
|
|
73
|
+
message says when rows were cut off. `truncated` is a fact, not a guess: one row beyond the cap is
|
|
74
|
+
fetched to distinguish "exactly N matched" from "more than N matched", then discarded. Aggregate
|
|
75
|
+
(`COUNT()`), `OFFSET` and `FOR UPDATE` queries are handled without being rewritten into invalid SOQL.
|
|
76
|
+
|
|
77
|
+
### Fixed: `sf_find_tool` returned nothing for multi-word queries (since v3.0.0)
|
|
78
|
+
|
|
79
|
+
The search required *every* token to appear in a tool name, so any natural-language phrase returned
|
|
80
|
+
`matches: []` — including two of the tool's own documented examples. That is the worst moment to
|
|
81
|
+
return an empty list: this tool is reached precisely when someone cannot find a capability and is
|
|
82
|
+
describing it in words, and an empty result reads as "no such tool exists" rather than "rephrase".
|
|
83
|
+
|
|
84
|
+
Matching is now scored rather than filtered — any token can match, ranked by how many hit, with an
|
|
85
|
+
exact tool name always first. Tool **titles and descriptions are searched too**, at a lower weight
|
|
86
|
+
than names, because a phrase like *"run some apex code in the org"* cannot be resolved from names
|
|
87
|
+
alone (`sf_run_apex_tests` wins on tokens while the caller means `sf_execute_anonymous_apex`).
|
|
88
|
+
Common filler words are dropped.
|
|
89
|
+
|
|
90
|
+
Two guards came with it, since a looser match could otherwise undo the point of lazy toolsets:
|
|
91
|
+
results are capped at the 25 best matches, and a single search auto-loads at most 3 toolsets — those
|
|
92
|
+
from the top-scoring band only. Lower-ranked matches are named in the response instead of loaded.
|
|
93
|
+
|
|
94
|
+
### Fixed: Salesforce error messages truncated mid-sentence (since v2.6.5)
|
|
95
|
+
|
|
96
|
+
API error bodies were cut with a flat `slice(0, 300)`, and Salesforce puts the actionable part at
|
|
97
|
+
the end — *"…be sure to append the '__c' after the entity name. Please refer to…"* arrived as
|
|
98
|
+
*"…Please ref"*. A cap is still wanted, since an error body can be a whole HTML page, so oversized
|
|
99
|
+
errors now keep a generous head **and** the tail, with a count of what was dropped.
|
|
100
|
+
|
|
101
|
+
### Notes
|
|
102
|
+
|
|
103
|
+
- `sf_find_tool` now attaches input schemas to the top 3 **ranked** matches rather than only when
|
|
104
|
+
the whole result set was ≤3. Broader matching pushed even exact-name queries past that threshold,
|
|
105
|
+
which cost them the schema `sf_call_tool` needs.
|
|
106
|
+
- **Inlined schemas are now bounded by size, not just count** — found by measuring the release
|
|
107
|
+
rather than from a report. A count is not a bound: `sf_create_flow` serialises to ~28KB, so
|
|
108
|
+
`sf_find_tool("create flow")` was returning ~8,500 tokens, most of what a whole default startup
|
|
109
|
+
costs. v3.0.0 had moved that tool out of the default toolset for exactly this reason, and inlining
|
|
110
|
+
it on search quietly put it back. Oversized schemas are now named with a pointer to
|
|
111
|
+
`sf_tool_schema` instead of being sent. Measured after: 8,532 → 1,721 tokens for the same call,
|
|
112
|
+
and every `sf_find_tool` response now lands between ~1.4k and ~2.5k tokens.
|
|
113
|
+
- New `qa-query-limit.mjs` (17 checks) covers the cap, its precedence, truncation reporting, the
|
|
114
|
+
aggregate/OFFSET edge cases and the error-text length. `qa-toolsets.mjs` gains natural-language
|
|
115
|
+
search coverage and the auto-load bounds (26 → 38 checks).
|
|
116
|
+
- Verified against `demo-org` before and after: `limit: 3` returned 1,764 rows before the fix and 3
|
|
117
|
+
after.
|
|
118
|
+
|
|
119
|
+
## [3.1.0] - 2026-09-09
|
|
120
|
+
|
|
121
|
+
### Fixed: loaded toolsets could be unreachable on clients that ignore `list_changed`
|
|
122
|
+
|
|
123
|
+
Reported against Claude Desktop: `sf_load_toolset(["apex","data"])` returned `success: true` with
|
|
124
|
+
`residentTools` moving 18 → 47, `sf_find_tool` confirmed the tools existed in a loaded toolset, and
|
|
125
|
+
not one of them was ever callable. The entire write surface of the server — anonymous Apex, record
|
|
126
|
+
creation, bulk insert — was unreachable for a whole session while every status payload reported
|
|
127
|
+
healthy. Reads kept working, because the initially-resident set was never affected.
|
|
128
|
+
|
|
129
|
+
The server was not violating the protocol. It declares `tools.listChanged`, emits exactly one
|
|
130
|
+
`notifications/tools/list_changed` per load, and `tools/list` genuinely changes — all now asserted
|
|
131
|
+
end-to-end in `qa-client-refetch.mjs`. The client never re-fetched.
|
|
132
|
+
|
|
133
|
+
Being protocol-correct was not enough. Lazy toolsets put 210 of 228 tools behind an *optional*
|
|
134
|
+
client behaviour with no fallback when it is absent, so v3.1.0 adds one that needs no re-fetch:
|
|
135
|
+
|
|
136
|
+
- **`sf_call_tool({ tool, arguments })`** — invokes any tool by name whether or not its toolset is
|
|
137
|
+
loaded and whether or not it appears in the client's tool list. Arguments are validated against
|
|
138
|
+
the same zod schema, and the handler it calls is the guard-wrapped one, so the production guard
|
|
139
|
+
applies exactly as on a direct call. It cannot invoke itself or the other meta-tools.
|
|
140
|
+
- **`sf_tool_schema({ tool })`** — returns any tool's input schema, loaded or not, so a model can
|
|
141
|
+
construct that call for a tool it cannot see.
|
|
142
|
+
- **`sf_find_tool`** now returns input schemas inline when a query matches three tools or fewer,
|
|
143
|
+
collapsing find → call into a single round trip. Wider searches still return names only, so this
|
|
144
|
+
does not reintroduce the context cost lazy toolsets exist to avoid.
|
|
145
|
+
- **`sf_load_toolset`** now checks whether the client re-fetched `tools/list` after the notification
|
|
146
|
+
and reports `clientDidNotRefresh: true` with instructions to use `sf_call_tool`, rather than
|
|
147
|
+
claiming an unqualified success for tools that have silently become unreachable.
|
|
148
|
+
|
|
149
|
+
With this, the full 228-tool surface is reachable from the handshake tool list alone, even under
|
|
150
|
+
`SF_TOOLSETS=none`.
|
|
151
|
+
|
|
152
|
+
The regression this adds is deliberately end-to-end. Every internal signal — `loadedToolsets`,
|
|
153
|
+
`residentTools`, `sf_find_tool` matches — was correct throughout the reported session, so asserting
|
|
154
|
+
on any of them would have passed cleanly while the server was unusable. `qa-client-refetch.mjs`
|
|
155
|
+
instead drives two simulated clients, one that honours `list_changed` and one that never re-fetches,
|
|
156
|
+
and asserts what the model can actually call.
|
|
157
|
+
|
|
158
|
+
Also fixed along the way: `sf_call_tool`'s first implementation assumed `inputSchema` was always a
|
|
159
|
+
`ZodRawShape`, but the shared schemas in `src/schemas` are built `ZodObject`s. Both forms are now
|
|
160
|
+
normalised, matching what the SDK itself does.
|
|
161
|
+
|
|
162
|
+
### Notes
|
|
163
|
+
|
|
164
|
+
- Meta-tool count goes from 3 to 5, so the default startup listing is 20 tools rather than 18 and
|
|
165
|
+
`SF_TOOLSETS=all` lists 233 rather than 231. No Salesforce tool was added, removed or renamed.
|
|
166
|
+
- `zod-to-json-schema` is now a direct dependency. It was already present transitively — the MCP SDK
|
|
167
|
+
uses it to serialise Zod v3 schemas — and this server now uses it for the same purpose.
|
|
168
|
+
|
|
169
|
+
## [3.0.0] - 2026-09-02
|
|
4
170
|
|
|
5
171
|
### Production write guard
|
|
6
172
|
|
package/README.md
CHANGED
|
@@ -76,26 +76,46 @@ starts with a small core loaded and pulls in the rest on demand:
|
|
|
76
76
|
|
|
77
77
|
| Startup | Tools listed | Approx. tokens |
|
|
78
78
|
|---------|-------------:|---------------:|
|
|
79
|
-
| Default (`core,metadata`) |
|
|
80
|
-
| After loading two more toolsets |
|
|
81
|
-
| `SF_TOOLSETS=all` |
|
|
79
|
+
| Default (`core,metadata`) | 20 | **~9,900** |
|
|
80
|
+
| After loading two more toolsets | 43 | ~20,800 |
|
|
81
|
+
| `SF_TOOLSETS=all` | 233 | ~99,100 |
|
|
82
82
|
|
|
83
83
|
The default covers what nearly every session needs: describe/list objects, SOQL query,
|
|
84
84
|
deploy/retrieve/delete metadata, deploy status, and core schema creation (objects, fields, formula
|
|
85
85
|
fields, picklist values, validation rules, approval processes).
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
Five tools are always present and make everything else reachable:
|
|
88
88
|
|
|
89
89
|
- **`sf_find_tool`** — search all 228 tools by name and load whatever contains the matches, in one
|
|
90
90
|
call. Ask for *"create an omniscript"* and it finds the tools, loads `omnistudio`, and they are
|
|
91
91
|
callable immediately. This is usually all you or the model needs.
|
|
92
92
|
- **`sf_load_toolset`** — load named toolsets explicitly.
|
|
93
93
|
- **`sf_list_toolsets`** — browse all toolsets, their tool counts, and what is loaded.
|
|
94
|
+
- **`sf_call_tool`** — invoke any tool by name, loaded or not, without it having to appear in the
|
|
95
|
+
tool list first. Arguments are validated and the production guard applies exactly as on a direct
|
|
96
|
+
call.
|
|
97
|
+
- **`sf_tool_schema`** — return any tool's input schema, so `sf_call_tool` can be constructed for a
|
|
98
|
+
tool the client cannot see.
|
|
94
99
|
|
|
95
100
|
In practice you don't manage this by hand: ask for what you want, and the model loads what it needs.
|
|
96
101
|
|
|
102
|
+
### If loaded tools don't show up
|
|
103
|
+
|
|
104
|
+
Loading a toolset enables it on the server and emits `notifications/tools/list_changed`. A client is
|
|
105
|
+
supposed to re-fetch `tools/list` when it sees that. Some clients don't — and when that happens, the
|
|
106
|
+
load reports success, the tools really are enabled, and the model still cannot call any of them
|
|
107
|
+
because they never entered its tool list.
|
|
108
|
+
|
|
109
|
+
Two things handle this, so no client can lose access to a tool:
|
|
110
|
+
|
|
111
|
+
- `sf_load_toolset` checks whether the client re-fetched and says so in its response, instead of
|
|
112
|
+
reporting an unqualified success for tools that have become unreachable.
|
|
113
|
+
- `sf_call_tool` reaches every tool without needing a re-fetch at all. Combined with `sf_tool_schema`,
|
|
114
|
+
the full 228-tool surface is usable from the handshake tool list alone — even under
|
|
115
|
+
`SF_TOOLSETS=none`.
|
|
116
|
+
|
|
97
117
|
To restore the previous behaviour of loading everything at startup, set `SF_TOOLSETS=all`. To start
|
|
98
|
-
with only the
|
|
118
|
+
with only the five meta-tools, set `SF_TOOLSETS=none`. To pick your own core, pass a list:
|
|
99
119
|
|
|
100
120
|
```json
|
|
101
121
|
{ "env": { "SF_TOOLSETS": "metadata,objects,automation,security" } }
|