Uh oh!
There was an error while loading. Please reload this page.
Extract gateway: the dispatch conventions both instances duplicated - #3
Merged
Conversation
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.There was a problem hiding this comment.
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.
| File | Description |
|---|---|
gateway/gateway.go | Implements the reusable domain gateway. |
gateway/gateway_test.go | Tests 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.
Uh oh!
There was an error while loading. Please reload this page.
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.
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 in247417c), whose shape duplicated basecamp-mcp-server's domain registry near-verbatim:{"action", "params"}calling convention and the action enum in the generated input schema.describe— the reserved action serving per-operation schemas on demand, including the present-but-non-stringparams.actionrejection.isErrorfailures per MCP convention — dispatch never surfaces protocol errors for caller mistakes.ReadOnlyHint, and dispatch refuses writes even when a client ignores the schema.BASECAMP_MCP_ENABLED_DOMAINSsemantics (basecamp-mcp-serverinternal/tools/domains.go).The seam
The gateway serves any catalog implementing the small
Domaininterface (name, tool name, description, schema, action lookup, describe, read-only filter). What an action does when dispatched is the product'sHandler— hey's spike stub and basecamp's per-action handlers stay per-product, as does server identity (anmcp.Implementationparameter).Polish (only where the instances trivially disagree, or the split forces it)
FilterReadOnlyreports(Domain, bool)instead of a typed-nil interface.ErrorResult/JSONResultare exported — both instances carried private copies for their handlers.Tests port the spike's wire-level suite (
internal/server/server_test.go) onto a fake catalog, running throughmcptest(#2).catalog(PR 4 of 4) follows: the generated implementation of this interface.