MCP Servers

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

MCP server that lets AI agents discover, pay for, and execute SKILL.md endpoints via the x402 protocol

Created 3/14/2026
Updated about 7 hours ago
Repository documentation and setup instructions

@402md/mcp

npm version License: MIT Node.js TypeScript MCP

MCP server that transforms SKILL.md files into executable tools for AI agents. Point to any skill — URL, local file, or marketplace name — and the server parses it, auto-pays via x402, and returns the result.

Table of Contents

Quick Start

npx @402md/mcp

Or install globally:

npm install -g @402md/mcp
402md-mcp

The server starts in read-only mode by default. You can browse and inspect skills immediately. To execute paid endpoints, configure a wallet (see Wallet Setup).

Networks

The server supports four networks across two blockchain ecosystems:

Stellar

| Network | ID | Use Case | USDC Contract | |---------|-----|----------|---------------| | Stellar Mainnet | stellar | Production payments with real USDC | Native Stellar USDC (Centre) | | Stellar Testnet | stellar-testnet | Development & testing with free testnet USDC | Testnet USDC |

Stellar is the default and preferred network. It offers sub-second finality, near-zero fees (~0.00001 XLM per tx), and native USDC support.

  • Testnet faucet: Use Stellar Laboratory to create and fund testnet accounts.
  • Mainnet: Fund your account via any Stellar DEX, exchange, or on-ramp that supports USDC on Stellar.

EVM (Base)

| Network | ID | Use Case | USDC Contract | |---------|-----|----------|---------------| | Base Mainnet | base | Production payments on Base L2 | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | | Base Sepolia | base-sepolia | Development & testing on Base testnet | Sepolia USDC |

Base is an Ethereum L2 with low gas fees and fast confirmations.

  • Sepolia faucet: Get testnet ETH from Alchemy Faucet or Coinbase Faucet (needed for gas). Then bridge or mint testnet USDC.
  • Mainnet: Bridge USDC to Base from Ethereum, or buy directly on Base via Coinbase or any supported on-ramp.

Choosing a Network

  • Just getting started? Use stellar-testnet — no real money, instant setup with create_wallet.
  • Testing EVM skills? Use base-sepolia — free testnet, good for EVM-specific endpoints.
  • Production? Use stellar (lower fees) or base depending on what the skill accepts.

The server automatically selects the best compatible network when calling a skill. If a skill supports multiple networks and your wallet has both keys configured, Stellar is preferred.

Wallet Setup

There are three ways to configure a wallet, listed by priority (highest first):

Option 1: Environment Variables (recommended for production)

# Stellar
export STELLAR_SECRET="SCZANGBA5YHTNYVVV3C7CAZMCLXPILHSE6PGYV2FHHUQ5DGQJWRZ4GXT"
export NETWORK="stellar-testnet"

# EVM (Base)
export EVM_PRIVATE_KEY="0x4c0883a69102937d6231471b5dbb6204fe512961708279f23efb3c0c90..."
export NETWORK="base-sepolia"

# Both (FULL mode)
export STELLAR_SECRET="S..."
export EVM_PRIVATE_KEY="0x..."
export NETWORK="stellar"  # default network when both are available

Option 2: create_wallet Tool (recommended for development)

If no wallet is configured, ask your AI agent to use the create_wallet tool:

"Create a new wallet on stellar-testnet"

This generates a keypair and saves it to ~/.402md/wallet.json. The server reloads automatically.

Important: create_wallet refuses to run if a wallet is already configured. To replace an existing wallet, delete ~/.402md/wallet.json manually first.

Option 3: Wallet File (manual)

Create ~/.402md/wallet.json manually:

{
  "stellarSecret": "SCZANGBA5YHTNYVVV3C7CAZMCLXPILHSE6PGYV2FHHUQ5DGQJWRZ4GXT",
  "evmPrivateKey": "0x4c0883a69102937d6231471b5dbb6204fe512961708279f23efb3c0c90...",
  "network": "stellar-testnet",
  "createdAt": "2026-01-15T10:30:00.000Z"
}

The file is created with 0o600 permissions (owner read/write only). The directory ~/.402md/ is created with 0o700.

Generating Keys Manually

Stellar:

# Using stellar-sdk in Node.js
node -e "const { Keypair } = require('@stellar/stellar-sdk'); const kp = Keypair.random(); console.log('Secret:', kp.secret()); console.log('Public:', kp.publicKey())"

EVM:

# Using viem in Node.js
node -e "const { generatePrivateKey, privateKeyToAccount } = require('viem/accounts'); const pk = generatePrivateKey(); const acc = privateKeyToAccount(pk); console.log('Private Key:', pk); console.log('Address:', acc.address)"

# Or using openssl
openssl rand -hex 32 | sed 's/^/0x/'

How Payments Work

The payment flow is handled automatically by the x402 protocol:

Agent calls use_skill("my-skill", "/api/generate")
  │
  ├─ 1. Resolve skill → parse SKILL.md manifest
  ├─ 2. Validate manifest (schema, required fields)
  ├─ 3. Check budget limits (per-call & daily)
  ├─ 4. Select compatible network (skill networks ∩ wallet networks)
  ├─ 5. Create PaymentClient for that network
  ├─ 6. client.fetch(url) → x402 auto-payment:
  │     a. First request returns 402 Payment Required
  │     b. Client signs USDC payment (on-chain)
  │     c. Retries request with payment proof header
  │     d. Server verifies payment, returns response
  ├─ 7. Record spending (amount, skill, endpoint, network)
  └─ 8. Return response to agent

The agent never sees the payment mechanics — it just calls use_skill and gets a result. All USDC amounts use 6 decimal places (e.g., "0.050000").

What Happens If the Endpoint Fails?

If payment succeeds but the endpoint returns an error (4xx/5xx), the spending is still recorded (the payment was already made on-chain) but the tool returns isError: true with a clear message:

Endpoint returned 500. Payment was sent but the request failed.

{"error": "Internal server error"}

This lets the agent (or user) know to contact the skill provider.

Claude Desktop Configuration

Add to your claude_desktop_config.json:

Stellar Testnet (getting started)

{
  "mcpServers": {
    "402md": {
      "command": "npx",
      "args": ["-y", "@402md/mcp"],
      "env": {
        "STELLAR_SECRET": "SCZANGBA5YHTNYVVV3C7CAZMCLXPILHSE6PGYV2FHHUQ5DGQJWRZ4GXT",
        "NETWORK": "stellar-testnet",
        "MAX_PER_CALL": "0.10",
        "MAX_PER_DAY": "5.00"
      }
    }
  }
}

Base Sepolia (EVM testing)

{
  "mcpServers": {
    "402md": {
      "command": "npx",
      "args": ["-y", "@402md/mcp"],
      "env": {
        "EVM_PRIVATE_KEY": "0x4c0883a69102937d623147...",
        "NETWORK": "base-sepolia",
        "MAX_PER_CALL": "0.10",
        "MAX_PER_DAY": "5.00"
      }
    }
  }
}

Production (both networks)

{
  "mcpServers": {
    "402md": {
      "command": "npx",
      "args": ["-y", "@402md/mcp"],
      "env": {
        "STELLAR_SECRET": "S...",
        "EVM_PRIVATE_KEY": "0x...",
        "NETWORK": "stellar",
        "MAX_PER_CALL": "1.00",
        "MAX_PER_DAY": "50.00"
      }
    }
  }
}

Read-only (no wallet)

{
  "mcpServers": {
    "402md": {
      "command": "npx",
      "args": ["-y", "@402md/mcp"]
    }
  }
}

Environment Variables

| Variable | Default | Description | |----------|---------|-------------| | STELLAR_SECRET | — | Stellar secret key (starts with S). Enables Stellar payments. | | EVM_PRIVATE_KEY | — | EVM private key (hex, starts with 0x). Enables Base payments. | | NETWORK | stellar | Default network: stellar, stellar-testnet, base, base-sepolia | | MAX_PER_CALL | 0.10 | Maximum USDC allowed per individual skill call | | MAX_PER_DAY | 20.00 | Maximum USDC allowed per calendar day | | REGISTRY_URL | https://api.402.md | 402.md marketplace API base URL |

Environment variables always take priority over the wallet file (~/.402md/wallet.json).

Wallet File

Located at ~/.402md/wallet.json. Created automatically by create_wallet or manually.

{
  "stellarSecret": "S...",
  "evmPrivateKey": "0x...",
  "network": "stellar-testnet",
  "createdAt": "2026-01-15T10:30:00.000Z"
}
  • Permissions: 0o600 (read/write owner only)
  • Directory: ~/.402md/ with 0o700
  • Merge behavior: saveWalletConfig merges new fields with existing data, so adding an EVM key won't erase an existing Stellar key
  • Priority: env vars > wallet file > defaults

Budget & Spending Limits

The server enforces two spending limits:

| Limit | Default | Env Variable | Description | |-------|---------|--------------|-------------| | Per-call | 0.10 USDC | MAX_PER_CALL | Maximum for a single skill invocation | | Per-day | 20.00 USDC | MAX_PER_DAY | Maximum total across all calls in a calendar day |

Budget is checked before each payment. If a call would exceed either limit, the request is rejected with an error (no payment is made).

Use spending_summary to check current spending and configured limits:

{
  "spentToday": "1.2500",
  "spentSession": "0.3000",
  "limits": {
    "maxPerCall": "1.00",
    "maxPerDay": "50.00"
  },
  "recentPayments": [
    {
      "skillName": "image-gen",
      "endpoint": "/api/generate",
      "amount": "0.15",
      "network": "stellar-testnet",
      "timestamp": "2026-01-15T14:30:00.000Z"
    }
  ]
}

Tools

use_skill

Execute a paid SKILL.md endpoint. Resolves the skill, validates the manifest, auto-pays via x402, and returns the result.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | skill | string | Yes | Skill source: URL, local file path, or marketplace name | | endpoint | string | No | Endpoint path (defaults to first endpoint in manifest) | | method | string | No | HTTP method: GET, POST, PUT, DELETE, PATCH (defaults to spec) | | body | string | No | Request body as JSON string | | headers | object | No | Additional request headers |

Requires: configured wallet (STELLAR_ONLY, EVM_ONLY, or FULL mode).

Validation: the manifest is validated before any payment is attempted. If the SKILL.md is invalid, the tool returns an error without spending.

Examples:

# By marketplace name
use_skill({ skill: "image-gen", body: '{"prompt": "a sunset"}' })

# By URL
use_skill({ skill: "https://example.com/SKILL.md", endpoint: "/api/v2/generate" })

# By local file
use_skill({ skill: "./skills/my-skill/SKILL.md", method: "POST", body: '{"input": "hello"}' })

# With custom headers
use_skill({ skill: "translate", headers: { "X-Target-Lang": "pt-BR" }, body: '{"text": "hello"}' })

read_skill

Read and parse a SKILL.md without executing it. Returns full manifest details, endpoints, pricing, and validation results. Works in any mode (including READ_ONLY).

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | skill | string | Yes | Skill source: URL, local file path, or marketplace name |

Returns: name, description, version, author, base URL, endpoints (with pricing and schemas), payment info (networks, asset, payTo), tags, category, validation result, and current wallet mode.

check_balance

Check the USDC balance and address for the configured wallet.

No parameters.

Returns:

{
  "address": "GCXZ...",
  "balance": "45.250000 USDC",
  "network": "stellar-testnet",
  "mode": "STELLAR_ONLY"
}

spending_summary

View spending summary: amounts spent today and in the current session, budget limits, and the last 10 payments.

No parameters.

Returns: see Budget & Spending Limits for output format.

create_wallet

Generate a new wallet keypair and save it to ~/.402md/wallet.json.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | network | string | Yes | stellar, stellar-testnet, base, or base-sepolia |

Network-to-wallet mapping:

  • stellar or stellar-testnet → generates a Stellar keypair (Keypair.random())
  • base or base-sepolia → generates an EVM keypair (generatePrivateKey())

Safety:

  • Refuses to run if a wallet is already configured (any mode except READ_ONLY).
  • To replace an existing wallet, delete ~/.402md/wallet.json first.
  • New keys are merged with existing config — adding an EVM wallet won't erase a Stellar key.

Returns:

{
  "type": "stellar",
  "publicKey": "GCXZ...",
  "network": "stellar-testnet",
  "savedTo": "/Users/you/.402md/wallet.json",
  "note": "Fund this address on the Stellar testnet faucet before use."
}

search_skills

Search the 402.md marketplace for available skills.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | query | string | Yes | Search keywords | | maxPrice | string | No | Max price per call in USDC (e.g., "0.50") | | category | string | No | Filter by category |

Returns: up to 20 results with name, description, min price, and supported networks.

Modes

The server operates in one of four modes based on which keys are available:

| Mode | Condition | Available Tools | |------|-----------|-----------------| | READ_ONLY | No keys configured | read_skill, search_skills, create_wallet | | STELLAR_ONLY | STELLAR_SECRET set | All tools — pays via Stellar | | EVM_ONLY | EVM_PRIVATE_KEY set | All tools — pays via Base | | FULL | Both keys set | All tools — pays via best available network |

In FULL mode, when a skill supports both Stellar and EVM networks, Stellar is preferred (lower fees and faster finality).

Skill Resolution

The skill parameter in use_skill and read_skill accepts three formats:

| Format | Example | How It Resolves | |--------|---------|-----------------| | URL | https://example.com/SKILL.md | Fetches directly via HTTP | | Local file | ./skills/my-skill/SKILL.md | Reads from filesystem | | Marketplace name | image-gen | Queries {REGISTRY_URL}/api/v1/discovery/skills/{name}/skill-md |

Local file detection: any source starting with /, ./, ../, or ending with .md is treated as a file path.

Architecture

┌──────────────────────────────────────────────────────┐
│  AI Agent (Claude, etc.)                             │
│  Calls MCP tools: use_skill, check_balance, etc.     │
└──────────────┬───────────────────────────────────────┘
               │ stdio (MCP protocol)
┌──────────────▼───────────────────────────────────────┐
│  @402md/mcp Server                                   │
│                                                      │
│  ┌─────────────┐  ┌────────────┐  ┌───────────────┐ │
│  │ Use Tools   │  │ Wallet     │  │ Registry      │ │
│  │ use_skill   │  │ check_bal  │  │ search_skills │ │
│  │ read_skill  │  │ spending   │  │               │ │
│  │             │  │ create_wal │  │               │ │
│  └──────┬──────┘  └─────┬──────┘  └───────┬───────┘ │
│         │               │                 │          │
│  ┌──────▼──────┐  ┌─────▼──────┐  ┌───────▼───────┐ │
│  │ resolveSkill│  │ Spending   │  │ fetch()       │ │
│  │ validateSkil│  │ Tracker    │  │ → Registry API│ │
│  │ selectNetwrk│  │ (budget)   │  │               │ │
│  └──────┬──────┘  └────────────┘  └───────────────┘ │
│         │                                            │
│  ┌──────▼──────────────────────────────────────────┐ │
│  │ ClientCache → PaymentClient (@402md/x402)       │ │
│  │   ├─ StellarClient (@stellar/stellar-sdk)       │ │
│  │   └─ EvmClient (viem)                           │ │
│  └──────┬──────────────────────────────────────────┘ │
└─────────┼────────────────────────────────────────────┘
          │ x402 payment protocol
┌─────────▼────────────────────────────────────────────┐
│  Skill Provider (HTTP server)                        │
│  Returns 402 → receives payment proof → returns data │
└──────────────────────────────────────────────────────┘

Key patterns:

  • Lazy client loading: ClientCache creates PaymentClient instances on first use per network
  • Budget enforcement: SpendingTracker wraps BudgetTracker from @402md/x402 — checked before, recorded after
  • Smart network selection: intersects skill's supported networks with wallet capabilities, prefers Stellar
  • Config reload: create_wallet triggers config.reload() so new keys take effect immediately
  • Optional deps: @stellar/stellar-sdk and viem are optional — only loaded when the corresponding network is used

Examples

End-to-end: first-time setup to skill execution

User: "Search for image generation skills"
→ Agent calls search_skills({ query: "image generation" })
→ Returns list of skills with pricing

User: "Create a wallet so we can use one"
→ Agent calls create_wallet({ network: "stellar-testnet" })
→ Returns public key + "fund on testnet faucet"

User: "Use the image-gen skill to create a sunset"
→ Agent calls use_skill({ skill: "image-gen", body: '{"prompt":"a sunset over mountains"}' })
→ Server resolves skill → validates → checks budget → pays → returns image URL

User: "How much have we spent?"
→ Agent calls spending_summary()
→ Returns { spentToday: "0.0500", spentSession: "0.0500", limits: { maxPerCall: "0.10", maxPerDay: "20.00" }, ... }

Using a local SKILL.md during development

User: "Test my local skill"
→ Agent calls read_skill({ skill: "./my-skill/SKILL.md" })
→ Returns parsed manifest with validation errors/warnings

→ Agent calls use_skill({ skill: "./my-skill/SKILL.md", endpoint: "/api/test" })
→ Executes against your local server

Budget rejection

→ Agent calls use_skill for a skill priced at 5.00 USDC
→ MAX_PER_CALL is 0.10
→ Error: "Exceeds per-call limit (0.10 USDC)"
→ No payment is made

Development

Prerequisites

  • Node.js >= 18
  • npm

Setup

git clone https://github.com/402md/mcp.git
cd mcp
npm install

Scripts

| Command | Description | |---------|-------------| | npm run build | Build with tsup (ESM, shebang included) | | npm run dev | Build in watch mode | | npm run typecheck | Run TypeScript type checking (tsc --noEmit) | | npm run lint | Lint source files with ESLint | | npm run lint:fix | Lint and auto-fix | | npm run format | Format with Prettier | | npm run format:check | Check formatting without writing | | npm test | Run tests with Vitest | | npm run test:watch | Run tests in watch mode |

Project Structure

mcp/
├── src/
│   ├── index.ts          # CLI entry point (stdio transport)
│   ├── server.ts         # MCP server setup, tool registration
│   ├── config.ts         # Env vars + wallet file → McpConfig
│   ├── types.ts          # McpConfig, SpendingRecord, WalletFileConfig
│   ├── clients.ts        # ClientCache + network selection logic
│   ├── resolve.ts        # Skill resolution (URL / file / registry)
│   ├── spending.ts       # SpendingTracker (budget enforcement)
│   ├── wallet-store.ts   # Read/write ~/.402md/wallet.json
│   └── tools/
│       ├── use.ts        # use_skill, read_skill
│       ├── wallet.ts     # check_balance, spending_summary, create_wallet
│       └── registry.ts   # search_skills
├── __tests__/
│   ├── config.test.ts
│   ├── spending.test.ts
│   ├── resolve.test.ts
│   └── wallet-store.test.ts
├── tsup.config.ts        # Build config (ESM, shebang, sourcemaps)
├── tsconfig.json
├── eslint.config.mjs
└── .prettierrc

Code Conventions

  • No semicolons, single quotes, no trailing commas (Prettier)
  • ESM only ("type": "module", .js extensions in imports)
  • Strict TypeScript (strict: true)
  • Tools are thin handlers — business logic lives in modules (spending.ts, clients.ts, resolve.ts)
  • @stellar/stellar-sdk and viem are optional deps, dynamically imported only when the matching network is used

Adding a New Tool

  1. Create or edit a file in src/tools/
  2. Export a register*Tools(server, config, spending, clientCache) function
  3. Call it from src/server.ts
  4. Add tests in __tests__/
  5. Run npm run typecheck && npm run lint && npm test

Running Locally

# Build and run
npm run build
node dist/index.js

# Or in dev mode (rebuilds on change)
npm run dev

# In another terminal, test with MCP inspector
npx @modelcontextprotocol/inspector node dist/index.js

To test with environment variables:

STELLAR_SECRET="S..." NETWORK="stellar-testnet" node dist/index.js

License

MIT

Quick Setup
Installation guide for this server

Install Package (if required)

npx @modelcontextprotocol/server-mcp

Cursor configuration (mcp.json)

{ "mcpServers": { "402-md-mcp": { "command": "npx", "args": [ "402-md-mcp" ] } } }