Skip to content

Gateway MCP: add write mode behind --allow-write, and align tool naming #357

Description

@leggetter

Why

hookdeck gateway mcp is read-only, with two exceptions (pause, unpause). That was a deliberate call in #228: skills + CLI for building, MCP for investigating.

The Outpost MCP server (#346) took a different route — full write support, off by default, behind --allow-write — because the reasoning behind #228 does not transfer: it assumed skills + CLI already covered the build path, which is true for the Event Gateway and was not true for Outpost.

Having shipped that, the asymmetry is now the problem. Two servers in the same CLI, one of which can change things and one of which cannot, with no difference in how they are configured or described. This proposes bringing Gateway to the same model.

The machinery already exists. mcpcore.RequireWrite, the schema-gating pattern and the --allow-write / --read-only flags were built in the shared package precisely so a second server could adopt them. Every write method this needs is already on the API client — this is a tool-layer change, not new API work.

Scope: write actions to add

Existing tools gain actions; no new tools, no renamed tools in this part.

Tool Today Add Client methods (already present)
connections list, get, pause, unpause create, upsert, update, delete, enable, disable CreateConnection UpsertConnection UpdateConnection DeleteConnection EnableConnection DisableConnection
sources list, get create, upsert, update, delete, enable, disable CreateSource UpsertSource UpdateSource DeleteSource EnableSource DisableSource
destinations list, get create, upsert, update, delete, enable, disable CreateDestination UpsertDestination UpdateDestination DeleteDestination EnableDestination DisableDestination
transformations list, get create, upsert, update, delete, run CreateTransformation UpsertTransformation UpdateTransformation DeleteTransformation RunTransformation
events list, get, raw_body retry, cancel, mute RetryEvent CancelEvent MuteEvent
requests list, get, raw_body, events, ignored_events retry RetryRequest
issues list, get update, dismiss UpdateIssue DismissIssue
attempts, metrics read-only none

Rules to carry over from Outpost

  • Read-only by default. --allow-write, also read from HOOKDECK_MCP_ALLOW_WRITE; --read-only accepted explicitly and wins if both are passed.
  • Gate in the schema, not at call time. In read-only mode write actions are absent from the action enum and the description, so an agent is never offered something it cannot do. mcpcore.RequireWrite sits behind that as defence in depth.
  • Annotations: ReadOnlyHint on read-only tools, DestructiveHint on delete, cancel, mute and dismiss. Per the MCP spec these are client UX hints and not a security boundary — the flag enforces.
  • pause and unpause stay available in read-only mode. They are already shipped as read-mode actions and moving them behind the flag would be a regression for anyone relying on them today.

Worth deciding

  • Does transformations run count as a write? It executes code but changes nothing. Leaning read, with the reasoning recorded either way.
  • Does anything here need a second credential? Outpost's publish did. Nothing in the Gateway list appears to, but confirm before assuming.

Scope: the tool rename

Tracked separately in #352, and part of the v3.0.0 epic #354. Summarised here because the two interact.

Product tools take the product prefix; platform tools keep hookdeck_. Logging in and switching projects are Hookdeck operations whichever product you are in — Outpost already follows this rule.

hookdeck_connections     -> gateway_connections
hookdeck_sources         -> gateway_sources
hookdeck_destinations    -> gateway_destinations
hookdeck_transformations -> gateway_transformations
hookdeck_requests        -> gateway_requests
hookdeck_events          -> gateway_events
hookdeck_attempts        -> gateway_attempts
hookdeck_issues          -> gateway_issues
hookdeck_metrics         -> gateway_metrics
hookdeck_help            -> gateway_help

hookdeck_login           unchanged   (platform)
hookdeck_projects        unchanged   (platform)

Sequencing — the decision this issue needs

The two halves are independent, and they sit in different SemVer tiers:

Change Bump
Write mode Additive: a new flag, new entries in existing enums. Nothing existing changes. MINOR
Rename Per-tool permission grants and allowedTools config do not carry across a rename MAJOR

So there is a real choice:

A. Write mode first, in a minor. Rename later with v3.
Write support ships as soon as it is ready. Users get it without waiting for a major, and adopt the rename separately.

B. Both together, in v3.
One migration for users: new capability and new names at once, one set of release notes, one re-grant of permissions. But write mode then waits for however long v3 takes.

Recommendation: A. Write mode is useful on its own and has no reason to be held behind a breaking change. It does make the rename more valuable — more tools with more power makes "which product is hookdeck_connections?" matter more — but that argues for keeping #352 queued, not for coupling them.

The cost of A is that users re-grant tool permissions at v3 having just gained new tools at the minor. Worth stating in both sets of release notes.

Testing

  • Unit tests for action-set construction in both modes, and the RequireWrite guard behind it
  • Acceptance coverage under the existing mcp tag: tools/list omits write actions without the flag and includes them with it, mirroring test/acceptance/outpost_mcp_test.go
  • The existing Gateway MCP suite must pass unchanged in the default (read-only) configuration — that is the regression gate for not disturbing what is already shipped
  • Run the tagged suites locally before pushing; they do not run on PRs targeting a feature branch

Related

  • #346 — Outpost CLI epic, where this pattern was established
  • #228 — the read-only decision this revisits
  • #350 — Gateway MCP project-type guard, a separate gap in the same server
  • #352 — the rename
  • #354 — v3.0.0 epic

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions