AI 에이전트에 x402 결제 실행을 추가합니다. MCP 도구를 통해 작업별 예산, 지출 통제, 비수탁형 지갑을 제공합니다. 에이전트가 API, 서비스, 다른 에이전트에 비용을 지불해야 할 때 사용합니다.
내장 지출 통제를 사용해 AI 에이전트가 자율적으로 결제할 수 있게 합니다. x402 HTTP 결제 프로토콜과 MCP 도구를 사용하므로, 에이전트가 외부 서비스, API, 다른 에이전트에 비용을 지불하면서도 수탁 위험을 피할 수 있습니다.
에이전트가 API 호출 비용을 결제해야 하거나, 서비스를 구매해야 하거나, 다른 에이전트와 정산해야 하거나, 작업별 지출 한도를 강제해야 하거나, 비수탁형 지갑을 관리해야 할 때 사용합니다. cost-aware-llm-pipeline, security-review와 함께 쓰기 좋습니다.
x402는 HTTP 402(Payment Required)를 기계 간 협상 가능한 흐름으로 확장합니다. 서버가 402를 반환하면 에이전트의 결제 도구가 자동으로 가격을 협상하고, 예산을 확인하고, 거래에 서명한 뒤 재시도합니다. 사람 개입은 필요하지 않습니다.
모든 결제 도구 호출은 SpendingPolicy를 강제합니다.
에이전트는 ERC-4337 스마트 계정을 통해 자신의 키를 직접 보유합니다. 오케스트레이터가 위임 전에 정책을 설정하고, 에이전트는 그 범위 안에서만 지출할 수 있습니다. 공동 자금도 없고 수탁 위험도 없습니다.
결제 레이어는 어떤 Claude Code 또는 에이전트 하니스 구성에도 끼워 넣을 수 있는 표준 MCP 도구를 노출합니다.
보안 참고: 패키지 버전은 항상 고정하세요. 이 도구는 개인 키를 다루므로, 버전 고정 없는
npx설치는 공급망 위험을 만듭니다.
{
"mcpServers": {
"agentpay": {
"command": "npx",
"args": ["[email protected]"]
}
}
}
| Tool | Purpose |
|---|---|
get_balance | 에이전트 지갑 잔액 확인 |
send_payment | 주소 또는 ENS로 결제 전송 |
check_spending | 남은 예산 조회 |
list_transactions | 전체 결제 감사 기록 조회 |
참고: 지출 정책은 에이전트 자신이 아니라 오케스트레이터가 위임 전에 설정합니다. 이렇게 해야 에이전트가 자신의 지출 한도를 스스로 늘리지 못합니다. 정책은 오케스트레이션 레이어나 작업 전 훅에서
set_policy로 설정하고, 에이전트 호출 가능 도구로 노출하지 마세요.
agentpay MCP 서버를 호출하는 오케스트레이터를 만들 때는, 유료 도구 호출을 보내기 전에 예산을 강제합니다.
사전 조건: MCP 설정을 추가하기 전에 패키지를 먼저 설치하세요.
-y없는npx는 비대화형 환경에서 확인을 요구해 서버를 멈추게 할 수 있습니다.npm install -g [email protected]
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
async function main() {
// 1. Validate credentials before constructing the transport.
// A missing key must fail immediately — never let the subprocess start without auth.
const walletKey = process.env.WALLET_PRIVATE_KEY;
if (!walletKey) {
throw new Error("WALLET_PRIVATE_KEY is not set — refusing to start payment server");
}
// Connect to the agentpay MCP server via stdio transport.
// Whitelist only the env vars the server needs — never forward all of process.env
// to a third-party subprocess that manages private keys.
const transport = new StdioClientTransport({
command: "npx",
args: ["[email protected]"],
env: {
PATH: process.env.PATH ?? "",
NODE_ENV: process.env.NODE_ENV ?? "production",
WALLET_PRIVATE_KEY: walletKey,
},
});
const agentpay = new Client({ name: "orchestrator", version: "1.0.0" });
await agentpay.connect(transport);
// 2. Set spending policy before delegating to the agent.
// Always verify success — a silent failure means no controls are active.
const policyResult = await agentpay.callTool({
name: "set_policy",
arguments: {
per_task_budget: 0.50,
per_session_budget: 5.00,
allowlisted_recipients: ["api.example.com"],
},
});
if (policyResult.isError) {
throw new Error(
`Failed to set spending policy — do not delegate: ${JSON.stringify(policyResult.content)}`
);
}
// 3. Use preToolCheck before any paid action
await preToolCheck(agentpay, 0.01);
}
// Pre-tool hook: fail-closed budget enforcement with four distinct error paths.
async function preToolCheck(agentpay: Client, apiCost: number): Promise<void> {
// Path 1: Reject invalid input (NaN/Infinity bypass the < comparison)
if (!Number.isFinite(apiCost) || apiCost < 0) {
throw new Error(`Invalid apiCost: ${apiCost} — action blocked`);
}
// Path 2: Transport/connectivity failure
let result;
try {
result = await agentpay.callTool({ name: "check_spending" });
} catch (err) {
throw new Error(`Payment service unreachable — action blocked: ${err}`);
}
// Path 3: Tool returned an error (e.g., auth failure, wallet not initialised)
if (result.isError) {
throw new Error(
`check_spending failed — action blocked: ${JSON.stringify(result.content)}`
);
}
// Path 4: Parse and validate the response shape
let remaining: number;
try {
const parsed = JSON.parse(
(result.content as Array<{ text: string }>)[0].text
);
if (!Number.isFinite(parsed?.remaining)) {
throw new TypeError("missing or non-finite 'remaining' field");
}
remaining = parsed.remaining;
} catch (err) {
throw new Error(
`check_spending returned unexpected format — action blocked: ${err}`
);
}
// Path 5: Budget exceeded
if (remaining < apiCost) {
throw new Error(
`Budget exceeded: need $${apiCost} but only $${remaining} remaining`
);
}
}
main().catch((err) => {
console.error(err);
process.exitCode = 1;
});
SpendingPolicy를 붙입니다. 무제한 지출 권한을 주지 마세요.[email protected]. 운영 반영 전에 패키지 무결성을 확인하세요.list_transactions를 사용해 무엇에 왜 비용이 나갔는지 기록합니다.security-review와 함께 사용: 결제 도구는 고권한 기능입니다. 셸 접근과 같은 수준의 검토가 필요합니다.agentwallet-sdk