# MythosForge Proof / Anchor — Base Custom Plugin

> [!IMPORTANT]
> ## STOP — COMPLETE BASE MCP ONBOARDING FIRST
>
> Before using any action in this plugin you MUST complete Base MCP onboarding:
>
> 1. Call **`get_wallets`** (Detection). The user's wallet address is only
>    confirmed during Detection — do not assume or hard-code it.
> 2. Present the wallet status and the standard Base MCP **disclaimer** to the
>    user, and get explicit confirmation before preparing or sending any
>    transaction.
> 3. Only after the wallet is confirmed and the disclaimer is acknowledged may
>    you call the **Prepare** action and then **`send_calls`**.
>
> This plugin NEVER asks for or handles a private key. All signing happens in
> the user's own wallet via Base MCP `send_calls`. The plugin server only reads
> public state and returns *unsigned* calldata.

MythosForge is a daily, fully-on-chain AI image contest on **Base mainnet**
(`chainId 8453`). This plugin lets an assistant (a) read public proof /
provenance state and (b) prepare an on-chain **Proof-of-Creation anchor** for a
submission, then execute it through the user's wallet.

Base URL: `https://www.mythosforge.xyz`

What an anchor is: a call to the permissionless, emit-only `MythosForgeAnchor`
contract that records "manifest `H` for agent `X` (declared wallet `W`)" as a
canonical on-chain event. It moves no funds and writes no storage.

> **Scope note.** This is an assistant/plugin path *around* the same MythosForge
> Base contract — it does **not** replace the website's wallet flow or the
> platform's server-side anchor worker. Third-party/assistant anchors are valid
> on-chain events but do **not** earn the platform-attested badge (that badge is
> decided by caller address). Minting (`MythosForgeSubmissions`) is **not** in
> this plugin: a mint requires a pre-built ≤24 KB on-chain `tokenURI` blob that
> only the platform's `v1-mint` worker can construct, so mint stays read-only
> here.

---

## Read endpoints (GET — no auth, no wallet needed)

Use these to inspect public state before preparing anything. All are read-only
and return JSON.

| Purpose | Endpoint |
| --- | --- |
| Proof-integrity report (latest provenance cron output: `pass`, counts, broken/unverified, on-chain tx hashes) | `GET /api/v1/proof-report` |
| Round status / recent rounds | `GET /api/v1/rounds` |
| Public contest stats | `GET /api/v1/stats` |
| On-chain token image for a minted submission | `GET /api/v1/image/{id}` |

Typical read flow: hit `/api/v1/proof-report` to confirm the platform's
provenance state, and `/api/v1/rounds` to find the round/submission you care
about, before deciding whether to anchor.

---

## Prepare endpoint (returns UNSIGNED calldata — never broadcasts)

```
GET /api/v1/prepare-anchor?manifestHash={hex64}&agentId={uuid}&agentWallet={address-or-empty}
```

- `manifestHash` (**required**) — 64 hex chars (sha256 of the canonical PoC
  manifest). `0x` prefix optional.
- `agentId` (**required**) — the submission's `agent_id`.
- `agentWallet` (optional) — the agent's declared wallet; may be empty.

The server computes `agentIdHash = keccak256(utf8Bytes(agentId))` and ABI-encodes
`MythosForgeAnchor.anchor(...)`. It returns the unsigned call:

```json
{
  "ok": true,
  "to": "0x936cc31Ce3D0e0abcD76ED29851Ab8bC5f8bEFf9",
  "value": "0x0",
  "data": "0x...",
  "chainId": 8453,
  "chain": "base",
  "calls": [
    { "to": "0x936cc31Ce3D0e0abcD76ED29851Ab8bC5f8bEFf9", "value": "0x0", "data": "0x..." }
  ],
  "summary": "MythosForgeAnchor.anchor() — emit-only PoC anchor, permissionless, 0 ETH. ..."
}
```

Field mapping: `to` ← contract address, `value` ← `0x0` (anchor is never
payable), `data` ← encoded `anchor()` calldata, `chain`/`chainId` ← Base
mainnet. On bad input the endpoint returns `{ "ok": false, "error": ... }` with
HTTP 400 — surface the error to the user and do not call `send_calls`.

---

## send_calls mapping

The prepare response is already shaped for Base MCP. Pass the returned `calls`
array straight through with the returned `chain`:

```json
{
  "chain": "base",
  "calls": [
    { "to": "0x936cc31Ce3D0e0abcD76ED29851Ab8bC5f8bEFf9", "value": "0x0", "data": "0x..." }
  ]
}
```

`value` defaults to `0x0` if omitted. Do not invent additional calls — anchor is
a single transaction.

---

## Orchestration pattern

1. **Onboard.** `get_wallets` → show wallet + disclaimer → get user confirmation.
   (Stop here if no wallet / no confirmation.)
2. **Read.** Optionally `GET /api/v1/proof-report` and `/api/v1/rounds` to
   identify the `manifestHash` / `agentId` to anchor and confirm it isn't
   already covered.
3. **Prepare.** `GET /api/v1/prepare-anchor?manifestHash=…&agentId=…&agentWallet=…`.
   If `ok:false`, stop and report the error.
4. **Confirm.** Show the user the `summary` plus `to` / `chain` and that it
   costs only gas (0 ETH value). Get explicit go-ahead.
5. **Execute.** `send_calls({ chain, calls })` using the prepare response.
6. **Report.** Return the resulting tx hash and note it is a *permissionless*
   anchor (valid on-chain event; no platform-attested badge for third-party
   callers).

### Hard rules

- Never request, store, or transmit a private key. Signing is the user's wallet
  via `send_calls` only.
- Never broadcast from the plugin server. `prepare-anchor` only encodes calldata.
- Never claim platform attestation for an assistant-initiated anchor.
- Treat all read endpoints as public/sanitized; do not send secrets in queries.
