@aifinpay/mcp 1.5.3 → 2.0.0-rc.13
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 +116 -69
- package/bin/aifinpay-mcp.js +119 -49
- package/dist/api.d.ts +2 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +6 -0
- package/dist/api.js.map +1 -0
- package/dist/config.d.ts +35 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +63 -0
- package/dist/config.js.map +1 -1
- package/dist/identity.d.ts +9 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +75 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +3 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -6
- package/dist/index.js.map +1 -1
- package/dist/safe-fetch.d.ts +2 -0
- package/dist/safe-fetch.d.ts.map +1 -1
- package/dist/safe-fetch.js +76 -17
- package/dist/safe-fetch.js.map +1 -1
- package/dist/server.d.ts +11 -8
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +106 -102
- package/dist/server.js.map +1 -1
- package/dist/tools/agent-address.d.ts.map +1 -1
- package/dist/tools/agent-address.js +6 -27
- package/dist/tools/agent-address.js.map +1 -1
- package/dist/tools/agent-history.d.ts +58 -0
- package/dist/tools/agent-history.d.ts.map +1 -0
- package/dist/tools/agent-history.js +73 -0
- package/dist/tools/agent-history.js.map +1 -0
- package/dist/tools/agent-quota.d.ts +40 -0
- package/dist/tools/agent-quota.d.ts.map +1 -0
- package/dist/tools/agent-quota.js +95 -0
- package/dist/tools/agent-quota.js.map +1 -0
- package/dist/tools/dev-payment-quote.d.ts +39 -0
- package/dist/tools/dev-payment-quote.d.ts.map +1 -0
- package/dist/tools/dev-payment-quote.js +52 -0
- package/dist/tools/dev-payment-quote.js.map +1 -0
- package/dist/tools/production-control.d.ts +108 -0
- package/dist/tools/production-control.d.ts.map +1 -0
- package/dist/tools/production-control.js +132 -0
- package/dist/tools/production-control.js.map +1 -0
- package/package.json +8 -22
- package/skills/SKILL.md +143 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAcvF,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,cAAc,GAAG,IAAI,CA0C3E"}
|
package/dist/identity.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { readFileSync, existsSync, statSync } from "node:fs";
|
|
2
|
+
import { resolve, join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { scryptSync, createDecipheriv } from "node:crypto";
|
|
5
|
+
function seed(value) {
|
|
6
|
+
if (typeof value !== "string" || !/^(0x)?[0-9a-fA-F]{64}$/.test(value)) {
|
|
7
|
+
throw new Error("SEED_HASH must contain a 32-byte hex seed (64 hex characters); no mnemonic or double hashing");
|
|
8
|
+
}
|
|
9
|
+
return value.replace(/^0x/, "");
|
|
10
|
+
}
|
|
11
|
+
function readJson(path) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new Error(`Cannot read wallet JSON at ${path}; refusing to generate a replacement wallet`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Local inputs only. Never put the returned secrets in an MCP response/log. */
|
|
20
|
+
export function loadWalletIdentity(config) {
|
|
21
|
+
if (config.seedHash !== undefined)
|
|
22
|
+
return { source: "SEED_HASH", seedHash: seed(config.seedHash) };
|
|
23
|
+
const path = resolve(config.agentsFile || "./aifinpay/agents.json");
|
|
24
|
+
if (existsSync(path)) {
|
|
25
|
+
const document = readJson(path);
|
|
26
|
+
const agents = document?.agents;
|
|
27
|
+
if (!Array.isArray(agents) || agents.length === 0)
|
|
28
|
+
throw new Error("agents.json requires a non-empty agents array");
|
|
29
|
+
const matches = config.agentId
|
|
30
|
+
? agents.filter((a) => a?.id === config.agentId)
|
|
31
|
+
: agents;
|
|
32
|
+
if (matches.length !== 1)
|
|
33
|
+
throw new Error("Select exactly one agents.json record with AIFINPAY_AGENT_ID; refusing an ambiguous wallet");
|
|
34
|
+
return { source: "agents.json", seedHash: seed(matches[0]?.seed_hash) };
|
|
35
|
+
}
|
|
36
|
+
if (config.agentsFile)
|
|
37
|
+
throw new Error("Configured AIFINPAY_AGENTS_FILE does not exist");
|
|
38
|
+
if (config.agentSecretB58)
|
|
39
|
+
return { source: "AIFINPAY_AGENT_SECRET", secretB58: config.agentSecretB58 };
|
|
40
|
+
const legacyPath = join(config.walletHome || join(homedir(), ".aifinpay"), "agent.json");
|
|
41
|
+
if (!existsSync(legacyPath))
|
|
42
|
+
return null;
|
|
43
|
+
const legacy = readJson(legacyPath);
|
|
44
|
+
try {
|
|
45
|
+
const mode = statSync(legacyPath).mode & 0o777;
|
|
46
|
+
if (mode & 0o077) {
|
|
47
|
+
const message = `[aifinpay-mcp] ${legacyPath} is mode ${mode.toString(8)} — readable beyond your user. Run: chmod 600 ${legacyPath}`;
|
|
48
|
+
if (config.logFn)
|
|
49
|
+
config.logFn("warn", message);
|
|
50
|
+
else
|
|
51
|
+
process.stderr.write(`[warn] ${message}\n`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch { /* A stat failure does not change which wallet was selected. */ }
|
|
55
|
+
if (legacy.enc) {
|
|
56
|
+
if (legacy.enc !== "scrypt-aes-256-gcm" || !config.walletPassphrase) {
|
|
57
|
+
throw new Error("Encrypted keystore requires AIFINPAY_WALLET_PASSPHRASE and the supported encryption format");
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const key = scryptSync(config.walletPassphrase, Buffer.from(legacy.salt, "base64"), 32, { N: 1 << 15, r: 8, p: 1, maxmem: 64 * 1024 * 1024 });
|
|
61
|
+
const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(legacy.iv, "base64"));
|
|
62
|
+
decipher.setAuthTag(Buffer.from(legacy.tag, "base64"));
|
|
63
|
+
return { source: "legacy-keystore", secretB58: Buffer.concat([
|
|
64
|
+
decipher.update(Buffer.from(legacy.ct, "base64")), decipher.final(),
|
|
65
|
+
]).toString("utf8") };
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new Error("Cannot decrypt keystore; refusing to generate a replacement wallet");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (typeof legacy.secretB58 !== "string" || !legacy.secretB58)
|
|
72
|
+
throw new Error("Invalid legacy keystore");
|
|
73
|
+
return { source: "legacy-keystore", secretB58: legacy.secretB58 };
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
* @aifinpay/mcp — MCP server exposing AiFinPay's autonomous x402 payment
|
|
3
3
|
* loop as agent-callable tools.
|
|
4
4
|
*
|
|
5
|
-
* Tools:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - payable_fetch(url, opts?) — raw-URL paid fetch (legacy Solana path)
|
|
9
|
-
* - agent_quote(url) — inspect 402 cost before paying
|
|
10
|
-
* - pay_with_split / quote_split — direct B2BSplitter invoice (advanced)
|
|
5
|
+
* Tools: public addresses, history, quotas, passport resolution, non-signing
|
|
6
|
+
* settlement invoices, local wallet reload, and opt-in dev batch quoting.
|
|
7
|
+
* Signing tools are not registered in this RC.
|
|
11
8
|
*
|
|
12
9
|
* Quick start (stdio transport for Claude Desktop / MCP-aware runtimes):
|
|
13
10
|
*
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
* @aifinpay/mcp — MCP server exposing AiFinPay's autonomous x402 payment
|
|
3
3
|
* loop as agent-callable tools.
|
|
4
4
|
*
|
|
5
|
-
* Tools:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - payable_fetch(url, opts?) — raw-URL paid fetch (legacy Solana path)
|
|
9
|
-
* - agent_quote(url) — inspect 402 cost before paying
|
|
10
|
-
* - pay_with_split / quote_split — direct B2BSplitter invoice (advanced)
|
|
5
|
+
* Tools: public addresses, history, quotas, passport resolution, non-signing
|
|
6
|
+
* settlement invoices, local wallet reload, and opt-in dev batch quoting.
|
|
7
|
+
* Signing tools are not registered in this RC.
|
|
11
8
|
*
|
|
12
9
|
* Quick start (stdio transport for Claude Desktop / MCP-aware runtimes):
|
|
13
10
|
*
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/safe-fetch.d.ts
CHANGED
|
@@ -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
|
package/dist/safe-fetch.d.ts.map
CHANGED
|
@@ -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,
|
|
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,CA0BtD;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,CAkD1G"}
|
package/dist/safe-fetch.js
CHANGED
|
@@ -65,12 +65,22 @@ export function isBlockedAddress(addr) {
|
|
|
65
65
|
return BLOCKED_V4.some(([lo, hi]) => n >= lo && n <= hi);
|
|
66
66
|
}
|
|
67
67
|
if (family === 6) {
|
|
68
|
-
|
|
68
|
+
// URL canonicalization collapses expanded zeros AND rewrites mapped dotted
|
|
69
|
+
// IPv4 into hex, e.g. ::ffff:127.0.0.1 becomes ::ffff:7f00:1.
|
|
70
|
+
let lower;
|
|
71
|
+
try {
|
|
72
|
+
lower = new URL(`https://[${addr}]/`).hostname.replace(/^\[|\]$/g, "");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
69
77
|
// An IPv4-mapped address is an IPv4 address wearing a hat. Unwrap it, or
|
|
70
78
|
// ::ffff:127.0.0.1 walks straight through an IPv6-only check.
|
|
71
|
-
const mapped = lower.match(/^::ffff:(
|
|
72
|
-
if (mapped)
|
|
73
|
-
|
|
79
|
+
const mapped = lower.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
|
|
80
|
+
if (mapped) {
|
|
81
|
+
const n = (parseInt(mapped[1], 16) * 65536 + parseInt(mapped[2], 16)) >>> 0;
|
|
82
|
+
return BLOCKED_V4.some(([lo, hi]) => n >= lo && n <= hi);
|
|
83
|
+
}
|
|
74
84
|
if (lower === "::1" || lower === "::")
|
|
75
85
|
return true;
|
|
76
86
|
if (/^f[cd][0-9a-f]{2}:/.test(lower))
|
|
@@ -116,12 +126,32 @@ export async function assertRequestAllowed(rawUrl, opts = {}) {
|
|
|
116
126
|
}
|
|
117
127
|
return url;
|
|
118
128
|
}
|
|
129
|
+
// An operator naming this exact host has vouched for it, so the resolution
|
|
130
|
+
// step is skipped for it and only for it.
|
|
131
|
+
//
|
|
132
|
+
// Why this exists: behind an HTTP proxy the client does not resolve at all —
|
|
133
|
+
// the proxy does — so lookup() fails with getaddrinfo EAI_AGAIN and every
|
|
134
|
+
// host is refused as "cannot resolve". That is the guard misfiring on the
|
|
135
|
+
// environment rather than on a threat, and it made the MCP server unusable in
|
|
136
|
+
// proxied setups (external E2E, 2026-08-27).
|
|
137
|
+
//
|
|
138
|
+
// Deliberately per-host and not a proxy-detection switch. "We seem to be
|
|
139
|
+
// behind a proxy, disable the SSRF check" turns one environment quirk into a
|
|
140
|
+
// blanket bypass, which is the shape of the vulnerability this file exists to
|
|
141
|
+
// prevent. Exact hostnames only — no suffix matching, because "example.com"
|
|
142
|
+
// trusting "evil-example.com" is the classic way an allowlist stops meaning
|
|
143
|
+
// anything.
|
|
144
|
+
if (opts.trustedHosts?.some((h) => h.toLowerCase() === host.toLowerCase())) {
|
|
145
|
+
return url;
|
|
146
|
+
}
|
|
119
147
|
let addresses;
|
|
120
148
|
try {
|
|
121
149
|
addresses = await lookup(host, { all: true });
|
|
122
150
|
}
|
|
123
151
|
catch (e) {
|
|
124
|
-
throw new BlockedRequestError(`cannot resolve ${host}: ${e.message}`
|
|
152
|
+
throw new BlockedRequestError(`cannot resolve ${host}: ${e.message}. `
|
|
153
|
+
+ `If this host is reachable only through a proxy, name it in `
|
|
154
|
+
+ `AIFINPAY_TRUSTED_HOSTS to skip the DNS pre-check for it alone.`);
|
|
125
155
|
}
|
|
126
156
|
if (!addresses.length) {
|
|
127
157
|
throw new BlockedRequestError(`${host} resolved to nothing`);
|
|
@@ -143,22 +173,51 @@ export async function assertRequestAllowed(rawUrl, opts = {}) {
|
|
|
143
173
|
*/
|
|
144
174
|
export function makeSafeFetch(opts = {}) {
|
|
145
175
|
const safeFetch = async (input, init) => {
|
|
146
|
-
let
|
|
147
|
-
|
|
148
|
-
: input.url;
|
|
149
|
-
let request = init;
|
|
176
|
+
let request = new Request(input, init);
|
|
177
|
+
const redirect = request.redirect;
|
|
150
178
|
for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
|
|
151
|
-
await assertRequestAllowed(
|
|
152
|
-
const res = await globalThis.fetch(
|
|
153
|
-
const location =
|
|
179
|
+
await assertRequestAllowed(request.url, opts);
|
|
180
|
+
const res = await globalThis.fetch(request.clone(), { redirect: "manual" });
|
|
181
|
+
const location = [301, 302, 303, 307, 308].includes(res.status) ? res.headers.get("location") : null;
|
|
154
182
|
if (!location)
|
|
155
183
|
return res;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
184
|
+
if (redirect === "manual")
|
|
185
|
+
return res;
|
|
186
|
+
await res.body?.cancel();
|
|
187
|
+
if (redirect === "error")
|
|
188
|
+
throw new BlockedRequestError("redirect forbidden by request policy");
|
|
189
|
+
if (hop === MAX_REDIRECTS)
|
|
190
|
+
break;
|
|
191
|
+
const next = new URL(location, request.url);
|
|
192
|
+
const crossOrigin = next.origin !== new URL(request.url).origin;
|
|
193
|
+
const dropBody = (res.status === 303 && !["GET", "HEAD"].includes(request.method))
|
|
194
|
+
|| ([301, 302].includes(res.status) && request.method === "POST");
|
|
195
|
+
if (crossOrigin && request.body && !dropBody) {
|
|
196
|
+
throw new BlockedRequestError("refusing cross-origin redirect with a request body");
|
|
197
|
+
}
|
|
198
|
+
const headers = new Headers(request.headers);
|
|
199
|
+
if (crossOrigin) {
|
|
200
|
+
// Only explicitly public negotiation headers may leave the original
|
|
201
|
+
// origin. A denylist cannot anticipate every merchant's secret header.
|
|
202
|
+
const publicHeaders = new Set(["accept", "accept-language", "cache-control", "pragma"]);
|
|
203
|
+
for (const key of Array.from(headers.keys()))
|
|
204
|
+
if (!publicHeaders.has(key))
|
|
205
|
+
headers.delete(key);
|
|
206
|
+
}
|
|
207
|
+
if (dropBody) {
|
|
208
|
+
for (const key of ["content-type", "content-length", "content-encoding", "content-language", "content-location"])
|
|
209
|
+
headers.delete(key);
|
|
161
210
|
}
|
|
211
|
+
const redirectInit = dropBody ? {
|
|
212
|
+
method: "GET", signal: request.signal, credentials: request.credentials,
|
|
213
|
+
cache: request.cache, mode: request.mode, redirect, referrer: request.referrer,
|
|
214
|
+
referrerPolicy: request.referrerPolicy, integrity: request.integrity, keepalive: request.keepalive,
|
|
215
|
+
} : request;
|
|
216
|
+
const redirected = new Request(next, redirectInit);
|
|
217
|
+
request = new Request(redirected, {
|
|
218
|
+
headers,
|
|
219
|
+
...(crossOrigin ? { credentials: "omit", referrer: "", referrerPolicy: "no-referrer" } : {}),
|
|
220
|
+
});
|
|
162
221
|
}
|
|
163
222
|
throw new BlockedRequestError(`too many redirects (more than ${MAX_REDIRECTS})`);
|
|
164
223
|
};
|
package/dist/safe-fetch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-fetch.js","sourceRoot":"","sources":["../src/safe-fetch.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,wEAAwE;AACxE,8EAA8E;AAC9E,+EAA+E;AAC/E,uEAAuE;AACvE,EAAE;AACF,gFAAgF;AAChF,mEAAmE;AACnE,sEAAsE;AACtE,6EAA6E;AAC7E,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,uDAAuD;AAEvD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,qDAAqD;AACrD,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,UAAU,GAAuB;IACrC,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,YAAY;IACtC,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,aAAa;IACvC,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,4BAA4B;IACtD,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,uBAAuB;IACjD,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,uCAAuC;IACjE,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,gBAAgB;IAC1C,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,eAAe;IACzC,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,iBAAiB;IAC3C,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,6BAA6B;IACvD,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,wBAAwB;IAClD,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,mCAAmC;CAC9D,CAAC;AAEF,SAAS,OAAO,CAAC,EAAU;IACzB,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG;YAAE,OAAO,IAAI,CAAC;QAC1D,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,CAAC,4CAA4C;QACzE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,
|
|
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
|
-
*
|
|
5
|
+
* Production-RC MCP surface.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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<{
|
|
@@ -47,7 +50,7 @@ export declare function createServer(config?: McpConfig): Promise<{
|
|
|
47
50
|
} | undefined;
|
|
48
51
|
} | undefined;
|
|
49
52
|
}>;
|
|
50
|
-
agent: AiFinPayAgent;
|
|
53
|
+
readonly agent: AiFinPayAgent;
|
|
51
54
|
}>;
|
|
52
55
|
export interface ToolContext {
|
|
53
56
|
agent: AiFinPayAgent;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,
|
|
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;AAQnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AA2B7C;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAAC,MAAM,GAAE,SAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+GxD;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,139 +1,143 @@
|
|
|
1
1
|
import { AiFinPayAgent } from "@aifinpay/agent";
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
import {
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { loadWalletIdentity } from "./identity.js";
|
|
6
|
+
import { agentHistoryTool, runAgentHistory } from "./tools/agent-history.js";
|
|
7
|
+
import { devPaymentQuoteTool, runDevPaymentQuote } from "./tools/dev-payment-quote.js";
|
|
5
8
|
import { agentAddressTool, runAgentAddress } from "./tools/agent-address.js";
|
|
6
|
-
import {
|
|
7
|
-
import { agentCallTool, runAgentCall } from "./tools/agent-call.js";
|
|
8
|
-
import { agentClaimSelfTool, runAgentClaimSelf } from "./tools/agent-claim-self.js";
|
|
9
|
+
import { agentQuotaTool, runAgentQuota } from "./tools/agent-quota.js";
|
|
9
10
|
import { makeSafeFetch } from "./safe-fetch.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
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.
|
|
11
|
+
import { loadConfigFromEnv } from "./config.js";
|
|
12
|
+
import { agentPassportResolveTool, runAgentPassportResolve, settlementRoutesTool, runSettlementRoutes, settlementInvoiceTool, runSettlementInvoice, } from "./tools/production-control.js";
|
|
13
|
+
// Every backend/public request made by this server goes through safeFetch.
|
|
14
|
+
// Public deployments never lift private-network protection; local development
|
|
15
|
+
// may explicitly use AIFINPAY_ALLOW_PRIVATE_FETCH=1.
|
|
21
16
|
const safeFetch = makeSafeFetch({
|
|
22
17
|
allowPrivate: process.env.AIFINPAY_ALLOW_PRIVATE_FETCH === "1",
|
|
18
|
+
// Exact hosts an operator vouches for. Skips only the DNS pre-check, and only
|
|
19
|
+
// for those names — see safe-fetch.ts for why this is not a proxy switch.
|
|
20
|
+
trustedHosts: loadConfigFromEnv().trustedHosts,
|
|
23
21
|
});
|
|
24
22
|
/**
|
|
25
|
-
*
|
|
23
|
+
* Production-RC MCP surface.
|
|
26
24
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
25
|
+
* IMPORTANT: the old agent_call, payable_fetch, pay_with_split, quote_split,
|
|
26
|
+
* agent_claim_self and agent_quote tools are deliberately NOT registered.
|
|
27
|
+
* They depend on @aifinpay/agent 1.x legacy splitter/x402 semantics and must not
|
|
28
|
+
* be available to a model after the 0% AIFP-2 / gross-inclusive AIFP-1 change.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
30
|
+
* This server remains useful before the SDK 2.0 package is published: it can
|
|
31
|
+
* expose the wallet address, resolve Agent Passport identity, read verified
|
|
32
|
+
* v1.3 routes, and construct a non-signing settlement invoice. Signing/moving
|
|
33
|
+
* value returns only after MCP depends on the published v2 executor and its E2E
|
|
34
|
+
* release gate has passed.
|
|
34
35
|
*/
|
|
35
36
|
export async function createServer(config = {}) {
|
|
36
37
|
const log = config.logFn ?? defaultLog;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
log("warn", `[aifinpay-mcp] no AIFINPAY_AGENT_SECRET set — generated an EPHEMERAL, NON-RECOVERABLE agent.\n` +
|
|
65
|
-
` solana_address: ${a.solanaAddress}\n` +
|
|
66
|
-
` evm_address: ${a.evmAddress}\n` +
|
|
67
|
-
` >> DO NOT FUND these addresses. This identity is lost when the process exits.\n` +
|
|
68
|
-
` >> For a persistent wallet, create one from a seed you back up\n` +
|
|
69
|
-
` (AiFinPayAgent.fromSeed / \`aifinpay init\`) and set AIFINPAY_AGENT_SECRET.`);
|
|
70
|
-
return a;
|
|
71
|
-
})();
|
|
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.
|
|
38
|
+
let identitySource = "ephemeral";
|
|
39
|
+
async function configuredAgent() {
|
|
40
|
+
const identity = loadWalletIdentity(config);
|
|
41
|
+
if (!identity)
|
|
42
|
+
return null;
|
|
43
|
+
const options = { fetchImpl: safeFetch, baseUrl: config.baseUrl, timeoutMs: config.timeoutMs };
|
|
44
|
+
const loaded = identity.seedHash
|
|
45
|
+
? await AiFinPayAgent.fromSeed(identity.seedHash, options)
|
|
46
|
+
: await AiFinPayAgent.fromSolanaSecret(identity.secretB58, options);
|
|
47
|
+
return { loaded, source: identity.source };
|
|
48
|
+
}
|
|
49
|
+
const configured = await configuredAgent();
|
|
50
|
+
let agent = configured?.loaded ?? await AiFinPayAgent.new({
|
|
51
|
+
fetchImpl: safeFetch, baseUrl: config.baseUrl, timeoutMs: config.timeoutMs,
|
|
52
|
+
});
|
|
53
|
+
identitySource = configured?.source ?? "ephemeral";
|
|
54
|
+
if (!configured)
|
|
55
|
+
log("warn", "[aifinpay-mcp] EPHEMERAL wallet — DO NOT FUND. Run `npx @aifinpay/mcp init`, then agent_reload in this connection.");
|
|
56
|
+
// Keep the legacy agent budget configured even though this RC exposes no
|
|
57
|
+
// signing tool. It remains an additional defence for downstream/private code
|
|
58
|
+
// and for the subsequent v2 MCP executor integration.
|
|
76
59
|
if (config.maxAmountUsd !== undefined && Number.isFinite(config.maxAmountUsd)) {
|
|
77
60
|
agent.setBudget({ per_call_usd: config.maxAmountUsd });
|
|
78
61
|
log("info", `[aifinpay-mcp] per-call cap: $${config.maxAmountUsd} (AIFINPAY_MAX_USD)`);
|
|
79
62
|
}
|
|
80
|
-
|
|
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}`);
|
|
63
|
+
log("info", `[aifinpay-mcp] production RC safe surface · solana: ${agent.solanaAddress} · evm: ${agent.evmAddress}`);
|
|
84
64
|
const server = new Server({
|
|
85
65
|
name: "@aifinpay/mcp",
|
|
86
|
-
version: "
|
|
87
|
-
}, {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
server.setRequestHandler(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
agentClaimSelfTool(),
|
|
98
|
-
payableFetchTool(),
|
|
99
|
-
agentQuoteTool(),
|
|
100
|
-
payWithSplitTool(),
|
|
101
|
-
quoteSplitTool(),
|
|
102
|
-
],
|
|
103
|
-
};
|
|
66
|
+
version: JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version,
|
|
67
|
+
}, { capabilities: { tools: {}, resources: {} } });
|
|
68
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [{
|
|
69
|
+
uri: "aifinpay://skill", name: "AiFinPay skill", mimeType: "text/markdown",
|
|
70
|
+
description: "Wallet source priority, payment history routes, reconnect behavior and dev testing limits",
|
|
71
|
+
}] }));
|
|
72
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
73
|
+
if (request.params.uri !== "aifinpay://skill")
|
|
74
|
+
throw new Error("Unknown resource");
|
|
75
|
+
return { contents: [{ uri: "aifinpay://skill", mimeType: "text/markdown",
|
|
76
|
+
text: readFileSync(new URL("../skills/SKILL.md", import.meta.url), "utf8") }] };
|
|
104
77
|
});
|
|
78
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
79
|
+
tools: [
|
|
80
|
+
agentAddressTool(),
|
|
81
|
+
{
|
|
82
|
+
name: "agent_reload",
|
|
83
|
+
description: "Reload the configured local wallet files after init or a file update, without starting a new conversation. Returns public addresses only. Shell environment changes still require reconnecting the MCP process.",
|
|
84
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
85
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
86
|
+
},
|
|
87
|
+
agentQuotaTool(),
|
|
88
|
+
agentHistoryTool(),
|
|
89
|
+
...(config.devMode ? [devPaymentQuoteTool()] : []),
|
|
90
|
+
agentPassportResolveTool(),
|
|
91
|
+
settlementRoutesTool(),
|
|
92
|
+
settlementInvoiceTool(),
|
|
93
|
+
],
|
|
94
|
+
}));
|
|
105
95
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
106
96
|
const { name, arguments: args } = request.params;
|
|
107
97
|
const ctx = { agent, config, log };
|
|
108
98
|
switch (name) {
|
|
99
|
+
case "agent_reload":
|
|
100
|
+
try {
|
|
101
|
+
const replacement = await configuredAgent();
|
|
102
|
+
if (!replacement)
|
|
103
|
+
throw new Error("No persistent wallet configured; run init or configure the project wallet first");
|
|
104
|
+
if (config.maxAmountUsd !== undefined && Number.isFinite(config.maxAmountUsd)) {
|
|
105
|
+
replacement.loaded.setBudget({ per_call_usd: config.maxAmountUsd });
|
|
106
|
+
}
|
|
107
|
+
agent = replacement.loaded;
|
|
108
|
+
identitySource = replacement.source;
|
|
109
|
+
return { content: [{ type: "text", text: JSON.stringify({
|
|
110
|
+
source: identitySource, solana: agent.solanaAddress, evm: agent.evmAddress,
|
|
111
|
+
casper: agent.casperAddress, reloaded: true,
|
|
112
|
+
}) }] };
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return { isError: true, content: [{ type: "text", text: `Wallet reload failed: ${error.message}` }] };
|
|
116
|
+
}
|
|
109
117
|
case "agent_address":
|
|
110
118
|
return runAgentAddress(ctx, args ?? {});
|
|
111
|
-
case "
|
|
112
|
-
return
|
|
113
|
-
case "
|
|
114
|
-
return
|
|
115
|
-
case "
|
|
116
|
-
return
|
|
117
|
-
case "
|
|
118
|
-
return
|
|
119
|
-
case "
|
|
120
|
-
return
|
|
121
|
-
case "
|
|
122
|
-
return
|
|
119
|
+
case "agent_quota":
|
|
120
|
+
return runAgentQuota(ctx, args ?? {});
|
|
121
|
+
case "agent_history":
|
|
122
|
+
return runAgentHistory(ctx, args ?? {});
|
|
123
|
+
case "dev_payment_quote":
|
|
124
|
+
return runDevPaymentQuote(ctx, args ?? {});
|
|
125
|
+
case "agent_passport_resolve":
|
|
126
|
+
return runAgentPassportResolve(ctx, args ?? {});
|
|
127
|
+
case "settlement_routes":
|
|
128
|
+
return runSettlementRoutes(ctx, args ?? {});
|
|
129
|
+
case "settlement_invoice":
|
|
130
|
+
return runSettlementInvoice(ctx, args ?? {});
|
|
123
131
|
default:
|
|
124
132
|
return {
|
|
125
133
|
isError: true,
|
|
126
|
-
content: [
|
|
127
|
-
{ type: "text", text: `unknown tool: ${name}` },
|
|
128
|
-
],
|
|
134
|
+
content: [{ type: "text", text: `unknown or retired tool: ${name}` }],
|
|
129
135
|
};
|
|
130
136
|
}
|
|
131
137
|
});
|
|
132
|
-
return { server, agent };
|
|
138
|
+
return { server, get agent() { return agent; } };
|
|
133
139
|
}
|
|
134
140
|
function defaultLog(level, msg) {
|
|
135
|
-
// MCP stdio servers MUST NOT write to stdout — the transport owns it.
|
|
136
|
-
// stderr is safe.
|
|
137
141
|
process.stderr.write(`[${level}] ${msg}\n`);
|
|
138
142
|
}
|
|
139
143
|
//# sourceMappingURL=server.js.map
|