@a3stack/data

Data / MCP

MCP server and client with built-in identity verification and payment gating. Expose tools only verified, paying agents can use.

What is MCP?

MCP (Model Context Protocol) is Anthropic's open protocol for AI tool calls. It lets any AI client call functions exposed by any MCP server over HTTP.

@a3stack/data wraps the official MCP SDK and adds:

  • Identity layer — expose your ERC-8004 registration as an agent://identity resource
  • Payment gating — require x402 payment before serving tool calls
  • Auto-resolving client — connect by global ID instead of hardcoded URL
  • Auto-payment — client pays x402 automatically, no manual flow

MCP Server

createAgentMcpServer()

typescript
import { createAgentMcpServer } from "@a3stack/data";
import { z } from "zod";

const server = createAgentMcpServer({
  name: "MarketDataAgent",
  version: "1.0.0",
  port: 3000,          // default: 3000

  // Identity: expose your ERC-8004 registration as agent://identity resource
  identity: {
    chainId: 8453,     // Base
    agentId: 42,       // your registered agent ID
  },

  // Payment gating: require USDC per tool call
  payment: {
    payTo: "0xYourWallet...",
    amount: "1000",            // 0.001 USDC
    asset: USDC_BASE,
    network: "eip155:8453",
    description: "MarketDataAgent: 0.001 USDC per call",
    freeTools: ["ping"],       // these tools are always free
  },
});
typescript
import { z } from "zod";

// Register tools (MCP API passthrough)
server.tool(
  "get-price",
  "Get the current price of a cryptocurrency",
  { symbol: z.string().describe("Token symbol, e.g. ETH, BTC") },
  async ({ symbol }) => ({
    content: [{ type: "text", text: JSON.stringify({ symbol, price: 2800 }) }],
  })
);

server.tool(
  "analyze-token",
  "Get detailed analysis for a token",
  {
    symbol: z.string(),
    depth: z.enum(["basic", "detailed"]).default("basic"),
  },
  async ({ symbol, depth }) => ({
    content: [{ type: "text", text: `Analysis for ${symbol} (${depth})` }],
  })
);

// Start server
const { url } = await server.listen();
console.log(`Serving at ${url}`);
// → http://localhost:3000/mcp

server.listen()

Returns { url, app, transport }. The server runs athttp://localhost:{port}/mcp by default.

Free tools

Some tools can bypass the payment gate (e.g. health checks, ping):

typescript
// Free tools are always served without payment check
const server = createAgentMcpServer({
  name: "MyAgent",
  payment: {
    payTo: "0x...",
    amount: "10000",
    freeTools: ["ping", "health", "info"],  // these bypass payment gate
  },
});

// "ping" is automatically registered as a free tool
// It returns: { status: "ok", requiresPayment: true, freeTools: [...] }

Identity resource

typescript
// When identity config is set, the server auto-exposes:
// Resource: agent://identity
//
// Clients can read it with:
const identity = await client.getAgentIdentity();
// Returns the full AgentRegistration from ERC-8004

// The resource URI is: "agent://identity"
// content-type: application/json

MCP Client

createAgentMcpClient()

Connect by global ID (recommended — auto-resolves, auto-pays):

typescript
import { createAgentMcpClient } from "@a3stack/data";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.PRIVATE_KEY);

// Connect by ERC-8004 global ID (recommended)
// Auto-resolves endpoint, verifies identity, auto-pays x402
const client = await createAgentMcpClient({
  agentId: "eip155:8453:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432#2376",
  payer: {
    account,
    maxAmount: "100000", // max 0.10 USDC auto-pay per session
  },
});

Connect by URL directly:

typescript
// Connect by direct URL (no identity check, no auto-payment)
const client = await createAgentMcpClient({
  url: "https://mcp.someagent.ai/mcp",
});

Client methods

typescript
// List available tools
const tools = await client.listTools();
tools.forEach(t => console.log(`- ${t.name}: ${t.description}`));

// Call a tool (auto-pays if required)
const result = await client.callTool("get-price", { symbol: "ETH" });
const data = JSON.parse(result.content[0].text);
console.log(data.price); // 2800

// Read the agent's identity resource
const identity = await client.getAgentIdentity();
console.log(identity?.name);      // "MarketDataAgent"
console.log(identity?.services);  // [{ name: "MCP", ... }]

// Close when done
await client.close();

probeAgent()

Discover what an agent offers before connecting. No wallet needed — read-only.

typescript
import { probeAgent } from "@a3stack/core";

// Discover an agent without connecting (no wallet needed)
const info = await probeAgent("eip155:8453:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432#2376");

console.log(info.verified);           // true — on-chain verified
console.log(info.owner);              // "0x1be93C..."
console.log(info.endpoints.mcp);      // "https://mcp.arcabot.ai/mcp"
console.log(info.endpoints.a2a);      // null (not configured)
console.log(info.acceptsPayment);     // true
console.log(info.services);           // [{ name: "MCP", endpoint: "...", version: "..." }]
console.log(info.registrations);      // cross-chain IDs

Server config reference

OptionTypeDescription
namestringMCP server name
versionstringServer version
portnumberHTTP port (default: 3000)
identity.chainIdnumberChain for ERC-8004 lookup
identity.agentIdnumberYour registered agent token ID
payment.payTostringWallet receiving USDC
payment.amountstringUSDC amount in base units
payment.networkstringCAIP-2 network (default: eip155:8453)
payment.freeToolsstring[]Tool names exempt from payment

Client config reference

OptionTypeDescription
agentIdstringERC-8004 global ID (auto-resolves URL)
urlstringDirect MCP endpoint URL
payer.accountAccountviem Account for signing payments
payer.maxAmountstringMax USDC to auto-pay per session