Skip to content

Extract gateway: the dispatch conventions both instances duplicated - #3

Merged
jeremy merged 2 commits into
mainfrom
extract-gateway
Aug 28, 2026
Merged

Extract gateway: the dispatch conventions both instances duplicated#3
jeremy merged 2 commits into
mainfrom
extract-gateway

Conversation

@jeremy

@jeremyjeremy commented Aug 28, 2026

Copy link
Copy Markdown
Member

Extraction PR 3 of 4 per the program board's plan of record (internal): the domain-gateway dispatch conventions, moved from where both product instances proved them by duplication.

What moves

From hey-mcp-server's internal/server (as merged in 247417c), whose shape duplicated basecamp-mcp-server's domain registry near-verbatim:

  • One MCP tool per domain with the {"action", "params"} calling convention and the action enum in the generated input schema.
  • In-band describe — the reserved action serving per-operation schemas on demand, including the present-but-non-string params.action rejection.
  • In-band isError failures per MCP convention — dispatch never surfaces protocol errors for caller mistakes.
  • Read-only filtering — write actions drop from the catalog, all-read-only tools get ReadOnlyHint, and dispatch refuses writes even when a client ignores the schema.
  • Fail-closed domain narrowing — unknown domain names are a startup error, mirroring BASECAMP_MCP_ENABLED_DOMAINS semantics (basecamp-mcp-server internal/tools/domains.go).

The seam

The gateway serves any catalog implementing the small Domain interface (name, tool name, description, schema, action lookup, describe, read-only filter). What an action does when dispatched is the product's Handler — hey's spike stub and basecamp's per-action handlers stay per-product, as does server identity (an mcp.Implementation parameter).

Polish (only where the instances trivially disagree, or the split forces it)

  • FilterReadOnly reports (Domain, bool) instead of a typed-nil interface.
  • ErrorResult/JSONResult are exported — both instances carried private copies for their handlers.
  • The read-only gate is enforced at dispatch time as well as filter time (the spike checked both; the test pins it with a deliberately leaky catalog).

Tests port the spike's wire-level suite (internal/server/server_test.go) onto a fake catalog, running through mcptest (#2).

catalog (PR 4 of 4) follows: the generated implementation of this interface.

One MCP tool per domain, the {"action", "params"} calling convention,
an in-band describe action, isError failures instead of protocol errors,
read-only filtering, and fail-closed domain narrowing — moved from
hey-mcp-server's server package, whose shape duplicated
basecamp-mcp-server's domain registry near-verbatim.
The gateway serves any catalog implementing the small Domain interface;
what an action does when dispatched is the product-supplied Handler.
Per-product pieces stay behind: server identity moves to an
mcp.Implementation parameter, and the handler body (hey's spike stub,
basecamp's action handlers) is the product's.
Polish where the instances trivially disagreed: FilterReadOnly reports
ok instead of returning a typed nil, ErrorResult/JSONResult are exported
for product handlers, and the read-only gate is checked at dispatch time
as well as at filter time.
CopilotAI balanced review requested due to automatic review settings August 28, 2026 08:29

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extracts reusable MCP domain dispatch conventions into the shared toolkit.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Changes:

  • Adds domain routing, describe handling, read-only filtering, and fail-closed narrowing.
  • Exports shared MCP result helpers.
  • Adds wire-level gateway tests using mcptest.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
gateway/gateway.goImplements the reusable domain gateway.
gateway/gateway_test.goTests dispatch, filtering, narrowing, and errors.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadgateway/gateway.go
Dispatch always routes the describe action to Domain.Describe, so an
operation a catalog registers under that name is silently unreachable
while still advertised. The extracted catalog package refuses such a
collision at build time, but the gateway serves any Domain
implementation — fail closed in New rather than trusting every catalog
to enforce the reservation.
@jeremy
jeremy merged commit 469eb45 into mainAug 28, 2026
2 checks passed
@jeremy
jeremy deleted the extract-gateway branch August 28, 2026 09:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@jeremy