Skip to content

feat(mcp-remote): remote MCP server for ChatGPT and Claude connectors, reusing device-code approval - #41

Merged
PancheI merged 9 commits into
mainfrom
feat/remote-mcp
Sep 9, 2026
Merged

PancheI merged 9 commits into
mainfrom
feat/remote-mcp

Conversation

@PancheI

@PancheI PancheI commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Why

ChatGPT, Claude.ai and other hosted assistants cannot run the stdio MCP server or paste an API key. They speak OAuth 2.1 to a remote MCP server (MCP authorization spec: protected-resource metadata, PKCE S256, CIMD / dynamic client registration), verified against OpenAI's plugin auth doc and Claude's custom-connector article.

What

packages/mcp-remote: a Cloudflare Worker at mcp.agentaos.ai/mcp.

  • @cloudflare/workers-oauth-provider is the OAuth front door (/authorize, /token, /register, metadata).
  • The human step reuses the platform's existing device-code approval: /authorize starts a device code and sends the merchant to the app's approve page with a return address; /callback redeems it for a one-shot session, mints a normal secret key named after the client ("ChatGPT"), and seals it into the OAuth token. The session is dropped.
  • /mcp serves the same seven agenta_pay_* tools as the CLI over stateless Streamable HTTP, bound to that key. Revoking the key on Developers disconnects the assistant; the tools then say so.
  • The seven tools take a client factory (registerPayTools(server, getClient), exported as agentaos/mcp) so the stdio CLI and the Worker share one implementation.

Verified locally

wrangler dev --local-protocol https --host localhost:8788 against the local API and app: 401 → discovery → registration → approve page → key minted with the client's name → tokens → tools/listlist_customers, create_checkout; revoke → every tool refuses with the reconnect sentence. Run with the MCP SDK client and with the official MCP Inspector.

Deploy (after the platform release that ships the approve page and secret_keys.label)

wrangler loginwrangler kv namespace create OAUTH_KV (paste id) → wrangler deploy (custom domain mcp.agentaos.ai).

Replaces #40, which GitHub closed when its base branch (#39, merged) was deleted. Includes the changeset for agentaos 3.0.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KdSgWa5Lf9mnHxY3x76Fy1

PancheI and others added 9 commits September 9, 2026 17:01
Each agenta_pay_* tool now receives a `getClient` factory instead of
reading AGENTAOS_GATEWAY_KEY itself, so the same seven tools can run
behind a per-connection key in a remote server. `runMcp` passes the
env-based `createPayClient`; `agentaos/mcp` exports `registerPayTools`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdSgWa5Lf9mnHxY3x76Fy1
…, reusing device-code approval

A Cloudflare Worker at mcp.agentaos.ai serves the seven agenta_pay_* tools
over Streamable HTTP behind OAuth 2.1 (@cloudflare/workers-oauth-provider).
The consent step is the existing device-code approve page: /authorize
starts a device code and parks the OAuth request in KV; /callback redeems
the one-shot session, mints a test or live secret key for that mode's
networks, and stores only the key in the grant. Revoking the key on
Developers disconnects the assistant.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdSgWa5Lf9mnHxY3x76Fy1
… local dev over https

- The issuer and resource URL come from MCP_PUBLIC_URL at request time, so
  wrangler dev on localhost advertises localhost and production advertises
  mcp.agentaos.ai. Verified end to end against the local API and app:
  401 → registration → approval → key → tokens → tools/list → tool calls;
  revoking the key on Developers makes every tool refuse.
- A bare fetch stored on the API client is invoked with the wrong this on
  Workers (illegal invocation); wrap it. Node tolerated it, so the tests
  with a fake fetch did not catch it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdSgWa5Lf9mnHxY3x76Fy1
The Developers tab shows the key's label, so a ChatGPT connection reads as
"ChatGPT" rather than one more anonymous sk_ row (Panche: "I don't know
which key is used for what"). Platform side: secret_keys.label (00147).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdSgWa5Lf9mnHxY3x76Fy1
Revoking a connector's key on Developers is how a merchant disconnects
ChatGPT or Claude.ai; the tools now say so instead of a bare 401.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdSgWa5Lf9mnHxY3x76Fy1
…1 (week-old releases)

The first lockfile pulled wrangler 4.130.0 with workerd, miniflare and
workers-types published the day before; the seven-day cooldown the platform
repo enforces did not exist here. Pinned back by hand; the guard follows.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdSgWa5Lf9mnHxY3x76Fy1
…repo

pnpm 10.33.3 with minimum-release-age=10080. The first lockfile of the
remote MCP Worker pulled releases published the day before; pnpm 9 had no
way to refuse them. Verified: with this setting pnpm refuses wrangler
4.130.0 (one day old) and installs 4.128.0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdSgWa5Lf9mnHxY3x76Fy1
@PancheI
PancheI merged commit 828f3eb into main Sep 9, 2026
1 check passed
@PancheI
PancheI deleted the feat/remote-mcp branch September 9, 2026 15:16
@github-actions github-actions Bot mentioned this pull request Sep 9, 2026
PancheI pushed a commit that referenced this pull request Sep 9, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## agentaos@3.0.0

### Major Changes

- [#41](#41)
[`828f3eb`](828f3eb)
Thanks [@PancheI](https://github.com/PancheI)! - The `agentaos` /
`agenta` CLI is now the merchant's CLI and MCP server for AgentaOS
payments.

- Removed the self-custody crypto-wallet surface: `send`, `sign`,
`balance`, `receive`, `deploy`, `x402`, signer and network management,
and the seventeen token-moving MCP tools. Scripts that relied on them
must pin `agentaos@2`.
- The CLI now covers onboarding and money: `login` (browser approval),
`status`, `audit`, `verify`, `products`, `pay`, `subscriptions`.
- The MCP server (run the binary with no arguments from an agent)
exposes the seven `agenta_pay_*` tools against `AGENTAOS_GATEWAY_KEY`;
the same tools are exported as `agentaos/mcp` (`registerPayTools(server,
getClient)`) for hosted servers.
- A refused key tells the merchant it may have been revoked in AgentaOS
and how to reconnect.

### Patch Changes

- Updated dependencies
[[`5f52799`](5f52799),
[`0b3489a`](0b3489a),
[`0d4ab68`](0d4ab68)]:
  - @agentaos/pay@2.2.0
## @agentaos/pay@2.2.0

### Minor Changes

- [#35](#35)
[`5f52799`](5f52799)
Thanks [@PancheI](https://github.com/PancheI)! - Add
`subscriptions.invoices(id)` and expose `linkId` / cancel fields on
`Subscription` so a logged-in SaaS can match a product, list billing
history, and cancel.

- [#37](#37)
[`0b3489a`](0b3489a)
Thanks [@PancheI](https://github.com/PancheI)! - Add `livemode` to
webhook payloads and type the subscription webhook events.

- Every `WebhookEvent` `data` now carries `livemode: boolean` (`true` =
live mode, `false` = test mode) — the account-mode signal. This replaces
the previous chain-specific `testnet` field, which was meaningless for
card/bank rails; use `network` for the chain/rail.
- New `SubscriptionData` type and five `subscription.*` events on the
`WebhookEvent` union: `subscription.created`, `subscription.renewed`,
`subscription.payment_failed`, `subscription.updated`,
`subscription.canceled`.

### Patch Changes

- [#36](#36)
[`0d4ab68`](0d4ab68)
Thanks [@PancheI](https://github.com/PancheI)! - Make
`webhooks.verify()` errors self-explaining for the most common
integration mistake — passing a parsed body instead of the raw request
bytes. A non-string/Buffer payload (e.g. `req.body` after
`express.json()`) now throws an actionable message pointing at
`express.raw()`, and a genuine signature mismatch asks whether the raw
body was used, mirroring Stripe's hint. No change to the verification
algorithm.
## @agentaos/chains@3.0.0

### Patch Changes

- Updated dependencies []:
  - @agentaos/core@3.0.0
## @agentaos/engine@3.0.0

### Patch Changes

- Updated dependencies []:
  - @agentaos/core@3.0.0
  - @agentaos/crypto@3.0.0
## @agentaos/sdk@3.0.0

### Patch Changes

- Updated dependencies []:
  - @agentaos/core@3.0.0
  - @agentaos/engine@3.0.0
## @agentaos/core@3.0.0


## @agentaos/crypto@3.0.0


## @agentaos/mcp-remote@0.1.1

### Patch Changes

- Updated dependencies
[[`828f3eb`](828f3eb),
[`5f52799`](5f52799),
[`0b3489a`](0b3489a),
[`0d4ab68`](0d4ab68)]:
  - agentaos@3.0.0
  - @agentaos/pay@2.2.0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to 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.

1 participant