{"_id":"@402md/mcp","_rev":"2-fa151d56170d9eaa7a5472b158e33f2e","name":"@402md/mcp","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@402md/mcp","version":"0.1.0","keywords":["402md","mcp","skill.md","x402","ai-agents","payment","model-context-protocol","claude"],"license":"MIT","_id":"@402md/mcp@0.1.0","maintainers":[{"name":"henriquebreim","email":"developers@402.md"}],"homepage":"https://github.com/402md/mcp#readme","bugs":{"url":"https://github.com/402md/mcp/issues"},"bin":{"402md-mcp":"dist/index.js"},"dist":{"shasum":"504f25b93cf44a9bb644e8624451bcbcc500689c","tarball":"https://registry.npmjs.org/@402md/mcp/-/mcp-0.1.0.tgz","fileCount":4,"integrity":"sha512-4+1KPitu7rfzhHbsSgdbdE4BYL37zwRWcNFUPm5wLPSTqpx+yGcjB8fKZXaLkTItqGKtHqNPtbWBcb4LZK1s0Q==","signatures":[{"sig":"MEUCIQCV5xo9ApTdHWNtQBqFv4N89JRH2xT2IjzNcglfNwH5ZAIgUcROb2pv/U9eWDB8DMiFom8PAzycSa6CSqscLbrJuiw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6907666},"type":"module","engines":{"node":">=18"},"gitHead":"93213f22259a243ffc120bba8305a269846f010b","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","format":"prettier --write 'src/**/*.ts' '__tests__/**/*.ts'","lint:fix":"eslint src/ --fix","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check 'src/**/*.ts' '__tests__/**/*.ts'","prepublishOnly":"npm run build"},"_npmUser":{"name":"henriquebreim","email":"developers@402.md"},"repository":{"url":"git+https://github.com/402md/mcp.git","type":"git"},"_npmVersion":"11.2.0","description":"MCP server that transforms SKILL.md files into executable tools for AI agents — auto-pays via x402","directories":{},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"_nodeVersion":"22.13.1","dependencies":{"zod":"^3.23.0","@402md/x402":"^0.1.0","@402md/skillmd":"^0.1.1","@modelcontextprotocol/sdk":"^1.26.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","eslint":"^9.28.0","vitest":"^3.2.1","globals":"^16.1.0","prettier":"^3.5.3","@eslint/js":"^9.28.0","typescript":"^5.8.3","@types/node":"^22.15.21","lint-staged":"^16.1.0","typescript-eslint":"^8.34.0","eslint-config-prettier":"^10.1.5","eslint-plugin-prettier":"^5.4.0"},"optionalDependencies":{"viem":"^2.0.0","@stellar/stellar-sdk":"^12.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_0.1.0_1773456163280_0.7014831990805921","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@402md/mcp","version":"0.1.1","description":"MCP server that transforms SKILL.md files into executable tools for AI agents — auto-pays via x402","type":"module","bin":{"402md-mcp":"dist/index.js"},"scripts":{"build":"tsup","dev":"tsup --watch","lint":"eslint src/","lint:fix":"eslint src/ --fix","format":"prettier --write 'src/**/*.ts' '__tests__/**/*.ts'","format:check":"prettier --check 'src/**/*.ts' '__tests__/**/*.ts'","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","prepublishOnly":"npm run build"},"dependencies":{"@402md/skillmd":"^0.1.3","@402md/x402":"^0.1.0","@modelcontextprotocol/sdk":"^1.26.0","zod":"^3.23.0"},"optionalDependencies":{"viem":"^2.0.0","@stellar/stellar-sdk":"^12.0.0"},"devDependencies":{"@eslint/js":"^9.28.0","@types/node":"^22.15.21","eslint":"^9.28.0","eslint-config-prettier":"^10.1.5","eslint-plugin-prettier":"^5.4.0","globals":"^16.1.0","lint-staged":"^16.1.0","prettier":"^3.5.3","tsup":"^8.5.0","typescript":"^5.8.3","typescript-eslint":"^8.34.0","vitest":"^3.2.1"},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"keywords":["402md","mcp","skill.md","x402","ai-agents","payment","model-context-protocol","claude"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/402md/mcp.git"},"engines":{"node":">=18"},"_id":"@402md/mcp@0.1.1","gitHead":"8d6389e0d9d1365fe90992cb47f9a41920b18b68","bugs":{"url":"https://github.com/402md/mcp/issues"},"homepage":"https://github.com/402md/mcp#readme","_nodeVersion":"22.13.1","_npmVersion":"11.2.0","dist":{"integrity":"sha512-if3wYiJcavTsQ8GwToSKvJV3srRtl9Wb8txEO8DMgm0VXqSMkECG0NisYh/ipDT3cOggmc6R1RkXoLWXr6DWlw==","shasum":"af286af095808c02e459ebde264bde00f162e1e2","tarball":"https://registry.npmjs.org/@402md/mcp/-/mcp-0.1.1.tgz","fileCount":4,"unpackedSize":6908594,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHOMyLnEnYmikRvMCK2/uQGkYeQdZM6sSL7uU4/egQBmAiAq06W1SWnpAc5Uw/Jrr5uJC5UrHks9Ca0h4L+nAgK5ng=="}]},"_npmUser":{"name":"henriquebreim","email":"developers@402.md"},"directories":{},"maintainers":[{"name":"henriquebreim","email":"developers@402.md"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp_0.1.1_1773538982279_0.8035308765635392"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-14T02:42:43.219Z","modified":"2026-03-15T01:43:02.577Z","0.1.0":"2026-03-14T02:42:43.710Z","0.1.1":"2026-03-15T01:43:02.446Z"},"bugs":{"url":"https://github.com/402md/mcp/issues"},"license":"MIT","homepage":"https://github.com/402md/mcp#readme","keywords":["402md","mcp","skill.md","x402","ai-agents","payment","model-context-protocol","claude"],"repository":{"type":"git","url":"git+https://github.com/402md/mcp.git"},"description":"MCP server that transforms SKILL.md files into executable tools for AI agents — auto-pays via x402","maintainers":[{"name":"henriquebreim","email":"developers@402.md"}],"readme":"# @402md/mcp\n\n[![npm version](https://img.shields.io/npm/v/@402md/mcp)](https://www.npmjs.com/package/@402md/mcp)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)\n[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/)\n[![MCP](https://img.shields.io/badge/MCP-compatible-purple)](https://modelcontextprotocol.io)\n\nMCP server that transforms [SKILL.md](https://402.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.\n\n## Table of Contents\n\n- [Quick Start](#quick-start)\n- [Networks](#networks)\n- [Wallet Setup](#wallet-setup)\n- [How Payments Work](#how-payments-work)\n- [Claude Desktop Configuration](#claude-desktop-configuration)\n- [Environment Variables](#environment-variables)\n- [Wallet File](#wallet-file)\n- [Budget & Spending Limits](#budget--spending-limits)\n- [Tools](#tools)\n- [Modes](#modes)\n- [Skill Resolution](#skill-resolution)\n- [Architecture](#architecture)\n- [Examples](#examples)\n- [Development](#development)\n- [License](#license)\n\n## Quick Start\n\n```bash\nnpx @402md/mcp\n```\n\nOr install globally:\n\n```bash\nnpm install -g @402md/mcp\n402md-mcp\n```\n\nThe 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](#wallet-setup)).\n\n## Networks\n\nThe server supports four networks across two blockchain ecosystems:\n\n### Stellar\n\n| Network | ID | Use Case | USDC Contract |\n|---------|-----|----------|---------------|\n| **Stellar Mainnet** | `stellar` | Production payments with real USDC | Native Stellar USDC (Centre) |\n| **Stellar Testnet** | `stellar-testnet` | Development & testing with free testnet USDC | Testnet USDC |\n\n**Stellar** is the default and preferred network. It offers sub-second finality, near-zero fees (~0.00001 XLM per tx), and native USDC support.\n\n- **Testnet faucet**: Use [Stellar Laboratory](https://laboratory.stellar.org/#account-creator?network=test) to create and fund testnet accounts.\n- **Mainnet**: Fund your account via any Stellar DEX, exchange, or on-ramp that supports USDC on Stellar.\n\n### EVM (Base)\n\n| Network | ID | Use Case | USDC Contract |\n|---------|-----|----------|---------------|\n| **Base Mainnet** | `base` | Production payments on Base L2 | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |\n| **Base Sepolia** | `base-sepolia` | Development & testing on Base testnet | Sepolia USDC |\n\n**Base** is an Ethereum L2 with low gas fees and fast confirmations.\n\n- **Sepolia faucet**: Get testnet ETH from [Alchemy Faucet](https://www.alchemy.com/faucets/base-sepolia) or [Coinbase Faucet](https://faucet.quicknode.com/base/sepolia) (needed for gas). Then bridge or mint testnet USDC.\n- **Mainnet**: Bridge USDC to Base from Ethereum, or buy directly on Base via Coinbase or any supported on-ramp.\n\n### Choosing a Network\n\n- **Just getting started?** Use `stellar-testnet` — no real money, instant setup with `create_wallet`.\n- **Testing EVM skills?** Use `base-sepolia` — free testnet, good for EVM-specific endpoints.\n- **Production?** Use `stellar` (lower fees) or `base` depending on what the skill accepts.\n\nThe 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.\n\n## Wallet Setup\n\nThere are three ways to configure a wallet, listed by priority (highest first):\n\n### Option 1: Environment Variables (recommended for production)\n\n```bash\n# Stellar\nexport STELLAR_SECRET=\"SCZANGBA5YHTNYVVV3C7CAZMCLXPILHSE6PGYV2FHHUQ5DGQJWRZ4GXT\"\nexport NETWORK=\"stellar-testnet\"\n\n# EVM (Base)\nexport EVM_PRIVATE_KEY=\"0x4c0883a69102937d6231471b5dbb6204fe512961708279f23efb3c0c90...\"\nexport NETWORK=\"base-sepolia\"\n\n# Both (FULL mode)\nexport STELLAR_SECRET=\"S...\"\nexport EVM_PRIVATE_KEY=\"0x...\"\nexport NETWORK=\"stellar\"  # default network when both are available\n```\n\n### Option 2: `create_wallet` Tool (recommended for development)\n\nIf no wallet is configured, ask your AI agent to use the `create_wallet` tool:\n\n```\n\"Create a new wallet on stellar-testnet\"\n```\n\nThis generates a keypair and saves it to `~/.402md/wallet.json`. The server reloads automatically.\n\n**Important**: `create_wallet` refuses to run if a wallet is already configured. To replace an existing wallet, delete `~/.402md/wallet.json` manually first.\n\n### Option 3: Wallet File (manual)\n\nCreate `~/.402md/wallet.json` manually:\n\n```json\n{\n  \"stellarSecret\": \"SCZANGBA5YHTNYVVV3C7CAZMCLXPILHSE6PGYV2FHHUQ5DGQJWRZ4GXT\",\n  \"evmPrivateKey\": \"0x4c0883a69102937d6231471b5dbb6204fe512961708279f23efb3c0c90...\",\n  \"network\": \"stellar-testnet\",\n  \"createdAt\": \"2026-01-15T10:30:00.000Z\"\n}\n```\n\nThe file is created with `0o600` permissions (owner read/write only). The directory `~/.402md/` is created with `0o700`.\n\n### Generating Keys Manually\n\n**Stellar**:\n```bash\n# Using stellar-sdk in Node.js\nnode -e \"const { Keypair } = require('@stellar/stellar-sdk'); const kp = Keypair.random(); console.log('Secret:', kp.secret()); console.log('Public:', kp.publicKey())\"\n```\n\n**EVM**:\n```bash\n# Using viem in Node.js\nnode -e \"const { generatePrivateKey, privateKeyToAccount } = require('viem/accounts'); const pk = generatePrivateKey(); const acc = privateKeyToAccount(pk); console.log('Private Key:', pk); console.log('Address:', acc.address)\"\n\n# Or using openssl\nopenssl rand -hex 32 | sed 's/^/0x/'\n```\n\n## How Payments Work\n\nThe payment flow is handled automatically by the x402 protocol:\n\n```\nAgent calls use_skill(\"my-skill\", \"/api/generate\")\n  │\n  ├─ 1. Resolve skill → parse SKILL.md manifest\n  ├─ 2. Validate manifest (schema, required fields)\n  ├─ 3. Check budget limits (per-call & daily)\n  ├─ 4. Select compatible network (skill networks ∩ wallet networks)\n  ├─ 5. Create PaymentClient for that network\n  ├─ 6. client.fetch(url) → x402 auto-payment:\n  │     a. First request returns 402 Payment Required\n  │     b. Client signs USDC payment (on-chain)\n  │     c. Retries request with payment proof header\n  │     d. Server verifies payment, returns response\n  ├─ 7. Record spending (amount, skill, endpoint, network)\n  └─ 8. Return response to agent\n```\n\nThe 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\"`).\n\n### What Happens If the Endpoint Fails?\n\nIf 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:\n\n```\nEndpoint returned 500. Payment was sent but the request failed.\n\n{\"error\": \"Internal server error\"}\n```\n\nThis lets the agent (or user) know to contact the skill provider.\n\n## Claude Desktop Configuration\n\nAdd to your `claude_desktop_config.json`:\n\n### Stellar Testnet (getting started)\n\n```json\n{\n  \"mcpServers\": {\n    \"402md\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@402md/mcp\"],\n      \"env\": {\n        \"STELLAR_SECRET\": \"SCZANGBA5YHTNYVVV3C7CAZMCLXPILHSE6PGYV2FHHUQ5DGQJWRZ4GXT\",\n        \"NETWORK\": \"stellar-testnet\",\n        \"MAX_PER_CALL\": \"0.10\",\n        \"MAX_PER_DAY\": \"5.00\"\n      }\n    }\n  }\n}\n```\n\n### Base Sepolia (EVM testing)\n\n```json\n{\n  \"mcpServers\": {\n    \"402md\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@402md/mcp\"],\n      \"env\": {\n        \"EVM_PRIVATE_KEY\": \"0x4c0883a69102937d623147...\",\n        \"NETWORK\": \"base-sepolia\",\n        \"MAX_PER_CALL\": \"0.10\",\n        \"MAX_PER_DAY\": \"5.00\"\n      }\n    }\n  }\n}\n```\n\n### Production (both networks)\n\n```json\n{\n  \"mcpServers\": {\n    \"402md\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@402md/mcp\"],\n      \"env\": {\n        \"STELLAR_SECRET\": \"S...\",\n        \"EVM_PRIVATE_KEY\": \"0x...\",\n        \"NETWORK\": \"stellar\",\n        \"MAX_PER_CALL\": \"1.00\",\n        \"MAX_PER_DAY\": \"50.00\"\n      }\n    }\n  }\n}\n```\n\n### Read-only (no wallet)\n\n```json\n{\n  \"mcpServers\": {\n    \"402md\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@402md/mcp\"]\n    }\n  }\n}\n```\n\n## Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `STELLAR_SECRET` | — | Stellar secret key (starts with `S`). Enables Stellar payments. |\n| `EVM_PRIVATE_KEY` | — | EVM private key (hex, starts with `0x`). Enables Base payments. |\n| `NETWORK` | `stellar` | Default network: `stellar`, `stellar-testnet`, `base`, `base-sepolia` |\n| `MAX_PER_CALL` | `0.10` | Maximum USDC allowed per individual skill call |\n| `MAX_PER_DAY` | `20.00` | Maximum USDC allowed per calendar day |\n| `REGISTRY_URL` | `https://api.402.md` | 402.md marketplace API base URL |\n\nEnvironment variables always take priority over the wallet file (`~/.402md/wallet.json`).\n\n## Wallet File\n\nLocated at `~/.402md/wallet.json`. Created automatically by `create_wallet` or manually.\n\n```json\n{\n  \"stellarSecret\": \"S...\",\n  \"evmPrivateKey\": \"0x...\",\n  \"network\": \"stellar-testnet\",\n  \"createdAt\": \"2026-01-15T10:30:00.000Z\"\n}\n```\n\n- **Permissions**: `0o600` (read/write owner only)\n- **Directory**: `~/.402md/` with `0o700`\n- **Merge behavior**: `saveWalletConfig` merges new fields with existing data, so adding an EVM key won't erase an existing Stellar key\n- **Priority**: env vars > wallet file > defaults\n\n## Budget & Spending Limits\n\nThe server enforces two spending limits:\n\n| Limit | Default | Env Variable | Description |\n|-------|---------|--------------|-------------|\n| **Per-call** | `0.10 USDC` | `MAX_PER_CALL` | Maximum for a single skill invocation |\n| **Per-day** | `20.00 USDC` | `MAX_PER_DAY` | Maximum total across all calls in a calendar day |\n\nBudget is checked **before** each payment. If a call would exceed either limit, the request is rejected with an error (no payment is made).\n\nUse `spending_summary` to check current spending and configured limits:\n\n```json\n{\n  \"spentToday\": \"1.2500\",\n  \"spentSession\": \"0.3000\",\n  \"limits\": {\n    \"maxPerCall\": \"1.00\",\n    \"maxPerDay\": \"50.00\"\n  },\n  \"recentPayments\": [\n    {\n      \"skillName\": \"image-gen\",\n      \"endpoint\": \"/api/generate\",\n      \"amount\": \"0.15\",\n      \"network\": \"stellar-testnet\",\n      \"timestamp\": \"2026-01-15T14:30:00.000Z\"\n    }\n  ]\n}\n```\n\n## Tools\n\n### `use_skill`\n\nExecute a paid SKILL.md endpoint. Resolves the skill, validates the manifest, auto-pays via x402, and returns the result.\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `skill` | `string` | Yes | Skill source: URL, local file path, or marketplace name |\n| `endpoint` | `string` | No | Endpoint path (defaults to first endpoint in manifest) |\n| `method` | `string` | No | HTTP method: `GET`, `POST`, `PUT`, `DELETE`, `PATCH` (defaults to spec) |\n| `body` | `string` | No | Request body as JSON string |\n| `headers` | `object` | No | Additional request headers |\n\n**Requires**: configured wallet (`STELLAR_ONLY`, `EVM_ONLY`, or `FULL` mode).\n\n**Validation**: the manifest is validated before any payment is attempted. If the SKILL.md is invalid, the tool returns an error without spending.\n\n**Examples**:\n\n```\n# By marketplace name\nuse_skill({ skill: \"image-gen\", body: '{\"prompt\": \"a sunset\"}' })\n\n# By URL\nuse_skill({ skill: \"https://example.com/SKILL.md\", endpoint: \"/api/v2/generate\" })\n\n# By local file\nuse_skill({ skill: \"./skills/my-skill/SKILL.md\", method: \"POST\", body: '{\"input\": \"hello\"}' })\n\n# With custom headers\nuse_skill({ skill: \"translate\", headers: { \"X-Target-Lang\": \"pt-BR\" }, body: '{\"text\": \"hello\"}' })\n```\n\n### `read_skill`\n\nRead and parse a SKILL.md without executing it. Returns full manifest details, endpoints, pricing, and validation results. Works in any mode (including `READ_ONLY`).\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `skill` | `string` | Yes | Skill source: URL, local file path, or marketplace name |\n\n**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.\n\n### `check_balance`\n\nCheck the USDC balance and address for the configured wallet.\n\n**No parameters.**\n\n**Returns**:\n\n```json\n{\n  \"address\": \"GCXZ...\",\n  \"balance\": \"45.250000 USDC\",\n  \"network\": \"stellar-testnet\",\n  \"mode\": \"STELLAR_ONLY\"\n}\n```\n\n### `spending_summary`\n\nView spending summary: amounts spent today and in the current session, budget limits, and the last 10 payments.\n\n**No parameters.**\n\n**Returns**: see [Budget & Spending Limits](#budget--spending-limits) for output format.\n\n### `create_wallet`\n\nGenerate a new wallet keypair and save it to `~/.402md/wallet.json`.\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `network` | `string` | Yes | `stellar`, `stellar-testnet`, `base`, or `base-sepolia` |\n\n**Network-to-wallet mapping**:\n- `stellar` or `stellar-testnet` → generates a Stellar keypair (`Keypair.random()`)\n- `base` or `base-sepolia` → generates an EVM keypair (`generatePrivateKey()`)\n\n**Safety**:\n- Refuses to run if a wallet is already configured (any mode except `READ_ONLY`).\n- To replace an existing wallet, delete `~/.402md/wallet.json` first.\n- New keys are merged with existing config — adding an EVM wallet won't erase a Stellar key.\n\n**Returns**:\n\n```json\n{\n  \"type\": \"stellar\",\n  \"publicKey\": \"GCXZ...\",\n  \"network\": \"stellar-testnet\",\n  \"savedTo\": \"/Users/you/.402md/wallet.json\",\n  \"note\": \"Fund this address on the Stellar testnet faucet before use.\"\n}\n```\n\n### `search_skills`\n\nSearch the 402.md marketplace for available skills.\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `query` | `string` | Yes | Search keywords |\n| `maxPrice` | `string` | No | Max price per call in USDC (e.g., `\"0.50\"`) |\n| `category` | `string` | No | Filter by category |\n\n**Returns**: up to 20 results with name, description, min price, and supported networks.\n\n## Modes\n\nThe server operates in one of four modes based on which keys are available:\n\n| Mode | Condition | Available Tools |\n|------|-----------|-----------------|\n| `READ_ONLY` | No keys configured | `read_skill`, `search_skills`, `create_wallet` |\n| `STELLAR_ONLY` | `STELLAR_SECRET` set | All tools — pays via Stellar |\n| `EVM_ONLY` | `EVM_PRIVATE_KEY` set | All tools — pays via Base |\n| `FULL` | Both keys set | All tools — pays via best available network |\n\nIn `FULL` mode, when a skill supports both Stellar and EVM networks, Stellar is preferred (lower fees and faster finality).\n\n## Skill Resolution\n\nThe `skill` parameter in `use_skill` and `read_skill` accepts three formats:\n\n| Format | Example | How It Resolves |\n|--------|---------|-----------------|\n| **URL** | `https://example.com/SKILL.md` | Fetches directly via HTTP |\n| **Local file** | `./skills/my-skill/SKILL.md` | Reads from filesystem |\n| **Marketplace name** | `image-gen` | Queries `{REGISTRY_URL}/api/v1/discovery/skills/{name}/skill-md` |\n\nLocal file detection: any source starting with `/`, `./`, `../`, or ending with `.md` is treated as a file path.\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────┐\n│  AI Agent (Claude, etc.)                             │\n│  Calls MCP tools: use_skill, check_balance, etc.     │\n└──────────────┬───────────────────────────────────────┘\n               │ stdio (MCP protocol)\n┌──────────────▼───────────────────────────────────────┐\n│  @402md/mcp Server                                   │\n│                                                      │\n│  ┌─────────────┐  ┌────────────┐  ┌───────────────┐ │\n│  │ Use Tools   │  │ Wallet     │  │ Registry      │ │\n│  │ use_skill   │  │ check_bal  │  │ search_skills │ │\n│  │ read_skill  │  │ spending   │  │               │ │\n│  │             │  │ create_wal │  │               │ │\n│  └──────┬──────┘  └─────┬──────┘  └───────┬───────┘ │\n│         │               │                 │          │\n│  ┌──────▼──────┐  ┌─────▼──────┐  ┌───────▼───────┐ │\n│  │ resolveSkill│  │ Spending   │  │ fetch()       │ │\n│  │ validateSkil│  │ Tracker    │  │ → Registry API│ │\n│  │ selectNetwrk│  │ (budget)   │  │               │ │\n│  └──────┬──────┘  └────────────┘  └───────────────┘ │\n│         │                                            │\n│  ┌──────▼──────────────────────────────────────────┐ │\n│  │ ClientCache → PaymentClient (@402md/x402)       │ │\n│  │   ├─ StellarClient (@stellar/stellar-sdk)       │ │\n│  │   └─ EvmClient (viem)                           │ │\n│  └──────┬──────────────────────────────────────────┘ │\n└─────────┼────────────────────────────────────────────┘\n          │ x402 payment protocol\n┌─────────▼────────────────────────────────────────────┐\n│  Skill Provider (HTTP server)                        │\n│  Returns 402 → receives payment proof → returns data │\n└──────────────────────────────────────────────────────┘\n```\n\n**Key patterns**:\n- **Lazy client loading**: `ClientCache` creates `PaymentClient` instances on first use per network\n- **Budget enforcement**: `SpendingTracker` wraps `BudgetTracker` from `@402md/x402` — checked before, recorded after\n- **Smart network selection**: intersects skill's supported networks with wallet capabilities, prefers Stellar\n- **Config reload**: `create_wallet` triggers `config.reload()` so new keys take effect immediately\n- **Optional deps**: `@stellar/stellar-sdk` and `viem` are optional — only loaded when the corresponding network is used\n\n## Examples\n\n### End-to-end: first-time setup to skill execution\n\n```\nUser: \"Search for image generation skills\"\n→ Agent calls search_skills({ query: \"image generation\" })\n→ Returns list of skills with pricing\n\nUser: \"Create a wallet so we can use one\"\n→ Agent calls create_wallet({ network: \"stellar-testnet\" })\n→ Returns public key + \"fund on testnet faucet\"\n\nUser: \"Use the image-gen skill to create a sunset\"\n→ Agent calls use_skill({ skill: \"image-gen\", body: '{\"prompt\":\"a sunset over mountains\"}' })\n→ Server resolves skill → validates → checks budget → pays → returns image URL\n\nUser: \"How much have we spent?\"\n→ Agent calls spending_summary()\n→ Returns { spentToday: \"0.0500\", spentSession: \"0.0500\", limits: { maxPerCall: \"0.10\", maxPerDay: \"20.00\" }, ... }\n```\n\n### Using a local SKILL.md during development\n\n```\nUser: \"Test my local skill\"\n→ Agent calls read_skill({ skill: \"./my-skill/SKILL.md\" })\n→ Returns parsed manifest with validation errors/warnings\n\n→ Agent calls use_skill({ skill: \"./my-skill/SKILL.md\", endpoint: \"/api/test\" })\n→ Executes against your local server\n```\n\n### Budget rejection\n\n```\n→ Agent calls use_skill for a skill priced at 5.00 USDC\n→ MAX_PER_CALL is 0.10\n→ Error: \"Exceeds per-call limit (0.10 USDC)\"\n→ No payment is made\n```\n\n## Development\n\n### Prerequisites\n\n- Node.js >= 18\n- npm\n\n### Setup\n\n```bash\ngit clone https://github.com/402md/mcp.git\ncd mcp\nnpm install\n```\n\n### Scripts\n\n| Command | Description |\n|---------|-------------|\n| `npm run build` | Build with tsup (ESM, shebang included) |\n| `npm run dev` | Build in watch mode |\n| `npm run typecheck` | Run TypeScript type checking (`tsc --noEmit`) |\n| `npm run lint` | Lint source files with ESLint |\n| `npm run lint:fix` | Lint and auto-fix |\n| `npm run format` | Format with Prettier |\n| `npm run format:check` | Check formatting without writing |\n| `npm test` | Run tests with Vitest |\n| `npm run test:watch` | Run tests in watch mode |\n\n### Project Structure\n\n```\nmcp/\n├── src/\n│   ├── index.ts          # CLI entry point (stdio transport)\n│   ├── server.ts         # MCP server setup, tool registration\n│   ├── config.ts         # Env vars + wallet file → McpConfig\n│   ├── types.ts          # McpConfig, SpendingRecord, WalletFileConfig\n│   ├── clients.ts        # ClientCache + network selection logic\n│   ├── resolve.ts        # Skill resolution (URL / file / registry)\n│   ├── spending.ts       # SpendingTracker (budget enforcement)\n│   ├── wallet-store.ts   # Read/write ~/.402md/wallet.json\n│   └── tools/\n│       ├── use.ts        # use_skill, read_skill\n│       ├── wallet.ts     # check_balance, spending_summary, create_wallet\n│       └── registry.ts   # search_skills\n├── __tests__/\n│   ├── config.test.ts\n│   ├── spending.test.ts\n│   ├── resolve.test.ts\n│   └── wallet-store.test.ts\n├── tsup.config.ts        # Build config (ESM, shebang, sourcemaps)\n├── tsconfig.json\n├── eslint.config.mjs\n└── .prettierrc\n```\n\n### Code Conventions\n\n- **No semicolons**, single quotes, no trailing commas (Prettier)\n- ESM only (`\"type\": \"module\"`, `.js` extensions in imports)\n- Strict TypeScript (`strict: true`)\n- Tools are thin handlers — business logic lives in modules (`spending.ts`, `clients.ts`, `resolve.ts`)\n- `@stellar/stellar-sdk` and `viem` are optional deps, dynamically imported only when the matching network is used\n\n### Adding a New Tool\n\n1. Create or edit a file in `src/tools/`\n2. Export a `register*Tools(server, config, spending, clientCache)` function\n3. Call it from `src/server.ts`\n4. Add tests in `__tests__/`\n5. Run `npm run typecheck && npm run lint && npm test`\n\n### Running Locally\n\n```bash\n# Build and run\nnpm run build\nnode dist/index.js\n\n# Or in dev mode (rebuilds on change)\nnpm run dev\n\n# In another terminal, test with MCP inspector\nnpx @modelcontextprotocol/inspector node dist/index.js\n```\n\nTo test with environment variables:\n\n```bash\nSTELLAR_SECRET=\"S...\" NETWORK=\"stellar-testnet\" node dist/index.js\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}