Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .agent/context/20260913T021012Z-clarify-personal-mcp-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Session Context: clarify-personal-mcp-plan

## Date/time

- UTC: 2026-09-13T02:10:12Z

## User goal

Split the PR #112 plan into clear, ordered subtasks and reduce ambiguity about
the first deliverable.

## Original prompt/request

"Раздели md plan, сделай четче на подзадачи." Follow-up: keep the plan focused
only on the direct `arc_payment` flow.

## Assumptions

- The first release exposes exactly one MCP tool, `arc_payment`.
- The first release reuses the existing Privy server execution wallet and Arc
settlement worker.
- Personal wallets and additional signers are a later milestone.

## Plan

1. Rewrite the plan around one minimal server-wallet MCP milestone.
2. Give every task dependencies, concrete work, and an exit condition.
3. Preserve personal-wallet architecture as a separately gated milestone.
4. Validate Markdown, obtain Gate A, update PR #112, wait for CI, then obtain
Gate B.

## Key decisions

- `arc_payment` is a direct Arc USDC transfer.
- Browser and MetaMask wallets do not participate in milestone 1 payment
execution.
- The MCP handler reuses existing durable intent and settlement paths.
- Aggregate exposure needs a rolling cap or one-intent demo quota; a
per-payment cap alone is insufficient.

## Files/components touched

- `docs/PERSONAL_MCP_PRIVY_AGENT_PAYMENTS_PLAN.md`
- `.agent/context/20260913T021012Z-clarify-personal-mcp-plan.md`

## Commands/checks

- `markdownlint-cli2` for the plan and context: PASS, 0 errors.
- `git diff --check`: PASS.
- Prettier check for the plan and context: PASS.

## External-doc findings

- Privy supports additional signers with per-signer override policies; this is
retained for milestone 2 rather than milestone 1.
- Privy policy evaluation defaults to deny when no rule matches.

## Unresolved questions

- Choose a Privy rolling cap or a one-intent quota before the MCP endpoint is
exposed beyond a controlled demo.

## Git and PR state

- Branch: `plan/personal-mcp-privy-agent-payments`
- Base: current `origin/develop` merged before editing.
- PR: #112, draft.

## Gate A/B state

- Gate A: pending for the rewritten candidate tree.
- Gate B: pending after push and CI.

## Handoff/next steps

- Complete Gate A, commit, push, CI, and Gate B.
246 changes: 246 additions & 0 deletions docs/PERSONAL_MCP_PRIVY_AGENT_PAYMENTS_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
# OneShot MCP `arc_payment` implementation plan

## Decision

Ship one remote MCP tool first:

```text
arc_payment({
request_key: string,
recipient: 0x-address,
amount_usdc: decimal-string,
purpose: string
})
```

The first release uses the existing policy-bound Privy server execution wallet
and the existing Arc settlement worker. Privy login authenticates the browser;
it does not select the payer. The MCP tool does not use MetaMask, an embedded
browser wallet, or a per-payment wallet popup.

Personal Privy wallets and delegated agent signers remain a separate milestone.
They must not block the first working `arc_payment` demo.

## Non-negotiable behavior

- Arc Testnet (`eip155:5042002`) and Arc USDC are the only network and asset.
- One stable `request_key` maps to one stable Business Intent.
- Repeating the same request returns the existing intent and settlement.
- Reusing the key with different immutable fields returns a conflict.
- `SUBMITTING` and `UNKNOWN` never create a replacement payment.
- Privy policy and OneShot both validate the amount and transaction scope.
- The tool returns authoritative OneShot state, not an inferred success.
- A real demo payment is complete only after a verified Arc receipt is durable.

## Delivery order

| Order | Task | Depends on | Exit condition |
| ----- | --------------------------------------- | ---------- | ----------------------------------------------- |
| 1 | Freeze the tool contract | None | Input and output schemas are approved |
| 2 | Add MCP-only authentication | Task 1 | `/mcp` accepts only the dedicated credential |
| 3 | Mount Streamable HTTP MCP | Task 2 | `initialize` and `tools/list` expose one tool |
| 4 | Connect `arc_payment` to the ledger | Task 3 | Calls create or replay one durable intent |
| 5 | Enforce payment scope and spend bounds | Task 4 | Invalid or excessive requests broadcast nothing |
| 6 | Return status and proof | Task 4 | Replays report the same authoritative state |
| 7 | Verify failure and concurrency behavior | Tasks 4-6 | Required payment tests pass |
| 8 | Document and run one demo | Task 7 | One real Arc Testnet settlement is verified |

## Task 1: freeze the tool contract

### Work

- Accept only `request_key`, `recipient`, `amount_usdc`, and `purpose`.
- Parse `amount_usdc` as a six-decimal string into integer atomic units. Never
use JavaScript floating point.
- Reject malformed addresses, zero amounts, excess precision, empty purposes,
and oversized strings before creating an intent.
- Define a structured result with:
- request and Business Intent IDs;
- authoritative payment state;
- payer, recipient, decimal amount, and atomic amount;
- transaction hash and ArcScan link when known;
- one safe next action: `WAIT`, `CHECK_STATUS`, `VIEW_PROOF`, or
`FIX_REQUEST`.

### Done when

- The JSON schemas and examples cover accepted, rejected, pending, unknown,
and committed results.

## Task 2: add MCP-only authentication

### Work

- Add one dedicated high-entropy bearer credential in deployment secret
storage for the first release.
- Accept it only on `/mcp`; do not let it authorize `/v1/*` routes.
- Keep Privy JWT authentication for the browser and the existing internal
service credential for internal or legacy routes.
- Bind the MCP principal to one explicit demo workspace.
- Compare credentials in constant time and never log or return them.

### Done when

- Missing, invalid, and browser credentials fail on `/mcp`.
- The MCP credential succeeds on `/mcp` and fails on browser/API routes.

Self-service token generation, token tables, HMAC peppers, and per-user token
rotation are deferred until there is more than one MCP user.

## Task 3: mount the MCP transport

### Work

- Mount the official stateless Streamable HTTP MCP handler in the existing
Cloud Run API.
- Proxy `/mcp` through the existing Cloudflare Worker without creating another
deployment.
- Expose exactly one tool named `arc_payment`.
- Return protocol errors as MCP errors without leaking provider responses or
credentials.

### Done when

- `initialize` succeeds through `https://oneshot.kapustazh.dev/mcp`.
- `tools/list` returns only `arc_payment` with the frozen schema from Task 1.

## Task 4: reuse the durable payment path

### Work

- Derive the Business Intent identity from the workspace and normalized
`request_key` with one versioned deterministic function.
- Convert the tool input into the existing Arc USDC intent contract.
- Call the existing create-or-replay ledger path and existing worker. Do not
add a second settlement implementation inside the MCP handler.
- Preserve the existing submission lease, provider idempotency key, receipt
verification, `UNKNOWN` handling, and recovery behavior.
- Treat another call with the same key as create-or-read, never as a new
payment command.

### Done when

- One valid tool call reaches the existing `SERVER_PRIVY` payment mode.
- Identical replay returns the same Business Intent.
- Changed recipient, amount, asset, network, or purpose conflicts explicitly.

## Task 5: enforce payment scope and total exposure

### Work

- Keep Privy rules for Arc chain ID `5042002`, the Arc USDC contract,
zero native value, ERC-20 `transfer`, and the approved per-payment cap.
- Validate the recipient as an EVM address even when the current policy permits
any recipient.
- Keep the application settlement cap equal to or lower than the Privy cap.
- Add a cumulative control before allowing repeated unique requests. Choose
one:
- a Privy rolling USDC spending cap for normal use; or
- a one-intent quota for a literal single-payment demo.
- Fail readiness when the attached wallet, policy, digest, network, token, or
cap differs from the reviewed deployment baseline.

### Done when

- Above-cap and exhausted-quota requests create zero broadcasts and zero
settlements.
- Policy drift makes the tool unavailable before submission.

## Task 6: return authoritative status and proof

### Work

- Wait only for a short bounded interval after create-or-replay.
- Return the current durable state when settlement is still processing.
- Include an ArcScan link only for a stored transaction hash.
- Return the original result for a committed replay.
- For `UNKNOWN`, instruct the caller to repeat the same tool call or inspect the
proof. Never suggest a new `request_key`.

### Done when

- Every non-final response gives a safe next action.
- No response claims payment from queue acceptance alone.

## Task 7: test the payment boundary

### Required tests

- Valid and invalid MCP authentication; route isolation.
- `tools/list` exposes exactly one tool.
- Input validation and exact decimal-to-atomic conversion.
- One normal request produces exactly one committed settlement.
- Ten sequential identical calls produce one settlement.
- Ten parallel identical calls produce one settlement.
- Same key with changed immutable payload returns a conflict.
- Privy denial, amount above cap, and exhausted quota produce zero broadcasts.
- Crash or lost response after possible submission enters `UNKNOWN`, then
reconciliation finds the original payment without resubmission.
- Committed replay returns the original transaction and result.

### Validation commands

- Focused MCP, API, worker, Privy adapter, and storage tests.
- PostgreSQL integration tests.
- Full unit suite, typecheck, lint, formatting check, production build, and
browser tests.

## Task 8: document and demonstrate

### Work

- Add one short `/docs/mcp` page with generic Streamable HTTP configuration and
one copy-ready client example.
- Use an environment-variable placeholder for the bearer credential.
- Demonstrate one real `arc_payment` call, one identical replay, and one proof
view.
- Record the Business Intent ID, verified Arc transaction, policy identity,
and zero-duplicate result without recording secrets.

### Done when

- A fresh client can connect using the page and list `arc_payment`.
- The demo shows one real Arc Testnet USDC settlement and no replacement
settlement on replay.

Client-specific setup pages and an installable `oneshot-arc-payment` skill are
deferred until the tool contract is stable.

## Milestone 2: personal Privy wallets

Start this milestone only after the server-wallet MCP path is working.

1. Replace the fixed MCP principal with a principal containing credential kind,
Privy subject, opaque workspace ID, and MCP token ID.
2. Create one isolated workspace and one revocable MCP token per Privy user.
3. Store only a SHA-256 digest of each random 32-byte token and enforce
one-active-token generation atomically.
4. Bind jobs, intents, recovery, activity, results, and proofs to the workspace;
return `404` for cross-workspace identifiers.
5. Discover the user's embedded Ethereum wallet and show funding/readiness.
6. Create a user-owned Privy override policy and add the OneShot P-256 key
quorum as an additional signer after one explicit user authorization.
7. Resolve wallet and signer policy per workspace in the worker while keeping
the existing global execution wallet only for legacy service requests.
8. Add the **Agents** tab for wallet status, signer enable/disable, policy
controls, and MCP token lifecycle.
9. Replace the current request list with a workspace-bound unified feed.
10. Add multi-user isolation, concurrent token generation, signer attachment,
policy update, and browser accessibility tests.

## Explicitly excluded from the first release

- MetaMask or browser-wallet payment execution.
- Personal embedded-wallet settlement.
- Arbitrary policy editor.
- MCP OAuth.
- Multiple active MCP credentials.
- OneShot team or Privy teammate administration.
- Multiple MCP tools, assets, or networks.

## References

- [Arc MCP setup](https://docs.arc.io/ai/mcp)
- [MCP Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)
- [Privy server signers](https://docs.privy.io/recipes/wallets/user-and-server-signers)
- [Privy policy capabilities](https://docs.privy.io/controls/policies/overview)
Loading