@aifinpay/mcp 1.5.1 → 2.0.0-rc.3

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 CHANGED
@@ -1,17 +1,13 @@
1
1
  # @aifinpay/mcp
2
2
 
3
- MCP server exposing AiFinPay's autonomous x402 payment loop as
4
- agent-callable tools. Drop it into Claude Desktop, MCP Inspector, or any
5
- MCP-aware agent runtime — your agent can now buy services autonomously.
6
-
7
- Canonical domain: **aifinpay.io** (the legacy `aifinpay.company` host is
8
- retired — ignore any docs pointing there). SDK settlement runs on
9
- **Polygon (default) and Solana**; the underlying Node SDK (≥ 1.3.0) also
10
- supports direct splitter settlement on Base, Optimism, Unichain, BOT Chain
11
- and XRPL EVM (native-token path), while the MCP `pay_with_split` /
12
- `quote_split` tools stay Polygon + Solana (backend invoice flow). The
13
- protocol itself is live across 13 networks — see
14
- [aifinpay.io/llms.txt](https://aifinpay.io/llms.txt).
3
+ MCP server exposing AiFinPay payment and quote primitives to MCP-aware
4
+ agent runtimes. Canonical domain: **aifinpay.io**.
5
+
6
+ AIFP-1 is gross-inclusive: payer total equals the quote, merchant receives 99%,
7
+ AiFinPay receives 1%, creator/referral receives 0%. AIFP-2/x402 currently
8
+ charges 0% at the protocol layer. Fund-moving paths fail closed until the exact
9
+ deployment, runtime hash, governance profile, merchant target, asset and paid
10
+ E2E evidence are verified.
15
11
 
16
12
  ## Tools
17
13
 
@@ -21,8 +17,8 @@ protocol itself is live across 13 networks — see
21
17
  | `agent_address()` | Return the agent's funding addresses on **both** chains — Polygon `0x…` (default settlement: io.net, Exa, Venice bridges) and Solana base58 (Seat PDA / leaderboard). One seed, two chains — fund either. |
22
18
  | `agent_quote(url)` | Inspect a 402 challenge without paying. Shows the merchant's quoted amount + facilitator flavor. |
23
19
  | `agent_call(provider, …)` | Call a live provider from the AiFinPay directory (io.net, Exa, Venice, …) with automatic payment. |
24
- | `pay_with_split(chain, merchant, amount, …)` | Direct fee-on-top payment to any merchant wallet — merchant receives 100% of the amount, AiFinPay adds 1% on top. |
25
- | `quote_split(chain, amount)` | Preview the exact on-chain amounts of a `pay_with_split` before paying. |
20
+ | `pay_with_split(…)` | Retired compatibility tool. Returns `legacy_split_route_retired`; never creates an invoice or moves funds. |
21
+ | `quote_split(…)` | Retired compatibility tool. Use a canonical AIFP-1 quote. |
26
22
  | `agent_claim_self(magic_link_url)` | Link this agent to your dashboard account via a one-shot magic link from `aifinpay.io/login` — spend history shows up in the dashboard. |
27
23
 
28
24
  ## Install
@@ -77,7 +73,7 @@ agent identity (and any funded Seat) persists across restarts.
77
73
  | `AIFINPAY_AGENT_SECRET` | — | Base58 secret. If absent → ephemeral agent printed to stderr. |
78
74
  | `AIFINPAY_BASE_URL` | `https://aifinpay.io` | Backend URL for nonce + funding probes. |
79
75
  | `AIFINPAY_TIMEOUT_MS` | `30000` | Request timeout. |
80
- | `AIFINPAY_MAX_USD` | — | Hard cap per single payment. Strongly recommended. |
76
+ | `AIFINPAY_MAX_USD` | — | Mandatory positive finite operator ceiling for any fund-moving tool. |
81
77
 
82
78
  ## Programmatic use
83
79
 
@@ -4,14 +4,171 @@
4
4
  * in stdio mode. Compatible with Claude Desktop, MCP Inspector, and any
5
5
  * MCP-aware agent runtime.
6
6
  *
7
- * Configure via env:
8
- * AIFINPAY_AGENT_SECRET base58 secret (load existing identity)
7
+ * Two commands, and the first one only has to be run once:
8
+ *
9
+ * npx @aifinpay/mcp init create a persistent wallet, print the config
10
+ * npx @aifinpay/mcp start the server
11
+ *
12
+ * WHY `init` EXISTS
13
+ *
14
+ * Running the server with nothing configured produced an EPHEMERAL wallet and
15
+ * a "DO NOT FUND" warning — correct, and a dead end. The warning pointed at
16
+ * `aifinpay init`, which is not published to npm: `@aifinpay/cli` and
17
+ * `aifinpay` both 404. So the documented way out of the dead end did not exist,
18
+ * and the only real path was to write a Node script against the SDK, which is
19
+ * not something you ask of someone whose goal is "give my agent a wallet".
20
+ *
21
+ * The gap was exactly one subcommand. This is it.
22
+ *
23
+ * Configure via env (all optional; the secret is read from the keystore below
24
+ * when the variable is unset):
25
+ * AIFINPAY_AGENT_SECRET base58 secret — overrides the keystore
9
26
  * AIFINPAY_BASE_URL default https://aifinpay.io
10
27
  * AIFINPAY_TIMEOUT_MS default 30000
11
28
  * AIFINPAY_MAX_USD hard cap per single payment (no default)
12
29
  */
13
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14
- import { createServer, loadConfigFromEnv } from "../dist/index.js";
30
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync, chmodSync } from "node:fs";
31
+ import { join } from "node:path";
32
+ import { homedir } from "node:os";
33
+ import { createRequire } from "node:module";
34
+
35
+ const HOME = process.env.AIFINPAY_HOME || join(homedir(), ".aifinpay");
36
+ const KEYSTORE = join(HOME, "agent.json");
37
+
38
+ const require_ = createRequire(import.meta.url);
39
+ const VERSION = (() => {
40
+ try {
41
+ return require_("../package.json").version;
42
+ } catch {
43
+ return "unknown";
44
+ }
45
+ })();
46
+
47
+ const arg = (process.argv[2] || "").toLowerCase();
48
+
49
+ // ── help / version ────────────────────────────────────────────────────────
50
+ // These used to start the server. `--help` launching a stdio server is not a
51
+ // harmless quirk: the process appears to hang, because it is waiting for MCP
52
+ // framing on stdin that a human is never going to type.
53
+
54
+ if (arg === "--help" || arg === "-h" || arg === "help") {
55
+ process.stdout.write(`aifinpay-mcp ${VERSION}
56
+
57
+ npx @aifinpay/mcp init create a persistent wallet and print the config
58
+ npx @aifinpay/mcp start the MCP server (stdio)
59
+
60
+ Run init once. It writes ${KEYSTORE} with mode 600 and the server picks
61
+ it up automatically — you do not have to put the secret in a config file.
62
+
63
+ Env (all optional):
64
+ AIFINPAY_AGENT_SECRET base58 secret; overrides the keystore
65
+ AIFINPAY_MAX_USD hard cap per single payment — set this
66
+ AIFINPAY_BASE_URL default https://aifinpay.io
67
+ AIFINPAY_TIMEOUT_MS default 30000
68
+ AIFINPAY_HOME default ~/.aifinpay
69
+ `);
70
+ process.exit(0);
71
+ }
72
+
73
+ if (arg === "--version" || arg === "-v" || arg === "version") {
74
+ process.stdout.write(`${VERSION}\n`);
75
+ process.exit(0);
76
+ }
77
+
78
+ // ── init ──────────────────────────────────────────────────────────────────
79
+
80
+ function readKeystore() {
81
+ if (!existsSync(KEYSTORE)) return null;
82
+ try {
83
+ const parsed = JSON.parse(readFileSync(KEYSTORE, "utf8"));
84
+ return typeof parsed?.secretB58 === "string" ? parsed : null;
85
+ } catch {
86
+ return null;
87
+ }
88
+ }
89
+
90
+ if (arg === "init") {
91
+ const { Agent, AiFinPayAgent } = await import("@aifinpay/agent");
92
+
93
+ let store = readKeystore();
94
+ if (store) {
95
+ // Never silently overwrite. The file is the only copy of a key that may
96
+ // already hold funds; a second `init` that regenerated it would destroy a
97
+ // wallet to save one line of output.
98
+ process.stdout.write(`Existing wallet found at ${KEYSTORE} — keeping it.\n\n`);
99
+ } else {
100
+ mkdirSync(HOME, { recursive: true, mode: 0o700 });
101
+ store = { secretB58: Agent.new().secretB58, created: new Date().toISOString() };
102
+ writeFileSync(KEYSTORE, JSON.stringify(store, null, 2) + "\n", { mode: 0o600 });
103
+ chmodSync(KEYSTORE, 0o600); // writeFileSync honours umask; this does not
104
+ process.stdout.write(`Created ${KEYSTORE} (mode 600).\n\n`);
105
+ }
106
+
107
+ const agent = await AiFinPayAgent.fromSolanaSecret(store.secretB58);
108
+
109
+ process.stdout.write(
110
+ `Your agent's addresses — the EVM one is the same on every EVM chain:\n\n` +
111
+ ` EVM ${agent.evmAddress}\n` +
112
+ ` Solana ${agent.solanaAddress}\n` +
113
+ ` Casper ${agent.casperAddress}\n\n` +
114
+ `Add this to your MCP client config and restart it:\n\n` +
115
+ JSON.stringify(
116
+ {
117
+ mcpServers: {
118
+ aifinpay: {
119
+ command: "npx",
120
+ args: ["-y", "@aifinpay/mcp"],
121
+ env: { AIFINPAY_MAX_USD: "0.10" },
122
+ },
123
+ },
124
+ },
125
+ null,
126
+ 2,
127
+ ) +
128
+ `\n\nThe secret is NOT in that block on purpose — the server reads the\n` +
129
+ `keystore. Config files get pasted into chats and committed to git.\n\n` +
130
+ `Back up ${KEYSTORE}. It is the only copy. The derivation is not\n` +
131
+ `BIP-39, so no standard wallet can recover this from a phrase.\n\n` +
132
+ `The addresses hold nothing yet. Send POL to the EVM address to let the\n` +
133
+ `agent pay for calls.\n`,
134
+ );
135
+ process.exit(0);
136
+ }
137
+
138
+ if (arg && !arg.startsWith("-")) {
139
+ process.stderr.write(`aifinpay-mcp: unknown command "${arg}". Try --help.\n`);
140
+ process.exit(2);
141
+ }
142
+
143
+ // ── server ────────────────────────────────────────────────────────────────
144
+
145
+ // Fall back to the keystore so `init` once is genuinely enough. Env still wins:
146
+ // a caller that sets AIFINPAY_AGENT_SECRET explicitly means it.
147
+ if (!process.env.AIFINPAY_AGENT_SECRET) {
148
+ const store = readKeystore();
149
+ if (store) {
150
+ try {
151
+ const mode = statSync(KEYSTORE).mode & 0o777;
152
+ if (mode & 0o077) {
153
+ process.stderr.write(
154
+ `[warn] [aifinpay-mcp] ${KEYSTORE} is mode ${mode.toString(8)} — readable beyond your user. ` +
155
+ `Run: chmod 600 ${KEYSTORE}\n`,
156
+ );
157
+ }
158
+ } catch {
159
+ /* stat failure is not a reason to refuse to start */
160
+ }
161
+ process.env.AIFINPAY_AGENT_SECRET = store.secretB58;
162
+ } else {
163
+ process.stderr.write(
164
+ `[info] [aifinpay-mcp] no wallet configured. Run \`npx @aifinpay/mcp init\` once ` +
165
+ `for a persistent one; starting with a throwaway identity for now.\n`,
166
+ );
167
+ }
168
+ }
169
+
170
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
171
+ const { createServer, loadConfigFromEnv } = await import("../dist/index.js");
15
172
 
16
173
  const { server } = await createServer(loadConfigFromEnv());
17
174
  const transport = new StdioServerTransport();
package/dist/config.d.ts CHANGED
@@ -9,6 +9,30 @@ export interface McpConfig {
9
9
  timeoutMs?: number;
10
10
  /** Hard cap on a single payment to prevent runaway agents. */
11
11
  maxAmountUsd?: number;
12
+ /** Gateway origins this agent may settle against.
13
+ *
14
+ * The SDK has always supported this (parseGatewayUrl in @aifinpay/agent) and
15
+ * this wrapper never exposed it, so a self-hosted merchant was unreachable:
16
+ * payable_fetch reached the 402 and then refused with "dev.ratersapp.com is
17
+ * not a known AiFinPay gateway; allowed: https://gateway.aifinpay.io".
18
+ * Observed in an external E2E run on 2026-08-27.
19
+ *
20
+ * Set by the OPERATOR, exact origins only, never a wildcard. The refusal it
21
+ * relaxes is not paranoia: a 402 is unauthenticated, so paying an
22
+ * unrecognised host means paying whoever answered. Naming a host here is a
23
+ * statement that you know who that is. */
24
+ gatewayOrigins?: string[];
25
+ /** Hosts whose DNS pre-check is skipped, exact names only.
26
+ *
27
+ * safe-fetch resolves a hostname and refuses if any answer is a private
28
+ * address — an SSRF guard. Behind an HTTP proxy the client cannot resolve at
29
+ * all (getaddrinfo EAI_AGAIN), so every host is refused for the wrong reason.
30
+ *
31
+ * This is deliberately NOT a proxy-detection switch that disables the check
32
+ * globally: that would turn one environment quirk into a blanket SSRF
33
+ * bypass. An operator names the hosts they vouch for, one at a time, and the
34
+ * guard stays on for everything else. */
35
+ trustedHosts?: string[];
12
36
  /** Optional log destination (defaults to stderr). */
13
37
  logFn?: (level: "info" | "warn" | "error", msg: string) => void;
14
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,WAAW,SAAS;IACxB;kFAC8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,qDAAqD;IACrD,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjE;AAED,wBAAgB,iBAAiB,IAAI,SAAS,CAW7C"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,WAAW,SAAS;IACxB;kFAC8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;+CAW2C;IAC3C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B;;;;;;;;;8CAS0C;IAC1C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB,qDAAqD;IACrD,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjE;AAED,wBAAgB,iBAAiB,IAAI,SAAS,CAa7C"}
package/dist/config.js CHANGED
@@ -8,6 +8,52 @@ export function loadConfigFromEnv() {
8
8
  maxAmountUsd: process.env.AIFINPAY_MAX_USD
9
9
  ? Number(process.env.AIFINPAY_MAX_USD)
10
10
  : undefined,
11
+ gatewayOrigins: splitOrigins(process.env.AIFINPAY_GATEWAY_ORIGINS),
12
+ trustedHosts: splitList(process.env.AIFINPAY_TRUSTED_HOSTS),
11
13
  };
12
14
  }
15
+ /** Comma-separated list → trimmed entries, or undefined when unset/empty.
16
+ * Undefined and [] mean different things downstream: undefined keeps the SDK
17
+ * default, [] would mean "no origin is payable". An operator who sets the
18
+ * variable to an empty string means the former. */
19
+ function splitList(raw) {
20
+ if (!raw)
21
+ return undefined;
22
+ const out = raw.split(",").map((s) => s.trim()).filter(Boolean);
23
+ return out.length ? out : undefined;
24
+ }
25
+ /** Same, but each entry must be a bare https origin.
26
+ * A path or a wildcard here would read as allowed and match nothing, which is
27
+ * the failure that looks like the feature is broken rather than misconfigured. */
28
+ function splitOrigins(raw) {
29
+ const list = splitList(raw);
30
+ if (!list)
31
+ return undefined;
32
+ for (const entry of list) {
33
+ let u;
34
+ try {
35
+ u = new URL(entry);
36
+ }
37
+ catch {
38
+ throw new Error(`AIFINPAY_GATEWAY_ORIGINS: "${entry}" is not a URL`);
39
+ }
40
+ if (u.origin !== entry.replace(/\/+$/, "")) {
41
+ throw new Error(`AIFINPAY_GATEWAY_ORIGINS: "${entry}" must be a bare origin like https://dev.example.com `
42
+ + `(got path/query "${u.pathname}${u.search}")`);
43
+ }
44
+ // WHATWG URL accepts "*" in a hostname, so new URL("https://*.example.com")
45
+ // parses and its origin round-trips — the entry would be stored, matched
46
+ // against nothing, and read as "I allowed this host". Rejecting it here is
47
+ // the difference between a misconfiguration that says so and one that looks
48
+ // like the feature is broken.
49
+ if (!/^[a-z0-9.-]+$/i.test(u.hostname) || u.hostname.startsWith("-") || u.hostname.includes("..")) {
50
+ throw new Error(`AIFINPAY_GATEWAY_ORIGINS: "${entry}" is not a plain hostname. `
51
+ + `Wildcards are not supported — name each origin you settle against.`);
52
+ }
53
+ if (u.protocol !== "https:" && u.hostname !== "localhost" && u.hostname !== "127.0.0.1") {
54
+ throw new Error(`AIFINPAY_GATEWAY_ORIGINS: "${entry}" is not https — refusing to settle over plaintext`);
55
+ }
56
+ }
57
+ return list;
58
+ }
13
59
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAmBA,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,SAAS;QAC9D,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,SAAS;QACnD,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB;YACxC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;YACzC,CAAC,CAAC,SAAS;QACb,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;YACxC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;YACtC,CAAC,CAAC,SAAS;KACd,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA6CA,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,SAAS;QAC9D,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,SAAS;QACnD,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB;YACxC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;YACzC,CAAC,CAAC,SAAS;QACb,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;YACxC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;YACtC,CAAC,CAAC,SAAS;QACb,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;QAClE,YAAY,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED;;;oDAGoD;AACpD,SAAS,SAAS,CAAC,GAAuB;IACxC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChE,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACtC,CAAC;AAED;;mFAEmF;AACnF,SAAS,YAAY,CAAC,GAAuB;IAC3C,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,IAAI,CAAM,CAAC;QACX,IAAI,CAAC;YAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAAC,CAAC;QAC3B,MAAM,CAAC;YAAC,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,gBAAgB,CAAC,CAAC;QAAC,CAAC;QAC/E,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CACb,8BAA8B,KAAK,uDAAuD;kBACxF,oBAAoB,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,IAAI,CAChD,CAAC;QACJ,CAAC;QACD,4EAA4E;QAC5E,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,8BAA8B;QAC9B,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAClG,MAAM,IAAI,KAAK,CACb,8BAA8B,KAAK,6BAA6B;kBAC9D,oEAAoE,CACvE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,WAAW,IAAI,CAAC,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;YACxF,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,oDAAoD,CAAC,CAAC;QAC3G,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -12,6 +12,7 @@ export declare class BlockedRequestError extends Error {
12
12
  */
13
13
  export declare function assertRequestAllowed(rawUrl: string, opts?: {
14
14
  allowPrivate?: boolean;
15
+ trustedHosts?: string[];
15
16
  }): Promise<URL>;
16
17
  /**
17
18
  * A `fetch` that validates the target, and every redirect target, before going.
@@ -21,5 +22,6 @@ export declare function assertRequestAllowed(rawUrl: string, opts?: {
21
22
  */
22
23
  export declare function makeSafeFetch(opts?: {
23
24
  allowPrivate?: boolean;
25
+ trustedHosts?: string[];
24
26
  }): typeof fetch;
25
27
  //# sourceMappingURL=safe-fetch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"safe-fetch.d.ts","sourceRoot":"","sources":["../src/safe-fetch.ts"],"names":[],"mappings":"AA4DA,oDAAoD;AACpD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmBtD;AAED,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AAEjD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC,OAAO,CAAC,GAAG,CAAC,CAkDd;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,KAAK,CA4BjF"}
1
+ {"version":3,"file":"safe-fetch.d.ts","sourceRoot":"","sources":["../src/safe-fetch.ts"],"names":[],"mappings":"AA4DA,oDAAoD;AACpD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmBtD;AAED,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AAEjD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GAC7D,OAAO,CAAC,GAAG,CAAC,CAyEd;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GAAG,OAAO,KAAK,CA4B1G"}
@@ -116,12 +116,32 @@ export async function assertRequestAllowed(rawUrl, opts = {}) {
116
116
  }
117
117
  return url;
118
118
  }
119
+ // An operator naming this exact host has vouched for it, so the resolution
120
+ // step is skipped for it and only for it.
121
+ //
122
+ // Why this exists: behind an HTTP proxy the client does not resolve at all —
123
+ // the proxy does — so lookup() fails with getaddrinfo EAI_AGAIN and every
124
+ // host is refused as "cannot resolve". That is the guard misfiring on the
125
+ // environment rather than on a threat, and it made the MCP server unusable in
126
+ // proxied setups (external E2E, 2026-08-27).
127
+ //
128
+ // Deliberately per-host and not a proxy-detection switch. "We seem to be
129
+ // behind a proxy, disable the SSRF check" turns one environment quirk into a
130
+ // blanket bypass, which is the shape of the vulnerability this file exists to
131
+ // prevent. Exact hostnames only — no suffix matching, because "example.com"
132
+ // trusting "evil-example.com" is the classic way an allowlist stops meaning
133
+ // anything.
134
+ if (opts.trustedHosts?.some((h) => h.toLowerCase() === host.toLowerCase())) {
135
+ return url;
136
+ }
119
137
  let addresses;
120
138
  try {
121
139
  addresses = await lookup(host, { all: true });
122
140
  }
123
141
  catch (e) {
124
- throw new BlockedRequestError(`cannot resolve ${host}: ${e.message}`);
142
+ throw new BlockedRequestError(`cannot resolve ${host}: ${e.message}. `
143
+ + `If this host is reachable only through a proxy, name it in `
144
+ + `AIFINPAY_TRUSTED_HOSTS to skip the DNS pre-check for it alone.`);
125
145
  }
126
146
  if (!addresses.length) {
127
147
  throw new BlockedRequestError(`${host} resolved to nothing`);
@@ -1 +1 @@
1
- {"version":3,"file":"safe-fetch.js","sourceRoot":"","sources":["../src/safe-fetch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"safe-fetch.js","sourceRoot":"","sources":["../src/safe-fetch.ts"],"names":[],"mappings":"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"}
package/dist/server.d.ts CHANGED
@@ -2,15 +2,18 @@ import { AiFinPayAgent } from "@aifinpay/agent";
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import type { McpConfig } from "./config.js";
4
4
  /**
5
- * Build an MCP server that wraps the AiFinPay agent SDK as MCP tools.
5
+ * Production-RC MCP surface.
6
6
  *
7
- * Returned server is unstarted — caller wires up a transport (stdio, SSE,
8
- * etc.) via the official `@modelcontextprotocol/sdk` package.
7
+ * IMPORTANT: the old agent_call, payable_fetch, pay_with_split, quote_split,
8
+ * agent_claim_self and agent_quote tools are deliberately NOT registered.
9
+ * They depend on @aifinpay/agent 1.x legacy splitter/x402 semantics and must not
10
+ * be available to a model after the 0% AIFP-2 / gross-inclusive AIFP-1 change.
9
11
  *
10
- * As of 0.1.0-alpha.3 the wrapped identity is `AiFinPayAgent` — dual-chain
11
- * (Solana base58 pubkey AND Polygon EVM address from one secret). Tools
12
- * that previously called legacy `Agent` methods now reach them via
13
- * `agent.inner.*`.
12
+ * This server remains useful before the SDK 2.0 package is published: it can
13
+ * expose the wallet address, resolve Agent Passport identity, read verified
14
+ * v1.3 routes, and construct a non-signing settlement invoice. Signing/moving
15
+ * value returns only after MCP depends on the published v2 executor and its E2E
16
+ * release gate has passed.
14
17
  */
15
18
  export declare function createServer(config?: McpConfig): Promise<{
16
19
  server: Server<{
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAS,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AA4B7C;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,MAAM,GAAE,SAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiHxD;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9D"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAwB7C;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAAC,MAAM,GAAE,SAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiFxD;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9D"}
package/dist/server.js CHANGED
@@ -1,41 +1,36 @@
1
1
  import { AiFinPayAgent } from "@aifinpay/agent";
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
- import { payableFetchTool, runPayableFetch } from "./tools/payable-fetch.js";
5
4
  import { agentAddressTool, runAgentAddress } from "./tools/agent-address.js";
6
- import { agentQuoteTool, runAgentQuote } from "./tools/agent-quote.js";
7
- import { agentCallTool, runAgentCall } from "./tools/agent-call.js";
8
- import { agentClaimSelfTool, runAgentClaimSelf } from "./tools/agent-claim-self.js";
5
+ import { agentQuotaTool, runAgentQuota } from "./tools/agent-quota.js";
9
6
  import { makeSafeFetch } from "./safe-fetch.js";
10
- import { payWithSplitTool, runPayWithSplit, quoteSplitTool, runQuoteSplit, } from "./tools/pay-with-split.js";
11
- // Every request the agent makes goes through this, including redirects.
12
- //
13
- // payable_fetch hands a caller-supplied URL to the agent, which requests it
14
- // and, on a 402, pays and requests it again. Nothing checked the URL, so a
15
- // caller — or a prompt injection reaching the tool — could name
16
- // http://127.0.0.1:4001 or http://169.254.169.254/latest/meta-data/ and read
17
- // the answer. It is the tool exposed over HTTP today.
18
- //
19
- // Set AIFINPAY_ALLOW_PRIVATE_FETCH=1 to lift it, which is what a local bridge
20
- // on localhost needs and what a public deployment must never have.
7
+ import { loadConfigFromEnv } from "./config.js";
8
+ import { agentPassportResolveTool, runAgentPassportResolve, settlementRoutesTool, runSettlementRoutes, settlementInvoiceTool, runSettlementInvoice, } from "./tools/production-control.js";
9
+ // Every backend/public request made by this server goes through safeFetch.
10
+ // Public deployments never lift private-network protection; local development
11
+ // may explicitly use AIFINPAY_ALLOW_PRIVATE_FETCH=1.
21
12
  const safeFetch = makeSafeFetch({
22
13
  allowPrivate: process.env.AIFINPAY_ALLOW_PRIVATE_FETCH === "1",
14
+ // Exact hosts an operator vouches for. Skips only the DNS pre-check, and only
15
+ // for those names — see safe-fetch.ts for why this is not a proxy switch.
16
+ trustedHosts: loadConfigFromEnv().trustedHosts,
23
17
  });
24
18
  /**
25
- * Build an MCP server that wraps the AiFinPay agent SDK as MCP tools.
19
+ * Production-RC MCP surface.
26
20
  *
27
- * Returned server is unstarted — caller wires up a transport (stdio, SSE,
28
- * etc.) via the official `@modelcontextprotocol/sdk` package.
21
+ * IMPORTANT: the old agent_call, payable_fetch, pay_with_split, quote_split,
22
+ * agent_claim_self and agent_quote tools are deliberately NOT registered.
23
+ * They depend on @aifinpay/agent 1.x legacy splitter/x402 semantics and must not
24
+ * be available to a model after the 0% AIFP-2 / gross-inclusive AIFP-1 change.
29
25
  *
30
- * As of 0.1.0-alpha.3 the wrapped identity is `AiFinPayAgent` — dual-chain
31
- * (Solana base58 pubkey AND Polygon EVM address from one secret). Tools
32
- * that previously called legacy `Agent` methods now reach them via
33
- * `agent.inner.*`.
26
+ * This server remains useful before the SDK 2.0 package is published: it can
27
+ * expose the wallet address, resolve Agent Passport identity, read verified
28
+ * v1.3 routes, and construct a non-signing settlement invoice. Signing/moving
29
+ * value returns only after MCP depends on the published v2 executor and its E2E
30
+ * release gate has passed.
34
31
  */
35
32
  export async function createServer(config = {}) {
36
33
  const log = config.logFn ?? defaultLog;
37
- // Agent identity: load from env secret if provided, else generate one
38
- // and print to stderr so the human knows what to fund.
39
34
  const agent = config.agentSecretB58
40
35
  ? await AiFinPayAgent.fromSolanaSecret(config.agentSecretB58, {
41
36
  fetchImpl: safeFetch,
@@ -48,19 +43,6 @@ export async function createServer(config = {}) {
48
43
  baseUrl: config.baseUrl,
49
44
  timeoutMs: config.timeoutMs,
50
45
  });
51
- // NEVER log private key material. stderr is captured by Docker,
52
- // journald, CI logs, support bundles and monitoring, so printing the
53
- // secret here leaked it into all of them.
54
- //
55
- // The warning below stays, but not for the reason it once gave. That
56
- // reason — that new() produced an EVM key independent of the Solana
57
- // one, so no backup could restore both — was fixed: everything now
58
- // derives from a single random seed. What makes this identity
59
- // ephemeral is narrower and still true: the seed is never printed and
60
- // never written anywhere, deliberately, because stderr ends up in
61
- // Docker, journald, CI logs and support bundles. Recoverable in
62
- // principle, unrecoverable in practice, so still not an address to
63
- // fund.
64
46
  log("warn", `[aifinpay-mcp] no AIFINPAY_AGENT_SECRET set — generated an EPHEMERAL, NON-RECOVERABLE agent.\n` +
65
47
  ` solana_address: ${a.solanaAddress}\n` +
66
48
  ` evm_address: ${a.evmAddress}\n` +
@@ -69,71 +51,51 @@ export async function createServer(config = {}) {
69
51
  ` (AiFinPayAgent.fromSeed / \`aifinpay init\`) and set AIFINPAY_AGENT_SECRET.`);
70
52
  return a;
71
53
  })();
72
- // AIFINPAY_MAX_USD is the documented "hard cap on a single payment".
73
- // payable_fetch enforces it via PayOptions, but agent_call settles through
74
- // AiFinPayAgent.call() which only honours the agent's budget caps — wire
75
- // the cap there too, otherwise the primary tool has NO runaway protection.
54
+ // Keep the legacy agent budget configured even though this RC exposes no
55
+ // signing tool. It remains an additional defence for downstream/private code
56
+ // and for the subsequent v2 MCP executor integration.
76
57
  if (config.maxAmountUsd !== undefined && Number.isFinite(config.maxAmountUsd)) {
77
58
  agent.setBudget({ per_call_usd: config.maxAmountUsd });
78
59
  log("info", `[aifinpay-mcp] per-call cap: $${config.maxAmountUsd} (AIFINPAY_MAX_USD)`);
79
60
  }
80
- else {
81
- log("warn", "[aifinpay-mcp] AIFINPAY_MAX_USD not set — agent_call/payable_fetch have NO per-payment cap. Strongly recommended.");
82
- }
83
- log("info", `[aifinpay-mcp] solana: ${agent.solanaAddress} · evm: ${agent.evmAddress}`);
61
+ log("info", `[aifinpay-mcp] production RC safe surface · solana: ${agent.solanaAddress} · evm: ${agent.evmAddress}`);
84
62
  const server = new Server({
85
63
  name: "@aifinpay/mcp",
86
- version: "1.0.0",
87
- }, {
88
- capabilities: {
89
- tools: {},
90
- },
91
- });
92
- server.setRequestHandler(ListToolsRequestSchema, async () => {
93
- return {
94
- tools: [
95
- agentAddressTool(),
96
- agentCallTool(),
97
- agentClaimSelfTool(),
98
- payableFetchTool(),
99
- agentQuoteTool(),
100
- payWithSplitTool(),
101
- quoteSplitTool(),
102
- ],
103
- };
104
- });
64
+ version: "2.0.0-rc.1",
65
+ }, { capabilities: { tools: {} } });
66
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
67
+ tools: [
68
+ agentAddressTool(),
69
+ agentQuotaTool(),
70
+ agentPassportResolveTool(),
71
+ settlementRoutesTool(),
72
+ settlementInvoiceTool(),
73
+ ],
74
+ }));
105
75
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
106
76
  const { name, arguments: args } = request.params;
107
77
  const ctx = { agent, config, log };
108
78
  switch (name) {
109
79
  case "agent_address":
110
80
  return runAgentAddress(ctx, args ?? {});
111
- case "agent_call":
112
- return runAgentCall(ctx, args ?? {});
113
- case "agent_claim_self":
114
- return runAgentClaimSelf(ctx, args ?? {});
115
- case "payable_fetch":
116
- return runPayableFetch(ctx, args ?? {});
117
- case "agent_quote":
118
- return runAgentQuote(ctx, args ?? {});
119
- case "pay_with_split":
120
- return runPayWithSplit(ctx, args ?? {});
121
- case "quote_split":
122
- return runQuoteSplit(ctx, args ?? {});
81
+ case "agent_quota":
82
+ return runAgentQuota(ctx, args ?? {});
83
+ case "agent_passport_resolve":
84
+ return runAgentPassportResolve(ctx, args ?? {});
85
+ case "settlement_routes":
86
+ return runSettlementRoutes(ctx, args ?? {});
87
+ case "settlement_invoice":
88
+ return runSettlementInvoice(ctx, args ?? {});
123
89
  default:
124
90
  return {
125
91
  isError: true,
126
- content: [
127
- { type: "text", text: `unknown tool: ${name}` },
128
- ],
92
+ content: [{ type: "text", text: `unknown or retired tool: ${name}` }],
129
93
  };
130
94
  }
131
95
  });
132
96
  return { server, agent };
133
97
  }
134
98
  function defaultLog(level, msg) {
135
- // MCP stdio servers MUST NOT write to stdout — the transport owns it.
136
- // stderr is safe.
137
99
  process.stderr.write(`[${level}] ${msg}\n`);
138
100
  }
139
101
  //# sourceMappingURL=server.js.map