feat(mcp): permissions management, per-wallet activity, and a published tool list - #365
Merged
Merged
Conversation
…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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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 free
to 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.
Re-targets the work from #363 onto
preprod.#363 was merged into
claude/mcp-ipfs-rationaleat 11:21, five minutes after that branch had already gone into preprod as #362 (11:16). The commit landed on a branch nothing points at any more, so none of it reached preprod. This is the same cherry-picked commit, byte-identical tree, based directly on preprod.It also means the heavier CI workflows never ran on it — they only trigger for PRs targeting
main/preprod, and #363 targeted a feature branch. This PR is the first real CI run for this change.Permissions are enforced, not decorative
A permissions UI on top of the previous code would have been a lie. The MCP endpoint trusted the access token's
scopeclaim, and those tokens are self-contained with an hour's life — so unticking a permission, or revoking a connection outright, would have done nothing until the token happened to expire. The card would say "revoked" while the client kept working.resolveMcpCallernow reads the stored grant on every request: no grant → 401, and the token's scopes are intersected with the grant's. Changes apply on the next call, and a token cannot gain reach it wasn't issued with even if the grant is later widened. Three tests pin those cases. Cost is one extra indexed lookup on a path that already does one for bots.The card gets per-scope checkboxes; saving 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 — that leaves a connection that authenticates but does nothing, which reads as broken. Revoke is the honest action.
Tool-call logging
Every tool call writes an
AuditLogrow from the single wrapper all tools pass through — tool, client, scope, status, duration — keyed to the wallet it touched, so the drill-down is an indexed lookup. OnlywalletIdis taken from the arguments; tool inputs carry rationale prose that shouldn't live in an audit row. Fire-and-forget, so a logging failure can't break a call.The wallet overview card lists clients that used that wallet, expandable to individual calls — a different question from the profile card's "what have I connected".
Tool list
All 13 on the landing page, grouped by permission. It lives in a plain data file because importing the registry would pull the API handlers and Mesh WASM into the browser bundle; a test keeps the two in sync name-for-name and scope-for-scope.
Verification
tsc --noEmitclean,npm run buildgreen, 821 + 85 tests passing. Tool list verified rendering in a browser.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
walletClientsrefuses a wallet you are not a signer of — but the rendered result will first be real on preprod.🤖 Generated with Claude Code