v4.1.0 release notes
Released 2026-09-11. The default tool surface falls from 41 tools / 44,968 bytes to 23 / 15,620 — 66% smaller — and nothing becomes unreachable.
What changed, in one paragraph
Section titled “What changed, in one paragraph”Nineteen analysis tools were listed on every surface, and on the index they were 30,683 of
44,968 bytes: 68% of the payload whose entire purpose is being small. They were not there by
oversight. cyberchef_describe_operation refused them and pointed at tools/list, so the listing
was their only schema path and a tool absent from it could not be called at all. This release
removed that dependency rather than the tools, and only then took them off the index.
The three pieces, in the order they had to land
Section titled “The three pieces, in the order they had to land”describe_operation serves their schemas |
Instead of redirecting. More useful even on curated/all: it returns one tool’s schema rather than requiring a re-read of the whole list |
categories lists them |
Under a separate analysisTools key, not a sixteenth operation category — everything in categories can be run by cyberchef_bake and enumerated by cyberchef_list_operations, and an analysis tool is neither |
cyberchef_analyse(tool, arguments) |
Dispatches by name, accepting the bare and prefixed spellings because this server’s own navigation output uses both |
Ordering was the whole risk. Between removing them from the index and having a schema and an invocation path, they would have been neither listed nor reachable.
Two doors, one body
Section titled “Two doors, one body”The rule was that a dispatched call must behave identically to a direct one. The only way to be
sure of that is for there to be one body: analyse resolves the tool and then falls into the
same statements the direct call runs. Verified byte-identical through a real client, on both
spellings, with all 19 tools reachable and empty-argument refusals naming the selected tool rather
than the dispatcher.
Authorisation is priced by the selection
Section titled “Authorisation is priced by the selection”requiredScopes reads a tool’s own annotations. Authorising analyse as itself would have run the
check against a wrapper while the work ran against the caller’s choice — and this is the release
that stops listing those tools, so the bypass would have shipped in the same change that removed
the evidence of it.
cyberchef_analyse therefore joins cyberchef_bake and cyberchef_batch in RECIPE_SCOPED_TOOLS
and is priced by the tool it names. It belongs there and cyberchef_recipe_execute does not, for a
mechanical reason rather than a preference: analyse carries a name, resolvable against a Map
fixed at construction, so the check stays first; recipe_execute carries an id, and resolving
that would move the guard after a storage read.
Asserted through a real client in both directions, per tool discovered from the registry: a token holding the selected tool’s scope gets through, a scopeless one is refused naming that tool’s scope, and direct and dispatched calls agree on every allow/deny pair — so the dispatcher is neither laxer nor stricter.
The charter was wrong, and the gate is what found it
Section titled “The charter was wrong, and the gate is what found it”The charter’s headline claim — “at 59 total registry tools the index costs more than curated” —
reordered the entire v4 release line. It is arithmetically impossible, and building the gate
for it is what disproved it. Forty synthetic registry tools were injected to force a failure:
index curated all curated - indexbefore 44,968 108,214 425,372 63,246after +40 121,408 184,654 501,812 63,246Every surface grew by the same 76,440 bytes. Registry tools are listed on every surface —
CYBERCHEF_TOOL_SURFACE selects how many operation schemas to pre-load and has never governed
them — so the gap is a constant and the index cannot cross curated at any number.
What survived is the finding that was always the real one, and it needed no comparison at all:
68% of the index, and a 41-tool index sitting inside the 30–50 tool band where
Anthropic measures tool-selection accuracy degrading. At 23 tools it is below that band. Full
detail in docs/internal/v4.1.0-findings-log.md.
Measured
Section titled “Measured”surface tools bytes beforeindex 23 15,620 41 / 44,968 -66%curated 120 109,548 119 / 108,214 +1 tool (the dispatcher)all 545 426,706 544 / 425,372 +1 tool
index + one operation schema 17,398 bytes 24.5x cheaper than `all` (was 9.1x)growth per registry tool added on the index 1,615 bytes -> ZEROAlso fixed
Section titled “Also fixed”tests/mcp/auth.test.mjs’s callTool helper leaked an HTTP session per call against a cap of 100.
It was invisible until something crossed the threshold: the new dispatcher tests are the first here
to loop over every registry tool, and the failure surfaced on an unrelated assertion several
tests later as -32001 Session not found. Sessions are now closed with DELETE, which is what
the server’s own capacity message tells callers to do.
Compatibility
Section titled “Compatibility”Additive. No call that worked before stops working.
- Every analysis tool remains callable by name on
curatedandall, and on every surface throughcyberchef_analyse. - A client on the default
indexsurface that previously calledcyberchef_hash_identifydirectly will no longer find it intools/list. Usecyberchef_analyse({tool: "hash_identify", ...}), or setCYBERCHEF_TOOL_SURFACE=curatedto keep the old listing. cyberchef_describe_operationanswers differently for a registry tool: it returns the schema instead of an error with a redirect hint.
Verification
Section titled “Verification”npm run test:mcp 1,720 tests, 77 files -- all passnpm run lint cleannpm run check:versions all references agreenpm run test:coverage 95.15 / 89.31 / 96.57 / 96.14 (thresholds 95 / 89 / 96 / 96)