Skip to content

MCP: permissions management, per-wallet activity, published tool list - #363

Merged
QSchlegel merged 1 commit into
claude/mcp-ipfs-rationalefrom
claude/mcp-permissions-ui
Aug 13, 2026
Merged

MCP: permissions management, per-wallet activity, published tool list#363
QSchlegel merged 1 commit into
claude/mcp-ipfs-rationalefrom
claude/mcp-permissions-ui

Conversation

@QSchlegel

Copy link
Copy Markdown
Collaborator

Three connected changes: users can see what the tools are, control what each client may do, and audit what it actually did.

Permissions are now enforceable, not just displayed

The MCP endpoint trusted the access token's scope claim. Those tokens are self-contained and live an hour, so removing a permission — or revoking a connection outright — did nothing until the token expired. A permissions UI on top of that would have been a lie.

resolveMcpCaller now reads the stored OAuthGrant on every request:

  • a missing grant is a 401 (revocation is immediate, not eventual)
  • the token's scopes are intersected with the grant's, so a change applies on the very next call and a token can never gain reach it wasn't issued with

Costs one indexed lookup, on a path that already does one for bot callers. Three tests pin this: revoked → 401, narrowed grant drops the tool even though the token still claims the scope, widened grant does not widen the token.

The profile card gains per-scope checkboxes and a save action. updateConnectionScopes moves the grant and its live refresh tokens in one transaction, so a refresh can't re-widen what was just narrowed. Emptying a grant is refused — a connection that authenticates but can do nothing reads as broken; revoking is the honest action.

Tool calls are recorded

Every invocation writes an AuditLog row from the single wrapper in createMcpServer — tool, client, scope, status, duration — keyed to the wallet it touched, so the per-wallet view is an indexed lookup rather than a scan.

Only the walletId is read from the arguments: tool inputs carry user-authored prose (rationales, descriptions) that has no place in an audit row. Writes are fire-and-forget; an audit miss must never break a tool call.

Wallet overview: AI client activity

A new card lists the clients that used this wallet, with call counts and failures, expandable to individual calls. The profile card answers "what have I connected"; this answers "what has touched this wallet", which is the question a co-signer asks.

Landing page lists the tools

All 13, grouped by the permission each needs. The list lives in src/data/mcp-tools.ts rather than importing the registry — that would drag the API handlers and Mesh WASM into the client bundle — and a test asserts the two match name-for-name and scope-for-scope. A second test pins the audit action string the tRPC router hard-codes for the same reason, since a drift there would silently empty the activity view.

Verification

tsc clean, build green, 821 + 85 tests. Tool list verified rendering in a browser (13 tools, correctly grouped).

Not verified visually: the permissions checkboxes and the wallet activity card both need a wallet session and a database, neither available locally. Their procedures are unit-tested — including that walletClients refuses a wallet the caller isn't a signer of.

Based on #362, since the tool list includes ballot_publish_rationale. GitHub retargets to preprod once that merges.

🤖 Generated with Claude Code

…ed tool list

Three connected changes: users can now see what the tools are, control what
each client may do, and audit what it actually did.

**Permissions are now enforceable, not just displayed.** The MCP endpoint
previously trusted the access token's `scope` claim. Those tokens are
self-contained and live an hour, so removing a permission — or revoking a
connection outright — did nothing until the token happened to expire, which
makes a permissions UI a lie. `resolveMcpCaller` now reads the stored
OAuthGrant on every request: a missing grant is a 401, and the token's
scopes are *intersected* with the grant's, so a change applies on the very
next call and a token can never gain reach it was not issued with. Costs
one indexed lookup, on the same path that already does one for bots.

The profile card gains per-scope checkboxes and a save action.
`updateConnectionScopes` moves the grant and its live refresh tokens in one
transaction, so a refresh cannot re-widen what was just narrowed. Emptying
a grant is refused: a connection that authenticates but can do nothing
reads as broken, and revoking is the honest action.

**Tool calls are now recorded.** Every invocation writes an AuditLog row
from the single wrapper in `createMcpServer` — tool, client, scope, status,
duration — keyed to the wallet it touched, so the per-wallet view is an
indexed lookup rather than a scan. Only the walletId is read from the
arguments: tool inputs carry user-authored prose (rationales, descriptions)
that has no place in an audit row. Writes are fire-and-forget; an audit miss
must never break a tool call.

**Wallet overview gains an AI client activity card**, listing the clients
that used *this* wallet with call counts and failures, expandable to the
individual calls. The profile answers "what have I connected"; this answers
"what has touched this wallet", which is the question a co-signer asks.

**The landing page lists the tools**, grouped by the permission each needs.
The list lives in `src/data/mcp-tools.ts` rather than importing the registry,
which would drag the API handlers and Mesh WASM into the client bundle; a
test asserts the two match name-for-name and scope-for-scope. A second test
pins the audit action string the tRPC router hard-codes for the same reason.

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

vercel Bot commented Aug 13, 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 13, 2026 11:21am

Request Review

@QSchlegel
QSchlegel merged commit 3b9f80c into claude/mcp-ipfs-rationale Aug 13, 2026
3 checks passed
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