Skip to content

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.

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.

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.

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 - index
before 44,968 108,214 425,372 63,246
after +40 121,408 184,654 501,812 63,246

Every 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.

surface tools bytes before
index 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 -> ZERO

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.

Additive. No call that worked before stops working.

  • Every analysis tool remains callable by name on curated and all, and on every surface through cyberchef_analyse.
  • A client on the default index surface that previously called cyberchef_hash_identify directly will no longer find it in tools/list. Use cyberchef_analyse({tool: "hash_identify", ...}), or set CYBERCHEF_TOOL_SURFACE=curated to keep the old listing.
  • cyberchef_describe_operation answers differently for a registry tool: it returns the schema instead of an error with a redirect hint.
npm run test:mcp 1,720 tests, 77 files -- all pass
npm run lint clean
npm run check:versions all references agree
npm run test:coverage 95.15 / 89.31 / 96.57 / 96.14 (thresholds 95 / 89 / 96 / 96)