@magnolia-financial/banking-mcp 1.0.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.
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # Magnolia MCP Server
2
+
3
+ An MCP (Model Context Protocol) server that wraps the Magnolia banking API, letting AI agents manage crypto wallets, bank accounts, trading, fiat operations, and Lightning Network payments.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js 18+
8
+ - A Magnolia API key (get one at [clawbot.cash](https://clawbot.cash))
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install
14
+ npm run build
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ Set your API key as an environment variable:
20
+
21
+ ```bash
22
+ export MAGNOLIA_API_KEY=magfi_your_api_key_here
23
+ ```
24
+
25
+ | Variable | Required | Default | Description |
26
+ |----------|----------|---------|-------------|
27
+ | `MAGNOLIA_API_KEY` | Yes | — | API key from ClawBot.cash KYC flow |
28
+ | `MAGNOLIA_API_URL` | No | `https://api.magfi.net` | Magnolia API base URL |
29
+
30
+ ## Usage
31
+
32
+ ### Claude Desktop
33
+
34
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
35
+
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "magnolia": {
40
+ "command": "node",
41
+ "args": ["/path/to/clawbot.cash/mcp-server/dist/index.js"],
42
+ "env": {
43
+ "MAGNOLIA_API_KEY": "magfi_your_api_key_here"
44
+ }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Claude Code
51
+
52
+ Add to your project's `.mcp.json`:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "magnolia": {
58
+ "command": "node",
59
+ "args": ["/path/to/clawbot.cash/mcp-server/dist/index.js"],
60
+ "env": {
61
+ "MAGNOLIA_API_KEY": "magfi_your_api_key_here"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ ### Direct
69
+
70
+ ```bash
71
+ MAGNOLIA_API_KEY=magfi_... node dist/index.js
72
+ ```
73
+
74
+ ## Available Tools (30 total)
75
+
76
+ ### Enterprise
77
+
78
+ | Tool | Description |
79
+ |------|-------------|
80
+ | `list_enterprises` | List all enterprises accessible to this API key |
81
+
82
+ ### Crypto Wallets
83
+
84
+ | Tool | Description |
85
+ |------|-------------|
86
+ | `list_wallets` | List wallets for a cryptocurrency (btc, eth, tbtc, teth) |
87
+ | `get_wallet` | Get wallet details by coin and wallet ID |
88
+ | `generate_address` | Generate a new receive address for a wallet |
89
+ | `list_addresses` | List all addresses for a wallet |
90
+ | `get_address_balances` | Get address balances for a wallet |
91
+ | `send_crypto` | Send a cryptocurrency transaction |
92
+ | `get_wallet_transfer` | Get transfer status for a wallet |
93
+ | `list_wallet_transfers` | List all transfers for a wallet |
94
+
95
+ ### Bank Accounts
96
+
97
+ | Tool | Description |
98
+ |------|-------------|
99
+ | `list_bank_accounts` | List linked bank accounts (filterable by type, state, enterprise) |
100
+ | `get_bank_account` | Get bank account details by ID |
101
+ | `add_bank_account` | Add a new bank account (wire, ACH, or SEPA) |
102
+ | `delete_bank_account` | Delete a bank account |
103
+ | `get_deposit_info` | Get deposit/wiring instructions |
104
+
105
+ ### Trading
106
+
107
+ | Tool | Description |
108
+ |------|-------------|
109
+ | `get_trading_balances` | Get balances for a trading account |
110
+ | `get_trading_products` | List available trading products/pairs |
111
+ | `place_order` | Place a trading order (market, limit, TWAP, steady pace) |
112
+ | `list_orders` | List orders for a trading account |
113
+ | `get_order` | Get order details by ID |
114
+ | `cancel_order` | Cancel a pending order |
115
+
116
+ ### Fiat/ACH
117
+
118
+ | Tool | Description |
119
+ |------|-------------|
120
+ | `get_ach_agreement` | Get the ACH debit agreement |
121
+ | `accept_ach_agreement` | Accept the ACH debit agreement for a bank account |
122
+ | `create_ach_debit` | Create an ACH debit to pull funds from a bank account |
123
+
124
+ ### Lightning Network
125
+
126
+ | Tool | Description |
127
+ |------|-------------|
128
+ | `create_lightning_invoice` | Create a Lightning invoice to receive payment |
129
+ | `get_lightning_invoice` | Check Lightning invoice status |
130
+ | `pay_lightning_invoice` | Pay a Lightning invoice |
131
+ | `list_lightning_transactions` | List Lightning transactions for a wallet |
132
+
133
+ ### Utility
134
+
135
+ | Tool | Description |
136
+ |------|-------------|
137
+ | `lookup_routing_number` | Look up bank info by ACH or wire routing number |
138
+
139
+ ### Auth
140
+
141
+ | Tool | Description |
142
+ |------|-------------|
143
+ | `list_api_keys` | List all API keys for the authenticated user |
144
+ | `delete_api_key` | Delete an API key by ID |
145
+
146
+ ## Example Interactions
147
+
148
+ Once configured, you can ask your AI agent things like:
149
+
150
+ - "List my BTC wallets"
151
+ - "Generate a new receive address for my ETH wallet"
152
+ - "Send 0.001 BTC to address bc1q..."
153
+ - "Show my bank accounts"
154
+ - "Add my Chase checking account for ACH"
155
+ - "Place a market order to buy $100 of BTC"
156
+ - "What are my trading balances?"
157
+ - "Create a Lightning invoice for 10000 sats"
158
+ - "Look up routing number 021000021"
159
+
160
+ ## Testing
161
+
162
+ ```bash
163
+ npm test # Run all tests
164
+ npm run test:watch # Watch mode
165
+ npm run test:integration # Integration tests only
166
+ ```
167
+
168
+ 118 tests across 5 suites covering auth, API paths, client methods, edge cases, and backward compatibility.
169
+
170
+ ## Development
171
+
172
+ ```bash
173
+ npm run dev # Watch mode (recompiles on change)
174
+ npm run build # One-time build
175
+ npm start # Run the compiled server
176
+ ```
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Magnolia MCP Server
4
+ *
5
+ * An MCP server that wraps the Magnolia banking API, allowing AI agents
6
+ * to manage crypto wallets, bank accounts, trading, fiat operations,
7
+ * and Lightning Network payments.
8
+ *
9
+ * API paths are based on the official Magnolia documentation at
10
+ * docs.magnolia.financial.
11
+ *
12
+ * Usage:
13
+ * MAGNOLIA_API_KEY=magfi_... node dist/index.js
14
+ * MAGNOLIA_API_KEY=magfi_... MAGNOLIA_API_URL=https://api.magfi.net node dist/index.js
15
+ */
16
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,582 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Magnolia MCP Server
4
+ *
5
+ * An MCP server that wraps the Magnolia banking API, allowing AI agents
6
+ * to manage crypto wallets, bank accounts, trading, fiat operations,
7
+ * and Lightning Network payments.
8
+ *
9
+ * API paths are based on the official Magnolia documentation at
10
+ * docs.magnolia.financial.
11
+ *
12
+ * Usage:
13
+ * MAGNOLIA_API_KEY=magfi_... node dist/index.js
14
+ * MAGNOLIA_API_KEY=magfi_... MAGNOLIA_API_URL=https://api.magfi.net node dist/index.js
15
+ */
16
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18
+ import { z } from "zod";
19
+ import { MagnoliaClient } from "./magnolia-client.js";
20
+ const API_KEY = process.env.MAGNOLIA_API_KEY;
21
+ const API_URL = process.env.MAGNOLIA_API_URL;
22
+ if (!API_KEY) {
23
+ console.error("Error: MAGNOLIA_API_KEY environment variable is required.\n" +
24
+ "Get your API key at https://clawbot.cash");
25
+ process.exit(1);
26
+ }
27
+ const client = new MagnoliaClient(API_KEY, API_URL);
28
+ const server = new McpServer({
29
+ name: "magnolia",
30
+ version: "1.0.0",
31
+ });
32
+ // Helper to format JSON responses
33
+ function jsonResponse(data) {
34
+ return {
35
+ content: [
36
+ {
37
+ type: "text",
38
+ text: JSON.stringify(data, null, 2),
39
+ },
40
+ ],
41
+ };
42
+ }
43
+ function errorResponse(error) {
44
+ const message = error instanceof Error ? error.message : String(error);
45
+ return {
46
+ content: [
47
+ {
48
+ type: "text",
49
+ text: `Error: ${message}`,
50
+ },
51
+ ],
52
+ isError: true,
53
+ };
54
+ }
55
+ // ==========================================
56
+ // Enterprise Tools
57
+ // ==========================================
58
+ server.registerTool("list_enterprises", {
59
+ description: "List all enterprises accessible to this API key.",
60
+ inputSchema: {},
61
+ }, async () => {
62
+ try {
63
+ const enterprises = await client.getEnterprises();
64
+ return jsonResponse(enterprises);
65
+ }
66
+ catch (e) {
67
+ return errorResponse(e);
68
+ }
69
+ });
70
+ // ==========================================
71
+ // Crypto Wallet Tools — /api/v2/{coin}/wallet
72
+ // ==========================================
73
+ server.registerTool("list_wallets", {
74
+ description: "List wallets for a specific cryptocurrency. Use coin tickers like 'btc', 'eth', 'tbtc' (testnet BTC), 'teth' (testnet ETH).",
75
+ inputSchema: {
76
+ coin: z.string().describe("Coin ticker (e.g., 'btc', 'eth', 'tbtc', 'teth')"),
77
+ },
78
+ }, async ({ coin }) => {
79
+ try {
80
+ const wallets = await client.listWallets(coin);
81
+ return jsonResponse(wallets);
82
+ }
83
+ catch (e) {
84
+ return errorResponse(e);
85
+ }
86
+ });
87
+ server.registerTool("get_wallet", {
88
+ description: "Get details for a specific wallet by coin and wallet ID.",
89
+ inputSchema: {
90
+ coin: z.string().describe("Coin ticker (e.g., 'btc', 'eth')"),
91
+ walletId: z.string().describe("The wallet ID"),
92
+ },
93
+ }, async ({ coin, walletId }) => {
94
+ try {
95
+ const wallet = await client.getWallet(coin, walletId);
96
+ return jsonResponse(wallet);
97
+ }
98
+ catch (e) {
99
+ return errorResponse(e);
100
+ }
101
+ });
102
+ server.registerTool("generate_address", {
103
+ description: "Generate a new receive address for a crypto wallet.",
104
+ inputSchema: {
105
+ coin: z.string().describe("Coin ticker (e.g., 'btc', 'eth')"),
106
+ walletId: z.string().describe("The wallet ID"),
107
+ },
108
+ }, async ({ coin, walletId }) => {
109
+ try {
110
+ const address = await client.generateAddress(coin, walletId);
111
+ return jsonResponse(address);
112
+ }
113
+ catch (e) {
114
+ return errorResponse(e);
115
+ }
116
+ });
117
+ server.registerTool("list_addresses", {
118
+ description: "List all addresses for a crypto wallet.",
119
+ inputSchema: {
120
+ coin: z.string().describe("Coin ticker (e.g., 'btc', 'eth')"),
121
+ walletId: z.string().describe("The wallet ID"),
122
+ },
123
+ }, async ({ coin, walletId }) => {
124
+ try {
125
+ const addresses = await client.listAddresses(coin, walletId);
126
+ return jsonResponse(addresses);
127
+ }
128
+ catch (e) {
129
+ return errorResponse(e);
130
+ }
131
+ });
132
+ server.registerTool("get_address_balances", {
133
+ description: "Get address balances for a crypto wallet.",
134
+ inputSchema: {
135
+ coin: z.string().describe("Coin ticker (e.g., 'btc', 'eth')"),
136
+ walletId: z.string().describe("The wallet ID"),
137
+ },
138
+ }, async ({ coin, walletId }) => {
139
+ try {
140
+ const balances = await client.getAddressBalances(coin, walletId);
141
+ return jsonResponse(balances);
142
+ }
143
+ catch (e) {
144
+ return errorResponse(e);
145
+ }
146
+ });
147
+ server.registerTool("send_crypto", {
148
+ description: "Send a cryptocurrency transaction from a wallet.",
149
+ inputSchema: {
150
+ coin: z.string().describe("Coin ticker (e.g., 'btc', 'eth')"),
151
+ walletId: z.string().describe("Source wallet ID"),
152
+ address: z.string().describe("Destination address"),
153
+ amount: z.string().describe("Amount to send (in base units)"),
154
+ walletPassphrase: z.string().optional().describe("Wallet passphrase for signing"),
155
+ memo: z.string().optional().describe("Optional memo/tag"),
156
+ },
157
+ }, async ({ coin, walletId, address, amount, walletPassphrase, memo }) => {
158
+ try {
159
+ const tx = await client.sendTransaction(coin, walletId, {
160
+ address,
161
+ amount,
162
+ walletPassphrase,
163
+ memo,
164
+ });
165
+ return jsonResponse(tx);
166
+ }
167
+ catch (e) {
168
+ return errorResponse(e);
169
+ }
170
+ });
171
+ server.registerTool("get_wallet_transfer", {
172
+ description: "Get the status of a specific transfer for a wallet.",
173
+ inputSchema: {
174
+ coin: z.string().describe("Coin ticker (e.g., 'btc', 'eth')"),
175
+ walletId: z.string().describe("The wallet ID"),
176
+ transferId: z.string().describe("The transfer ID"),
177
+ },
178
+ }, async ({ coin, walletId, transferId }) => {
179
+ try {
180
+ const transfer = await client.getWalletTransfer(coin, walletId, transferId);
181
+ return jsonResponse(transfer);
182
+ }
183
+ catch (e) {
184
+ return errorResponse(e);
185
+ }
186
+ });
187
+ server.registerTool("list_wallet_transfers", {
188
+ description: "List all transfers for a specific crypto wallet.",
189
+ inputSchema: {
190
+ coin: z.string().describe("Coin ticker (e.g., 'btc', 'eth')"),
191
+ walletId: z.string().describe("The wallet ID"),
192
+ },
193
+ }, async ({ coin, walletId }) => {
194
+ try {
195
+ const transfers = await client.listWalletTransfers(coin, walletId);
196
+ return jsonResponse(transfers);
197
+ }
198
+ catch (e) {
199
+ return errorResponse(e);
200
+ }
201
+ });
202
+ // ==========================================
203
+ // Bank Account Tools — /api/v2/bankaccounts
204
+ // ==========================================
205
+ server.registerTool("list_bank_accounts", {
206
+ description: "List all linked bank accounts. Can filter by type, verification state, or enterprise.",
207
+ inputSchema: {
208
+ type: z.string().optional().describe("Filter by type: 'wire', 'ach', or 'sepa'"),
209
+ verificationState: z.string().optional().describe("Filter by verification state"),
210
+ enterpriseId: z.string().optional().describe("Filter by enterprise ID"),
211
+ },
212
+ }, async ({ type, verificationState, enterpriseId }) => {
213
+ try {
214
+ const accounts = await client.listBankAccounts({
215
+ type,
216
+ verificationState,
217
+ enterpriseId,
218
+ });
219
+ return jsonResponse(accounts);
220
+ }
221
+ catch (e) {
222
+ return errorResponse(e);
223
+ }
224
+ });
225
+ server.registerTool("get_bank_account", {
226
+ description: "Get details for a specific bank account by ID.",
227
+ inputSchema: {
228
+ bankAccountId: z.string().describe("The bank account ID"),
229
+ },
230
+ }, async ({ bankAccountId }) => {
231
+ try {
232
+ const account = await client.getBankAccount(bankAccountId);
233
+ return jsonResponse(account);
234
+ }
235
+ catch (e) {
236
+ return errorResponse(e);
237
+ }
238
+ });
239
+ server.registerTool("add_bank_account", {
240
+ description: "Add a new bank account for deposits and withdrawals.",
241
+ inputSchema: {
242
+ type: z.enum(["wire", "ach", "sepa"]).describe("Bank account type"),
243
+ name: z.string().describe("A label for this bank account"),
244
+ ownerName: z.string().describe("Name of the account owner"),
245
+ shortCountryCode: z.string().describe("Two-letter country code (e.g., 'US')"),
246
+ accountNumber: z.string().describe("Bank account number"),
247
+ currency: z.string().describe("Currency code (e.g., 'USD')"),
248
+ routingNumber: z.string().optional().describe("Routing number (required for ACH/wire in US)"),
249
+ swiftCode: z.string().optional().describe("SWIFT code (for international wire)"),
250
+ accountType: z.enum(["checking", "saving"]).optional().describe("Account type"),
251
+ ownerAddressCountryCode: z.string().describe("Two-letter country code for the owner's address (e.g., 'US')"),
252
+ ownerAddress: z.object({
253
+ address_line_1: z.string().describe("Street address"),
254
+ city_locality: z.string().describe("City"),
255
+ state_province: z.string().describe("State or province"),
256
+ postal_code: z.string().describe("Postal/ZIP code"),
257
+ }).describe("Owner's physical address"),
258
+ },
259
+ }, async ({ type, name, ownerName, shortCountryCode, accountNumber, currency, routingNumber, swiftCode, accountType, ownerAddressCountryCode, ownerAddress }) => {
260
+ try {
261
+ const account = await client.addBankAccount({
262
+ type,
263
+ name,
264
+ ownerName,
265
+ shortCountryCode,
266
+ accountNumber,
267
+ currency,
268
+ routingNumber,
269
+ swiftCode,
270
+ accountType,
271
+ ownerAddressCountryCode,
272
+ ownerAddress,
273
+ });
274
+ return jsonResponse(account);
275
+ }
276
+ catch (e) {
277
+ return errorResponse(e);
278
+ }
279
+ });
280
+ server.registerTool("delete_bank_account", {
281
+ description: "Delete a bank account by ID.",
282
+ inputSchema: {
283
+ bankAccountId: z.string().describe("The bank account ID to delete"),
284
+ },
285
+ }, async ({ bankAccountId }) => {
286
+ try {
287
+ const result = await client.deleteBankAccount(bankAccountId);
288
+ return jsonResponse(result);
289
+ }
290
+ catch (e) {
291
+ return errorResponse(e);
292
+ }
293
+ });
294
+ server.registerTool("get_deposit_info", {
295
+ description: "Get deposit information for bank accounts (wiring instructions, etc.).",
296
+ inputSchema: {},
297
+ }, async () => {
298
+ try {
299
+ const info = await client.getDepositInfo();
300
+ return jsonResponse(info);
301
+ }
302
+ catch (e) {
303
+ return errorResponse(e);
304
+ }
305
+ });
306
+ // ==========================================
307
+ // Trading Tools — /api/prime/trading/v1
308
+ // ==========================================
309
+ server.registerTool("get_trading_balances", {
310
+ description: "Get balances for a trading account.",
311
+ inputSchema: {
312
+ accountId: z.string().describe("Trading account ID"),
313
+ },
314
+ }, async ({ accountId }) => {
315
+ try {
316
+ const balances = await client.getTradingBalances(accountId);
317
+ return jsonResponse(balances);
318
+ }
319
+ catch (e) {
320
+ return errorResponse(e);
321
+ }
322
+ });
323
+ server.registerTool("get_trading_products", {
324
+ description: "List available trading products (pairs) for an account.",
325
+ inputSchema: {
326
+ accountId: z.string().describe("Trading account ID"),
327
+ },
328
+ }, async ({ accountId }) => {
329
+ try {
330
+ const products = await client.getTradingProducts(accountId);
331
+ return jsonResponse(products);
332
+ }
333
+ catch (e) {
334
+ return errorResponse(e);
335
+ }
336
+ });
337
+ server.registerTool("place_order", {
338
+ description: "Place a trading order (market, limit, TWAP, or steady pace).",
339
+ inputSchema: {
340
+ accountId: z.string().describe("Trading account ID"),
341
+ type: z.enum(["market", "limit", "twap", "steady_pace"]).describe("Order type"),
342
+ product: z.string().describe("Trading product/pair (e.g., 'BTC-USD')"),
343
+ side: z.enum(["buy", "sell"]).describe("Order side"),
344
+ quantity: z.string().describe("Order quantity"),
345
+ quantityCurrency: z.string().describe("Currency of the quantity (e.g., 'USD' or 'BTC')"),
346
+ limitPrice: z.string().optional().describe("Limit price (required for limit orders)"),
347
+ duration: z.number().optional().describe("Duration in seconds (for TWAP/steady pace)"),
348
+ clientOrderId: z.string().optional().describe("Client-defined order ID"),
349
+ fundingType: z.enum(["margin", "funded"]).optional().describe("Funding type"),
350
+ },
351
+ }, async ({ accountId, type, product, side, quantity, quantityCurrency, limitPrice, duration, clientOrderId, fundingType }) => {
352
+ try {
353
+ const order = await client.placeOrder(accountId, {
354
+ type,
355
+ product,
356
+ side,
357
+ quantity,
358
+ quantityCurrency,
359
+ limitPrice,
360
+ duration,
361
+ clientOrderId,
362
+ fundingType,
363
+ });
364
+ return jsonResponse(order);
365
+ }
366
+ catch (e) {
367
+ return errorResponse(e);
368
+ }
369
+ });
370
+ server.registerTool("list_orders", {
371
+ description: "List orders for a trading account.",
372
+ inputSchema: {
373
+ accountId: z.string().describe("Trading account ID"),
374
+ limit: z.number().optional().describe("Max number of orders to return"),
375
+ offset: z.number().optional().describe("Offset for pagination"),
376
+ },
377
+ }, async ({ accountId, limit, offset }) => {
378
+ try {
379
+ const orders = await client.listOrders(accountId, { limit, offset });
380
+ return jsonResponse(orders);
381
+ }
382
+ catch (e) {
383
+ return errorResponse(e);
384
+ }
385
+ });
386
+ server.registerTool("get_order", {
387
+ description: "Get details for a specific trading order.",
388
+ inputSchema: {
389
+ accountId: z.string().describe("Trading account ID"),
390
+ orderId: z.string().describe("The order ID"),
391
+ },
392
+ }, async ({ accountId, orderId }) => {
393
+ try {
394
+ const order = await client.getOrder(accountId, orderId);
395
+ return jsonResponse(order);
396
+ }
397
+ catch (e) {
398
+ return errorResponse(e);
399
+ }
400
+ });
401
+ server.registerTool("cancel_order", {
402
+ description: "Cancel a pending trading order.",
403
+ inputSchema: {
404
+ accountId: z.string().describe("Trading account ID"),
405
+ orderId: z.string().describe("The order ID to cancel"),
406
+ },
407
+ }, async ({ accountId, orderId }) => {
408
+ try {
409
+ const result = await client.cancelOrder(accountId, orderId);
410
+ return jsonResponse(result);
411
+ }
412
+ catch (e) {
413
+ return errorResponse(e);
414
+ }
415
+ });
416
+ // ==========================================
417
+ // Fiat/ACH Tools — /api/fiat/v1
418
+ // ==========================================
419
+ server.registerTool("get_ach_agreement", {
420
+ description: "Get the ACH debit agreement that must be accepted before making ACH debits.",
421
+ inputSchema: {},
422
+ }, async () => {
423
+ try {
424
+ const agreement = await client.getAchAgreement();
425
+ return jsonResponse(agreement);
426
+ }
427
+ catch (e) {
428
+ return errorResponse(e);
429
+ }
430
+ });
431
+ server.registerTool("accept_ach_agreement", {
432
+ description: "Accept the ACH debit agreement for a specific bank account.",
433
+ inputSchema: {
434
+ bankAccountId: z.string().describe("The bank account ID to accept the agreement for"),
435
+ },
436
+ }, async ({ bankAccountId }) => {
437
+ try {
438
+ const result = await client.acceptAchAgreement({ bankAccountId });
439
+ return jsonResponse(result);
440
+ }
441
+ catch (e) {
442
+ return errorResponse(e);
443
+ }
444
+ });
445
+ server.registerTool("create_ach_debit", {
446
+ description: "Create an ACH debit transaction to pull funds from a linked bank account.",
447
+ inputSchema: {
448
+ bankAccountId: z.string().describe("Source bank account ID"),
449
+ amount: z.string().describe("Amount to debit (e.g., '100.00')"),
450
+ currency: z.string().describe("Currency code (e.g., 'USD')"),
451
+ },
452
+ }, async ({ bankAccountId, amount, currency }) => {
453
+ try {
454
+ const tx = await client.createAchDebit({ bankAccountId, amount, currency });
455
+ return jsonResponse(tx);
456
+ }
457
+ catch (e) {
458
+ return errorResponse(e);
459
+ }
460
+ });
461
+ // ==========================================
462
+ // Lightning Network Tools
463
+ // ==========================================
464
+ server.registerTool("create_lightning_invoice", {
465
+ description: "Create a Lightning Network invoice to receive a payment.",
466
+ inputSchema: {
467
+ walletId: z.string().describe("Bitcoin wallet ID"),
468
+ amount: z.string().describe("Amount in satoshis"),
469
+ memo: z.string().optional().describe("Invoice memo/description"),
470
+ },
471
+ }, async ({ walletId, amount, memo }) => {
472
+ try {
473
+ const invoice = await client.createLightningInvoice(walletId, { amount, memo });
474
+ return jsonResponse(invoice);
475
+ }
476
+ catch (e) {
477
+ return errorResponse(e);
478
+ }
479
+ });
480
+ server.registerTool("get_lightning_invoice", {
481
+ description: "Check the status of a Lightning Network invoice.",
482
+ inputSchema: {
483
+ walletId: z.string().describe("Bitcoin wallet ID"),
484
+ paymentHash: z.string().describe("The payment hash of the invoice"),
485
+ },
486
+ }, async ({ walletId, paymentHash }) => {
487
+ try {
488
+ const invoice = await client.getLightningInvoice(walletId, paymentHash);
489
+ return jsonResponse(invoice);
490
+ }
491
+ catch (e) {
492
+ return errorResponse(e);
493
+ }
494
+ });
495
+ server.registerTool("pay_lightning_invoice", {
496
+ description: "Pay a Lightning Network invoice.",
497
+ inputSchema: {
498
+ walletId: z.string().describe("Bitcoin wallet ID to pay from"),
499
+ invoice: z.string().describe("Lightning invoice string (lnbc...)"),
500
+ },
501
+ }, async ({ walletId, invoice }) => {
502
+ try {
503
+ const payment = await client.makeLightningPayment(walletId, { invoice });
504
+ return jsonResponse(payment);
505
+ }
506
+ catch (e) {
507
+ return errorResponse(e);
508
+ }
509
+ });
510
+ server.registerTool("list_lightning_transactions", {
511
+ description: "List Lightning Network transactions for a wallet.",
512
+ inputSchema: {
513
+ walletId: z.string().describe("Bitcoin wallet ID"),
514
+ },
515
+ }, async ({ walletId }) => {
516
+ try {
517
+ const txs = await client.listLightningTransactions(walletId);
518
+ return jsonResponse(txs);
519
+ }
520
+ catch (e) {
521
+ return errorResponse(e);
522
+ }
523
+ });
524
+ // ==========================================
525
+ // Utility Tools
526
+ // ==========================================
527
+ server.registerTool("lookup_routing_number", {
528
+ description: "Look up bank information by routing number. Returns bank name and address.",
529
+ inputSchema: {
530
+ bankType: z.enum(["ach", "wire"]).describe("Type of routing number: 'ach' or 'wire'"),
531
+ routingNumber: z.string().describe("The 9-digit routing number"),
532
+ },
533
+ }, async ({ bankType, routingNumber }) => {
534
+ try {
535
+ const info = await client.lookupRoutingNumber(bankType, routingNumber);
536
+ return jsonResponse(info);
537
+ }
538
+ catch (e) {
539
+ return errorResponse(e);
540
+ }
541
+ });
542
+ // ==========================================
543
+ // Auth Tools
544
+ // ==========================================
545
+ server.registerTool("list_api_keys", {
546
+ description: "List all API keys for the authenticated user.",
547
+ inputSchema: {},
548
+ }, async () => {
549
+ try {
550
+ const keys = await client.listApiKeys();
551
+ return jsonResponse(keys);
552
+ }
553
+ catch (e) {
554
+ return errorResponse(e);
555
+ }
556
+ });
557
+ server.registerTool("delete_api_key", {
558
+ description: "Delete an API key by ID. Use list_api_keys first to find the key ID.",
559
+ inputSchema: {
560
+ keyId: z.string().describe("The API key ID to delete"),
561
+ },
562
+ }, async ({ keyId }) => {
563
+ try {
564
+ const result = await client.deleteApiKey(keyId);
565
+ return jsonResponse(result);
566
+ }
567
+ catch (e) {
568
+ return errorResponse(e);
569
+ }
570
+ });
571
+ // ==========================================
572
+ // Start Server
573
+ // ==========================================
574
+ async function main() {
575
+ const transport = new StdioServerTransport();
576
+ await server.connect(transport);
577
+ console.error("Magnolia MCP server v1.0 running on stdio");
578
+ }
579
+ main().catch((error) => {
580
+ console.error("Fatal error:", error);
581
+ process.exit(1);
582
+ });
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Magnolia API client for the MCP server.
3
+ * Uses API keys obtained from the ClawBot.cash KYC flow.
4
+ *
5
+ * IMPORTANT: API paths are based on the official Magnolia documentation at
6
+ * docs.magnolia.financial. The API has multiple services:
7
+ *
8
+ * - /auth/* — Authentication (login, API keys)
9
+ * - /api/v2/{coin}/wallet/* — Crypto wallet operations
10
+ * - /api/v2/bankaccounts/* — Bank account management
11
+ * - /api/v2/enterprise/* — Enterprise management
12
+ * - /api/prime/trading/v1/accounts/* — Trading (orders, balances, products)
13
+ * - /api/fiat/v1/* — Fiat/ACH operations
14
+ * - /api/evs/v1/* — KYC/Identity verification
15
+ * - /api/tradfi/v1/* — Traditional finance utilities
16
+ */
17
+ export declare class MagnoliaClient {
18
+ private apiUrl;
19
+ private apiKey;
20
+ constructor(apiKey: string, apiUrl?: string);
21
+ private request;
22
+ /**
23
+ * Login with email/password to get a JWT token.
24
+ * JWT tokens expire after 1 hour.
25
+ */
26
+ login(email: string, password: string): Promise<unknown>;
27
+ /**
28
+ * List all API keys for the authenticated user.
29
+ */
30
+ listApiKeys(): Promise<unknown>;
31
+ /**
32
+ * Delete an API key by ID.
33
+ */
34
+ deleteApiKey(keyId: string): Promise<unknown>;
35
+ /**
36
+ * List enterprises accessible to this user.
37
+ */
38
+ getEnterprises(): Promise<unknown>;
39
+ /**
40
+ * List wallets for a specific cryptocurrency.
41
+ * @param coin - Coin ticker (e.g., "btc", "eth", "tbtc" for testnet)
42
+ */
43
+ listWallets(coin: string): Promise<unknown>;
44
+ /**
45
+ * Get a specific wallet by ID.
46
+ */
47
+ getWallet(coin: string, walletId: string): Promise<unknown>;
48
+ /**
49
+ * Generate a new receive address for a wallet.
50
+ */
51
+ generateAddress(coin: string, walletId: string): Promise<unknown>;
52
+ /**
53
+ * List all addresses for a wallet.
54
+ */
55
+ listAddresses(coin: string, walletId: string): Promise<unknown>;
56
+ /**
57
+ * Get address balances for a wallet.
58
+ */
59
+ getAddressBalances(coin: string, walletId: string): Promise<unknown>;
60
+ /**
61
+ * Send a transaction from a wallet.
62
+ */
63
+ sendTransaction(coin: string, walletId: string, data: {
64
+ address: string;
65
+ amount: string;
66
+ walletPassphrase?: string;
67
+ memo?: string;
68
+ }): Promise<unknown>;
69
+ /**
70
+ * Get transfer status for a wallet.
71
+ */
72
+ getWalletTransfer(coin: string, walletId: string, transferId: string): Promise<unknown>;
73
+ /**
74
+ * List transfers for a wallet.
75
+ */
76
+ listWalletTransfers(coin: string, walletId: string): Promise<unknown>;
77
+ /**
78
+ * List all bank accounts.
79
+ */
80
+ listBankAccounts(params?: {
81
+ type?: string;
82
+ verificationState?: string;
83
+ enterpriseId?: string;
84
+ }): Promise<unknown>;
85
+ /**
86
+ * Get a specific bank account by ID.
87
+ */
88
+ getBankAccount(bankAccountId: string): Promise<unknown>;
89
+ /**
90
+ * Add a new bank account.
91
+ *
92
+ * NOTE: API requires complete owner address information (discovered via E2E testing).
93
+ * The ownerAddress object must contain these fields (snake_case):
94
+ * - address_line_1
95
+ * - city_locality
96
+ * - state_province
97
+ * - postal_code
98
+ */
99
+ addBankAccount(data: {
100
+ type: "wire" | "ach" | "sepa";
101
+ name: string;
102
+ ownerName: string;
103
+ shortCountryCode: string;
104
+ accountNumber: string;
105
+ currency: string;
106
+ routingNumber?: string;
107
+ swiftCode?: string;
108
+ accountType?: "checking" | "saving";
109
+ ownerAddressCountryCode: string;
110
+ ownerAddress: {
111
+ address_line_1: string;
112
+ city_locality: string;
113
+ state_province: string;
114
+ postal_code: string;
115
+ };
116
+ }): Promise<unknown>;
117
+ /**
118
+ * Update a bank account.
119
+ */
120
+ updateBankAccount(bankAccountId: string, data: Record<string, unknown>): Promise<unknown>;
121
+ /**
122
+ * Delete a bank account.
123
+ */
124
+ deleteBankAccount(bankAccountId: string): Promise<unknown>;
125
+ /**
126
+ * Get deposit info for bank accounts.
127
+ */
128
+ getDepositInfo(): Promise<unknown>;
129
+ /**
130
+ * Get account balances for a trading account.
131
+ */
132
+ getTradingBalances(accountId: string): Promise<unknown>;
133
+ /**
134
+ * List available trading products for an account.
135
+ */
136
+ getTradingProducts(accountId: string): Promise<unknown>;
137
+ /**
138
+ * Place a trading order.
139
+ */
140
+ placeOrder(accountId: string, data: {
141
+ type: "market" | "limit" | "twap" | "steady_pace";
142
+ product: string;
143
+ side: "buy" | "sell";
144
+ quantity: string;
145
+ quantityCurrency: string;
146
+ limitPrice?: string;
147
+ duration?: number;
148
+ clientOrderId?: string;
149
+ fundingType?: "margin" | "funded";
150
+ }): Promise<unknown>;
151
+ /**
152
+ * List orders for a trading account.
153
+ */
154
+ listOrders(accountId: string, params?: {
155
+ limit?: number;
156
+ offset?: number;
157
+ }): Promise<unknown>;
158
+ /**
159
+ * Get a specific order by ID.
160
+ */
161
+ getOrder(accountId: string, orderId: string): Promise<unknown>;
162
+ /**
163
+ * Cancel an order.
164
+ */
165
+ cancelOrder(accountId: string, orderId: string): Promise<unknown>;
166
+ /**
167
+ * Get ACH debit agreement.
168
+ */
169
+ getAchAgreement(): Promise<unknown>;
170
+ /**
171
+ * Accept ACH debit agreement.
172
+ */
173
+ acceptAchAgreement(data: {
174
+ bankAccountId: string;
175
+ }): Promise<unknown>;
176
+ /**
177
+ * Create an ACH debit transaction.
178
+ */
179
+ createAchDebit(data: {
180
+ bankAccountId: string;
181
+ amount: string;
182
+ currency: string;
183
+ }): Promise<unknown>;
184
+ /**
185
+ * Create a lightning invoice.
186
+ */
187
+ createLightningInvoice(walletId: string, data: {
188
+ amount: string;
189
+ memo?: string;
190
+ }): Promise<unknown>;
191
+ /**
192
+ * Get a lightning invoice status.
193
+ */
194
+ getLightningInvoice(walletId: string, paymentHash: string): Promise<unknown>;
195
+ /**
196
+ * Make a lightning payment.
197
+ */
198
+ makeLightningPayment(walletId: string, data: {
199
+ invoice: string;
200
+ }): Promise<unknown>;
201
+ /**
202
+ * List lightning transactions.
203
+ */
204
+ listLightningTransactions(walletId: string): Promise<unknown>;
205
+ /**
206
+ * Look up bank routing number information.
207
+ * @param bankType - "ach" or "wire"
208
+ */
209
+ lookupRoutingNumber(bankType: "ach" | "wire", routingNumber: string): Promise<unknown>;
210
+ }
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Magnolia API client for the MCP server.
3
+ * Uses API keys obtained from the ClawBot.cash KYC flow.
4
+ *
5
+ * IMPORTANT: API paths are based on the official Magnolia documentation at
6
+ * docs.magnolia.financial. The API has multiple services:
7
+ *
8
+ * - /auth/* — Authentication (login, API keys)
9
+ * - /api/v2/{coin}/wallet/* — Crypto wallet operations
10
+ * - /api/v2/bankaccounts/* — Bank account management
11
+ * - /api/v2/enterprise/* — Enterprise management
12
+ * - /api/prime/trading/v1/accounts/* — Trading (orders, balances, products)
13
+ * - /api/fiat/v1/* — Fiat/ACH operations
14
+ * - /api/evs/v1/* — KYC/Identity verification
15
+ * - /api/tradfi/v1/* — Traditional finance utilities
16
+ */
17
+ const DEFAULT_API_URL = "https://api.magfi.net";
18
+ export class MagnoliaClient {
19
+ apiUrl;
20
+ apiKey;
21
+ constructor(apiKey, apiUrl) {
22
+ this.apiKey = apiKey;
23
+ this.apiUrl = apiUrl || DEFAULT_API_URL;
24
+ }
25
+ async request(path, options = {}) {
26
+ const headers = {
27
+ "User-Agent": "ClawBot-MCP/1.0",
28
+ Authorization: `Bearer ${this.apiKey}`,
29
+ ...(options.headers || {}),
30
+ };
31
+ if (options.body && typeof options.body === "string") {
32
+ headers["Content-Type"] = "application/json";
33
+ }
34
+ const res = await fetch(`${this.apiUrl}${path}`, {
35
+ ...options,
36
+ headers,
37
+ });
38
+ const text = await res.text();
39
+ if (!res.ok) {
40
+ throw new Error(`Magnolia API error ${res.status} on ${options.method || "GET"} ${path}: ${text}`);
41
+ }
42
+ try {
43
+ return JSON.parse(text);
44
+ }
45
+ catch {
46
+ return text;
47
+ }
48
+ }
49
+ // --- Authentication ---
50
+ /**
51
+ * Login with email/password to get a JWT token.
52
+ * JWT tokens expire after 1 hour.
53
+ */
54
+ async login(email, password) {
55
+ return this.request("/auth/login", {
56
+ method: "POST",
57
+ body: JSON.stringify({ email, password }),
58
+ // Don't send API key for login, use raw request
59
+ });
60
+ }
61
+ /**
62
+ * List all API keys for the authenticated user.
63
+ */
64
+ async listApiKeys() {
65
+ return this.request("/auth/api-key");
66
+ }
67
+ /**
68
+ * Delete an API key by ID.
69
+ */
70
+ async deleteApiKey(keyId) {
71
+ return this.request(`/auth/api-key/${keyId}`, {
72
+ method: "DELETE",
73
+ });
74
+ }
75
+ // --- Enterprise ---
76
+ /**
77
+ * List enterprises accessible to this user.
78
+ */
79
+ async getEnterprises() {
80
+ return this.request("/api/v2/enterprise");
81
+ }
82
+ // --- Crypto Wallets (v2/{coin}/wallet) ---
83
+ /**
84
+ * List wallets for a specific cryptocurrency.
85
+ * @param coin - Coin ticker (e.g., "btc", "eth", "tbtc" for testnet)
86
+ */
87
+ async listWallets(coin) {
88
+ return this.request(`/api/v2/${coin}/wallet`);
89
+ }
90
+ /**
91
+ * Get a specific wallet by ID.
92
+ */
93
+ async getWallet(coin, walletId) {
94
+ return this.request(`/api/v2/${coin}/wallet/${walletId}`);
95
+ }
96
+ /**
97
+ * Generate a new receive address for a wallet.
98
+ */
99
+ async generateAddress(coin, walletId) {
100
+ return this.request(`/api/v2/${coin}/wallet/${walletId}/address`, {
101
+ method: "POST",
102
+ });
103
+ }
104
+ /**
105
+ * List all addresses for a wallet.
106
+ */
107
+ async listAddresses(coin, walletId) {
108
+ return this.request(`/api/v2/${coin}/wallet/${walletId}/addresses`);
109
+ }
110
+ /**
111
+ * Get address balances for a wallet.
112
+ */
113
+ async getAddressBalances(coin, walletId) {
114
+ return this.request(`/api/v2/${coin}/wallet/${walletId}/addresses/balances`);
115
+ }
116
+ /**
117
+ * Send a transaction from a wallet.
118
+ */
119
+ async sendTransaction(coin, walletId, data) {
120
+ return this.request(`/api/v2/${coin}/wallet/${walletId}/tx/send`, {
121
+ method: "POST",
122
+ body: JSON.stringify(data),
123
+ });
124
+ }
125
+ /**
126
+ * Get transfer status for a wallet.
127
+ */
128
+ async getWalletTransfer(coin, walletId, transferId) {
129
+ return this.request(`/api/v2/${coin}/wallet/${walletId}/transfer/${transferId}`);
130
+ }
131
+ /**
132
+ * List transfers for a wallet.
133
+ */
134
+ async listWalletTransfers(coin, walletId) {
135
+ return this.request(`/api/v2/${coin}/wallet/${walletId}/transfer`);
136
+ }
137
+ // --- Bank Accounts (v2/bankaccounts) ---
138
+ /**
139
+ * List all bank accounts.
140
+ */
141
+ async listBankAccounts(params) {
142
+ const query = new URLSearchParams();
143
+ if (params?.type)
144
+ query.set("type", params.type);
145
+ if (params?.verificationState)
146
+ query.set("verificationState", params.verificationState);
147
+ if (params?.enterpriseId)
148
+ query.set("enterpriseId", params.enterpriseId);
149
+ const qs = query.toString();
150
+ return this.request(`/api/v2/bankaccounts${qs ? `?${qs}` : ""}`);
151
+ }
152
+ /**
153
+ * Get a specific bank account by ID.
154
+ */
155
+ async getBankAccount(bankAccountId) {
156
+ return this.request(`/api/v2/bankaccounts/${bankAccountId}`);
157
+ }
158
+ /**
159
+ * Add a new bank account.
160
+ *
161
+ * NOTE: API requires complete owner address information (discovered via E2E testing).
162
+ * The ownerAddress object must contain these fields (snake_case):
163
+ * - address_line_1
164
+ * - city_locality
165
+ * - state_province
166
+ * - postal_code
167
+ */
168
+ async addBankAccount(data) {
169
+ return this.request("/api/v2/bankaccounts", {
170
+ method: "POST",
171
+ body: JSON.stringify(data),
172
+ });
173
+ }
174
+ /**
175
+ * Update a bank account.
176
+ */
177
+ async updateBankAccount(bankAccountId, data) {
178
+ return this.request(`/api/v2/bankaccounts/${bankAccountId}`, {
179
+ method: "PUT",
180
+ body: JSON.stringify(data),
181
+ });
182
+ }
183
+ /**
184
+ * Delete a bank account.
185
+ */
186
+ async deleteBankAccount(bankAccountId) {
187
+ return this.request(`/api/v2/bankaccounts/${bankAccountId}`, { method: "DELETE" });
188
+ }
189
+ /**
190
+ * Get deposit info for bank accounts.
191
+ */
192
+ async getDepositInfo() {
193
+ return this.request("/api/v2/bankaccounts/deposit/info");
194
+ }
195
+ // --- Trading (Prime) ---
196
+ /**
197
+ * Get account balances for a trading account.
198
+ */
199
+ async getTradingBalances(accountId) {
200
+ return this.request(`/api/prime/trading/v1/accounts/${accountId}/balances`);
201
+ }
202
+ /**
203
+ * List available trading products for an account.
204
+ */
205
+ async getTradingProducts(accountId) {
206
+ return this.request(`/api/prime/trading/v1/accounts/${accountId}/products`);
207
+ }
208
+ /**
209
+ * Place a trading order.
210
+ */
211
+ async placeOrder(accountId, data) {
212
+ return this.request(`/api/prime/trading/v1/accounts/${accountId}/orders`, {
213
+ method: "POST",
214
+ body: JSON.stringify(data),
215
+ });
216
+ }
217
+ /**
218
+ * List orders for a trading account.
219
+ */
220
+ async listOrders(accountId, params) {
221
+ const query = new URLSearchParams();
222
+ if (params?.limit)
223
+ query.set("limit", String(params.limit));
224
+ if (params?.offset)
225
+ query.set("offset", String(params.offset));
226
+ const qs = query.toString();
227
+ return this.request(`/api/prime/trading/v1/accounts/${accountId}/orders${qs ? `?${qs}` : ""}`);
228
+ }
229
+ /**
230
+ * Get a specific order by ID.
231
+ */
232
+ async getOrder(accountId, orderId) {
233
+ return this.request(`/api/prime/trading/v1/accounts/${accountId}/orders/${orderId}`);
234
+ }
235
+ /**
236
+ * Cancel an order.
237
+ */
238
+ async cancelOrder(accountId, orderId) {
239
+ return this.request(`/api/prime/trading/v1/accounts/${accountId}/orders/${orderId}/cancel`, { method: "POST" });
240
+ }
241
+ // --- Fiat/ACH ---
242
+ /**
243
+ * Get ACH debit agreement.
244
+ */
245
+ async getAchAgreement() {
246
+ return this.request("/api/fiat/v1/transaction/ach-debit/agreement");
247
+ }
248
+ /**
249
+ * Accept ACH debit agreement.
250
+ */
251
+ async acceptAchAgreement(data) {
252
+ return this.request("/api/fiat/v1/transaction/ach-debit/agreement", {
253
+ method: "POST",
254
+ body: JSON.stringify(data),
255
+ });
256
+ }
257
+ /**
258
+ * Create an ACH debit transaction.
259
+ */
260
+ async createAchDebit(data) {
261
+ return this.request("/api/fiat/v1/transaction/ach-debit", {
262
+ method: "POST",
263
+ body: JSON.stringify(data),
264
+ });
265
+ }
266
+ // --- Lightning Network ---
267
+ /**
268
+ * Create a lightning invoice.
269
+ */
270
+ async createLightningInvoice(walletId, data) {
271
+ return this.request(`/api/v2/wallet/${walletId}/lightning/invoice`, {
272
+ method: "POST",
273
+ body: JSON.stringify(data),
274
+ });
275
+ }
276
+ /**
277
+ * Get a lightning invoice status.
278
+ */
279
+ async getLightningInvoice(walletId, paymentHash) {
280
+ return this.request(`/api/v2/wallet/${walletId}/lightning/invoice/${paymentHash}`);
281
+ }
282
+ /**
283
+ * Make a lightning payment.
284
+ */
285
+ async makeLightningPayment(walletId, data) {
286
+ return this.request(`/api/v2/wallet/${walletId}/lightning/payment`, {
287
+ method: "POST",
288
+ body: JSON.stringify(data),
289
+ });
290
+ }
291
+ /**
292
+ * List lightning transactions.
293
+ */
294
+ async listLightningTransactions(walletId) {
295
+ return this.request(`/api/v2/wallet/${walletId}/lightning/transaction`);
296
+ }
297
+ // --- Utility ---
298
+ /**
299
+ * Look up bank routing number information.
300
+ * @param bankType - "ach" or "wire"
301
+ */
302
+ async lookupRoutingNumber(bankType, routingNumber) {
303
+ return this.request(`/api/tradfi/v1/banks/${bankType}/${routingNumber}`);
304
+ }
305
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@magnolia-financial/banking-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server wrapping the Magnolia banking API for AI agents",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "banking-mcp": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist/",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "start": "node dist/index.js",
16
+ "dev": "tsc --watch",
17
+ "test": "vitest run",
18
+ "test:watch": "vitest",
19
+ "test:integration": "MAGNOLIA_API_KEY=${MAGNOLIA_API_KEY} MAGNOLIA_API_URL=https://api.dev.magfi.dev vitest run --reporter=verbose"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "magnolia",
24
+ "banking",
25
+ "ai-agent"
26
+ ],
27
+ "author": "ClawBot",
28
+ "license": "MIT",
29
+ "type": "module",
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.26.0",
32
+ "zod": "^4.3.6"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^25.2.2",
36
+ "typescript": "^5.9.3",
37
+ "vitest": "^4.0.18"
38
+ }
39
+ }