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.2.1"],
"env": {
"NONAGON_PRIVATE_KEY": "<Base58 secret key of your payment wallet>",
"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 -- npx -y --ignore-scripts @nonagon-link/mcp@0.2.1⚠️ Security note: always pin an exact version (e.g. @nonagon-link/mcp@0.2.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.
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 (maxPaymentUsdc cap required). Returns paymentInfo with amount, tx signature and timestamp |
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 wallet (latest N, filterable by status). Includes inline payments |
get_spending | Cumulative spending for this server's wallet by period (day / week / month / all), with a per-currency breakdown |
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_LINK_BASE_URL | (unset) | Required when using the search_listings tool. Base URL of the Nonagon Link API (e.g. https://api.nnglink.ai). If unset, the search tool returns 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 |
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",
"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> \
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_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 10)Response Structure
{
"success": true,
"data": { ... },
"paymentInfo": {
"amountUsdc": "0.01",
"txSignature": "5xKt..."
}
}If paymentInfo is null, the response was returned without an additional payment, either because the API is free or because a reusable token was used.
On Error
{
"success": false,
"error": "PAYMENT_REQUIRED",
"details": "Insufficient USDC balance"
}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 NONAGON_PRIVATE_KEY, NONAGON_NETWORK, NONAGON_LINK_BASE_URL, MCP_AUTH_TOKEN, and LOG_LEVEL.
pay_and_call returns SSRF blocked
{ "success": false, "error": "URL_PRIVATE_IP" }→ 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.