Nonagon Link MCP Server — Setup and Verification Guide

Setup and verification guide for the Nonagon Link MCP server


Quick start — connect by copy & paste (recommended)

With the npm package @nonagon-link/mcp you can connect without cloning the repository. Just add the following to your project's .claude/settings.json (or claude_desktop_config.json for Claude Desktop):

{
  "mcpServers": {
    "nonagon-link": {
      "command": "npx",
      "args": ["-y", "--ignore-scripts", "@nonagon-link/mcp@0.3.1"],
      "env": {
        "NONAGON_PRIVATE_KEY": "<Base58 secret key of your payment wallet>",
        "NONAGON_PAY_TO": "8yXpNyyPg9iGdMST8BRyz7HEJVbQG81VP6kHBCKXrdH8",
        "NONAGON_LINK_BASE_URL": "https://api.nnglink.ai"
      }
    }
  }
}

In Claude Code you can also add it with a single command:

claude mcp add nonagon-link -e NONAGON_PRIVATE_KEY=<Base58 secret key> -e NONAGON_LINK_BASE_URL=https://api.nnglink.ai -e NONAGON_PAY_TO=8yXpNyyPg9iGdMST8BRyz7HEJVbQG81VP6kHBCKXrdH8 -- npx -y --ignore-scripts @nonagon-link/mcp@0.3.1

⚠️ Security note: always pin an exact version (e.g. @nonagon-link/mcp@0.3.1). Because your private key is passed via env, running an unpinned latest version amplifies supply-chain risk. --ignore-scripts prevents dependency install scripts from running. Use a dedicated wallet with a small balance for payments. NONAGON_PAY_TO is the allow-list for Solana USDC payees; unset, every Solana USDC payment is refused. JPYC / EVM USDC payees are allowed separately by NONAGON_EVM_PAY_TO; unset, every EVM payment is refused (EVM payments also need NONAGON_EVM_PRIVATE_KEY). The NONAGON LINK payees (public values that appear as payTo in every 402) are below. Never copy them from an untrusted 402.

EnvironmentNONAGON_PAY_TO (Solana USDC)NONAGON_EVM_PAY_TO (JPYC / EVM USDC)
Production (Solana mainnet; Ethereum / Polygon / Base)8yXpNyyPg9iGdMST8BRyz7HEJVbQG81VP6kHBCKXrdH80x7b689c9b29e5e51700f19d4bb02b382fb418f34d
Testing (Solana devnet; Sepolia / Amoy / Base Sepolia)6JdW8xNvXrghY7acKXzMn2pJoK88g6nnAVU3YNo1YiF80xcaeb3ef5dcea1557765323379a292fc6b4011831

1. Overview

Nonagon Link MCP is a Model Context Protocol (MCP) server that lets AI agents such as Claude / GPT-4 call paid APIs with USDC via Nonagon Link's x402 payment infrastructure. Search (search_listings) → capped purchase (pay_and_call) → token management (get_active_tokens) → payment history and spending (get_payment_history / get_spending) can all be completed through MCP alone.

Available Tools

Tool nameFunction
pay_and_callPay the specified paid API and fetch data. Solana USDC (maxPaymentUsdc required, default cap 0.100, payTo on NONAGON_PAY_TO). With an EVM key and NONAGON_EVM_PAY_TO: JPYC / TEST_JPYSC (maxPaymentJpy); EVM USDC only when MCP_MAX_PAYMENT_EVM_USDC_CAP is set (maxPaymentEvmUsdc)
search_listingsKeyword search over public listings, returning what you need to buy (proxyUrl, price, parameter schema)
get_active_tokensSummary of currently valid masked tokens and remaining validity. May be empty because inline payments issue no access token
get_payment_historyPayment history for this server's wallets (latest N, filterable by status). Includes inline payments, and the EVM wallet's payments when an EVM key is set
get_spendingCumulative spending for this server's wallets by period (day / week / month / all), with a per-currency breakdown, including the EVM wallet when an EVM key is set

Transport Modes

ModeUse caseAuthentication
stdioDirect connection from Claude Code / Claude DesktopNone (local process)
HTTP streamConnection from a local HTTP client on the same machineMCP_AUTH_TOKEN (Bearer)

2. Prerequisites

  • Node.js >= 20.0.0
  • npm (monorepo dependencies installed)
  • A Solana wallet (private key) for payments — this wallet's USDC is used to pay API usage fees, and its SOL is used for transaction fees. The agent (MCP server) signs payments with this wallet's private key.

Creating a Solana Wallet for Payments

In production, use a wallet holding real USDC on mainnet. If you do not have a payment wallet yet, follow these steps (the example below is for devnet testing; for production, prepare mainnet USDC and keep NONAGON_NETWORK at its mainnet value):

# Generate a keypair with the Solana CLI
solana-keygen new --outfile ~/.config/solana/devnet.json --no-bip39-passphrase

# Check the public key
solana-keygen pubkey ~/.config/solana/devnet.json

# Get the private key in Base58 (used in the MCP configuration)
node -e "
const fs = require('fs');
const bs58 = require('bs58');
const keyData = JSON.parse(fs.readFileSync(process.env.HOME + '/.config/solana/devnet.json'));
console.log('Public key:', require('@solana/web3.js').Keypair.fromSecretKey(Uint8Array.from(keyData)).publicKey.toBase58());
console.log('Private key (Base58):', bs58.default.encode(Uint8Array.from(keyData)));
"

# Airdrop devnet SOL (for tx fees)
solana airdrop 2 --url devnet

If the Solana CLI is not installed, use brew install solana or see https://docs.solana.com/cli/install-solana-cli-tools.


3. Environment Variables

Required

Variable nameDescriptionExample
NONAGON_PRIVATE_KEYPrivate key (Base58) of the Solana wallet used for payments. USDC / SOL are paid from this wallet5Jh... (64-88 characters)

Optional

Variable nameDefaultDescription
NONAGON_NETWORKsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpSolana network (CAIP-2). Defaults to mainnet (production, real USDC). For devnet testing, specify solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
NONAGON_PAY_TO(unset = Solana payments refused)Comma-separated Solana 402 payTo allow-list for pay_and_call (NONAGON LINK platform wallet; values in the quick-start table). Do not copy payTo from an untrusted 402. Unset refuses every Solana USDC payment; EVM payments are governed by NONAGON_EVM_PAY_TO
MCP_MAX_PAYMENT_USDC_CAP0.100Ceiling for maxPaymentUsdc (Solana USDC only). 0.1–10, decimal notation, at most 6 decimals
NONAGON_EVM_PRIVATE_KEY(unset = Solana USDC only)EVM wallet key (0x + 64 hex) for JPYC / TEST_JPYSC, for EVM USDC when MCP_MAX_PAYMENT_EVM_USDC_CAP is set, and for signing the EIP-712 AgentChallenge that reads the EVM wallet's history and spending. Only valid when NONAGON_NETWORK is Solana mainnet or devnet (the server refuses to start otherwise). Use a dedicated wallet with a small balance
NONAGON_EVM_PAY_TO(unset = EVM payments refused)Comma-separated EVM 402 payTo allow-list (NONAGON LINK EVM treasury; values in the quick-start table). Unset refuses every EVM payment (JPYC and EVM USDC)
MCP_MAX_PAYMENT_JPY_CAP100Ceiling for maxPaymentJpy (yen-denominated listings). 100–1000 (below 100 the server does not start), at most 4 decimals. Ethereum JPYC listings start at 300
MCP_MAX_PAYMENT_EVM_USDC_CAP(unset = EVM USDC never paid)Turns on EVM USDC and caps maxPaymentEvmUsdc per call (2–10 USDC). Needs NONAGON_EVM_PRIVATE_KEY and NONAGON_EVM_PAY_TO
NONAGON_LINK_BASE_URL(unset)Required for search_listings, get_payment_history and get_spending. Base URL of the Nonagon Link API (e.g. https://api.nnglink.ai). If unset, these tools return an error
MCP_AUTH_TOKEN(unset)Required only in HTTP stream mode (not needed in stdio mode, the default for Claude Desktop/Code). A Bearer authentication token generated by the operator to protect the local HTTP endpoint. Generate a random secret of at least 32 bytes with openssl rand -base64 32 or similar (at least 43 base64url characters or 64 hex characters)
LOG_LEVELinfoLog level

⚠️ EVM payments (JPYC, EVM USDC): an EVM payment is a signed EIP-3009 authorization, and any third party that receives it (such as the host you call) can execute it on-chain itself. The money can only go to the treasury in NONAGON_EVM_PAY_TO, but you then get no service and no payment record. There is no session or daily total limit: keep each cap as low as you need, keep the EVM wallet balance small, and do not pay_and_call URLs you do not trust. When paymentStatus is unknown, do not retry; check authorizationState(from, nonce) on the token contract.


4. How to Verify Operation

Method A: Run from source (for contributors)

Use this only when you have cloned the repository and want to run the development source directly. For normal usage, follow the "Quick start" section at the top of this page (npm package @nonagon-link/mcp).

1. Create the MCP configuration file

Add to your project's .claude/settings.json:

{
  "mcpServers": {
    "nonagon-link": {
      "command": "npx",
      "args": ["tsx", "apps/nonagon_link/src/mcp/server.ts"],
      "cwd": "/path/to/nonagon_link",
      "env": {
        "NONAGON_PRIVATE_KEY": "<private key (Base58) of the Solana wallet used for payments>",
        "NONAGON_NETWORK": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
        "NONAGON_LINK_BASE_URL": "https://api.nnglink.ai",
        "NONAGON_PAY_TO": "8yXpNyyPg9iGdMST8BRyz7HEJVbQG81VP6kHBCKXrdH8",
        "LOG_LEVEL": "debug"
      }
    }
  }
}

2. Check in Claude Code

> /mcp

If the server with the configuration key name (nonagon-link) is shown as connected, it worked.

3. Test a tool call

Ask Claude something like:

Use the get_active_tokens tool to fetch a summary of the currently valid masked tokens

get_active_tokens returns maskedToken, proxyUrl, expiresAt, and remainingMinutes; it never returns raw token values.

Example of a successful response:

{
  "tokens": [
    {
      "maskedToken": "agt_abcd...mnop",
      "proxyUrl": "https://api.nnglink.ai/api/proxy/weather-api/current",
      "expiresAt": "2026-05-11T12:00:00Z",
      "remainingMinutes": 45
    }
  ]
}

Method B: MCP Inspector (GUI)

Use the official MCP GUI debugging tool.

# Run from the project root
NONAGON_PRIVATE_KEY=<private-key> \
NONAGON_PAY_TO=8yXpNyyPg9iGdMST8BRyz7HEJVbQG81VP6kHBCKXrdH8 \
NONAGON_LINK_BASE_URL=https://api.nnglink.ai \
npx @modelcontextprotocol/inspector npx tsx apps/nonagon_link/src/mcp/server.ts

A browser window opens, where you can check the following:

  • Tools tab: pay_and_call / search_listings / get_active_tokens / get_payment_history / get_spending are displayed
  • Tool execution: you can enter arguments and invoke tools directly
  • Schema check: verify that each tool's input schema is correct

Method C: HTTP stream mode

HTTP stream binds only to 127.0.0.1, so it is for local clients on the same machine. It cannot be exposed as a public endpoint over the network.

In HTTP stream mode, setting MCP_AUTH_TOKEN is required. Send Authorization: Bearer <MCP_AUTH_TOKEN> with every request.

Use a CSPRNG-generated secret of at least 32 bytes for MCP_AUTH_TOKEN. The configured value is expected to be at least 43 base64url characters or 64 hex characters.

# Terminal 1: start the server
NONAGON_PRIVATE_KEY=<private-key> \
NONAGON_PAY_TO=8yXpNyyPg9iGdMST8BRyz7HEJVbQG81VP6kHBCKXrdH8 \
NONAGON_LINK_BASE_URL=https://api.nnglink.ai \
NONAGON_NETWORK=solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp \
MCP_AUTH_TOKEN=<32-byte-random-base64url-token> \
npx tsx apps/nonagon_link/src/mcp/server.ts --transport http --port 3002
# Terminal 2: health check with curl (MCP initialize)
curl -X POST http://localhost:3002/ \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <32-byte-random-base64url-token>" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-03-26",
      "capabilities": {},
      "clientInfo": { "name": "test", "version": "1.0" }
    }
  }'

Example of a successful response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-03-26",
    "capabilities": { "tools": {} },
    "serverInfo": { "name": "nonagon_link", "version": "1.0.0" }
  }
}

5. pay_and_call Example

Prerequisites

  • A Solana wallet holding USDC for payments (production = real USDC on mainnet / testing = fake USDC on devnet). You need USDC on the same network as your NONAGON_NETWORK setting
  • An API Provider registered on Nonagon Link must exist

Calling from Claude Code

Use the pay_and_call tool to call the following API:
- URL: https://api.nnglink.ai/api/proxy/weather-api/current
- Method: POST
- Body: { "city": "Tokyo" }
- Payment cap: 0.05 USDC   # maxPaymentUsdc (required, greater than 0 up to a maximum of 0.100)

Response Structure

{
  "success": true,
  "data": { ... },
  "paymentStatus": "paid",
  "paymentInfo": {
    "currency": "USDC",
    "amount": "0.01",
    "amountUsdc": "0.01",
    "txSignature": "5xKt...",
    "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
    "payer": "<your wallet>",
    "paidAt": "2026-10-05T00:00:00.000Z"
  }
}

paymentStatus is paid, not_paid or unknown. On unknown (a signed payment was sent but the result is not confirmed), do not retry automatically: a retry can pay twice. For EVM payments, check authorizationState(from, nonce). For yen tokens (JPYC / TEST_JPYSC), amount is in that currency and amountUsdc is null.

Decide that nothing was paid only from paymentStatus being not_paid (a free API or a reused token, for example). paymentInfo can be null while paymentStatus is unknown (an EVM payment whose result is unknown), so a null paymentInfo alone does not mean nothing was paid.

On Error

Error responses carry paymentStatus too. An error after the payment was signed and sent can be paid or unknown, so do not treat success: false alone as «nothing was paid».

{
  "success": false,
  "error": "PAYMENT_EXCEEDS_LIMIT",
  "currency": "USDC",
  "required": "0.08",
  "max": "0.05",
  "requiredUsdc": "0.08",
  "maxPaymentUsdc": 0.05,
  "hint": "Increase maxPaymentUsdc if this price is acceptable, or choose a cheaper listing via search_listings.",
  "paymentStatus": "not_paid"
}

6. Security

SSRF Protection

The proxyUrl of pay_and_call is automatically validated as follows:

  • Private IPs rejected (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
  • Loopback rejected (127.0.0.0/8, ::1)
  • Link-local rejected (169.254.0.0/16)
  • DNS rebinding prevention (pinnedFetch couples DNS resolution and connection)
  • Only HTTPS URLs allowed
  • Exposing HTTP stream to anything other than 127.0.0.1 is prohibited

Log Redaction

  • URL query parameters/paths are masked (https://api.example.com/***)
  • Access tokens are masked (agt_abcd...mnop)
  • Private keys are never written to logs

HTTP Stream Mode Authentication

  • Bearer token authentication via MCP_AUTH_TOKEN
  • Binds only to 127.0.0.1 (local clients on the same machine only; remote exposure prohibited)
  • Per-session TokenStore isolates multiple clients

7. Troubleshooting

The MCP server does not start

MCP env validation failed:
  - NONAGON_PRIVATE_KEY: Base58 Solana private key format

→ Set the NONAGON_PRIVATE_KEY environment variable.

Unsupported MCP env keys: <legacy-key>. Use only live runtime env vars.

→ Legacy env vars are not supported. Use only the live runtime env vars in the table (including NONAGON_PAY_TO).

pay_and_call returns SSRF blocked

{ "success": false, "error": "Blocked destination: private/internal network", "paymentStatus": "not_paid" }

→ Private IPs / localhost cannot be called. Specify a public HTTPS URL.

get_active_tokens returns an empty array

→ There are no tokens yet because you have not called an API with pay_and_call. This is normal behavior.

The MCP server does not appear in Claude Code

  1. Check that the path in .claude/settings.json is correct
  2. Check that cwd points to the project root
  3. Run npx tsx apps/nonagon_link/src/mcp/server.ts directly and check for errors

HTTP stream mode returns 401

→ Check that the Authorization: Bearer <MCP_AUTH_TOKEN> header is correct.

当サイトでは、利用状況の把握と改善のためにアクセス解析 (Google アナリティクス) を使用します。「同意する」を選ぶと、閲覧ページ等の計測情報が Google LLC(米国)へ送信されます。詳細: プライバシーポリシー / 利用者情報の外部送信について