agent402-mcp 0.13.10 → 0.13.12
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 +2 -2
- package/index.js +29 -28
- package/networks.js +5 -5
- package/output-schemas.js +15 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
MCP server for [Agent402](https://agent402.tools), the applied layer of [Agentic Finance](https://agent402.tools/agentic-finance) - a catalog of **500+ pay-per-call web tools and curated multi-tool skill packs** for AI agents, paid per call in USDC via the [x402 protocol](https://www.x402.org), **with compute (proof-of-work)** when no wallet is configured, or through a prepaid credits key already issued. (The hosted API is dual-stack: it also accepts [MPP](https://agent402.tools/what-is-mpp) clients on the same 402, settling on Base/Celo or natively on Tempo; this package pays via x402 or credits.) Built by [Havok Holdings LLC](https://github.com/MikeyPetrillo/Agent402).
|
|
4
4
|
|
|
5
|
-
Your agent gets browser rendering, screenshots, PDF text extraction, URL→markdown, live web search **+ web answers with citations**, live **financial/crypto/macro data** (US equity quotes and daily bars from
|
|
5
|
+
Your agent gets browser rendering, screenshots, PDF text extraction, URL→markdown, live web search **+ web answers with citations**, live **financial/crypto/macro data** (US equity quotes and daily bars from a licensed feed, CoinGecko, FRED, ECB FX, World Bank, yield curve), **SEC EDGAR filings** (10-K/10-Q text, XBRL, insider, 13F, IPO calendar), **deterministic stats & forecasting** (Pearson correlation, OLS, Holt-Winters), **compression** (gzip/brotli), DNS/TLS/WHOIS + email-deliverability checks, wallet-keyed shared memory, and the pure-CPU utilities - plus **skill packs** like `security-audit`, `trend-analysis`, `structured-scrape`, `decode-blob`, and `forecasting-bake-off` callable as MCP prompts, and the **report products** (deep research, company dossier, 13F fund report, domain audit, token risk, FDA recall, insider flow, market brief) as ordinary catalog slugs. Payment handled invisibly underneath the MCP calls. No signup, no API key.
|
|
6
6
|
|
|
7
7
|
## Quick start
|
|
8
8
|
|
|
@@ -90,7 +90,7 @@ so set `AGENT402_MAX_PER_CALL` if you want a hard per-call ceiling.
|
|
|
90
90
|
- `payment.info` tells the model which mode it's in and what a wallet or a credits key would unlock.
|
|
91
91
|
- `server.describe` returns orientation (flagship-first tools, install one-liners, free vs paid, discovery URLs). Call it first.
|
|
92
92
|
- `sellers.list` returns the live leaderboards - `wire: "x402"` (default) ranks x402 sellers settling the most USDC (primarily on Base) in the last ~24h from on-chain transfers; `wire: "mpp"` ranks live-verified MPP sellers by inbound USDC.e transfers on Tempo. Free to call (no payment, no proof-of-work). Useful for agents discovering the wider x402 / MPP economy beyond this single service's catalog.
|
|
93
|
-
- `
|
|
93
|
+
- `router.execute` (formerly `route_and_execute`, still accepted) reaches tools **outside** this catalog in one call: give it a plain-language `task` and Agent402 resolves a proven external x402 seller (one with real on-chain settled volume), buys the result from that seller and sells it to you, marked `untrustedContent`. Wallet-only. Flat price, cheapest covering tier chosen from `maxUsd`:
|
|
94
94
|
|
|
95
95
|
| Underlying seller price | Fee | Route |
|
|
96
96
|
| --- | --- | --- |
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Agent402 MCP server
|
|
2
|
+
// Agent402 MCP server, exposes the agent402.tools catalog (1000+ pay-per-call
|
|
3
3
|
// web tools) to any MCP client (Claude, ChatGPT, custom agents) and settles
|
|
4
4
|
// payment underneath, so the model never sees the 402 dance:
|
|
5
5
|
//
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
//
|
|
14
14
|
// Config (env):
|
|
15
15
|
// AGENT402_URL target service (default https://agent402.tools)
|
|
16
|
-
// AGENT_KEY hex private key of a funded EVM wallet (USDC on Base/Polygon/Arbitrum)
|
|
17
|
-
// SOLANA_AGENT_KEY base58 (or JSON byte-array) secret key of a funded Solana wallet (USDC on Solana)
|
|
16
|
+
// AGENT_KEY hex private key of a funded EVM wallet (USDC on Base/Polygon/Arbitrum), optional
|
|
17
|
+
// SOLANA_AGENT_KEY base58 (or JSON byte-array) secret key of a funded Solana wallet (USDC on Solana), optional
|
|
18
18
|
// AGENT402_TOOLS comma-separated slugs to expose first-class (overrides default)
|
|
19
19
|
// AGENT402_MAX_PER_CALL refuse any single call priced above this many USD (e.g. 0.01)
|
|
20
20
|
// AGENT402_CREDITS_KEY a prepaid credits key (a402_...) already issued (new credits are not on
|
|
21
21
|
// sale); pays per successful call with no wallet
|
|
22
22
|
// AGENT402_BUDGET hard cap on total USDC spent this session (e.g. 1.00)
|
|
23
23
|
// AGENT402_NETWORKS restrict + order the chains to pay on (e.g. "robinhood" for USDG on
|
|
24
|
-
// Robinhood Chain, "base,solana", or a raw CAIP-2 like eip155:4663)
|
|
24
|
+
// Robinhood Chain, "base,solana", or a raw CAIP-2 like eip155:4663), optional
|
|
25
25
|
import { createHash } from "node:crypto";
|
|
26
26
|
import { readFileSync } from "node:fs";
|
|
27
27
|
import { parseNetworkPrefs, withNetworkPreference } from "./networks.js";
|
|
@@ -58,12 +58,12 @@ const HAS_WALLET = Boolean(AGENT_KEY || SOLANA_AGENT_KEY);
|
|
|
58
58
|
// list price only on a successful (200) call and returns X-Credits-Balance.
|
|
59
59
|
const CREDITS_KEY = (process.env.AGENT402_CREDITS_KEY || "").trim();
|
|
60
60
|
const HAS_CREDITS = /^a402_[A-Za-z0-9_-]{32,64}$/.test(CREDITS_KEY);
|
|
61
|
-
// Version comes from package.json
|
|
61
|
+
// Version comes from package.json, the serverInfo self-report drifted from
|
|
62
62
|
// the published version once (0.11.5 vs 0.12.1) because this was a hardcoded
|
|
63
63
|
// string bumped by hand. Reading the manifest makes drift impossible.
|
|
64
64
|
const VERSION = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8")).version;
|
|
65
65
|
|
|
66
|
-
// Spend controls
|
|
66
|
+
// Spend controls, enforced BEFORE a payment is ever signed, so a confused or
|
|
67
67
|
// runaway model cannot drain the wallet. Unset = unlimited (back-compat).
|
|
68
68
|
const num = (v) => (v !== undefined && v !== "" && Number.isFinite(Number(v)) ? Number(v) : undefined);
|
|
69
69
|
const MAX_PER_CALL = num(process.env.AGENT402_MAX_PER_CALL) ?? Infinity;
|
|
@@ -71,7 +71,7 @@ const BUDGET = num(process.env.AGENT402_BUDGET) ?? Infinity;
|
|
|
71
71
|
let spentUsd = 0;
|
|
72
72
|
|
|
73
73
|
const DEFAULT_CURATED = [
|
|
74
|
-
// Flagship demand set
|
|
74
|
+
// Flagship demand set, keep aligned with src/mcp-flagship.js FLAGSHIP_SLUGS.
|
|
75
75
|
// Search/answer is the front door; long tail stays behind catalog.search/catalog.call.
|
|
76
76
|
"search", "answer", "search-news", "render",
|
|
77
77
|
"stock-quote", "transcribe", "memory-read", "memory-write",
|
|
@@ -79,13 +79,13 @@ const DEFAULT_CURATED = [
|
|
|
79
79
|
"decide", "decide-execute",
|
|
80
80
|
];
|
|
81
81
|
|
|
82
|
-
// stdout is the MCP protocol channel
|
|
82
|
+
// stdout is the MCP protocol channel, all logging goes to stderr.
|
|
83
83
|
const log = (...a) => console.error("[agent402-mcp]", ...a);
|
|
84
84
|
|
|
85
85
|
// ---------------------------------------------------------------------------
|
|
86
86
|
// Catalog: built from the service's own machine-readable surfaces.
|
|
87
87
|
const catalog = new Map(); // slug -> { slug, method, path, price, description, category, computePayable, inputSchema }
|
|
88
|
-
// Skill packs
|
|
88
|
+
// Skill packs, curated multi-tool workflows, fetched at startup from the
|
|
89
89
|
// hosted service so the npm package picks up new packs without a republish.
|
|
90
90
|
// Empty if the discovery fetch fails (older services or transient errors);
|
|
91
91
|
// prompts/list will just return an empty array in that case.
|
|
@@ -162,7 +162,7 @@ function getPayFetch() {
|
|
|
162
162
|
return payFetchPromise;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
// @solana/kit KeyPairSigner from SOLANA_AGENT_KEY
|
|
165
|
+
// @solana/kit KeyPairSigner from SOLANA_AGENT_KEY, base58 64-byte secret key
|
|
166
166
|
// (Phantom / solana-keygen export) or a JSON byte array ([12,34,…]).
|
|
167
167
|
let solanaSignerPromise;
|
|
168
168
|
function solanaSigner() {
|
|
@@ -197,7 +197,7 @@ function walletRequiredText(tool) {
|
|
|
197
197
|
`"${tool.slug}" costs ${tool.price}/call and requires a USDC wallet (it is not eligible for the proof-of-work tier).`,
|
|
198
198
|
`To enable it: set AGENT_KEY on this MCP server to the hex private key of an EVM wallet funded with USDC`,
|
|
199
199
|
`on Base (or Polygon/Arbitrum), and/or SOLANA_AGENT_KEY to the base58 secret key of a Solana wallet funded`,
|
|
200
|
-
`with USDC on Solana. Payment is per call via the x402 protocol
|
|
200
|
+
`with USDC on Solana. Payment is per call via the x402 protocol: no signup or API key.`,
|
|
201
201
|
`Already hold a prepaid credits key? Set AGENT402_CREDITS_KEY to the a402_ key and it pays per successful call (new credits are not on sale).`,
|
|
202
202
|
`Pricing and details: ${BASE}/tools/${tool.slug}`,
|
|
203
203
|
].join(" ");
|
|
@@ -340,10 +340,10 @@ async function callEndpoint(tool, args = {}) {
|
|
|
340
340
|
const payFetch = await getPayFetch();
|
|
341
341
|
res = await payFetch(url, init);
|
|
342
342
|
// Count spend when the server confirms settlement (payment receipt header),
|
|
343
|
-
// falling back to any 2xx
|
|
343
|
+
// falling back to any 2xx, conservative in the buyer's favor.
|
|
344
344
|
if (res.headers.get("payment-response") || res.headers.get("x-payment-response") || res.ok) spentUsd += price;
|
|
345
345
|
} else if (tool.computePayable) {
|
|
346
|
-
// No wallet: pay with compute up front
|
|
346
|
+
// No wallet: pay with compute up front, solving before the call skips the
|
|
347
347
|
// 402 round-trip entirely (challenges are single-use and tool-scoped).
|
|
348
348
|
const challenge = await (await fetch(`${BASE}/api/pow/challenge?slug=${encodeURIComponent(tool.slug)}`)).json();
|
|
349
349
|
const nonce = solvePow(challenge);
|
|
@@ -382,7 +382,7 @@ function applyFrontDoorTerms(terms, q) {
|
|
|
382
382
|
if (!terms.includes("websearch") && (
|
|
383
383
|
/\b(search\s+the\s+web|web\s+search|search\s+online|google\s+|bing\s+|look\s+up\s+online|find\s+on\s+the\s+web)\b/.test(ql)
|
|
384
384
|
)) {
|
|
385
|
-
// pricing descriptions carry "web search" / "Live web search"
|
|
385
|
+
// pricing descriptions carry "web search" / "Live web search", boost those.
|
|
386
386
|
terms.push("web", "search");
|
|
387
387
|
}
|
|
388
388
|
if (!terms.includes("answer") && (
|
|
@@ -427,8 +427,8 @@ function searchTools(query, limit = 10) {
|
|
|
427
427
|
|
|
428
428
|
// Rank the curated multi-tool skill packs against the same query, so a single
|
|
429
429
|
// search_tools call also tells the agent "this looks like a `security-audit`
|
|
430
|
-
// or `email-deliverability` job
|
|
431
|
-
// Weighted slug/title/tagline/useCase/toolSlugs match
|
|
430
|
+
// or `email-deliverability` job, fetch the whole template via prompts/get".
|
|
431
|
+
// Weighted slug/title/tagline/useCase/toolSlugs match, same shape as the
|
|
432
432
|
// hosted /api/find ranking, kept inline here so the stdio package stays
|
|
433
433
|
// dependency-free. Returns [] when no pack scores above the noise floor.
|
|
434
434
|
function rankWorkflows(query, k = 2) {
|
|
@@ -482,7 +482,7 @@ const server = new Server(
|
|
|
482
482
|
{ capabilities: { tools: {}, prompts: {} }, instructions: INIT_INSTRUCTIONS },
|
|
483
483
|
);
|
|
484
484
|
|
|
485
|
-
// Skill packs are exposed as MCP prompts
|
|
485
|
+
// Skill packs are exposed as MCP prompts, discoverable in slash menus on any
|
|
486
486
|
// MCP-aware client. The list is fetched once at boot in loadCatalog(); the
|
|
487
487
|
// per-prompt rendering is delegated to the hosted service so the npm package
|
|
488
488
|
// stays thin and prompt text stays canonical with the website at /skills.
|
|
@@ -622,7 +622,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
622
622
|
outputSchema: META_OUTPUT_SCHEMAS["sellers.list"],
|
|
623
623
|
},
|
|
624
624
|
{
|
|
625
|
-
|
|
625
|
+
// Dotted like every other listed tool; route_and_execute stays a CallTool alias.
|
|
626
|
+
name: "router.execute",
|
|
626
627
|
title: "Route and execute an external x402 tool",
|
|
627
628
|
annotations: { title: "Route and execute an external x402 tool", ...OPEN },
|
|
628
629
|
description:
|
|
@@ -636,7 +637,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
636
637
|
},
|
|
637
638
|
required: ["task"],
|
|
638
639
|
},
|
|
639
|
-
outputSchema: META_OUTPUT_SCHEMAS.
|
|
640
|
+
outputSchema: META_OUTPUT_SCHEMAS["router.execute"],
|
|
640
641
|
}
|
|
641
642
|
);
|
|
642
643
|
return { tools };
|
|
@@ -815,7 +816,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
815
816
|
const include = args.include === "all" ? "all" : "external";
|
|
816
817
|
// /api/leaderboard is free + unpaywalled, so this stays free regardless
|
|
817
818
|
// of payment mode. Honor its query params verbatim so the surface is a
|
|
818
|
-
// thin pass-through
|
|
819
|
+
// thin pass-through, single source of truth for ranking + filtering.
|
|
819
820
|
const url = new URL(`${BASE}/api/leaderboard`);
|
|
820
821
|
url.searchParams.set("top", String(limit));
|
|
821
822
|
url.searchParams.set("sort", sort);
|
|
@@ -825,7 +826,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
825
826
|
return { content: [{ type: "text", text: `Failed to fetch leaderboard from ${BASE}: HTTP ${res.status}` }], isError: true };
|
|
826
827
|
}
|
|
827
828
|
const snap = await res.json();
|
|
828
|
-
// Trim to the same compact row shape the hosted MCP connector returns
|
|
829
|
+
// Trim to the same compact row shape the hosted MCP connector returns,
|
|
829
830
|
// cross-surface agents see one mental model. Full row (origins,
|
|
830
831
|
// endpoints, scan metadata) stays accessible at /api/leaderboard.
|
|
831
832
|
// Same untrusted-content marking as the hosted connector: a seller's
|
|
@@ -850,13 +851,13 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
850
851
|
include: snap.include || include,
|
|
851
852
|
totalSellers: snap.totalSellers ?? (snap.leaderboard || []).length,
|
|
852
853
|
results: rows,
|
|
853
|
-
...(snap.warming || snap.scanSkipped ? { note: "Cache is warming
|
|
854
|
+
...(snap.warming || snap.scanSkipped ? { note: "Cache is warming: results may be partial. Retry in ~60s." } : {}),
|
|
854
855
|
source: `${BASE}/api/leaderboard`,
|
|
855
856
|
});
|
|
856
857
|
}
|
|
857
|
-
if (name === "
|
|
858
|
+
if (name === "router.execute") {
|
|
858
859
|
const task = String(args.task ?? "").trim();
|
|
859
|
-
if (!task) return { content: [{ type: "text", text: "
|
|
860
|
+
if (!task) return { content: [{ type: "text", text: "router.execute requires a 'task' (plain-language description of what you need)." }], isError: true };
|
|
860
861
|
const maxUsd = Number(args.maxUsd) > 0 ? Number(args.maxUsd) : 0.005;
|
|
861
862
|
// Pick the CHEAPEST tier whose underlying cap covers maxUsd. The ladder
|
|
862
863
|
// has three rungs; skipping the middle one billed 11x for a mid-priced
|
|
@@ -866,13 +867,13 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
866
867
|
: maxUsd > 0.005 ? "route-execute-plus"
|
|
867
868
|
: "route-execute";
|
|
868
869
|
const tool = catalog.get(slug);
|
|
869
|
-
if (!tool) return { content: [{ type: "text", text: `External routing (${slug}) is not in the catalog from ${BASE} yet
|
|
870
|
+
if (!tool) return { content: [{ type: "text", text: `External routing (${slug}) is not in the catalog from ${BASE} yet. It may be rolling out; retry shortly.` }], isError: true };
|
|
870
871
|
// Body matches the route-execute input schema; include:"external" is what
|
|
871
872
|
// pays an OUTSIDE seller (vs this host's catalog). Paid via callEndpoint.
|
|
872
873
|
return await callEndpoint(tool, { task, include: "external", maxUsd, ...(args.params && typeof args.params === "object" ? { params: args.params } : {}) });
|
|
873
874
|
}
|
|
874
875
|
// Curated tools are exposed verb_noun for tools/list consistency, but the
|
|
875
|
-
// real slug is kebab
|
|
876
|
+
// real slug is kebab, accept exposed name, plain snake, or raw slug.
|
|
876
877
|
const mcpToSlug = new Map(Object.entries(FLAGSHIP_MCP_NAMES).map(([slug, mcp]) => [mcp, slug]));
|
|
877
878
|
const wanted = name === "catalog.call"
|
|
878
879
|
? String(args.slug ?? "")
|
|
@@ -902,11 +903,11 @@ try {
|
|
|
902
903
|
pricingInfo = await loadCatalog();
|
|
903
904
|
} catch (err) {
|
|
904
905
|
// Don't hard-exit: starting with an empty catalog still lets the server
|
|
905
|
-
// connect and answer introspection (tools/list)
|
|
906
|
+
// connect and answer introspection (tools/list), required to pass directory
|
|
906
907
|
// health checks (e.g. Glama) and more resilient if the catalog endpoint is
|
|
907
908
|
// briefly unreachable. catalog.search/catalog.call just return nothing until the
|
|
908
909
|
// catalog is reachable again.
|
|
909
|
-
log(`Could not load the catalog from ${BASE}: ${err.message}
|
|
910
|
+
log(`Could not load the catalog from ${BASE}: ${err.message}: starting with an empty catalog`);
|
|
910
911
|
}
|
|
911
912
|
const requested = (process.env.AGENT402_TOOLS || DEFAULT_CURATED.join(","))
|
|
912
913
|
.split(",").map((s) => s.trim()).filter(Boolean);
|
package/networks.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// Network preference for the x402 buyer
|
|
1
|
+
// Network preference for the x402 buyer, pure helpers, no side effects.
|
|
2
2
|
//
|
|
3
3
|
// AGENT402_NETWORKS (comma-separated, e.g. "robinhood" or "base,solana" or a
|
|
4
4
|
// raw CAIP-2 like "eip155:4663") restricts AND orders which payment options
|
|
5
5
|
// the buyer will consider from a seller's 402 `accepts`. Without it the
|
|
6
|
-
// underlying @x402 client picks whichever registered scheme matches first
|
|
6
|
+
// underlying @x402 client picks whichever registered scheme matches first,
|
|
7
7
|
// which on a multi-chain seller is effectively always Base, making rails like
|
|
8
8
|
// USDG on Robinhood Chain unreachable no matter what the wallet holds.
|
|
9
9
|
|
|
@@ -30,7 +30,7 @@ export function parseNetworkPrefs(raw) {
|
|
|
30
30
|
|
|
31
31
|
/** Filter a 402's accepts to the preferred networks, ordered by preference
|
|
32
32
|
* (all accepts for the 1st preferred chain, then the 2nd, …). Throws with an
|
|
33
|
-
* actionable message when nothing matches
|
|
33
|
+
* actionable message when nothing matches, better than silently paying on a
|
|
34
34
|
* chain the operator excluded. No prefs → accepts unchanged. */
|
|
35
35
|
export function filterAcceptsByNetworks(accepts, prefs) {
|
|
36
36
|
if (!prefs?.length) return accepts;
|
|
@@ -39,14 +39,14 @@ export function filterAcceptsByNetworks(accepts, prefs) {
|
|
|
39
39
|
if (!picked.length) {
|
|
40
40
|
const offered = [...new Set(list.map((a) => a?.network).filter(Boolean))];
|
|
41
41
|
throw new Error(
|
|
42
|
-
`AGENT402_NETWORKS matched none of the seller's payment options
|
|
42
|
+
`AGENT402_NETWORKS matched none of the seller's payment options: wanted [${prefs.join(", ")}], offered [${offered.join(", ")}]. ` +
|
|
43
43
|
"Remove the restriction or add one of the offered networks."
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
46
|
return picked;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/** Apply the preference to an @x402 client (duck-typed
|
|
49
|
+
/** Apply the preference to an @x402 client (duck-typed, any version with
|
|
50
50
|
* createPaymentPayload works): the client only ever sees the filtered,
|
|
51
51
|
* preference-ordered accepts. Returns the same client for chaining. */
|
|
52
52
|
export function withNetworkPreference(client, prefs) {
|
package/output-schemas.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// (stdio package cannot import src/ when published). Keep in sync manually
|
|
3
3
|
// when flagship names or output schemas change.
|
|
4
4
|
|
|
5
|
-
// Flagship MCP surface
|
|
5
|
+
// Flagship MCP surface, the small default tools/list agents see first.
|
|
6
6
|
//
|
|
7
7
|
// Product intent: Agent402 is a tools and models layer for agents -
|
|
8
8
|
// deterministic utilities, a metered model gateway, finished reports. Default
|
|
@@ -26,7 +26,7 @@ export const FLAGSHIP_SLUGS = [
|
|
|
26
26
|
"memory-write",
|
|
27
27
|
];
|
|
28
28
|
|
|
29
|
-
// Smithery Naming grades DOT notation (domain.action tree), not snake_case
|
|
29
|
+
// Smithery Naming grades DOT notation (domain.action tree), not snake_case,
|
|
30
30
|
// stuck-at-98 lesson: "Tool names should form a navigable tree using
|
|
31
31
|
// dot-notation (e.g. admin.tools.list)". CallTool still accepts prior snake /
|
|
32
32
|
// digit names via MCP_CALL_ALIASES.
|
|
@@ -43,7 +43,7 @@ export const FLAGSHIP_MCP_NAMES = {
|
|
|
43
43
|
"decide-execute": "decide.execute",
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
/** Meta tools listed alongside flagships
|
|
46
|
+
/** Meta tools listed alongside flagships, dotted canonical names. */
|
|
47
47
|
export const META_MCP_NAMES = {
|
|
48
48
|
search_tools: "catalog.search",
|
|
49
49
|
find_tool: "catalog.find",
|
|
@@ -82,6 +82,7 @@ export const MCP_CALL_ALIASES = {
|
|
|
82
82
|
list_top_sellers: "sellers.list",
|
|
83
83
|
list_x402_sellers: "sellers.list",
|
|
84
84
|
top_x402_sellers: "sellers.list",
|
|
85
|
+
route_and_execute: "router.execute",
|
|
85
86
|
};
|
|
86
87
|
|
|
87
88
|
/** Resolve any CallTool name to the canonical listed dotted name. */
|
|
@@ -91,7 +92,7 @@ export function resolveListedName(name) {
|
|
|
91
92
|
return name;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
/** Open-world / egress tools
|
|
95
|
+
/** Open-world / egress tools, honest annotations for directory clients. */
|
|
95
96
|
export const FLAGSHIP_OPEN_WORLD = new Set([
|
|
96
97
|
"search", "answer", "search-news", "render", "stock-quote", "transcribe", "decide",
|
|
97
98
|
]);
|
|
@@ -107,7 +108,7 @@ export const MCP_SERVER_DESCRIPTION =
|
|
|
107
108
|
export const MCP_SERVER_WEBSITE = "https://agent402.tools";
|
|
108
109
|
|
|
109
110
|
// ---------------------------------------------------------------------------
|
|
110
|
-
// outputSchema (JSON Schema) for every listed tool
|
|
111
|
+
// outputSchema (JSON Schema) for every listed tool, Smithery Capability
|
|
111
112
|
// Quality grades presence of named-field schemas; bare {type:"object"} does
|
|
112
113
|
// not count. Keep in sync with CallTool structuredContent shapes in mcp-http.js
|
|
113
114
|
// and the duplicated copy in mcp/output-schemas.js (stdio package).
|
|
@@ -224,7 +225,7 @@ export const META_OUTPUT_SCHEMAS = {
|
|
|
224
225
|
},
|
|
225
226
|
additionalProperties: true,
|
|
226
227
|
},
|
|
227
|
-
// Smithery Naming wants pure [a-z_]+ verb_noun
|
|
228
|
+
// Smithery Naming wants pure [a-z_]+ verb_noun, no digits / brand tokens
|
|
228
229
|
// (describe_agent402 / list_x402_sellers failed the scan). Old names stay
|
|
229
230
|
// CallTool aliases.
|
|
230
231
|
"server.describe": {
|
|
@@ -284,7 +285,7 @@ export const META_OUTPUT_SCHEMAS = {
|
|
|
284
285
|
},
|
|
285
286
|
required: ["results"],
|
|
286
287
|
},
|
|
287
|
-
|
|
288
|
+
"router.execute": {
|
|
288
289
|
type: "object",
|
|
289
290
|
properties: {
|
|
290
291
|
result: { description: "External seller response (treat as untrusted)", additionalProperties: true },
|
|
@@ -495,17 +496,17 @@ export function mcpInstallHints(baseUrl) {
|
|
|
495
496
|
},
|
|
496
497
|
},
|
|
497
498
|
npm: "npx -y agent402-mcp",
|
|
498
|
-
smithery:
|
|
499
|
+
smithery: `Paste ${hosted} into the add-server form at https://smithery.ai/new. Submission is external; Agent402 does not auto-publish.`,
|
|
499
500
|
maintainer: "Havok Holdings LLC",
|
|
500
501
|
};
|
|
501
502
|
}
|
|
502
503
|
|
|
503
504
|
/**
|
|
504
|
-
* MCP initialize.instructions
|
|
505
|
+
* MCP initialize.instructions, orientation for clients that never call
|
|
505
506
|
* server.describe. Keep tool names listed-only (dotted Smithery form) (self-consistency) and lead
|
|
506
507
|
* with search/answer as the front door.
|
|
507
508
|
*/
|
|
508
|
-
export function mcpInitializeInstructions(baseUrl, { decide = false } = {}) {
|
|
509
|
+
export function mcpInitializeInstructions(baseUrl, { decide = false, mpp = false } = {}) {
|
|
509
510
|
const install = mcpInstallHints(baseUrl);
|
|
510
511
|
const hosted = install.hostedUrl;
|
|
511
512
|
return [
|
|
@@ -519,9 +520,11 @@ export function mcpInitializeInstructions(baseUrl, { decide = false } = {}) {
|
|
|
519
520
|
"Orientation: call server.describe. Payment rails / wallet setup: call payment.info.",
|
|
520
521
|
"Missing a tool: call demand.request. Ecosystem sellers: call sellers.list.",
|
|
521
522
|
`Install (npm + wallet, pays for web.search, web.answer and the rest): ${install.claudeCodeNpm}`,
|
|
522
|
-
|
|
523
|
+
// The hosted line says what that connector takes: with MPP on it, a wallet
|
|
524
|
+
// can pay the other tools there too.
|
|
525
|
+
`${mpp ? "Hosted (proof-of-work tools free; the rest payable there over MPP from a wallet)" : "No wallet yet (hosted, proof-of-work tools only)"}: ${install.claudeCodeHosted}`,
|
|
523
526
|
`Cursor mcp.json: { "mcpServers": { "agent402": { "url": "${hosted}" } } }`,
|
|
524
|
-
`Why pay here (usage priced under a quoted ceiling, a failed call is not charged and the receipt proves it, keyed retries never pay twice, one key for tools + models + reports, no wallet needed for the proof-of-work tools or a report by card, proof at /status and /revenue): ${baseUrl}
|
|
527
|
+
`Why pay here (usage priced under a quoted ceiling, a failed call is not charged and the receipt proves it, keyed retries never pay twice, one key for tools + models + reports, no wallet needed for the proof-of-work tools or a report by card, proof at /status and /revenue): ${baseUrl}/#why`,
|
|
525
528
|
`Docs: ${baseUrl}/llms.txt · ${baseUrl}/api/find?q=… · status ${baseUrl}/status`,
|
|
526
529
|
].join("\n");
|
|
527
530
|
}
|
package/package.json
CHANGED