@aifinpay/mcp 1.5.3 → 2.0.0-rc.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 +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/{payable-fetch.d.ts → agent-history.d.ts} +21 -23
- 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-quote.d.ts → agent-quota.d.ts} +7 -8
- 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/{pay-with-split.d.ts → production-control.d.ts} +49 -34
- 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 +7 -21
- package/skills/SKILL.md +143 -0
- package/dist/directory.d.ts +0 -55
- package/dist/directory.d.ts.map +0 -1
- package/dist/directory.js +0 -56
- package/dist/directory.js.map +0 -1
- package/dist/tools/agent-call.d.ts +0 -68
- package/dist/tools/agent-call.d.ts.map +0 -1
- package/dist/tools/agent-call.js +0 -129
- package/dist/tools/agent-call.js.map +0 -1
- package/dist/tools/agent-claim-self.d.ts +0 -66
- package/dist/tools/agent-claim-self.d.ts.map +0 -1
- package/dist/tools/agent-claim-self.js +0 -299
- package/dist/tools/agent-claim-self.js.map +0 -1
- package/dist/tools/agent-quote.d.ts.map +0 -1
- package/dist/tools/agent-quote.js +0 -81
- package/dist/tools/agent-quote.js.map +0 -1
- package/dist/tools/pay-with-split.d.ts.map +0 -1
- package/dist/tools/pay-with-split.js +0 -138
- package/dist/tools/pay-with-split.js.map +0 -1
- package/dist/tools/payable-fetch.d.ts.map +0 -1
- package/dist/tools/payable-fetch.js +0 -135
- package/dist/tools/payable-fetch.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,109 +1,156 @@
|
|
|
1
1
|
# @aifinpay/mcp
|
|
2
2
|
|
|
3
|
-
MCP server
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
protocol itself is live across 13 networks — see
|
|
14
|
-
[aifinpay.io/llms.txt](https://aifinpay.io/llms.txt).
|
|
3
|
+
AiFinPay MCP server for persistent agent identity, Agent Passport resolution,
|
|
4
|
+
route discovery and non-signing settlement invoices. Canonical domain:
|
|
5
|
+
**aifinpay.io**.
|
|
6
|
+
|
|
7
|
+
This 2.0 release candidate does not expose payment-signing tools. Returning a
|
|
8
|
+
wallet address or preparing an invoice does not authorize or execute a payment.
|
|
9
|
+
Signing remains gated on the verified SDK v2 executor and its release evidence.
|
|
10
|
+
Local MCP tools for wallet discovery, payment history, quotas and non-signing
|
|
11
|
+
payment preparation. Read the bundled [skill](skills/SKILL.md), also exposed
|
|
12
|
+
as the MCP resource `aifinpay://skill`.
|
|
15
13
|
|
|
16
14
|
## Tools
|
|
17
15
|
|
|
18
|
-
| Tool |
|
|
16
|
+
| Tool | Purpose |
|
|
19
17
|
|---|---|
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
| `agent_address` | Read the current Solana, EVM and Casper addresses. |
|
|
19
|
+
| `agent_reload` | Reload local wallet files in the current MCP connection. |
|
|
20
|
+
| `agent_quota` | Read the agent's quota. |
|
|
21
|
+
| `agent_passport_resolve` | Resolve the global Agent Passport identity. |
|
|
22
|
+
| `settlement_routes` | Read the available verified settlement routes. |
|
|
23
|
+
| `settlement_invoice` | Prepare a non-signing settlement invoice. |
|
|
24
|
+
|
|
25
|
+
Legacy `payable_fetch`, `agent_call`, `agent_quote`, `pay_with_split`,
|
|
26
|
+
`quote_split` and `agent_claim_self` tools are not registered by this RC.
|
|
27
|
+
|
|
28
|
+
## Persistent wallet selection
|
|
29
|
+
|
|
30
|
+
The CLI (`init` and stdio), programmatic server and `agent_reload` use the same
|
|
31
|
+
priority:
|
|
32
|
+
|
|
33
|
+
1. `SEED_HASH`: an existing 32-byte seed encoded as 64 hex characters, optionally
|
|
34
|
+
prefixed with `0x`. It is passed directly to `AiFinPayAgent.fromSeed`; do not
|
|
35
|
+
hash it again or supply a mnemonic.
|
|
36
|
+
2. `./aifinpay/agents.json`, relative to the MCP process's working directory.
|
|
37
|
+
Set an absolute `AIFINPAY_AGENTS_FILE` when a desktop client's working
|
|
38
|
+
directory differs from the project directory. The file contains an `agents`
|
|
39
|
+
array of records with `id` and private `seed_hash` fields. If there is more
|
|
40
|
+
than one record, select exactly one with `AIFINPAY_AGENT_ID`.
|
|
41
|
+
3. `AIFINPAY_AGENT_SECRET`: a legacy base58 Solana secret.
|
|
42
|
+
4. `~/.aifinpay/agent.json`, or `AIFINPAY_HOME/agent.json`: the legacy CLI
|
|
43
|
+
keystore. Encrypted files require `AIFINPAY_WALLET_PASSPHRASE`.
|
|
44
|
+
|
|
45
|
+
Keep seeds and wallet files private and out of chat, source control and logs.
|
|
46
|
+
Invalid or ambiguous configured inputs fail; they never generate a replacement
|
|
47
|
+
wallet. With no configured wallet at all the server has an ephemeral identity:
|
|
48
|
+
**do not fund it**.
|
|
49
|
+
|
|
50
|
+
## Initialize and connect
|
|
51
|
+
This RC does not register payment-signing tools. Its legacy SDK dependency
|
|
52
|
+
supplies wallet derivation only; installing this MCP does not enable v2
|
|
53
|
+
settlement. An invoice or quote is not a completed payment.
|
|
54
|
+
|
|
55
|
+
## Local configuration
|
|
30
56
|
```bash
|
|
31
|
-
|
|
32
|
-
# (installs the latest stable — the old @alpha tag is retired, don't use it)
|
|
33
|
-
npm install -g @aifinpay/mcp
|
|
57
|
+
npx @aifinpay/mcp@next init
|
|
34
58
|
```
|
|
35
59
|
|
|
36
|
-
|
|
60
|
+
Select the wallet in this order: `SEED_HASH` → `./aifinpay/agents.json` →
|
|
61
|
+
legacy `AIFINPAY_AGENT_SECRET` → `~/.aifinpay/agent.json`.
|
|
62
|
+
SEED_HASH means a 32-byte hex seed, passed directly to SDK `fromSeed`.
|
|
63
|
+
See the skill for the exact project-file schema and multi-agent selection.
|
|
64
|
+
`init` prints the selected wallet's public addresses. If a seed or project wallet
|
|
65
|
+
is already configured, it does not create a second legacy wallet. With no
|
|
66
|
+
wallet, it creates the legacy keystore with mode `600`; existing keystores are
|
|
67
|
+
retained. Back up that file privately. The `@next` tag selects the 2.0 RC lane;
|
|
68
|
+
use the release containing these changes, or build this source checkout.
|
|
37
69
|
|
|
38
|
-
|
|
70
|
+
Use an absolute `AIFINPAY_AGENTS_FILE` path when the host's working directory
|
|
71
|
+
is not your project directory. Do not put the seed itself in a shared config.
|
|
72
|
+
A client configuration can use the keystore without embedding its secret:
|
|
39
73
|
|
|
40
74
|
```json
|
|
41
75
|
{
|
|
42
76
|
"mcpServers": {
|
|
43
77
|
"aifinpay": {
|
|
44
78
|
"command": "npx",
|
|
45
|
-
"args": ["@aifinpay/mcp"],
|
|
79
|
+
"args": ["-y", "@aifinpay/mcp@2.0.0-rc.12"],
|
|
46
80
|
"env": {
|
|
47
|
-
"
|
|
48
|
-
"
|
|
81
|
+
"AIFINPAY_AGENTS_FILE": "/absolute/project/aifinpay/agents.json",
|
|
82
|
+
"AIFINPAY_AGENT_ID": "research-agent"
|
|
49
83
|
}
|
|
50
84
|
}
|
|
51
85
|
}
|
|
52
86
|
}
|
|
53
87
|
```
|
|
54
88
|
|
|
55
|
-
|
|
56
|
-
|
|
89
|
+
The release above must be published before this npx command can install it.
|
|
90
|
+
For a source checkout, run `npm ci && npm run build` in mcp/ and configure
|
|
91
|
+
`node /absolute/path/to/mcp/bin/aifinpay-mcp.js` instead.
|
|
57
92
|
|
|
58
|
-
|
|
93
|
+
If using the legacy keystore, run `npx @aifinpay/mcp init` once and omit the
|
|
94
|
+
project-file variables. Optional `AIFINPAY_WALLET_PASSPHRASE` encrypts that
|
|
95
|
+
legacy file at creation. An already connected server can load it with
|
|
96
|
+
`agent_reload`; it does not need a new conversation. A changed package or
|
|
97
|
+
launch environment requires a server reconnect, subject to host support.
|
|
59
98
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
secret: 4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
67
|
-
>> Save this secret to AIFINPAY_AGENT_SECRET to keep the agent across restarts.
|
|
68
|
-
```
|
|
99
|
+
## History
|
|
100
|
+
Connect the MCP server once. After `init` or a wallet file update, call
|
|
101
|
+
`agent_reload` in that same connection; no new conversation is required. A
|
|
102
|
+
failed reload keeps the previous wallet. Changed shell environment variables
|
|
103
|
+
require reconnecting the MCP process, since its environment is a startup
|
|
104
|
+
snapshot. Verify `agent_address` against the wallet you intend to use.
|
|
69
105
|
|
|
70
|
-
|
|
71
|
-
agent identity (and any funded Seat) persists across restarts.
|
|
106
|
+
## Other environment variables
|
|
72
107
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
| Var | Default | Purpose |
|
|
108
|
+
| Variable | Default | Purpose |
|
|
76
109
|
|---|---|---|
|
|
77
|
-
| `
|
|
78
|
-
| `AIFINPAY_BASE_URL` | `https://aifinpay.io` | Backend URL for nonce + funding probes. |
|
|
110
|
+
| `AIFINPAY_BASE_URL` | `https://aifinpay.io` | Backend URL. |
|
|
79
111
|
| `AIFINPAY_TIMEOUT_MS` | `30000` | Request timeout. |
|
|
80
|
-
| `AIFINPAY_MAX_USD` | — |
|
|
112
|
+
| `AIFINPAY_MAX_USD` | — | Configures the underlying agent's per-call cap; it does not enable signing. |
|
|
113
|
+
| `AIFINPAY_GATEWAY_ORIGINS` | SDK default | Comma-separated exact HTTPS origins trusted for AIFP-1 settlement. |
|
|
114
|
+
| `AIFINPAY_GATEWAY_PATH_MODE` | `gateway` | Use `gateway` for merchant-slug identity or `direct` for full request-path identity. |
|
|
81
115
|
|
|
82
|
-
|
|
116
|
+
`agent_history({address, source:"transactions"})` reads indexed Polygon
|
|
117
|
+
settlements; `source:"receipts"` reads retained batches, including test-mode
|
|
118
|
+
payments. Pass `passport` instead of, or alongside, `address` to resolve a
|
|
119
|
+
public passport first. A backend without the passport resolver cannot satisfy
|
|
120
|
+
passport-only queries. No secret API key is needed for public metadata.
|
|
83
121
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
122
|
+
Responses do not include bearer receipts. Limits are 1..100; follow
|
|
123
|
+
`next_offset`. History source/coverage is explicit: it is not a full wallet
|
|
124
|
+
explorer. Dev networks currently use receipt history, not the Polygon ledger.
|
|
87
125
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
agentSecretB58: "your-secret-here",
|
|
91
|
-
maxAmountUsd: 0.10,
|
|
92
|
-
});
|
|
126
|
+
```
|
|
127
|
+
const { server } = await createServer(loadConfigFromEnv());
|
|
93
128
|
await server.connect(new StdioServerTransport());
|
|
94
129
|
```
|
|
95
130
|
|
|
96
|
-
|
|
131
|
+
The programmatic equivalents of identity environment options are `seedHash`,
|
|
132
|
+
`agentsFile`, `agentId`, `agentSecretB58`, `walletHome` and `walletPassphrase`.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
- AIFINPAY_BASE_URL: backend origin; default https://aifinpay.io.
|
|
136
|
+
- AIFINPAY_HOME: directory of the legacy agent.json keystore.
|
|
137
|
+
- AIFINPAY_MODE=dev: expose dev_payment_quote; requires a separate dev base URL.
|
|
138
|
+
|
|
139
|
+
- AIFINPAY_TIMEOUT_MS: SDK request timeout.
|
|
140
|
+
- AIFINPAY_MAX_USD: legacy per-call budget; does not enable signing.
|
|
141
|
+
- AIFINPAY_TRUSTED_HOSTS: exact hosts allowed to bypass the DNS pre-check.
|
|
142
|
+
- AIFINPAY_ALLOW_PRIVATE_FETCH=1: explicit local-development network access.
|
|
143
|
+
Never enable this on the public hosted MCP.
|
|
97
144
|
|
|
98
|
-
1
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
145
|
+
For a self-hosted AIFP-1 gateway, set `AIFINPAY_GATEWAY_ORIGINS` to exact HTTPS
|
|
146
|
+
origins. `AIFINPAY_GATEWAY_PATH_MODE=direct` makes the process use each request's
|
|
147
|
+
full pathname as its AIFP-1 resource identity; `gateway` uses the merchant slug.
|
|
148
|
+
The mode is process-wide, does not broaden the trusted-origin allowlist, and
|
|
149
|
+
does not enable the retired signing tools in the production RC.
|
|
103
150
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
151
|
+
Dev quoting does not bypass wallet signatures, issuer verification or
|
|
152
|
+
receipt metering. See the bundled skill for backend prerequisites and the
|
|
153
|
+
remaining Amoy executor requirement.
|
|
107
154
|
|
|
108
155
|
## License
|
|
109
156
|
|
package/bin/aifinpay-mcp.js
CHANGED
|
@@ -22,15 +22,18 @@
|
|
|
22
22
|
*
|
|
23
23
|
* Configure via env (all optional; the secret is read from the keystore below
|
|
24
24
|
* when the variable is unset):
|
|
25
|
-
*
|
|
25
|
+
* SEED_HASH 32-byte hex seed — highest priority
|
|
26
|
+
* AIFINPAY_AGENTS_FILE project agents file — second priority
|
|
27
|
+
* AIFINPAY_AGENT_SECRET legacy base58 secret — overrides the legacy keystore
|
|
26
28
|
* AIFINPAY_BASE_URL default https://aifinpay.io
|
|
27
29
|
* AIFINPAY_TIMEOUT_MS default 30000
|
|
28
30
|
* AIFINPAY_MAX_USD hard cap per single payment (no default)
|
|
29
31
|
*/
|
|
30
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync,
|
|
32
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, openSync, closeSync, fchmodSync, fsyncSync, linkSync, unlinkSync } from "node:fs";
|
|
31
33
|
import { join } from "node:path";
|
|
32
34
|
import { homedir } from "node:os";
|
|
33
35
|
import { createRequire } from "node:module";
|
|
36
|
+
import { scryptSync, randomBytes, createCipheriv, createDecipheriv } from "node:crypto";
|
|
34
37
|
|
|
35
38
|
const HOME = process.env.AIFINPAY_HOME || join(homedir(), ".aifinpay");
|
|
36
39
|
const KEYSTORE = join(HOME, "agent.json");
|
|
@@ -61,7 +64,7 @@ Run init once. It writes ${KEYSTORE} with mode 600 and the server picks
|
|
|
61
64
|
it up automatically — you do not have to put the secret in a config file.
|
|
62
65
|
|
|
63
66
|
Env (all optional):
|
|
64
|
-
AIFINPAY_AGENT_SECRET base58 secret;
|
|
67
|
+
SEED_HASH 32-byte hex seed; highest priority\n AIFINPAY_AGENTS_FILE default ./aifinpay/agents.json; second priority\n AIFINPAY_AGENT_ID select one record when the file has multiple agents\n AIFINPAY_AGENT_SECRET legacy base58 secret; after project wallet sources
|
|
65
68
|
AIFINPAY_MAX_USD hard cap per single payment — set this
|
|
66
69
|
AIFINPAY_BASE_URL default https://aifinpay.io
|
|
67
70
|
AIFINPAY_TIMEOUT_MS default 30000
|
|
@@ -77,31 +80,121 @@ if (arg === "--version" || arg === "-v" || arg === "version") {
|
|
|
77
80
|
|
|
78
81
|
// ── init ──────────────────────────────────────────────────────────────────
|
|
79
82
|
|
|
83
|
+
const PASSPHRASE = process.env.AIFINPAY_WALLET_PASSPHRASE || null;
|
|
84
|
+
|
|
85
|
+
// Optional at-rest encryption. Mode 600 keeps the keystore from OTHER users; it
|
|
86
|
+
// does nothing against malware running as YOU — the threat the OSINT write-up of
|
|
87
|
+
// a compromised dev machine described. A passphrase makes the on-disk file
|
|
88
|
+
// ciphertext; the secret is only in memory while the passphrase is supplied.
|
|
89
|
+
// Opt-in and env-supplied so the non-interactive `npx @aifinpay/mcp` launch path
|
|
90
|
+
// is unaffected: no passphrase => plaintext, exactly as before. scrypt to
|
|
91
|
+
// stretch, AES-256-GCM so tampering is detected rather than decrypting to junk.
|
|
92
|
+
function encryptSecret(secretB58) {
|
|
93
|
+
const salt = randomBytes(16), iv = randomBytes(12);
|
|
94
|
+
const key = scryptSync(PASSPHRASE, salt, 32, { N: 1 << 15, r: 8, p: 1, maxmem: 64 * 1024 * 1024 });
|
|
95
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
96
|
+
const ct = Buffer.concat([cipher.update(secretB58, "utf8"), cipher.final()]);
|
|
97
|
+
return { enc: "scrypt-aes-256-gcm", salt: salt.toString("base64"), iv: iv.toString("base64"),
|
|
98
|
+
tag: cipher.getAuthTag().toString("base64"), ct: ct.toString("base64"),
|
|
99
|
+
created: new Date().toISOString() };
|
|
100
|
+
}
|
|
101
|
+
function decryptSecret(store) {
|
|
102
|
+
if (!PASSPHRASE) throw new Error(`${KEYSTORE} is encrypted but AIFINPAY_WALLET_PASSPHRASE is not set.`);
|
|
103
|
+
const key = scryptSync(PASSPHRASE, Buffer.from(store.salt, "base64"), 32, { N: 1 << 15, r: 8, p: 1, maxmem: 64 * 1024 * 1024 });
|
|
104
|
+
const dc = createDecipheriv("aes-256-gcm", key, Buffer.from(store.iv, "base64"));
|
|
105
|
+
dc.setAuthTag(Buffer.from(store.tag, "base64"));
|
|
106
|
+
try { return Buffer.concat([dc.update(Buffer.from(store.ct, "base64")), dc.final()]).toString("utf8"); }
|
|
107
|
+
catch { throw new Error(`could not decrypt ${KEYSTORE}: wrong AIFINPAY_WALLET_PASSPHRASE or the file was modified.`); }
|
|
108
|
+
}
|
|
109
|
+
|
|
80
110
|
function readKeystore() {
|
|
81
111
|
if (!existsSync(KEYSTORE)) return null;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
112
|
+
let parsed;
|
|
113
|
+
try { parsed = JSON.parse(readFileSync(KEYSTORE, "utf8")); } catch { return null; }
|
|
114
|
+
// A decrypt failure THROWS rather than returning null — null reads as "no
|
|
115
|
+
// wallet" and mints a new one, which is how a mistyped passphrase loses a key.
|
|
116
|
+
if (parsed?.enc) return { secretB58: decryptSecret(parsed), created: parsed.created, encrypted: true };
|
|
117
|
+
return typeof parsed?.secretB58 === "string" ? parsed : null;
|
|
88
118
|
}
|
|
89
119
|
|
|
90
120
|
if (arg === "init") {
|
|
91
121
|
const { Agent, AiFinPayAgent } = await import("@aifinpay/agent");
|
|
92
122
|
|
|
123
|
+
const { loadConfigFromEnv } = await import("../dist/config.js");
|
|
124
|
+
const { loadWalletIdentity } = await import("../dist/identity.js");
|
|
125
|
+
const selected = loadWalletIdentity(loadConfigFromEnv());
|
|
126
|
+
if (selected && selected.source !== "legacy-keystore") {
|
|
127
|
+
const agent = selected.seedHash
|
|
128
|
+
? await AiFinPayAgent.fromSeed(selected.seedHash)
|
|
129
|
+
: await AiFinPayAgent.fromSolanaSecret(selected.secretB58);
|
|
130
|
+
process.stdout.write(`Using ${selected.source}; no replacement wallet created.\n` +
|
|
131
|
+
`EVM ${agent.evmAddress}\nSolana ${agent.solanaAddress}\nCasper ${agent.casperAddress}\n` +
|
|
132
|
+
`Keep your configured seed backed up privately. Call agent_reload in an already connected MCP server.\n`);
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
|
|
93
136
|
let store = readKeystore();
|
|
94
|
-
|
|
95
|
-
|
|
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 {
|
|
137
|
+
let freshlyCreated = false;
|
|
138
|
+
if (!store) {
|
|
100
139
|
mkdirSync(HOME, { recursive: true, mode: 0o700 });
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
140
|
+
const secretB58 = Agent.new().secretB58;
|
|
141
|
+
const candidate = { secretB58, created: new Date().toISOString() };
|
|
142
|
+
const onDisk = PASSPHRASE ? encryptSecret(secretB58) : candidate;
|
|
143
|
+
const temporary = join(HOME, `.agent-init-${process.pid}-${randomBytes(12).toString("hex")}.tmp`);
|
|
144
|
+
// Publish a complete file without replacing an existing wallet. Two init
|
|
145
|
+
// processes may both see no keystore; only one exclusive link can win.
|
|
146
|
+
// A direct exclusive write would expose a partially written file to readers.
|
|
147
|
+
let fd;
|
|
148
|
+
let ownsTemporary = false;
|
|
149
|
+
try {
|
|
150
|
+
fd = openSync(temporary, "wx", 0o600);
|
|
151
|
+
ownsTemporary = true;
|
|
152
|
+
fchmodSync(fd, 0o600);
|
|
153
|
+
writeFileSync(fd, JSON.stringify(onDisk, null, 2) + "\n");
|
|
154
|
+
fsyncSync(fd);
|
|
155
|
+
closeSync(fd);
|
|
156
|
+
fd = undefined;
|
|
157
|
+
try {
|
|
158
|
+
linkSync(temporary, KEYSTORE);
|
|
159
|
+
store = candidate;
|
|
160
|
+
freshlyCreated = true;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (error.code !== "EEXIST") throw error;
|
|
163
|
+
store = readKeystore();
|
|
164
|
+
if (!store) throw new Error(`Existing ${KEYSTORE} is unreadable; refusing to replace it.`);
|
|
165
|
+
}
|
|
166
|
+
} finally {
|
|
167
|
+
if (fd !== undefined) closeSync(fd);
|
|
168
|
+
if (ownsTemporary) unlinkSync(temporary);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (freshlyCreated) {
|
|
172
|
+
process.stdout.write(
|
|
173
|
+
PASSPHRASE
|
|
174
|
+
? `Created ${KEYSTORE} (mode 600, ENCRYPTED). Keep AIFINPAY_WALLET_PASSPHRASE — the wallet is unrecoverable without it.\n\n`
|
|
175
|
+
: `Created ${KEYSTORE} (mode 600, plaintext). For at-rest encryption, set AIFINPAY_WALLET_PASSPHRASE before init.\n\n`,
|
|
176
|
+
);
|
|
177
|
+
} else {
|
|
178
|
+
process.stdout.write(`Existing wallet found at ${KEYSTORE} — keeping it.\n\n`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// The one-time recovery output.
|
|
182
|
+
//
|
|
183
|
+
// Shown only to the successful creator of a plaintext wallet on a TTY.
|
|
184
|
+
// Piped output (including agent subprocess logs) contains public data only.
|
|
185
|
+
//
|
|
186
|
+
// Encrypted keystores print nothing here: recovery there IS the keystore file
|
|
187
|
+
// plus the passphrase, and re-printing the plaintext secret would defeat the
|
|
188
|
+
// encryption the user just asked for.
|
|
189
|
+
if (freshlyCreated && !PASSPHRASE && process.stdout.isTTY) {
|
|
190
|
+
process.stdout.write(
|
|
191
|
+
`\n RECOVERY KEY (shown once, never again):\n\n` +
|
|
192
|
+
` ${store.secretB58}\n\n` +
|
|
193
|
+
` Save this off this machine NOW. Anyone with it controls the wallet and\n` +
|
|
194
|
+
` its funds. Do NOT paste it into a chat, an issue, or a config file — the\n` +
|
|
195
|
+
` keystore at ${KEYSTORE} already holds it (encrypt it with\n` +
|
|
196
|
+
` AIFINPAY_WALLET_PASSPHRASE). This line is your OFF-machine backup.\n\n`,
|
|
197
|
+
);
|
|
105
198
|
}
|
|
106
199
|
|
|
107
200
|
const agent = await AiFinPayAgent.fromSolanaSecret(store.secretB58);
|
|
@@ -111,13 +204,13 @@ if (arg === "init") {
|
|
|
111
204
|
` EVM ${agent.evmAddress}\n` +
|
|
112
205
|
` Solana ${agent.solanaAddress}\n` +
|
|
113
206
|
` Casper ${agent.casperAddress}\n\n` +
|
|
114
|
-
`Add this to your MCP client config and
|
|
207
|
+
`Add this to your MCP client config and connect/reconnect this MCP server:\n\n` +
|
|
115
208
|
JSON.stringify(
|
|
116
209
|
{
|
|
117
210
|
mcpServers: {
|
|
118
211
|
aifinpay: {
|
|
119
212
|
command: "npx",
|
|
120
|
-
args: ["-y",
|
|
213
|
+
args: ["-y", `@aifinpay/mcp@${VERSION}`],
|
|
121
214
|
env: { AIFINPAY_MAX_USD: "0.10" },
|
|
122
215
|
},
|
|
123
216
|
},
|
|
@@ -126,11 +219,11 @@ if (arg === "init") {
|
|
|
126
219
|
2,
|
|
127
220
|
) +
|
|
128
221
|
`\n\nThe secret is NOT in that block on purpose — the server reads the\n` +
|
|
129
|
-
`keystore.
|
|
222
|
+
`keystore. If this server is already connected, call agent_reload after init.\nA new conversation is not required by this server.\n\n` +
|
|
130
223
|
`Back up ${KEYSTORE}. It is the only copy. The derivation is not\n` +
|
|
131
224
|
`BIP-39, so no standard wallet can recover this from a phrase.\n\n` +
|
|
132
|
-
`The
|
|
133
|
-
`
|
|
225
|
+
`The EVM address is used for Polygon payments. Check its balance before\n` +
|
|
226
|
+
`funding it or paying for calls.\n`,
|
|
134
227
|
);
|
|
135
228
|
process.exit(0);
|
|
136
229
|
}
|
|
@@ -142,31 +235,8 @@ if (arg && !arg.startsWith("-")) {
|
|
|
142
235
|
|
|
143
236
|
// ── server ────────────────────────────────────────────────────────────────
|
|
144
237
|
|
|
145
|
-
//
|
|
146
|
-
// a
|
|
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
|
-
|
|
238
|
+
// Identity selection is shared with the programmatic server and agent_reload.
|
|
239
|
+
// Do not copy a file secret into process.env: it would mask later file updates.
|
|
170
240
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
171
241
|
const { createServer, loadConfigFromEnv } = await import("../dist/index.js");
|
|
172
242
|
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzD"}
|
package/dist/api.js
ADDED
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,IAAY;IAC/C,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,iBAAiB,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnG,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC;AAC9C,CAAC"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,14 +1,47 @@
|
|
|
1
1
|
/** Runtime configuration loaded from env. */
|
|
2
2
|
export interface McpConfig {
|
|
3
|
-
/**
|
|
4
|
-
* generated AND printed to stderr at startup with a "save this!" warning. */
|
|
3
|
+
/** Legacy base58 secret, after SEED_HASH and the project agents file. */
|
|
5
4
|
agentSecretB58?: string;
|
|
5
|
+
/** 32-byte hex seed, consumed exactly as AiFinPayAgent.fromSeed does. */
|
|
6
|
+
seedHash?: string;
|
|
7
|
+
agentsFile?: string;
|
|
8
|
+
agentId?: string;
|
|
9
|
+
walletHome?: string;
|
|
10
|
+
walletPassphrase?: string;
|
|
11
|
+
/** Enables dev discovery/quoting only; never enables settlement signing. */
|
|
12
|
+
devMode?: boolean;
|
|
6
13
|
/** Custom AiFinPay backend URL. Defaults to production. */
|
|
7
14
|
baseUrl?: string;
|
|
8
15
|
/** Request timeout in ms. */
|
|
9
16
|
timeoutMs?: number;
|
|
10
17
|
/** Hard cap on a single payment to prevent runaway agents. */
|
|
11
18
|
maxAmountUsd?: number;
|
|
19
|
+
/** Gateway origins this agent may settle against.
|
|
20
|
+
*
|
|
21
|
+
* The SDK has always supported this (parseGatewayUrl in @aifinpay/agent) and
|
|
22
|
+
* this wrapper never exposed it, so a self-hosted merchant was unreachable:
|
|
23
|
+
* payable_fetch reached the 402 and then refused with "dev.ratersapp.com is
|
|
24
|
+
* not a known AiFinPay gateway; allowed: https://gateway.aifinpay.io".
|
|
25
|
+
* Observed in an external E2E run on 2026-08-27.
|
|
26
|
+
*
|
|
27
|
+
* Set by the OPERATOR, exact origins only, never a wildcard. The refusal it
|
|
28
|
+
* relaxes is not paranoia: a 402 is unauthenticated, so paying an
|
|
29
|
+
* unrecognised host means paying whoever answered. Naming a host here is a
|
|
30
|
+
* statement that you know who that is. */
|
|
31
|
+
gatewayOrigins?: string[];
|
|
32
|
+
/** AIFP-1 resource identity: merchant slug or full direct request path. */
|
|
33
|
+
gatewayPathMode?: "gateway" | "direct";
|
|
34
|
+
/** Hosts whose DNS pre-check is skipped, exact names only.
|
|
35
|
+
*
|
|
36
|
+
* safe-fetch resolves a hostname and refuses if any answer is a private
|
|
37
|
+
* address — an SSRF guard. Behind an HTTP proxy the client cannot resolve at
|
|
38
|
+
* all (getaddrinfo EAI_AGAIN), so every host is refused for the wrong reason.
|
|
39
|
+
*
|
|
40
|
+
* This is deliberately NOT a proxy-detection switch that disables the check
|
|
41
|
+
* globally: that would turn one environment quirk into a blanket SSRF
|
|
42
|
+
* bypass. An operator names the hosts they vouch for, one at a time, and the
|
|
43
|
+
* guard stays on for everything else. */
|
|
44
|
+
trustedHosts?: string[];
|
|
12
45
|
/** Optional log destination (defaults to stderr). */
|
|
13
46
|
logFn?: (level: "info" | "warn" | "error", msg: string) => void;
|
|
14
47
|
}
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,WAAW,SAAS;IACxB;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,WAAW,SAAS;IACxB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,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,2EAA2E;IAC3E,eAAe,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAEvC;;;;;;;;;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,CAuB7C"}
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
export function loadConfigFromEnv() {
|
|
2
|
+
if (process.env.AIFINPAY_MODE && !['live', 'dev'].includes(process.env.AIFINPAY_MODE)) {
|
|
3
|
+
throw new Error('AIFINPAY_MODE must be live or dev');
|
|
4
|
+
}
|
|
2
5
|
return {
|
|
6
|
+
devMode: process.env.AIFINPAY_MODE === 'dev',
|
|
7
|
+
seedHash: process.env.SEED_HASH,
|
|
8
|
+
agentsFile: process.env.AIFINPAY_AGENTS_FILE || undefined,
|
|
9
|
+
agentId: process.env.AIFINPAY_AGENT_ID || undefined,
|
|
10
|
+
walletHome: process.env.AIFINPAY_HOME || undefined,
|
|
11
|
+
walletPassphrase: process.env.AIFINPAY_WALLET_PASSPHRASE || undefined,
|
|
3
12
|
agentSecretB58: process.env.AIFINPAY_AGENT_SECRET || undefined,
|
|
4
13
|
baseUrl: process.env.AIFINPAY_BASE_URL || undefined,
|
|
5
14
|
timeoutMs: process.env.AIFINPAY_TIMEOUT_MS
|
|
@@ -8,6 +17,60 @@ export function loadConfigFromEnv() {
|
|
|
8
17
|
maxAmountUsd: process.env.AIFINPAY_MAX_USD
|
|
9
18
|
? Number(process.env.AIFINPAY_MAX_USD)
|
|
10
19
|
: undefined,
|
|
20
|
+
gatewayOrigins: splitOrigins(process.env.AIFINPAY_GATEWAY_ORIGINS),
|
|
21
|
+
gatewayPathMode: parseGatewayPathMode(process.env.AIFINPAY_GATEWAY_PATH_MODE),
|
|
22
|
+
trustedHosts: splitList(process.env.AIFINPAY_TRUSTED_HOSTS),
|
|
11
23
|
};
|
|
12
24
|
}
|
|
25
|
+
function parseGatewayPathMode(raw) {
|
|
26
|
+
if (raw === undefined || raw.trim() === "")
|
|
27
|
+
return "gateway";
|
|
28
|
+
if (raw === "gateway" || raw === "direct")
|
|
29
|
+
return raw;
|
|
30
|
+
throw new Error("AIFINPAY_GATEWAY_PATH_MODE must be either gateway or direct");
|
|
31
|
+
}
|
|
32
|
+
/** Comma-separated list → trimmed entries, or undefined when unset/empty.
|
|
33
|
+
* Undefined and [] mean different things downstream: undefined keeps the SDK
|
|
34
|
+
* default, [] would mean "no origin is payable". An operator who sets the
|
|
35
|
+
* variable to an empty string means the former. */
|
|
36
|
+
function splitList(raw) {
|
|
37
|
+
if (!raw)
|
|
38
|
+
return undefined;
|
|
39
|
+
const out = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
40
|
+
return out.length ? out : undefined;
|
|
41
|
+
}
|
|
42
|
+
/** Same, but each entry must be a bare https origin.
|
|
43
|
+
* A path or a wildcard here would read as allowed and match nothing, which is
|
|
44
|
+
* the failure that looks like the feature is broken rather than misconfigured. */
|
|
45
|
+
function splitOrigins(raw) {
|
|
46
|
+
const list = splitList(raw);
|
|
47
|
+
if (!list)
|
|
48
|
+
return undefined;
|
|
49
|
+
for (const entry of list) {
|
|
50
|
+
let u;
|
|
51
|
+
try {
|
|
52
|
+
u = new URL(entry);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
throw new Error(`AIFINPAY_GATEWAY_ORIGINS: "${entry}" is not a URL`);
|
|
56
|
+
}
|
|
57
|
+
if (u.origin !== entry.replace(/\/+$/, "")) {
|
|
58
|
+
throw new Error(`AIFINPAY_GATEWAY_ORIGINS: "${entry}" must be a bare origin like https://dev.example.com `
|
|
59
|
+
+ `(got path/query "${u.pathname}${u.search}")`);
|
|
60
|
+
}
|
|
61
|
+
// WHATWG URL accepts "*" in a hostname, so new URL("https://*.example.com")
|
|
62
|
+
// parses and its origin round-trips — the entry would be stored, matched
|
|
63
|
+
// against nothing, and read as "I allowed this host". Rejecting it here is
|
|
64
|
+
// the difference between a misconfiguration that says so and one that looks
|
|
65
|
+
// like the feature is broken.
|
|
66
|
+
if (!/^[a-z0-9.-]+$/i.test(u.hostname) || u.hostname.startsWith("-") || u.hostname.includes("..")) {
|
|
67
|
+
throw new Error(`AIFINPAY_GATEWAY_ORIGINS: "${entry}" is not a plain hostname. `
|
|
68
|
+
+ `Wildcards are not supported — name each origin you settle against.`);
|
|
69
|
+
}
|
|
70
|
+
if (u.protocol !== "https:" && u.hostname !== "localhost" && u.hostname !== "127.0.0.1") {
|
|
71
|
+
throw new Error(`AIFINPAY_GATEWAY_ORIGINS: "${entry}" is not https — refusing to settle over plaintext`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return list;
|
|
75
|
+
}
|
|
13
76
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { McpConfig } from "./config.js";
|
|
2
|
+
export type WalletIdentity = {
|
|
3
|
+
source: string;
|
|
4
|
+
seedHash?: string;
|
|
5
|
+
secretB58?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Local inputs only. Never put the returned secrets in an MCP response/log. */
|
|
8
|
+
export declare function loadWalletIdentity(config: McpConfig): WalletIdentity | null;
|
|
9
|
+
//# sourceMappingURL=identity.d.ts.map
|