agent402-mcp 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/index.js +45 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,8 +47,13 @@ Claude Code: `claude mcp add agent402 -- npx -y agent402-mcp`
|
|
|
47
47
|
| `AGENT_KEY` | _(unset)_ | Hex private key of a wallet funded with USDC on Base. Unset = proof-of-work mode. |
|
|
48
48
|
| `AGENT402_URL` | `https://agent402.tools` | Target service (point at your own deployment). |
|
|
49
49
|
| `AGENT402_TOOLS` | curated set | Comma-separated slugs to expose as first-class tools. |
|
|
50
|
+
| `AGENT402_MAX_PER_CALL` | unlimited | Refuse any single call priced above this many USD (e.g. `0.01`). |
|
|
51
|
+
| `AGENT402_BUDGET` | unlimited | Hard cap on total USDC spent per session (e.g. `1.00`). |
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
Spend controls are enforced **before a payment is signed** — a runaway model is
|
|
54
|
+
refused, not billed. `payment_info` reports the caps, what's been spent, and
|
|
55
|
+
what remains. Use a dedicated low-value wallet for `AGENT_KEY`, funded only
|
|
56
|
+
with what you intend to spend — calls cost $0.001–$0.02 each.
|
|
52
57
|
|
|
53
58
|
## Test
|
|
54
59
|
|
package/index.js
CHANGED
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
// search_tools + call_tool.
|
|
12
12
|
//
|
|
13
13
|
// Config (env):
|
|
14
|
-
// AGENT402_URL
|
|
15
|
-
// AGENT_KEY
|
|
16
|
-
// AGENT402_TOOLS
|
|
14
|
+
// AGENT402_URL target service (default https://agent402.tools)
|
|
15
|
+
// AGENT_KEY hex private key of a funded wallet (USDC on Base) — optional
|
|
16
|
+
// AGENT402_TOOLS comma-separated slugs to expose first-class (overrides default)
|
|
17
|
+
// AGENT402_MAX_PER_CALL refuse any single call priced above this many USD (e.g. 0.01)
|
|
18
|
+
// AGENT402_BUDGET hard cap on total USDC spent this session (e.g. 1.00)
|
|
17
19
|
import { createHash } from "node:crypto";
|
|
18
20
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
19
21
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -21,11 +23,18 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
|
|
|
21
23
|
|
|
22
24
|
const BASE = (process.env.AGENT402_URL || "https://agent402.tools").replace(/\/$/, "");
|
|
23
25
|
const AGENT_KEY = process.env.AGENT_KEY || "";
|
|
24
|
-
const VERSION = "0.
|
|
26
|
+
const VERSION = "0.2.0";
|
|
27
|
+
|
|
28
|
+
// Spend controls — enforced BEFORE a payment is ever signed, so a confused or
|
|
29
|
+
// runaway model cannot drain the wallet. Unset = unlimited (back-compat).
|
|
30
|
+
const num = (v) => (v !== undefined && v !== "" && Number.isFinite(Number(v)) ? Number(v) : undefined);
|
|
31
|
+
const MAX_PER_CALL = num(process.env.AGENT402_MAX_PER_CALL) ?? Infinity;
|
|
32
|
+
const BUDGET = num(process.env.AGENT402_BUDGET) ?? Infinity;
|
|
33
|
+
let spentUsd = 0;
|
|
25
34
|
|
|
26
35
|
const DEFAULT_CURATED = [
|
|
27
|
-
// the tools agents can't replicate locally:
|
|
28
|
-
"extract", "render", "screenshot", "pdf", "meta", "dns", "http-check", "tls-cert", "whois",
|
|
36
|
+
// the tools agents can't replicate locally: live search, browser, PDF, shared memory
|
|
37
|
+
"search", "extract", "render", "screenshot", "pdf", "meta", "dns", "http-check", "tls-cert", "whois",
|
|
29
38
|
"memory-write", "memory-read", "memory-remember", "memory-recall",
|
|
30
39
|
// one cheap pure-CPU tool so wallet-less clients see the proof-of-work path work
|
|
31
40
|
"hash",
|
|
@@ -133,8 +142,24 @@ async function callEndpoint(tool, args = {}) {
|
|
|
133
142
|
|
|
134
143
|
let res;
|
|
135
144
|
if (AGENT_KEY) {
|
|
145
|
+
const price = parseFloat(String(tool.price).replace(/[^0-9.]/g, "")) || 0;
|
|
146
|
+
if (price > MAX_PER_CALL) {
|
|
147
|
+
return {
|
|
148
|
+
content: [{ type: "text", text: `Refused without paying: "${tool.slug}" costs ${tool.price}/call, above the AGENT402_MAX_PER_CALL cap of $${MAX_PER_CALL}. Raise the cap on this MCP server to allow it.` }],
|
|
149
|
+
isError: true,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (spentUsd + price > BUDGET) {
|
|
153
|
+
return {
|
|
154
|
+
content: [{ type: "text", text: `Refused without paying: session budget exhausted ($${spentUsd.toFixed(4)} of $${BUDGET} spent; "${tool.slug}" costs ${tool.price}). Restart the MCP server or raise AGENT402_BUDGET.` }],
|
|
155
|
+
isError: true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
136
158
|
const payFetch = await getPayFetch();
|
|
137
159
|
res = await payFetch(url, init);
|
|
160
|
+
// Count spend when the server confirms settlement (payment receipt header),
|
|
161
|
+
// falling back to any 2xx — conservative in the buyer's favor.
|
|
162
|
+
if (res.headers.get("x-payment-response") || res.ok) spentUsd += price;
|
|
138
163
|
} else if (tool.computePayable) {
|
|
139
164
|
// No wallet: pay with compute up front — solving before the call skips the
|
|
140
165
|
// 402 round-trip entirely (challenges are single-use and tool-scoped).
|
|
@@ -263,8 +288,16 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
263
288
|
tools: catalog.size,
|
|
264
289
|
payableWithCompute: computePayable,
|
|
265
290
|
walletOnly: catalog.size - computePayable,
|
|
291
|
+
spendControls: AGENT_KEY
|
|
292
|
+
? {
|
|
293
|
+
maxPerCallUsd: MAX_PER_CALL === Infinity ? "unlimited" : MAX_PER_CALL,
|
|
294
|
+
sessionBudgetUsd: BUDGET === Infinity ? "unlimited" : BUDGET,
|
|
295
|
+
spentThisSessionUsd: Number(spentUsd.toFixed(6)),
|
|
296
|
+
remainingUsd: BUDGET === Infinity ? "unlimited" : Number(Math.max(0, BUDGET - spentUsd).toFixed(6)),
|
|
297
|
+
}
|
|
298
|
+
: "n/a (proof-of-work mode spends CPU, not money)",
|
|
266
299
|
note: AGENT_KEY
|
|
267
|
-
? "Every tool is available; each call is paid in USDC via x402 from the configured wallet."
|
|
300
|
+
? "Every tool is available; each call is paid in USDC via x402 from the configured wallet, within the spend controls above."
|
|
268
301
|
: `No AGENT_KEY configured: ${computePayable} pure-CPU tools are free via proof-of-work; the ${catalog.size - computePayable} network/browser/memory tools need a funded wallet (set AGENT_KEY).`,
|
|
269
302
|
}, null, 2),
|
|
270
303
|
}],
|
|
@@ -291,6 +324,10 @@ const requested = (process.env.AGENT402_TOOLS || DEFAULT_CURATED.join(","))
|
|
|
291
324
|
.split(",").map((s) => s.trim()).filter(Boolean);
|
|
292
325
|
curated = requested.map((slug) => catalog.get(slug)).filter(Boolean);
|
|
293
326
|
log(`catalog: ${catalog.size} tools from ${BASE}; ${curated.length} first-class, rest via search_tools/call_tool`);
|
|
294
|
-
log(
|
|
327
|
+
log(
|
|
328
|
+
AGENT_KEY
|
|
329
|
+
? `payment: USDC via x402 (wallet configured; max/call ${MAX_PER_CALL === Infinity ? "unlimited" : `$${MAX_PER_CALL}`}, budget ${BUDGET === Infinity ? "unlimited" : `$${BUDGET}`})`
|
|
330
|
+
: "payment: proof-of-work on eligible tools (no AGENT_KEY)"
|
|
331
|
+
);
|
|
295
332
|
|
|
296
333
|
await server.connect(new StdioServerTransport());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent402-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"author": "Mikey Petrillo (https://github.com/MikeyPetrillo)",
|
|
5
5
|
"homepage": "https://agent402.tools",
|
|
6
6
|
"description": "MCP server for Agent402 (agent402.tools) — 1000+ pay-per-call web tools for AI agents. Pays per call in USDC via the x402 protocol, or with compute (proof-of-work) when no wallet is configured.",
|