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.
| Environment | NONAGON_PAY_TO (Solana USDC) | NONAGON_EVM_PAY_TO (JPYC / EVM USDC) |
|---|---|---|
| Production (Solana mainnet; Ethereum / Polygon / Base) | 8yXpNyyPg9iGdMST8BRyz7HEJVbQG81VP6kHBCKXrdH8 | 0x7b689c9b29e5e51700f19d4bb02b382fb418f34d |
| Testing (Solana devnet; Sepolia / Amoy / Base Sepolia) | 6JdW8xNvXrghY7acKXzMn2pJoK88g6nnAVU3YNo1YiF8 | 0xcaeb3ef5dcea1557765323379a292fc6b4011831 |
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 name | Function |
|---|---|
pay_and_call | Pay 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_listings | Keyword search over public listings, returning what you need to buy (proxyUrl, price, parameter schema) |
get_active_tokens | Summary of currently valid masked tokens and remaining validity. May be empty because inline payments issue no access token |
get_payment_history | Payment 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_spending | Cumulative 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
| Mode | Use case | Authentication |
|---|---|---|
| stdio | Direct connection from Claude Code / Claude Desktop | None (local process) |
| HTTP stream | Connection from a local HTTP client on the same machine | MCP_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 devnetIf the Solana CLI is not installed, use
brew install solanaor see https://docs.solana.com/cli/install-solana-cli-tools.
3. Environment Variables
Required
| Variable name | Description | Example |
|---|---|---|
NONAGON_PRIVATE_KEY | Private key (Base58) of the Solana wallet used for payments. USDC / SOL are paid from this wallet | 5Jh... (64-88 characters) |
Optional
| Variable name | Default | Description |
|---|---|---|
NONAGON_NETWORK | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp | Solana 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_CAP | 0.100 | Ceiling 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_CAP | 100 | Ceiling 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_LEVEL | info | Log 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
> /mcpIf 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 tokensget_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.tsA browser window opens, where you can check the following:
- Tools tab:
pay_and_call/search_listings/get_active_tokens/get_payment_history/get_spendingare 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_NETWORKsetting - 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
- Check that the path in
.claude/settings.jsonis correct - Check that
cwdpoints to the project root - Run
npx tsx apps/nonagon_link/src/mcp/server.tsdirectly and check for errors
HTTP stream mode returns 401
→ Check that the Authorization: Bearer <MCP_AUTH_TOKEN> header is correct.