Serve HEY over MCP with hey mcp - #357
Conversation
aa11d9b to
2757539
Compare
There was a problem hiding this comment.
Pull request overview
Adds hey mcp, a stdio MCP server exposing authenticated HEY operations.
Changes:
- Adds five catalog-derived MCP gateway tools and dispatch.
- Adds model snapshots, tests, documentation, and dependencies.
- Also includes per-install OAuth identifier changes from #355.
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.
Reviewed changes
Copilot reviewed 25 out of 27 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
.surface |
Records the MCP command surface. |
README.md |
Documents MCP usage. |
go.mod |
Adds MCP dependencies. |
go.sum |
Records dependency checksums. |
scripts/sync-mcp-model.sh |
Synchronizes SDK model snapshots. |
internal/auth/auth.go |
Adds install IDs to OAuth requests. |
internal/auth/auth_test.go |
Tests OAuth install IDs. |
internal/auth/install_id.go |
Persists per-install identifiers. |
internal/auth/install_id_test.go |
Tests identifier persistence. |
internal/cmd/auth.go |
Displays the install ID. |
internal/cmd/help.go |
Categorizes the MCP command. |
internal/cmd/help_test.go |
Updates help expectations. |
internal/cmd/mcp.go |
Implements hey mcp. |
internal/cmd/mcp_test.go |
Tests command integration. |
internal/cmd/root.go |
Registers the command. |
internal/mcpserver/catalog.go |
Loads the embedded catalog. |
internal/mcpserver/catalog_test.go |
Tests catalog derivation. |
internal/mcpserver/dispatch.go |
Dispatches actions to the SDK. |
internal/mcpserver/dispatch_test.go |
Tests request construction. |
internal/mcpserver/domains.go |
Defines exposed domains. |
internal/mcpserver/server.go |
Configures the MCP gateway. |
internal/mcpserver/server_test.go |
Tests gateway behavior. |
internal/mcpserver/model/PROVENANCE.json |
Records snapshot provenance. |
internal/mcpserver/model/behavior-model.json |
Vendors operation traits. |
internal/mcpserver/model/openapi.json |
Vendors API schemas. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aa11d9b30d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
2757539 to
adba328
Compare
da2eea2 to
ec33a59
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ec33a59c10
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ec33a59 to
24a146f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 24a146f378
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
24a146f to
e38a672
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e38a672cd4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
hey mcp runs an MCP server on stdin/stdout: seven domain gateway tools
(boxes, search, threads, contacts, todos, calendar, identity) derived
from hey-sdk's model exports via the shared toolkit at
github.com/basecamp/mcp, dispatching real API calls through the CLI's
authenticated, account-scoped SDK client — the same keychain-stored
credentials every other command uses.
The catalog vendors a hey-sdk model snapshot (behavior-model.json +
openapi.json at go/v0.28.0, the version go.mod pins — kept in lockstep
by test), synced by scripts/sync-mcp-model.sh with provenance recorded,
and the dispatcher maps catalog operations onto the SDK's generic
verbs: path and required query params enforced, substituted, and
escaped, remaining params gathered into the request body with property
names checked against the body schema as a typo guard. Listings with
more pages surface their geared_pagination cursor as
{"next_page": cursor, "results": ...} — HEY pages by cursor, and a
numeric page is answered with the first page forever. Failures are
in-band isError results; stdout belongs to the MCP wire, logs go to
stderr.
--read-only serves only read-only actions and refuses write dispatch;
--domains narrows the served surface, failing closed on unknown keys.
e38a672 to
c0cfa54
Compare
What
hey mcpruns an MCP (Model Context Protocol) server on stdin/stdout, serving HEY as tools backed by the signed-in account. Seven domain gateway tools —hey_boxes,hey_search,hey_threads,hey_contacts,hey_todos,hey_calendar,hey_identity— derived from hey-sdk's model exports via the shared toolkit at github.com/basecamp/mcp, dispatching real API calls through the CLI's authenticated, account-scoped SDK client. One install, no separate binary:Design
Why not import hey-mcp-server? This repo is public;
basecamp/hey-mcp-serveris private, so a module dependency is a broken build for everyone outside the org, and vendoring it would publish it wholesale. Everything the server actually needs is already public — thebasecamp/mcptoolkit (catalog, gateway, mcptest) and hey-sdk's model exports — so the CLI assembles the same shape from those parts, per the toolkit's own two-instance-by-duplication convention. hey-mcp-server remains the home of the hosted/HTTP story; when its catalog curation finds a shared public home (hey-sdk being the natural candidate), both consumers converge on it.Real dispatch, not the stub. hey-mcp-server's handler is still the spike boundary (dispatch names the operation it would call). Here the CLI's
*hey.Client— auth, token refresh, retry, account scoping, body limits — is already in hand, so the dispatcher maps catalog operations onto the SDK's generic verbs: path params substituted and escaped, query params encoded, remaining params gathered into the request body with property names checked against the body schema (a typo guard — types and required properties stay the server's to enforce, with its errors surfaced in-band). Calling convention (path/query/body all flat inparams,describeserves the schema, paginated listings surface their geared_pagination cursor asnext_pageto pass back aspage) lives ininternal/mcpserver/dispatch.go; it should migrate to the toolkit once a second instance proves it.Catalog snapshot.
internal/mcpserver/model/vendors hey-sdk'sbehavior-model.json+openapi.jsonatgo/v0.28.0— the same version go.mod pins — synced byscripts/sync-mcp-model.shwith provenance recorded. Unmapped SDK tags are pinned by test, so growing the surface is a deliberate one-line change plus a snapshot refresh.Auth composition with #355/#356.
hey mcpnever touches the login flow: it reads stored credentials through the sameauth.Manager→ SDK bridge as every other command (requireAuth()+ the packagesdkclient), and the Manager's proactive refresh keeps a long-running server alive across token expiry. The ephemeral-port listener (#356) and per-install id (#355) change how tokens are acquired, not how they're read, so this composes with both unchanged — based on main; a rebase after they land is a no-op for this diff.Tests
internal/mcpserver: catalog derivation pinned (seven tools, unmapped tags accounted, snapshot ref asserted against go.mod's hey-sdk version);buildRequesttable (path substitution/escaping, scalar formatting, query encoding, body gathering, schema-checked strays, missing/malformed params); wire-level tests via the toolkit'smcptestharness (tools/list, describe, dispatch-through-API, read-only refusal, domain narrowing).internal/cmd: command registration, account-scope policy, auth requirement, and an end-to-end test running the actualhey mcpcommand against an httptest HEY server through the transport seam — MCP initialize + tools/list + a tool call, asserting the CLI's bearer token rides on the dispatched request.--read-only/--domainspassthrough proven over the wire.make checkgreen exceptinternal/tui's pre-existing unix-socket-path failures on this macOS sandbox (fail on main too, as Bind the OAuth callback listener to an ephemeral loopback port (RFC 8252 §7.3) #356 also notes).go test -raceclean on the new packages. Size budget: 34.3 MiB of 37 (the embedded 425 KB model is noise).