MCP Servers

A collection of Model Context Protocol servers, templates, tools and more.

MCP server for AI-mintable tokens via EIP-7702. OAuth 2.1 + SIWE wallet auth. Powers $SHIT on Ethereum mainnet.

Created 5/9/2026
Updated about 3 hours ago
Repository documentation and setup instructions

Dogeshit MCP

CI License: MIT

Reference implementation: EIP-7702 + Model Context Protocol (MCP) + on-chain mint. Powers $SHIT — the first production meme coin minted via claude.ai (Ethereum mainnet, 2026-05-07).

This is the backend that lets an AI agent (claude.ai) trigger on-chain mint() calls on a user's behalf, where the user retains full custody and the contract enforces "tokens always go to the caller's own wallet".

user → "mint me some shit" → claude.ai → MCP /mcp endpoint → relayer → mint()
                                           ↑                    ↑
                                    JWT.sub = wallet      tx.origin == relayer (gate)
                                    (issued via SIWE)     recipient = msg.sender (hardcoded)

The user never sees a private key for the relayer. The relayer never sees a way to direct tokens elsewhere. Everything is verifiable on-chain.


What's in the box

| Component | LoC | What it does | |---|---|---| | MCP server (src/mcp/) | ~1010 | MCP 2025-11-25 dispatcher + 5 tools (mint / balance / info / quota / auth status) + idempotency cache for client retries | | OAuth AS (src/oauth/) | ~970 | SIWE-based wallet auth, ES256 JWT issue + verify, dynamic client registration, sqlite store | | Relayer (src/relayer.ts) | ~560 | Pipelined nonce queue, per-caller in-flight cap (1/wallet), single-shot send + wait (no replacement — anti-rat-race), gas cap | | Rate limit + CIDR (src/rate-limit.ts, src/cidr.ts) | ~230 | Token bucket, peer-aware IP derivation, IPv4 CIDR matching | | Server entry (src/server.ts) | ~395 | Hono app, CORS, IP allowlist gate, /health (with queue metrics), /api/config, /api/revoke, graceful shutdown drain |

Total ≈ 3,500 lines TypeScript. 56 unit tests + 5 integration suites. CI runs audit + type check + tests + English-only guard on every push and PR.


Why this exists

EIP-7702 (live since Pectra) lets an EOA temporarily run smart contract code. Combined with MCP (Anthropic's protocol for AI tool use), an AI agent can call dApps as the user's authorized agent.

This codebase is the missing reference: how do you actually wire it up in production?

  • Token contract enforces tx.origin == relayer && recipient == msg.sender → AI cannot redirect tokens
  • 7702 authorization is signed and broadcast by the user's own wallet (not the backend) → backend never holds user's auth
  • MCP tools accept count parameter; relayer batches via batchMint(count) for gas savings
  • Pipelined nonce queue lets concurrent users mint without serial bottleneck

The architecture highlights below cover the key design decisions.


Quick start

# Prereqs:
#   1. bun        — https://bun.sh
#   2. An RPC URL — anvil for local, sepolia/mainnet for testnet/prod
#   3. Token + MintDelegate already deployed at known addresses
#      (see contracts repo: deploys via `forge script`)
#
# The backend reads contract immutables at startup (prewarm) and refuses
# to listen if the contracts aren't reachable — this is intentional
# fail-fast behavior, not a bug.

# 1. Install deps
bun install

# 2. Configure
cp .env.example .env
# Edit .env (required, process won't boot without these):
#   CHAIN, RPC_URL                        — your chain + RPC endpoint(s)
#   TOKEN_ADDRESS, MINT_DELEGATE_ADDRESS  — deployed contract addresses
#   RELAYER_PRIVATE_KEY                   — pre-funded wallet that signs mint txs
#   RELAYER_MAX_FEE_GWEI                  — gas cap (e.g. 4.2069 mainnet, 0.42069 L2)
# Strongly recommended for production:
#   MCP_ALLOWED_IP_CIDRS=160.79.104.0/21  — gate /mcp to claude.ai egress
#   RELAYER_MIN_BALANCE_ETH=0.05          — alert before relayer runs dry

# 3. Run
bun --hot --env-file=.env run src/server.ts        # backend on :42069

# 4. Verify
curl http://127.0.0.1:42069/health                 # liveness: rpc + sqlite + relayer ETH
curl http://127.0.0.1:42069/api/config             # chain + contract config
curl http://127.0.0.1:42069/jwks.json              # OAuth public keys
curl http://127.0.0.1:42069/.well-known/oauth-authorization-server

For zero-config local development, the contracts repo provides a one-shot script that runs anvil, deploys $SHIT + MintDelegate, and prints the addresses to paste into .env.


Tests

# Unit tests — auto-discovered by bun test (~5 sec, no external deps)
bun test

# Integration tests — manual, require anvil/sepolia running + contracts deployed
bun run tests/integration/full.ts            # full flow: OAuth → SIWE → 7702 self-tx → MCP mint
bun run tests/integration/oauth.ts            # OAuth AS standalone
bun run tests/integration/mcp.ts              # MCP protocol layer
MCP_DEV_AUTH=1 bun run tests/integration/mcp-edges.ts  # MCP/OAuth edge cases
bun run tests/integration/security.ts         # CORS / state / error leakage

Layout:

  • tests/*.test.ts — unit tests, auto-picked by bun test
  • tests/integration/*.ts — integration tests, run manually (each is an IIFE; needs running RPC + deployed contracts to assert against)
  • tests/_setup/test-env.ts — preload (see bunfig.toml); sanitizes MCP_LOG_PATH to /tmp before unit tests touch the logger

Coverage strategy: Unit tests cover protocol-layer logic (JWT verification, SIWE message parsing, OAuth store CRUD, rate-limit token bucket, log routing, MCP audit fields, mint idempotency cache). Relayer + MCP routing have low unit-coverage by design — they require real chain state, so they're covered by integration/full.ts (end-to-end mint flow) and integration/mcp.ts (MCP protocol layer).


Deploy

The mainnet deployment of $SHIT uses this codebase as-is. See DEPLOY.md for a full Cloudflare Tunnel walkthrough, hardening checklist, nginx alternative, and health-monitoring wiring.

Short version:

  1. Deploy Token.sol + MintDelegate.sol (see contracts repo)
  2. Fill TOKEN_ADDRESS, MINT_DELEGATE_ADDRESS, RELAYER_PRIVATE_KEY in .env
  3. Pre-fund the relayer wallet with ETH (~50k + N × 30k gas per batchMint(N))
  4. Set MCP_ALLOWED_IP_CIDRS=160.79.104.0/21 to gate /mcp to claude.ai egress
  5. Set RPC_URL to a comma-separated fallback list so a flaky primary doesn't tank the backend
  6. Set RELAYER_MIN_BALANCE_ETH so /health returns 503 before mints start failing
  7. Run behind a same-host reverse proxy (Cloudflare Tunnel recommended). The default bind is 127.0.0.1; the proxy connects locally
  8. Add the public /mcp URL to claude.ai → Settings → Connectors

Architecture highlights

Self-submit 7702 (no auth caching)

The user signs the EIP-7702 authorization and broadcasts the type-4 tx from their own wallet. The backend never sees the auth, never broadcasts the activation tx, never pays activation gas. This is a deliberate trust-minimization: the only key the backend holds is the relayer key, and the relayer can only sign type-2 mint txs (not type-4 delegations).

Token contract gates

function mint() external {
    if (tx.origin != RELAYER) revert NotViaRelayer();
    if (!_isDelegatedToMintDelegate(msg.sender)) revert NotDelegated();
    // ...
    _mint(msg.sender, MINT_AMOUNT);  // recipient HARDCODED to msg.sender
}

tx.origin gate ensures only the project relayer can trigger mint. msg.sender (= user's 7702-delegated EOA) is the hardcoded recipient — the relayer cannot direct tokens to any other address. Worst-case "forced exposure" for a user is MAX_PER_WALLET (10) × FEE_WEI (0.00111 ETH) ≈ $30-50 in exchange for 100M $SHIT.

Pipelined relayer

Concurrent MCP calls don't bottleneck on serial nonce reservation. The relayer queue locks only the getNonce → submit critical section, then releases. Subsequent senders proceed while previous ones wait for confirmation.

There is intentionally NO stuck-tx replacement: modern Ethereum nodes (geth/erigon/reth/nethermind) retain pending txs for hours, so a tx priced below current basefee waits in mempool and lands once basefee normalizes. If the wait times out we surface receipt_timeout; the MCP idempotency cache (60s) keeps a transport-retry from accidentally triggering a second mint at a different nonce.

Per-caller fairness cap: a single wallet may hold at most one in-flight tx (RELAYER_QUEUE_PER_KEY_MAX=1 default). Real users wait for confirmation before re-asking; transport-timeout retries are absorbed by the idempotency cache. With this cap, the global pool of RELAYER_QUEUE_MAX=50 slots serves up to 50 distinct callers simultaneously — no single wallet can head-of-line block the queue.

Graceful shutdown: on SIGINT/SIGTERM the server flips a flag that returns 503 from every new request (the load balancer takes the instance out of rotation), then polls the queue until empty or SHUTDOWN_TIMEOUT_MS (default 25s) elapses, letting in-flight receipt waits settle naturally before sqlite close + process.exit. Pairs with k8s default terminationGracePeriodSeconds=30.

Rate limiting

Three layers, ordered:

  1. IP bucket (/register 5/min, /mcp 120/min/IP) — skipped for MCP_ALLOWED_IP_CIDRS because trusted infra (claude.ai's outbound /21) multiplexes thousands of users; without the skip a popular launch self-DoSes on a single shared IP bucket.
  2. JWT-sub bucket (/mcp 60/min/wallet) — always applies, including from trusted IPs. Stops a single user (or stolen JWT) from spamming mint attempts and burning relayer gas on revert-by-cap txs, or flooding read-only tools to drain RPC quota.
  3. Contract cap (MAX_PER_WALLET = 10) — hard, immutable, last line of defense. Even with all rate limits bypassed, a wallet cannot mint more than 10 lifetime slots.

Source-IP derivation (clientIp()) is peer-aware: the backend trusts cf-connecting-ip / x-forwarded-for only when the connecting socket peer is loopback (i.e. behind a same-host reverse proxy). Public-facing peers cannot spoof their way past the IP allowlist.

Mint idempotency

token_mint is wrapped in a 60-second in-memory dedup cache keyed on (wallet, count). When the MCP client retries on transport timeout, the retry receives the same Promise as the original call — both successes and failures are cached, because a receipt_timeout doesn't tell us whether the tx eventually landed and re-submitting blindly risks a double mint. Validation (delegation-status checks, count range) re-runs every call, so a user activating mid-window doesn't see a stale "not_delegated" error.

Security audits

6+ rounds of full-stack audit (multi-agent, "find real bugs, no over-engineering"). Notable findings already addressed: X-Forwarded-For spoofing on the IP allowlist, missing address validation in token_balance, trusted-IP bypass of the per-wallet rate limit (now always enforced), mint duplication on MCP-client transport retry. Cumulative: 22+ real bugs fixed, 12+ real optimizations, 56 unit tests, 52+ over-defensive proposals rejected.


Configuration

See .env.example for every supported env var with inline notes.

Required (process refuses to boot without these):

  • CHAINanvil | sepolia | mainnet
  • RPC_URL — single URL or comma-separated list for fallback (viem's fallback transport tries them in order)
  • TOKEN_ADDRESS / MINT_DELEGATE_ADDRESS — deployed contract addrs
  • RELAYER_PRIVATE_KEY — relayer signs all mint txs with this
  • RELAYER_MAX_FEE_GWEI — gas-price cap; no default (forces operator to choose per chain)

Strongly recommended in production (silent default but logs a startup warning):

  • MCP_ALLOWED_IP_CIDRS=160.79.104.0/21 — IP allowlist for /mcp, /register, /token, /jwks.json, /.well-known/* (anthropic outbound; skips per-IP rate limit, per-wallet limit still applies)
  • RELAYER_MIN_BALANCE_ETH=0.05/health returns 503 below this ETH balance, paging on-call before mints fail

Optional:

  • HOST — bind address (default 127.0.0.1; set 0.0.0.0 to expose directly without a reverse proxy)
  • RELAYER_QUEUE_MAX (default 50) / RELAYER_QUEUE_PER_KEY_MAX (default 1) — global / per-caller in-flight pool sizing
  • SHUTDOWN_TIMEOUT_MS (default 25000) — graceful drain budget on SIGTERM
  • RELAYER_TASK_TIMEOUT_MS (default 120000) — receipt-wait budget per request

Forking for your own AI-mintable token

This codebase is MIT. To repurpose:

  1. Fork
  2. Deploy your own Token + MintDelegate (see contracts repo for templates)
  3. Update .env with your token address, relayer key, etc.
  4. Customize MCP tools in src/mcp/tools.ts (rename token_mint, etc., change descriptions)
  5. Register your /mcp URL with claude.ai

We deliberately did not abstract the token name — fork and grep-replace.


Contributing

This is a reference implementation, not a managed product. We welcome:

  • Bug reports + reproductions — open an Issue
  • Security disclosures — use GitHub's private vulnerability reporting. Do NOT file exploitable vulns in public Issues.
  • Discussion of EIP-7702 / MCP integration patterns

PR expectations: run bun run verify (audit + tsc + tests) locally — CI runs the same on every push. The repo enforces English-only via a CI grep guard. Default to "no refactor PRs" — this codebase has been through 6+ rounds of audit; refactors land only when they eliminate a concrete bug, real dead code, or measurable performance issue.

We cannot guarantee timely PR reviews. The mainnet $SHIT contracts are immutable; this backend is the only mutable component, and changes are made cautiously.


Links


License

MIT — see LICENSE.

Quick Setup
Installation guide for this server

Install Package (if required)

npx @modelcontextprotocol/server-dogeshit-mcp

Cursor configuration (mcp.json)

{ "mcpServers": { "dogeshitmeme-dogeshit-mcp": { "command": "npx", "args": [ "dogeshitmeme-dogeshit-mcp" ] } } }