v3.0.0
Summary
v3.0.0 brings Outpost to the command line. hookdeck outpost manages tenants, destinations and
the events delivered to them, publishes events, inspects attempts, and reads topics, destination
types, metrics, config and deployment status, with hookdeck outpost mcp exposing the same surface
to an agent. Every Outpost command is marked [BETA] — flags and output may still change.
The Event Gateway MCP server can now change data. Every tool was read-only in v2.6.0; create,
update, delete, retry, cancel, mute and dismiss are all available behind --allow-write.
Adding those actions to the existing tool names would have meant anyone who had already allowed
hookdeck_connections for list and get silently gained delete as well — clients grant
permission per tool name and cannot match on arguments, so one name carrying both has to be allowed
or denied whole. Each resource splits into a _read half and a _write half instead, which makes
"allow all reads, prompt on anything that changes data" a single rule:
mcp__hookdeck-gateway__*_read.
The Event Gateway's own tools also move from the hookdeck_* prefix to gateway_*. Both renames
land together so you re-grant once.
If you have automation built against v2.x, the three breaking changes are the ones to check.
Breaking changes
-
Every MCP tool name changes. The Event Gateway's product tools take a
gateway_prefix, and
each tool splits by what it does —hookdeck_connectionsbecomesgateway_connections_read
(list, get),gateway_connections_pause(pause, unpause) andgateway_connections_write(create,
upsert, update, delete, enable, disable). Outpost splits the same way.Three tools keep the
hookdeck_prefix, because they are platform operations shared by both
servers rather than one product's:hookdeck_login, andhookdeck_projectssplit into
hookdeck_projects_readandhookdeck_projects_use.Per-tool grants and
allowedToolsentries need updating once; they do not survive a rename
and MCP has no migration mechanism. -
Arguments an action ignores are now refused. Every property is scoped to the actions that read
it, so{"action": "get", "order_by": "created_at"}errors instead of silently dropping
order_by. An ignored filter made a result look filtered when it was not. Declared enums are
enforced for the same reason — they never were. -
--hookdeck-configwrites to the passed file location. It was honoured for reads and ignored
for writes, so run from a directory that happens to contain.hookdeck/config.toml, the switch
went to the local file while every later command carried on reading the file you passed — and so
carried on reporting the old project:$ hookdeck --hookdeck-config ./mine.toml project use "Acme" "Staging" Successfully set active project to: Acme / Staging # written to ./.hookdeck/config.toml $ hookdeck --hookdeck-config ./mine.toml whoami ... on project Production # ./mine.toml was never touched
The flag now decides the file for reads and writes alike.
(#424) -
An unknown subcommand now exits 1.
hookdeck gateway connection lstprinted help and exited
0, so a mistyped command in a script reported success. Applies to all 23 commands that exist only
to hold subcommands —hookdeck project,hookdeck outpost tenantand the like. Running one of
those on its own still prints its help and exits 0.
New features
-
Outpost support —
hookdeck outpost …[BETA].tenant,destination,destination-type,
event,attempt,publish,topic,metrics,configandstatus.hookdeck outpost config set TOPICS=orders.created # replaces the project's topic list hookdeck outpost status # repeat until it reports HEALTHY hookdeck outpost tenant upsert acme hookdeck outpost destination create --tenant-id acme --type webhook \ --config url=https://acme.example.com/hooks --topics orders.created hookdeck outpost publish --tenant-id acme --topic orders.created --data '{"id":"ord_1"}' \ --api-key $HOOKDECK_API_KEY
A destination can subscribe only to topics configured on the project; a new project has none.
A configuration change takes effect whenhookdeck outpost statusreportsHEALTHY.publish
requires a Project API key, passed with--api-keyor read fromHOOKDECK_API_KEY.Which fields a destination takes is decided by your Outpost instance, not by the CLI, so there is
no flag per field: pass repeatable--config key=valueand--credential key=valuepairs
instead, with--config a.b=cfor a nested object and--config-filefor anything too long for a
flag. The CLI fetches each type's schema and rejects unknown keys and missing required fields
before sending.destination create --type <type> --helplists that type's fields.
(#346,
#347) -
hookdeck outpost mcp[BETA]. The commands above, exposed as MCP tools, split_read/
_writeand gated the same way as the Gateway server: read-only unless you pass--allow-write
or setHOOKDECK_MCP_ALLOW_WRITE, with--read-onlywinning over both. The tenanttokenand
portalactions are gated with the writes even though they only read, because both hand back a
reusable credential. Publishing needs a separate key via--publish-api-keyor
HOOKDECK_OUTPOST_PUBLISH_API_KEY; without one the tool is not registered at all. -
Gateway MCP write mode, behind
--allow-write. create, upsert, update, delete, enable and
disable on connections, sources and destinations; create, upsert, update and delete on
transformations; retry, cancel and mute on events; retry on requests; update and dismiss on
issues.pause/unpausestay ungated — read-only is the mode you investigate an incident in —
andtransformations runcounts as a read because it creates no execution record and delivers
nothing. -
gateway_bulk_readandgateway_bulk_write. Bulk retry, cancel and replay.planreports
how many records an operation would touch without running it, and works without--allow-write—
so you can check the scope of a bulk retry before enabling writes at all. Fifteen of the nineteen
bulk endpoints were not reachable from the CLI or an agent before. -
Events and requests split into plural search and singular by-id tools.
hookdeck_events
carried six actions and 25 parameters, andidwas the only one more than one action used —
retry,cancelandmuteeach needed just that, and were offered all 25. A request's events
are nowgateway_events_readwith arequest_idfilter. Guessing wrong recovers: a singular tool
asked tolistnames the plural tool, and an id of the wrong type is caught before the call. -
Query filters the API documented but neither surface exposed.
gateway requests listgains
--search-term,--events-count,--ignored-countand--cli-events-count;gateway events listgains--search-term,--next-attempt-at-afterand--next-attempt-at-before.
--events-count 0finds requests that produced no events, the usual explanation for a webhook
that looks missing.
Fixes
MCP
-
Debug logs no longer persist credentials. Request redaction matched one field and responses
were logged verbatim, so--log-level debugwrote webhook secrets, bearer tokens and the CLI key
you receive when you sign in to disk. Redaction is now recursive, by field name, over requests and
responses. -
Filters sent as the wrong JSON type are no longer discarded. Models routinely send a boolean
or a number as a string —verified: "false",limit: "5"— and the tools accepted only the
declared type, so those filters were dropped without an error. A dropped filter reads as no
filter, so the API returned everything and the answer came back looking filtered when it was not. -
Argument values a tool cannot use are refused rather than dropped.
{"measures": ["count", 5]}queried one measure and reported success. -
hookdeck_connectionsaccepted adisabledfilter it could not honour — now
gateway_connections_read.disabled: falseset nothing and returned every connection as though
filtered to the enabled ones. There is no enabled-only filter to send, so it is refused with that
explanation instead. -
hookdeck_metricsrejected themeasuresit had just been given — now
gateway_metrics_read. It was the one tool not accepting the comma-separated form, so
"measures": "count"was dropped and you were toldmeasures is required.
Correction: this fix is not in v3.0.0 or v3.0.1. See
#440. -
The connections tools no longer say "id or name is required" to callers who passed
name.
nameis a declared property, but onlyidwas read. -
An abandoned re-authentication no longer signs you out.
hookdeck_loginwithreauth: true
deleted the stored credentials before opening the browser, so a closed laptop or a failed poll
left you signed out everywhere with nothing to show for it. -
The
mcp --helpexample did not work. Copied verbatim it printedserver is closing: EOFand
exited 1. (#428)
CLI
-
hookdeck ci --helpno longer prints your API key.--api-keytook its default from
HOOKDECK_API_KEY, and help output prints a flag's default — so with the variable exported your
key was echoed into the terminal and into any log that captured it.tools/generate-reference
reads the same defaults, so it could have written the key into the publicREFERENCE.md. -
A resource id can no longer retarget a request at a different resource. Ids were concatenated
into URL paths, and resolving a path normalises.., sosrc_1/../../destinations/des_2
addressed a different resource of a different type while the caller reported the id it was given. -
gateway event retry,cancelandmutereport the status the API returned. All three read
the status code and ignored the body, but the API answers 200 to a no-op — so cancelling a
delivered event printedcancelledwhile the event stayedSUCCESSFUL. -
gateway request retrysays how many events it produced, and fails when a retry aimed at a
connection the request never went through creates none. -
gateway issue dismisssays what it changed. Two QA passes reported "dismiss does not
dismiss" after checkingstatus. Nothing was broken — the API populatesdismissed_atand leaves
statusalone — so the CLI now says that. -
gateway connection update --rules '[]'clears a ruleset. An empty array was left out of the
request entirely, so the command changed nothing and exited 0. -
A 401 in a terminal no longer tells you to call an MCP tool. The hint was appended wherever
the error was printed, including to readers with no way to call one. -
A warning could corrupt
--output json. Source validation warned on stdout when the OpenAPI
spec fetch failed, so the warning landed ahead of the JSON. That fetch fails intermittently, so it
broke scripts at random.
Full Changelog: v2.6.0...v3.0.0