@402md/mcp
An MCP server that transforms SKILL.md files into executable tools for AI agents, allowing them to discover and pay for services via the x402 protocol. It supports automatic payments on Stellar and EVM networks, enabling seamless integration of premium API skills.
README
@402md/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
- Networks
- Wallet Setup
- How Payments Work
- Claude Desktop Configuration
- Environment Variables
- Wallet File
- Budget & Spending Limits
- Tools
- Modes
- Skill Resolution
- Architecture
- Examples
- Development
- License
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 withcreate_wallet. - Testing EVM skills? Use
base-sepolia— free testnet, good for EVM-specific endpoints. - Production? Use
stellar(lower fees) orbasedepending 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/with0o700 - Merge behavior:
saveWalletConfigmerges 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:
stellarorstellar-testnet→ generates a Stellar keypair (Keypair.random())baseorbase-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.jsonfirst. - 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:
ClientCachecreatesPaymentClientinstances on first use per network - Budget enforcement:
SpendingTrackerwrapsBudgetTrackerfrom@402md/x402— checked before, recorded after - Smart network selection: intersects skill's supported networks with wallet capabilities, prefers Stellar
- Config reload:
create_wallettriggersconfig.reload()so new keys take effect immediately - Optional deps:
@stellar/stellar-sdkandviemare 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",.jsextensions in imports) - Strict TypeScript (
strict: true) - Tools are thin handlers — business logic lives in modules (
spending.ts,clients.ts,resolve.ts) @stellar/stellar-sdkandviemare optional deps, dynamically imported only when the matching network is used
Adding a New Tool
- Create or edit a file in
src/tools/ - Export a
register*Tools(server, config, spending, clientCache)function - Call it from
src/server.ts - Add tests in
__tests__/ - 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
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。