[2.7.0] - 2026-08-21
Fixed
-
A cancelled request can no longer punish every other caller through the shared circuit
breaker. The identity probe runs once at boot, and a cache reset used to bump a generation
counter so a straggler could not write back into the fresh slot. That guarded the cache, but
not the breaker, which is process-wide state no generation counter covers: the abandoned
request stayed alive, and if it happened to hold the single half-open probe slot, its
eventual failure re-Opened the breaker and started a fresh cooldown that fast-failed every
other call for a minute. A reset now aborts the outstanding probe instead of merely
forgetting it, and the driver tells a caller cancellation apart from a per-attempt timeout:
a timeout is evidence the upstream is sick and still counts, while a cancellation carries no
evidence in either direction, so it releases the probe slot with no verdict and leaves the
breaker exactly where it stood. Cancelled requests report a newREQUEST_CANCELLEDcode
rather than a misleadingNETWORK_ERROR, and no longer log one either: rendering the abort
through the network-error path also put its stderr line ahead of the cancellation exit, which
in a host with a throwingconsole.errorescaped before the probe slot could be released and
let the backstop restore the very penalty this removes. Only the boot probe cancels anything
today, so no tool response changes. -
All five
search_*tools now enforce thefieldscontract the v2 spec actually states.
The spec modelsfieldson every search endpoint as "a comma-separated string array" whose
enumconstrains each token.search_productsread that enum as a single value, so
fields: "name,code"was rejected before the handler ran. The other four went the opposite
way and accepted any string at all, forwarding a token likebogusto Pipedrive to be
rejected or, worse, silently ignored. Both halves are now one shared validator: every token
is checked against that endpoint's allow-list, whitespace is trimmed so"name, code"
reaches the wire as"name,code"rather than silently narrowing the search against a token
Pipedrive matches literally, a trailing separator is a rejection rather than a no-op, and a
repeated token collapses instead of erroring, since a field-selection list is set membership.
The allow-lists are the ones the tool descriptions already published, so no call that
followed the documented parameter changes behavior.search_productsadditionally drops the
enumfrom its hand-writteninputSchemaliteral, which is the only shape hosts see and
where a JSON Schemaenummeans exact match.