# MCP server

Endpoint: `https://papertrade-yield.pages.dev/mcp`

It is an MCP **Streamable HTTP** server, stateless and read-only. There are no sessions, no cookies and no auth. Every tool reads public Papertrade and HyperEVM data. No tool signs or sends anything.

## Transport behavior

| Request | Response |
| --- | --- |
| `POST /mcp` with JSON-RPC 2.0 (single or batch of up to 20) | `application/json`. If `Accept` allows only `text/event-stream`, a single SSE `message` event. |
| `notifications/*` and client responses | `202 Accepted`, empty body. |
| `GET /mcp` with `Accept: text/event-stream` | `405`, `Allow: POST, OPTIONS`. |
| `GET /mcp` otherwise | A JSON description of the server with links to docs. |
| `DELETE /mcp` | `405`. |
| `OPTIONS /mcp` | `204` with CORS (`*`), allowing `Mcp-Session-Id`, `Mcp-Protocol-Version` and `Authorization`. |

Protocol versions `2025-06-18`, `2025-03-26` and `2024-11-05` are supported. `initialize` echoes the client's version when supported, otherwise answers `2025-06-18`. Capabilities are `{ "tools": { "listChanged": false } }`. `resources/list`, `resources/templates/list` and `prompts/list` return empty lists.

Errors follow JSON-RPC: `-32700` parse error (HTTP 400), `-32600` invalid request, `-32601` unknown method, `-32602` unknown tool or bad params. Invalid tool **arguments** are reported as a normal result with `isError: true` so the model can correct itself. Requests over 64 KB return `413`. Calls to `tools/call` are limited to 60 per minute per client IP and return `429` with `Retry-After` and JSON-RPC code `-32000` beyond that.

## Tools

All tools carry `readOnlyHint: true`, `destructiveHint: false`, `idempotentHint: true` and `openWorldHint: true`. Results contain a text summary in `content` and the same data in `structuredContent`.

| Tool | What it does |
| --- | --- |
| [`get_staking_stats`](#get_staking_stats) | PAPER staking stats |
| [`get_apr`](#get_apr) | Realized yield and implied APR |
| [`get_yield_history`](#get_yield_history) | Hourly staking reward history |
| [`get_claims_activity`](#get_claims_activity) | On-chain claims activity |
| [`get_wallet_staking`](#get_wallet_staking) | Wallet stake and rewards |
| [`project_rewards`](#project_rewards) | Projected staking rewards calculator |
| [`plan_staking_action`](#plan_staking_action) | Plan a stake, unstake or claim (unsigned) |

### get_staking_stats

**PAPER staking stats.** Current PAPER staking state on Papertrade: total supply, staked amount and share, undistributed staker rewards, minimum stake, the current mint rate (PAPER minted per $1 of loss), liquidity context, and the realized USDC yield per 1M PAPER per day over 24h, 7d and 30d. PAPER has no market price, so yield is quoted per 1M PAPER, not as a percentage. Read only.

This tool takes no arguments.

Structured output fields: `generatedAt`, `paperSupply`, `paperStaked`, `stakedShare`, `pendingRewardsUsd`, `minimumStakePaper`, `accRewardPerShareUsdPerPaper`, `mintRate`, `onFlatBranch`, `trackedLpUsd`, `cumulativeRewardsUsd`, `yieldWindows`, `method`, `pricing`, `contracts`.

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

### get_apr

**Realized yield and implied APR.** Realized staking yield for PAPER over 24h, 7d and 30d as USDC per 1M PAPER per day. PAPER has no market price, so a percentage APR exists only for a price you assume: pass assumedPaperPriceUsd to get a simple (non-compounding) implied APR for that price. Without a price, impliedAprPercent is null. Read only.

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `assumedPaperPriceUsd` | number (> 0) | no | A PAPER price in USD to test. It is an assumption, not a market quote. |
| `window` | `"24h"` or `"7d"` or `"30d"` | no | Trailing window of realized yield to use. |

Structured output fields: `assumedPaperPriceUsd`, `windows`, `method`, `pricing`.

```json
{
  "type": "object",
  "properties": {
    "assumedPaperPriceUsd": {
      "type": "number",
      "exclusiveMinimum": 0,
      "description": "A PAPER price in USD to test. It is an assumption, not a market quote."
    },
    "window": {
      "type": "string",
      "enum": [
        "24h",
        "7d",
        "30d"
      ],
      "description": "Trailing window of realized yield to use."
    }
  },
  "additionalProperties": false
}
```

### get_yield_history

**Hourly staking reward history.** Hourly history of USDC distributed to PAPER stakers, newest last: reward in USD, mean staked PAPER, USDC per PAPER and the pace as USDC per 1M PAPER per day, for the trailing number of hours. Buckets before staking rewards began are omitted. Read only.

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `hours` | integer (min 1, max 720, default 48) | no | How many trailing hourly buckets to return. |

Structured output fields: `hours`, `points`, `totalRewardsUsd`, `cumulativeRewardsUsd`.

```json
{
  "type": "object",
  "properties": {
    "hours": {
      "type": "integer",
      "minimum": 1,
      "maximum": 720,
      "default": 48,
      "description": "How many trailing hourly buckets to return."
    }
  },
  "additionalProperties": false
}
```

### get_claims_activity

**On-chain claims activity.** USDC that stakers claimed on chain (PaperStaking Claimed events on HyperEVM) over the trailing 1 to 72 hours, bucketed per hour, with unique claimers and the largest claim. Claiming is a choice, so this differs from USDC distributed. Read only.

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `hours` | integer (min 1, max 72, default 24) | no |  |

Structured output fields: `hours`, `totalClaimedUsd`, `claims`, `uniqueClaimers`, `largestClaimUsd`, `buckets`.

```json
{
  "type": "object",
  "properties": {
    "hours": {
      "type": "integer",
      "minimum": 1,
      "maximum": 72,
      "default": 24
    }
  },
  "additionalProperties": false
}
```

### get_wallet_staking

**Wallet stake and rewards.** Staking position of one wallet: staked PAPER (net of the wallet's Staked and Unstaked history), unstaked PAPER balance and pending USDC reward (both read on chain now), lifetime USDC claimed, and the ten most recent staking events. Amounts are in PAPER and USDC; raw 18-decimal values are included. Read only: it never needs or accepts a key.

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `address` | string (pattern `^0x[0-9a-fA-F]{40}$`) | yes | A 0x-prefixed 20-byte HyperEVM wallet address. Read only: this is a lookup, nothing is signed. |

Structured output fields: `address`, `paperStaked`, `paperUnstakedBalance`, `pendingRewardUsd`, `lifetimeClaimedUsd`, `raw`, `historyComplete`, `recentEvents`, `readAt`.

```json
{
  "type": "object",
  "properties": {
    "address": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$",
      "description": "A 0x-prefixed 20-byte HyperEVM wallet address. Read only: this is a lookup, nothing is signed."
    }
  },
  "required": [
    "address"
  ],
  "additionalProperties": false
}
```

### project_rewards

**Projected staking rewards calculator.** Calculator: what a PAPER position would earn if the realized pace of a trailing window simply continued. Give either paperAmount (PAPER you hold or would stake) or lossUsd (a hypothetical trading loss, converted to PAPER at today's mint rate; liquidations mint on the full loss, normal closes on 98%). The result is an extrapolation of history, not a forecast: the pace and the mint rate both move. Add assumedPaperPriceUsd for an implied APR. Read only.

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `paperAmount` | number (> 0) | no | PAPER staked. Give this or lossUsd. |
| `lossUsd` | number (> 0) | no | Hypothetical loss in USD. Give this or paperAmount. |
| `liquidated` | boolean (default false) | no | With lossUsd: the position was liquidated rather than closed at a loss. |
| `days` | integer (min 1, max 365, default 30) | no |  |
| `window` | `"24h"` or `"7d"` or `"30d"` | no | Trailing window of realized yield to use. |
| `assumedPaperPriceUsd` | number (> 0) | no |  |

Structured output fields: `paperAmount`, `lossUsd`, `mintRate`, `window`, `windowComplete`, `usdPer1MPaperPerDay`, `days`, `projectedUsdPerDay`, `projectedUsdOverDays`, `projectedUsdPerYear`, `breakEvenDays`, `impliedAprPercent`, `caveat`.

```json
{
  "type": "object",
  "properties": {
    "paperAmount": {
      "type": "number",
      "exclusiveMinimum": 0,
      "description": "PAPER staked. Give this or lossUsd."
    },
    "lossUsd": {
      "type": "number",
      "exclusiveMinimum": 0,
      "description": "Hypothetical loss in USD. Give this or paperAmount."
    },
    "liquidated": {
      "type": "boolean",
      "default": false,
      "description": "With lossUsd: the position was liquidated rather than closed at a loss."
    },
    "days": {
      "type": "integer",
      "minimum": 1,
      "maximum": 365,
      "default": 30
    },
    "window": {
      "type": "string",
      "enum": [
        "24h",
        "7d",
        "30d"
      ],
      "description": "Trailing window of realized yield to use."
    },
    "assumedPaperPriceUsd": {
      "type": "number",
      "exclusiveMinimum": 0
    }
  },
  "additionalProperties": false
}
```

### plan_staking_action

**Plan a stake, unstake or claim (unsigned).** Builds an UNSIGNED plan for staking, unstaking or claiming PAPER staking rewards: preflight checks against the wallet's real balance, staked amount and pending reward, the EIP-712 type and domain the wallet would be asked to sign, and a link to the app where the owner signs in their own wallet. This tool never signs, never submits and never moves funds; the nonce and deadline are only allocated when the owner signs. Read only.

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `action` | `"stake"` or `"unstake"` or `"claim"` | yes |  |
| `address` | string (pattern `^0x[0-9a-fA-F]{40}$`) | yes | A 0x-prefixed 20-byte HyperEVM wallet address. Read only: this is a lookup, nothing is signed. |
| `amountPaper` | string (pattern `^[0-9]+(\.[0-9]{1,18})?$`) | no | PAPER amount as a decimal string. Required for stake and unstake, ignored for claim. |

Structured output fields: `unsigned`, `signed`, `submitted`, `action`, `address`, `amountPaper`, `amountRaw`, `preflight`, `typedData`, `howToSign`, `signUrl`, `note`.

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "stake",
        "unstake",
        "claim"
      ]
    },
    "address": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$",
      "description": "A 0x-prefixed 20-byte HyperEVM wallet address. Read only: this is a lookup, nothing is signed."
    },
    "amountPaper": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]{1,18})?$",
      "description": "PAPER amount as a decimal string. Required for stake and unstake, ignored for claim."
    }
  },
  "required": [
    "action",
    "address"
  ],
  "additionalProperties": false
}
```

## Examples

Realized yield with an assumed price:

```bash
curl -s https://papertrade-yield.pages.dev/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_apr","arguments":{"assumedPaperPriceUsd":0.01,"window":"7d"}}}'
```

Project rewards for a loss, assuming it was liquidated:

```bash
curl -s https://papertrade-yield.pages.dev/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"project_rewards","arguments":{"lossUsd":500,"liquidated":true,"days":30}}}'
```

Plan a claim without signing it:

```bash
curl -s https://papertrade-yield.pages.dev/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"plan_staking_action","arguments":{"action":"claim","address":"0x0000000000000000000000000000000000000001"}}}'
```

The plan contains `unsigned: true`, `signed: false`, `submitted: false`, preflight checks, the EIP-712 typed data to be signed, and a link to the web app where the wallet owner signs.

## Inspect it

```bash
npx @modelcontextprotocol/inspector --cli https://papertrade-yield.pages.dev/mcp --transport http --method tools/list
```
