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.
Files changed (39) hide show
  1. package/CHANGELOG.md +167 -1
  2. package/README.md +25 -5
  3. package/TOOLS.md +326 -326
  4. package/dist/index.js +10 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/schemas/index.d.ts +494 -130
  7. package/dist/schemas/index.d.ts.map +1 -1
  8. package/dist/schemas/index.js +122 -29
  9. package/dist/schemas/index.js.map +1 -1
  10. package/dist/services/deployment.d.ts.map +1 -1
  11. package/dist/services/deployment.js +19 -3
  12. package/dist/services/deployment.js.map +1 -1
  13. package/dist/services/impact.d.ts.map +1 -1
  14. package/dist/services/impact.js +9 -1
  15. package/dist/services/impact.js.map +1 -1
  16. package/dist/services/salesforce.d.ts +29 -0
  17. package/dist/services/salesforce.d.ts.map +1 -1
  18. package/dist/services/salesforce.js +1342 -323
  19. package/dist/services/salesforce.js.map +1 -1
  20. package/dist/services/tooling.d.ts.map +1 -1
  21. package/dist/services/tooling.js +28 -3
  22. package/dist/services/tooling.js.map +1 -1
  23. package/dist/tools/automation.d.ts.map +1 -1
  24. package/dist/tools/automation.js +18 -6
  25. package/dist/tools/automation.js.map +1 -1
  26. package/dist/tools/data.d.ts.map +1 -1
  27. package/dist/tools/data.js +4 -2
  28. package/dist/tools/data.js.map +1 -1
  29. package/dist/tools/experience.d.ts.map +1 -1
  30. package/dist/tools/experience.js +8 -1
  31. package/dist/tools/experience.js.map +1 -1
  32. package/dist/tools/ui.d.ts.map +1 -1
  33. package/dist/tools/ui.js +16 -4
  34. package/dist/tools/ui.js.map +1 -1
  35. package/dist/toolsets.d.ts +101 -2
  36. package/dist/toolsets.d.ts.map +1 -1
  37. package/dist/toolsets.js +360 -25
  38. package/dist/toolsets.js.map +1 -1
  39. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -1,6 +1,172 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased]
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`) | 18 | **~9,400** |
80
- | After loading two more toolsets | 41 | ~20,300 |
81
- | `SF_TOOLSETS=all` | 231 | ~98,600 |
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
- Three tools are always present and make everything else reachable:
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 three meta-tools, set `SF_TOOLSETS=none`. To pick your own core, pass a list:
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" } }