crossmint-wallets-mcp 0.1.0

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -0
  3. package/dist/core/client.d.ts +24 -0
  4. package/dist/core/client.d.ts.map +1 -0
  5. package/dist/core/client.js +51 -0
  6. package/dist/core/client.js.map +1 -0
  7. package/dist/core/create-wallet.d.ts +17 -0
  8. package/dist/core/create-wallet.d.ts.map +1 -0
  9. package/dist/core/create-wallet.js +44 -0
  10. package/dist/core/create-wallet.js.map +1 -0
  11. package/dist/core/get-balance.d.ts +16 -0
  12. package/dist/core/get-balance.d.ts.map +1 -0
  13. package/dist/core/get-balance.js +31 -0
  14. package/dist/core/get-balance.js.map +1 -0
  15. package/dist/core/pay-x402-endpoint.d.ts +36 -0
  16. package/dist/core/pay-x402-endpoint.d.ts.map +1 -0
  17. package/dist/core/pay-x402-endpoint.js +189 -0
  18. package/dist/core/pay-x402-endpoint.js.map +1 -0
  19. package/dist/core/transfer-token.d.ts +20 -0
  20. package/dist/core/transfer-token.d.ts.map +1 -0
  21. package/dist/core/transfer-token.js +33 -0
  22. package/dist/core/transfer-token.js.map +1 -0
  23. package/dist/core/types.d.ts +46 -0
  24. package/dist/core/types.d.ts.map +1 -0
  25. package/dist/core/types.js +6 -0
  26. package/dist/core/types.js.map +1 -0
  27. package/dist/index.d.ts +14 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +13 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/mcp/errors.d.ts +23 -0
  32. package/dist/mcp/errors.d.ts.map +1 -0
  33. package/dist/mcp/errors.js +34 -0
  34. package/dist/mcp/errors.js.map +1 -0
  35. package/dist/mcp/server.d.ts +16 -0
  36. package/dist/mcp/server.d.ts.map +1 -0
  37. package/dist/mcp/server.js +34 -0
  38. package/dist/mcp/server.js.map +1 -0
  39. package/dist/mcp/tools.d.ts +3 -0
  40. package/dist/mcp/tools.d.ts.map +1 -0
  41. package/dist/mcp/tools.js +198 -0
  42. package/dist/mcp/tools.js.map +1 -0
  43. package/package.json +64 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,303 @@
1
+ # crossmint-wallets-mcp
2
+
3
+ > An MCP server that exposes Crossmint smart wallet primitives as tools for
4
+ > any MCP-native client: Claude Desktop, Continue.dev, Cline, Codex CLI,
5
+ > and anything else that speaks the Model Context Protocol.
6
+
7
+ **Status:** v0.1.0 — Solana mainnet verified end-to-end. Base EVM planned.
8
+
9
+ ## What this gives you
10
+
11
+ Four tools, callable from any MCP client, each wrapping a primitive from
12
+ [`@crossmint/wallets-sdk`](https://www.npmjs.com/package/@crossmint/wallets-sdk):
13
+
14
+ | Tool | What it does |
15
+ |-----------------------------------|---------------------------------------------------------------------|
16
+ | `crossmint_create_wallet` | Create a new Crossmint smart wallet on the given chain |
17
+ | `crossmint_get_balance` | Read native token + USDC + optional extra balances for a wallet |
18
+ | `crossmint_transfer_token` | Send USDC (or any supported token) from a Crossmint wallet |
19
+ | `crossmint_pay_x402_endpoint` | Fetch an HTTP URL, handle its x402 payment challenge, return the paid response |
20
+
21
+ The killer tool is `crossmint_pay_x402_endpoint`. One call, any URL, any
22
+ MCP client. The agent handles the 402 parse, the wallet signing, the
23
+ on-chain confirmation, and the retry automatically.
24
+
25
+ ## Why this exists
26
+
27
+ [lobster.cash](https://lobster.cash) is Crossmint's payment engine for AI
28
+ agents. It ships as a CLI (`@crossmint/lobster-cli`) that installs into
29
+ Claude Code, Cursor, and OpenClaw via the skills architecture. But a lot
30
+ of 2026's agent surface speaks MCP, not skills:
31
+
32
+ - **Claude Desktop** (Anthropic's desktop app)
33
+ - **Continue.dev** (IDE assistant)
34
+ - **Cline** (VS Code agent extension)
35
+ - **Codex CLI** (OpenAI's command-line agent)
36
+ - And everything else that has shipped against the MCP spec
37
+
38
+ `crossmint-wallets-mcp` is the MCP-native companion to lobster.cash —
39
+ same wallets, same payments, same chain, different transport. It is not
40
+ a replacement. It is the piece lobster.cash doesn't ship.
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ # Global install
46
+ npm install -g crossmint-wallets-mcp
47
+
48
+ # Or one-off via npx (recommended for MCP clients)
49
+ npx crossmint-wallets-mcp
50
+ ```
51
+
52
+ Node.js ≥ 20 required.
53
+
54
+ ## Configure
55
+
56
+ You need a **Crossmint server API key** with the following scopes:
57
+
58
+ - `wallets.create`
59
+ - `wallets.read`
60
+ - `wallets:transactions.create`
61
+ - `wallets:transactions.sign`
62
+ - `wallets:balance.read`
63
+
64
+ Create one at [crossmint.com/console](https://www.crossmint.com/console).
65
+
66
+ You also need a **server recovery signer secret** — any random 32+ char
67
+ string. Generate one with:
68
+
69
+ ```bash
70
+ node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
71
+ ```
72
+
73
+ This secret is what lets the server recover wallets if the wallet was
74
+ created with a `type: "server"` recovery config. Keep it safe; losing it
75
+ means losing access to wallets created under it.
76
+
77
+ Copy `.env.example` to `.env` and fill in the values, OR use the
78
+ file-reference pattern if you want secrets to live outside the project
79
+ tree (useful for Docker secrets, Kubernetes secrets, or streaming
80
+ safety):
81
+
82
+ ```bash
83
+ # Either inline:
84
+ CROSSMINT_API_KEY=sk_prod_...
85
+ CROSSMINT_RECOVERY_SECRET=...
86
+
87
+ # Or by reference:
88
+ CROSSMINT_API_KEY_FILE=/run/secrets/crossmint-api-key
89
+ CROSSMINT_RECOVERY_SECRET_FILE=/run/secrets/crossmint-recovery-secret
90
+
91
+ # Plus:
92
+ DEFAULT_CHAIN=solana
93
+ SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
94
+ ```
95
+
96
+ ## Hook it up to Claude Desktop
97
+
98
+ Edit `%APPDATA%\Claude\claude_desktop_config.json` on Windows, or
99
+ `~/Library/Application Support/Claude/claude_desktop_config.json` on
100
+ macOS, and add:
101
+
102
+ ```json
103
+ {
104
+ "mcpServers": {
105
+ "crossmint-wallets": {
106
+ "command": "npx",
107
+ "args": ["-y", "crossmint-wallets-mcp"],
108
+ "env": {
109
+ "CROSSMINT_API_KEY": "sk_prod_your_key_here",
110
+ "CROSSMINT_RECOVERY_SECRET": "your_random_hex_here",
111
+ "DEFAULT_CHAIN": "solana"
112
+ }
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
118
+ Restart Claude Desktop. You should see the MCP indicator appear in the
119
+ chat input. Try asking Claude:
120
+
121
+ > *Create a Crossmint smart wallet on Solana with alias "my-demo".*
122
+
123
+ Claude will call `crossmint_create_wallet` and return the wallet
124
+ address plus a Solana explorer link. You can then ask:
125
+
126
+ > *Pay the x402 endpoint at http://my.example.com/paid-data from that
127
+ > wallet, max 0.1 USDC.*
128
+
129
+ Claude will call `crossmint_pay_x402_endpoint`, move the USDC on-chain,
130
+ and return the paid response.
131
+
132
+ ## Hook it up to other MCP clients
133
+
134
+ ### Continue.dev
135
+
136
+ In your `~/.continue/config.json`:
137
+
138
+ ```json
139
+ {
140
+ "experimental": {
141
+ "modelContextProtocolServers": [
142
+ {
143
+ "transport": {
144
+ "type": "stdio",
145
+ "command": "npx",
146
+ "args": ["-y", "crossmint-wallets-mcp"],
147
+ "env": {
148
+ "CROSSMINT_API_KEY": "sk_prod_...",
149
+ "CROSSMINT_RECOVERY_SECRET": "...",
150
+ "DEFAULT_CHAIN": "solana"
151
+ }
152
+ }
153
+ }
154
+ ]
155
+ }
156
+ }
157
+ ```
158
+
159
+ ### Cline
160
+
161
+ In the Cline MCP settings panel (gear icon → MCP Servers), add:
162
+
163
+ - **Name:** `crossmint-wallets`
164
+ - **Command:** `npx`
165
+ - **Args:** `-y crossmint-wallets-mcp`
166
+ - **Env:** same three vars as above
167
+
168
+ ### Codex CLI
169
+
170
+ In `~/.codex/config.toml`:
171
+
172
+ ```toml
173
+ [mcp_servers.crossmint-wallets]
174
+ command = "npx"
175
+ args = ["-y", "crossmint-wallets-mcp"]
176
+
177
+ [mcp_servers.crossmint-wallets.env]
178
+ CROSSMINT_API_KEY = "sk_prod_..."
179
+ CROSSMINT_RECOVERY_SECRET = "..."
180
+ DEFAULT_CHAIN = "solana"
181
+ ```
182
+
183
+ ## Demo
184
+
185
+ The repo includes a standalone smoke test that exercises every tool
186
+ against Solana mainnet with real USDC. It:
187
+
188
+ 1. Creates (or loads from cache) a Crossmint smart wallet
189
+ 2. Reads its balances
190
+ 3. Boots a local x402 paywall server (`demo/paywall-server.ts`)
191
+ 4. Calls the paywall, handles the 402, signs + sends the payment via the
192
+ Crossmint wallet, retries with the `X-PAYMENT` header
193
+ 5. Re-reads merchant balances to confirm the transfer landed
194
+
195
+ Run it:
196
+
197
+ ```bash
198
+ pnpm install
199
+ pnpm tsx demo/create-merchant-wallet.ts # one-time: create a merchant wallet
200
+ pnpm demo
201
+ ```
202
+
203
+ Expected output (abbreviated):
204
+
205
+ ```
206
+ === WALLET READY ===
207
+ chain: solana
208
+ address: 4xHkMCaKVBGw4GtdpeKoNZhGFDMi1tMCJDvXvxUmL8hM
209
+ explorer: https://explorer.solana.com/address/4xHkMCaKVBGw4GtdpeKoNZhGFDMi1tMCJDvXvxUmL8hM
210
+ ====================
211
+
212
+ === PAYER BALANCES ===
213
+ native 0.015 (decimals=9)
214
+ usdc 2.000000 (decimals=6)
215
+ ======================
216
+
217
+ [paywall] listening on http://localhost:4021/paid-data
218
+ [payX402Endpoint] paying 0.01 usdc to Fxr4...yqo on solana...
219
+ [payX402Endpoint] tx confirmed: KRjW2uK7LBioyyy1P3xcJTkpS2ibpCjBq1Ektnf4icL6GH25VnesoCGdQN7DbWYbbyjv9MxHoFrS3hsx7ZgkbEg
220
+ [paywall] 200 — payment verified
221
+
222
+ === PAYMENT RESULT ===
223
+ status: 200
224
+ tx sig: KRjW2uK7LBioyyy1P3xcJTkpS2ibpCjBq1Ektnf4icL6GH25VnesoCGdQN7DbWYbbyjv9MxHoFrS3hsx7ZgkbEg
225
+ ======================
226
+
227
+ === MERCHANT BALANCES ===
228
+ usdc 0.01 (decimals=6)
229
+ =========================
230
+ ```
231
+
232
+ The first successful mainnet tx from this repo's smoke test is pinned at
233
+ [`KRjW2uK7LBioyyy1P3xcJTkpS2ibpCjBq1Ektnf4icL6GH25VnesoCGdQN7DbWYbbyjv9MxHoFrS3hsx7ZgkbEg`](https://explorer.solana.com/tx/KRjW2uK7LBioyyy1P3xcJTkpS2ibpCjBq1Ektnf4icL6GH25VnesoCGdQN7DbWYbbyjv9MxHoFrS3hsx7ZgkbEg)
234
+ for anyone who wants to verify the claim on-chain.
235
+
236
+ ## The Solana CPI nuance (and the companion skill)
237
+
238
+ Crossmint smart wallets on Solana are program-derived addresses (PDAs),
239
+ which means you **cannot** hand-roll a plain SPL token transfer to move
240
+ USDC out of one. The wallet PDA has no private key — only the Crossmint
241
+ wallet program can sign for it, via a cross-program invocation (CPI)
242
+ that wraps the SPL transfer as an inner instruction.
243
+
244
+ If you try to write the transaction directly, you will get "signer not
245
+ found" or "missing signature" errors and waste a day debugging it. The
246
+ high-level `Wallet.send()` method from the Crossmint SDK handles this
247
+ correctly — it builds a transaction that invokes the Crossmint wallet
248
+ program, which then CPIs into the SPL token program with the right
249
+ authority. `crossmint_transfer_token` and `crossmint_pay_x402_endpoint`
250
+ in this MCP server both use `wallet.send()` under the hood.
251
+
252
+ The companion repo
253
+ [`crossmint-cpi-skill`](https://github.com/0xultravioleta/crossmint-cpi-skill)
254
+ is a lobster.cash-compatible skill that teaches AI agents this nuance in
255
+ detail, including a working recipe, common errors, and guidance for
256
+ x402 facilitator authors who need to verify Crossmint payments via
257
+ inner-instruction parsing.
258
+
259
+ ## Fees
260
+
261
+ Crossmint charges a small service fee (approximately 0.001 USDC per
262
+ `wallet.send` operation) for the gasless relayer — Crossmint pays the
263
+ Solana network fee for the transaction, and recovers the cost from the
264
+ payer wallet. Budget for this when setting `maxUsdcAtomic` guardrails on
265
+ `crossmint_pay_x402_endpoint`.
266
+
267
+ ## Environment variables
268
+
269
+ | Variable | Required | Default | Description |
270
+ |-----------------------------------|----------|----------------------------------------------|---------------------------------------------------------|
271
+ | `CROSSMINT_API_KEY` | yes* | — | Crossmint server API key |
272
+ | `CROSSMINT_API_KEY_FILE` | yes* | — | Path to file containing the API key (alternative) |
273
+ | `CROSSMINT_RECOVERY_SECRET` | yes* | — | Server recovery signer (32+ char hex string) |
274
+ | `CROSSMINT_RECOVERY_SECRET_FILE` | yes* | — | Path to file containing the recovery secret |
275
+ | `DEFAULT_CHAIN` | no | `solana` | `solana`, `base`, or `base-sepolia` |
276
+ | `SOLANA_RPC_URL` | no | `https://api.mainnet-beta.solana.com` | Solana RPC endpoint (override for private RPCs) |
277
+
278
+ \* Exactly one of `CROSSMINT_API_KEY` or `CROSSMINT_API_KEY_FILE` is
279
+ required. Same for the recovery secret.
280
+
281
+ ## License
282
+
283
+ MIT — so Crossmint can fork this repo into the `@crossmint` organization
284
+ with zero friction if they want to ship it as an official package.
285
+
286
+ ## Acknowledgements
287
+
288
+ - [Crossmint](https://www.crossmint.com) for shipping the smart wallets
289
+ and the `@crossmint/wallets-sdk` that makes this possible
290
+ - [lobster.cash](https://lobster.cash) for the skill architecture and
291
+ the CLI that this MCP server is designed to complement
292
+ - [Faremeter](https://github.com/faremeter) for the x402 facilitator
293
+ pattern that correctly handles CPI inner-instruction verification
294
+ - [The x402 foundation](https://x402.org) for the protocol and the
295
+ `@x402/core` + `@x402/svm` reference implementation
296
+ - [The Model Context Protocol team](https://modelcontextprotocol.io)
297
+ for the spec and the TypeScript SDK
298
+
299
+ ## Companion artifact
300
+
301
+ - [`crossmint-cpi-skill`](https://github.com/0xultravioleta/crossmint-cpi-skill) —
302
+ the lobster.cash skill that teaches the Solana CPI inner-instruction
303
+ nuance this MCP server is built around
@@ -0,0 +1,24 @@
1
+ import { CrossmintWallets } from "@crossmint/wallets-sdk";
2
+ import type { Chain } from "./types.js";
3
+ /**
4
+ * Config loader.
5
+ *
6
+ * Supports two ways of providing secrets:
7
+ * 1. Direct env var: CROSSMINT_API_KEY / CROSSMINT_RECOVERY_SECRET
8
+ * 2. File ref env var: CROSSMINT_API_KEY_FILE / CROSSMINT_RECOVERY_SECRET_FILE
9
+ *
10
+ * The file-ref pattern is useful for Docker secrets, Kubernetes secrets, and
11
+ * for keeping secrets out of any file that could be shown on screen during
12
+ * streams or pair programming. If both are set, the direct env var wins.
13
+ */
14
+ export interface CrossmintConfig {
15
+ apiKey: string;
16
+ recoverySecret: string;
17
+ defaultChain: Chain;
18
+ solanaRpcUrl: string;
19
+ }
20
+ export declare function getConfig(): CrossmintConfig;
21
+ /** Reset the cached config. Only intended for tests. */
22
+ export declare function resetConfigCache(): void;
23
+ export declare function getWalletsClient(): ReturnType<typeof CrossmintWallets.from>;
24
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,KAAK,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAsCD,wBAAgB,SAAS,IAAI,eAAe,CAmB3C;AAED,wDAAwD;AACxD,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAID,wBAAgB,gBAAgB,IAAI,UAAU,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAM3E"}
@@ -0,0 +1,51 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { createCrossmint, CrossmintWallets } from "@crossmint/wallets-sdk";
3
+ function readSecret(directEnv, fileEnv, label) {
4
+ const direct = process.env[directEnv];
5
+ if (direct && direct.trim().length > 0)
6
+ return direct.trim();
7
+ const filePath = process.env[fileEnv];
8
+ if (filePath && filePath.trim().length > 0) {
9
+ try {
10
+ return readFileSync(filePath.trim(), "utf8").trim();
11
+ }
12
+ catch (err) {
13
+ const msg = err instanceof Error ? err.message : String(err);
14
+ throw new Error(`${label}: failed to read from ${fileEnv}=${filePath}: ${msg}`);
15
+ }
16
+ }
17
+ throw new Error(`${label} is required. Set ${directEnv} or ${fileEnv} in your environment.`);
18
+ }
19
+ function parseChain(value, fallback) {
20
+ const allowed = ["solana", "base", "base-sepolia"];
21
+ if (!value)
22
+ return fallback;
23
+ if (allowed.includes(value))
24
+ return value;
25
+ throw new Error(`DEFAULT_CHAIN=${value} is not supported. Allowed: ${allowed.join(", ")}`);
26
+ }
27
+ let cachedConfig = null;
28
+ export function getConfig() {
29
+ if (cachedConfig)
30
+ return cachedConfig;
31
+ const apiKey = readSecret("CROSSMINT_API_KEY", "CROSSMINT_API_KEY_FILE", "CROSSMINT_API_KEY");
32
+ const recoverySecret = readSecret("CROSSMINT_RECOVERY_SECRET", "CROSSMINT_RECOVERY_SECRET_FILE", "CROSSMINT_RECOVERY_SECRET");
33
+ const defaultChain = parseChain(process.env.DEFAULT_CHAIN, "solana");
34
+ const solanaRpcUrl = process.env.SOLANA_RPC_URL?.trim() || "https://api.mainnet-beta.solana.com";
35
+ cachedConfig = { apiKey, recoverySecret, defaultChain, solanaRpcUrl };
36
+ return cachedConfig;
37
+ }
38
+ /** Reset the cached config. Only intended for tests. */
39
+ export function resetConfigCache() {
40
+ cachedConfig = null;
41
+ }
42
+ let cachedWalletsClient = null;
43
+ export function getWalletsClient() {
44
+ if (cachedWalletsClient)
45
+ return cachedWalletsClient;
46
+ const { apiKey } = getConfig();
47
+ const crossmint = createCrossmint({ apiKey });
48
+ cachedWalletsClient = CrossmintWallets.from(crossmint);
49
+ return cachedWalletsClient;
50
+ }
51
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAqB3E,SAAS,UAAU,CACjB,SAAiB,EACjB,OAAe,EACf,KAAa;IAEb,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IAE7D,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,yBAAyB,OAAO,IAAI,QAAQ,KAAK,GAAG,EAAE,CAC/D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,qBAAqB,SAAS,OAAO,OAAO,uBAAuB,CAC5E,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,KAAyB,EAAE,QAAe;IAC5D,MAAM,OAAO,GAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;IAC5D,IAAI,CAAC,KAAK;QAAE,OAAO,QAAQ,CAAC;IAC5B,IAAK,OAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAc,CAAC;IACjE,MAAM,IAAI,KAAK,CACb,iBAAiB,KAAK,+BAA+B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC1E,CAAC;AACJ,CAAC;AAED,IAAI,YAAY,GAA2B,IAAI,CAAC;AAEhD,MAAM,UAAU,SAAS;IACvB,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IAEtC,MAAM,MAAM,GAAG,UAAU,CACvB,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,CACpB,CAAC;IACF,MAAM,cAAc,GAAG,UAAU,CAC/B,2BAA2B,EAC3B,gCAAgC,EAChC,2BAA2B,CAC5B,CAAC;IACF,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACrE,MAAM,YAAY,GAChB,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,qCAAqC,CAAC;IAE9E,YAAY,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;IACtE,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,gBAAgB;IAC9B,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,IAAI,mBAAmB,GAAoD,IAAI,CAAC;AAEhF,MAAM,UAAU,gBAAgB;IAC9B,IAAI,mBAAmB;QAAE,OAAO,mBAAmB,CAAC;IACpD,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,mBAAmB,GAAG,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvD,OAAO,mBAAmB,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Chain, CreateWalletResult } from "./types.js";
2
+ /**
3
+ * Creates a Crossmint smart wallet on the given chain, using the configured
4
+ * server recovery signer. This is idempotent for a given (API key + recovery
5
+ * secret + owner) tuple — calling it twice yields the same wallet.
6
+ *
7
+ * The `owner` field is an optional free-form identifier (email, user id,
8
+ * hashed handle, etc). It is passed straight through to the Crossmint API
9
+ * so downstream tools can resolve a wallet by owner later.
10
+ */
11
+ export declare function createWallet(opts: {
12
+ chain: Chain;
13
+ owner?: string;
14
+ alias?: string;
15
+ }): Promise<CreateWalletResult>;
16
+ export declare function getExplorerLink(address: string, chain: Chain): string;
17
+ //# sourceMappingURL=create-wallet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-wallet.d.ts","sourceRoot":"","sources":["../../src/core/create-wallet.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAwB9B;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CASrE"}
@@ -0,0 +1,44 @@
1
+ import { getConfig, getWalletsClient } from "./client.js";
2
+ /**
3
+ * Creates a Crossmint smart wallet on the given chain, using the configured
4
+ * server recovery signer. This is idempotent for a given (API key + recovery
5
+ * secret + owner) tuple — calling it twice yields the same wallet.
6
+ *
7
+ * The `owner` field is an optional free-form identifier (email, user id,
8
+ * hashed handle, etc). It is passed straight through to the Crossmint API
9
+ * so downstream tools can resolve a wallet by owner later.
10
+ */
11
+ export async function createWallet(opts) {
12
+ const { chain, owner, alias } = opts;
13
+ const { recoverySecret } = getConfig();
14
+ const wallets = getWalletsClient();
15
+ // Crossmint's owner field is a user locator — valid formats are the
16
+ // literal "COMPANY" or prefixed strings like "email:x@y.com",
17
+ // "userId:abc", "phoneNumber:+123", "twitter:handle", "x:handle". Free-form
18
+ // strings are rejected server-side. If the caller did not provide a
19
+ // locator, we omit the field entirely so the wallet is owned by the API
20
+ // key account (no specific user).
21
+ const wallet = await wallets.createWallet({
22
+ chain,
23
+ ...(owner ? { owner } : {}),
24
+ ...(alias ? { alias } : {}),
25
+ recovery: { type: "server", secret: recoverySecret },
26
+ });
27
+ return {
28
+ owner: owner ?? null,
29
+ chain,
30
+ address: wallet.address,
31
+ explorerLink: getExplorerLink(wallet.address, chain),
32
+ };
33
+ }
34
+ export function getExplorerLink(address, chain) {
35
+ switch (chain) {
36
+ case "solana":
37
+ return `https://explorer.solana.com/address/${address}`;
38
+ case "base":
39
+ return `https://basescan.org/address/${address}`;
40
+ case "base-sepolia":
41
+ return `https://sepolia.basescan.org/address/${address}`;
42
+ }
43
+ }
44
+ //# sourceMappingURL=create-wallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-wallet.js","sourceRoot":"","sources":["../../src/core/create-wallet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG1D;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAIlC;IACC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACrC,MAAM,EAAE,cAAc,EAAE,GAAG,SAAS,EAAE,CAAC;IACvC,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IAEnC,oEAAoE;IACpE,8DAA8D;IAC9D,4EAA4E;IAC5E,oEAAoE;IACpE,wEAAwE;IACxE,kCAAkC;IAClC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC;QACxC,KAAK;QACL,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE;KACrD,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,KAAK,IAAI,IAAI;QACpB,KAAK;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,YAAY,EAAE,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC;KACrD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,KAAY;IAC3D,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,QAAQ;YACX,OAAO,uCAAuC,OAAO,EAAE,CAAC;QAC1D,KAAK,MAAM;YACT,OAAO,gCAAgC,OAAO,EAAE,CAAC;QACnD,KAAK,cAAc;YACjB,OAAO,wCAAwC,OAAO,EAAE,CAAC;IAC7D,CAAC;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { BalanceResult, Chain } from "./types.js";
2
+ /**
3
+ * Reads the on-chain balances for a Crossmint wallet by address. Returns
4
+ * the native token (SOL/ETH), USDC (always included per SDK contract),
5
+ * and any other token balances the wallet holds.
6
+ *
7
+ * This is the Phase 4 Task 4.1 implementation from
8
+ * 03-mcp-build-and-skill-plan.md, lifted forward so we can verify funding
9
+ * before running Tool 4 (pay_x402_endpoint).
10
+ */
11
+ export declare function getBalance(opts: {
12
+ address: string;
13
+ chain: Chain;
14
+ tokens?: string[];
15
+ }): Promise<BalanceResult>;
16
+ //# sourceMappingURL=get-balance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-balance.d.ts","sourceRoot":"","sources":["../../src/core/get-balance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAgB,MAAM,YAAY,CAAC;AAErE;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC,aAAa,CAAC,CAsBzB"}
@@ -0,0 +1,31 @@
1
+ import { getWalletsClient } from "./client.js";
2
+ /**
3
+ * Reads the on-chain balances for a Crossmint wallet by address. Returns
4
+ * the native token (SOL/ETH), USDC (always included per SDK contract),
5
+ * and any other token balances the wallet holds.
6
+ *
7
+ * This is the Phase 4 Task 4.1 implementation from
8
+ * 03-mcp-build-and-skill-plan.md, lifted forward so we can verify funding
9
+ * before running Tool 4 (pay_x402_endpoint).
10
+ */
11
+ export async function getBalance(opts) {
12
+ const { address, chain, tokens } = opts;
13
+ const wallets = getWalletsClient();
14
+ const wallet = await wallets.getWallet(address, { chain });
15
+ const raw = await wallet.balances(tokens);
16
+ const formatted = [
17
+ { symbol: "native", amount: raw.nativeToken.amount, decimals: raw.nativeToken.decimals ?? 0 },
18
+ { symbol: "usdc", amount: raw.usdc.amount, decimals: raw.usdc.decimals ?? 0 },
19
+ ...raw.tokens.map((t) => ({
20
+ symbol: t.symbol,
21
+ amount: t.amount,
22
+ decimals: t.decimals ?? 0,
23
+ })),
24
+ ];
25
+ return {
26
+ address,
27
+ chain,
28
+ balances: formatted,
29
+ };
30
+ }
31
+ //# sourceMappingURL=get-balance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-balance.js","sourceRoot":"","sources":["../../src/core/get-balance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAIhC;IACC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxC,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IAEnC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE1C,MAAM,SAAS,GAAmB;QAChC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,WAAW,CAAC,QAAQ,IAAI,CAAC,EAAE;QAC7F,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE;QAC7E,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC;SAC1B,CAAC,CAAC;KACJ,CAAC;IAEF,OAAO;QACL,OAAO;QACP,KAAK;QACL,QAAQ,EAAE,SAAS;KACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { Chain, PayX402Result } from "./types.js";
2
+ /**
3
+ * Pay an x402-protected HTTP endpoint using a Crossmint smart wallet.
4
+ *
5
+ * Flow:
6
+ * 1. Fetch `url` with the caller's original headers/body
7
+ * 2. If the server responds 200, return directly — nothing to pay
8
+ * 3. If the server responds 402, parse the PaymentRequired body and pick
9
+ * the first PaymentRequirements entry on a supported chain (Solana
10
+ * mainnet in v0.1)
11
+ * 4. Load the payer wallet via `wallets.getWallet(payerAddress, ...)`,
12
+ * passing the server recovery signer through the args so the
13
+ * returned Wallet has its `#recovery` field populated. Without this,
14
+ * the SDK's useSigner path crashes because `isRecoverySigner` tries
15
+ * to call `.startsWith` on an undefined secret (chunk-XNZLCUTY:395).
16
+ * The WalletArgsFor TypeScript type doesn't declare a `recovery`
17
+ * field, but the runtime `createWalletInstance` reads it when
18
+ * present, so we pass it via an explicit cast.
19
+ * 5. Call `wallet.send(payTo, tokenSymbol, decimalAmount)` — Crossmint
20
+ * handles the CPI inner instruction wrapping, signing via the server
21
+ * recovery signer, fee payment, and confirmation internally
22
+ * 6. Build the X-PAYMENT header as base64(JSON(PaymentPayload)) with the
23
+ * resulting transaction signature in the payload
24
+ * 7. Retry the original request with the X-PAYMENT header appended
25
+ * 8. Return the response body plus the on-chain tx signature
26
+ */
27
+ export declare function payX402Endpoint(opts: {
28
+ url: string;
29
+ payerAddress: string;
30
+ chain: Chain;
31
+ headers?: Record<string, string>;
32
+ method?: string;
33
+ jsonBody?: unknown;
34
+ maxUsdcAtomic?: bigint;
35
+ }): Promise<PayX402Result>;
36
+ //# sourceMappingURL=pay-x402-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pay-x402-endpoint.d.ts","sourceRoot":"","sources":["../../src/core/pay-x402-endpoint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAyEvD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,aAAa,CAAC,CAuIzB"}