Skip to content

MCP: connection management, governance tools, vendor-neutral setup - #358

Merged
QSchlegel merged 1 commit into
preprodfrom
claude/mcp-connections-ui
Aug 12, 2026
Merged

MCP: connection management, governance tools, vendor-neutral setup#358
QSchlegel merged 1 commit into
preprodfrom
claude/mcp-connections-ui

Conversation

@QSchlegel

Copy link
Copy Markdown
Collaborator

Follow-up to #357, from connecting a real client to preprod and finding two gaps.

Connections are now visible and revocable

Approving an AI client on the consent screen was a one-way door: the OAuthGrant row was written but never read back, so there was no way to see what was connected and no way to disconnect it.

Adds a "Connected AI clients" card to the profile, next to the bot-key card it mirrors — bots authenticate with a key minted there, MCP clients with an OAuth grant approved on the consent screen. Each entry shows:

  • the client name and the client id it was actually verified as (the name is self-declared by whoever registered the client, so it isn't identity)
  • a verified metadata badge for CIMD clients
  • the granted scopes in plain language
  • whether a session is still live

Revoking drops the grant and revokes every refresh token under it in one transaction — a deleted grant with live refresh tokens would let the client keep renewing silently. Access tokens already issued are self-contained JWTs and stay valid until they expire within the hour; the confirmation dialog says so rather than implying an instant cutoff.

Fixes: two tools were unreachable

The first live connection came back with only wallets:read, so governance_list_active_proposals and ballot_upsert never appeared in the client's tool list.

Cause: the 401 challenge advertised scope="wallets:read", and clients request exactly the challenge's scope rather than scopes_supported from the metadata document. Anything omitted there is unreachable in practice, however well documented.

The challenge now advertises both read scopes. ballots:write stays out deliberately — it's the only scope that writes anything, so a client must ask for it explicitly. Pinned by a test so it can't drift back.

Landing page

The "Connect an AI agent" card is now three numbered setup steps — add the server, authorize with your wallet, ask it something — plus a read-only callout linking to the profile.

Verification

tsc --noEmit clean, npm run build green, 801 + 76 tests. Nine new tests for the router, covering the authorization boundary: revoke refuses an address the wallet session doesn't hold, refuses with no session at all, looks the grant up by (subject, client) rather than client alone, and 404s on a missing grant. Landing section verified in a browser.

Not verified visually: the profile card itself needs a wallet session and a database, so I couldn't render it locally — it self-hides without an address (confirmed no tRPC request fires). Its router is unit-tested, and the card will have real data to show on preprod.

Note the router tests live in the ESM jest project: importing a tRPC router pulls superjson, which is ESM-only and cannot load in the CJS project.

🤖 Generated with Claude Code

Approving an AI client on the consent screen was a one-way door: the
OAuthGrant row was written but never read back, so a user had no way to
see what they had connected, and no way to disconnect it.

Adds a "Connected AI clients" card to the profile, next to the bot-key
card it mirrors — bots authenticate with a key minted there, MCP clients
with an OAuth grant approved on the consent screen, and both now live in
one place. Each entry shows the client name, the client id it was
actually verified as (the name is self-declared), the granted scopes in
plain language, and whether any session is still live.

Revoking drops the grant and revokes every refresh token under it in one
transaction — a deleted grant with live refresh tokens would let the
client keep renewing silently. Access tokens already issued are
self-contained JWTs and stay valid until they expire within the hour,
which is the trade-off for stateless verification; the dialog says so
rather than implying instant cutoff.

Also fixes a real gap the first live connection exposed: the 401
challenge advertised only `wallets:read`, and clients request exactly the
challenge's `scope` rather than `scopes_supported` from the metadata
document. Two of the nine tools were therefore unreachable by any client
following the spec. The challenge now advertises both read scopes;
`ballots:write` stays out so the one scope that writes anything remains
opt-in.

Expands the landing "Connect an AI agent" card into three numbered setup
steps, and links to the profile for managing connections.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
multisig Ready Ready Preview Aug 12, 2026 2:47pm

Request Review

@QSchlegel
QSchlegel merged commit dee15bf into preprod Aug 12, 2026
8 checks passed
@QSchlegel QSchlegel changed the title Show and revoke MCP connections in the profile MCP: connection management, governance tools, vendor-neutral setup Aug 12, 2026
@QSchlegel

Copy link
Copy Markdown
Collaborator Author

Pushed a second commit (9b74cd4) covering the governance surface and the vendor-neutral guide.

Three governance tools

Tool Answers
governance_list_ballots the team's internal decision log — how signers decided, with drafted rationale
governance_vote_history what the DRep actually put on-chain, newest first, with proposal titles
governance_open_proposals active proposals not yet voted on — the outstanding decisions

governance_open_proposals degrades rather than fails: if the DRep is unset or Koios is unreachable it still returns the active set and flags voteHistoryUnavailable, so a model can say "I could not check" instead of implying nothing has been voted on. includeVoted: true returns the whole active set annotated with how we voted.

Two handler changes

  • botBallots gains a human branch, mirroring botBallotsUpsert. It was bot-only and required ballot:write even to GET, while a wallet signer can already read those ballots through the tRPC router.
  • drepInfo gains a wallet-access check. It previously looked up any walletId for any authenticated caller, letting anyone map a wallet id to its DRep credential. Pre-existing, fixed here because this change exposes it through MCP.

Vendor-neutral, hero-level

The hero leads with MCP rather than one vendor: the endpoint and a plain mcpServers JSON block that works in any client, with a per-vendor CLI mentioned only as an aside. Demo prompts rewritten to match tools we actually expose, including the governance ones.

805 + 76 tests, tsc clean, build green, hero verified in a browser.

Still to come

Rationale → IPFS publishing is not in this PR. /api/pinata-storage/put currently has no authentication at all, so agent-driven uploads shouldn't be built on it — that wants a properly gated endpoint, which is its own change.

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