@codespar/mcp-circle 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -49,19 +49,32 @@ Add to `.cursor/mcp.json` or `.vscode/mcp.json`:
49
49
  }
50
50
  ```
51
51
 
52
- ## Tools
52
+ ## Tools (23)
53
53
 
54
- | Tool | Description |
55
- |------|-------------|
56
- | `create_wallet` | Create a new Circle wallet |
54
+ | Tool | Purpose |
55
+ |---|---|
56
+ | `create_wallet` | Create a new Circle business-account wallet |
57
57
  | `get_wallet` | Get wallet details by ID |
58
+ | `list_wallets` | List all Circle wallets |
58
59
  | `create_payment` | Accept a USDC payment via Circle |
59
60
  | `get_payment` | Get payment details by ID |
60
61
  | `create_payout` | Create a payout from Circle (USDC to fiat) |
61
62
  | `get_payout` | Get payout details by ID |
62
- | `create_transfer` | Create a USDC transfer between Circle wallets |
63
+ | `list_payouts` | List payouts with optional filters |
64
+ | `create_transfer` | Create a USDC transfer between Circle wallets (or to blockchain address) |
63
65
  | `get_transfer` | Get transfer details by ID |
64
- | `get_balance` | Get account balance |
66
+ | `list_transfers` | List transfers with optional filters |
67
+ | `create_card` | Register card data for on-ramp payments |
68
+ | `get_card` | Get card details by ID |
69
+ | `list_cards` | List registered cards |
70
+ | `list_settlements` | List settlements (card payment batches) |
71
+ | `get_settlement` | Get settlement details by ID |
72
+ | `list_chargebacks` | List chargebacks |
73
+ | `get_chargeback` | Get chargeback details by ID |
74
+ | `create_subscription` | Register a notification subscription (webhook) |
75
+ | `list_subscriptions` | List notification subscriptions (webhooks) |
76
+ | `delete_subscription` | Delete a notification subscription |
77
+ | `get_balance` | Get business-account balance |
65
78
  | `list_transactions` | List transactions with optional filters |
66
79
 
67
80
  ## Authentication
package/dist/index.js CHANGED
@@ -3,15 +3,28 @@
3
3
  * MCP Server for Circle — USDC stablecoin infrastructure.
4
4
  *
5
5
  * Tools:
6
- * - create_wallet: Create a new Circle wallet
6
+ * - create_wallet: Create a new Circle business-account wallet
7
7
  * - get_wallet: Get wallet details by ID
8
+ * - list_wallets: List all wallets
8
9
  * - create_payment: Accept a USDC payment
9
10
  * - get_payment: Get payment details by ID
10
11
  * - create_payout: Create a payout (USDC to fiat)
11
12
  * - get_payout: Get payout details by ID
13
+ * - list_payouts: List payouts with filters
12
14
  * - create_transfer: Create a USDC transfer between wallets
13
15
  * - get_transfer: Get transfer details by ID
14
- * - get_balance: Get wallet balance
16
+ * - list_transfers: List transfers with filters
17
+ * - create_card: Register card data for on-ramp
18
+ * - get_card: Get card details by ID
19
+ * - list_cards: List cards
20
+ * - list_settlements: List settlements
21
+ * - get_settlement: Get settlement details by ID
22
+ * - list_chargebacks: List chargebacks
23
+ * - get_chargeback: Get chargeback by ID
24
+ * - create_subscription: Register a notification subscription (webhook)
25
+ * - list_subscriptions: List notification subscriptions
26
+ * - delete_subscription: Remove a notification subscription
27
+ * - get_balance: Get business-account balance
15
28
  * - list_transactions: List transactions with filters
16
29
  *
17
30
  * Environment:
@@ -39,12 +52,29 @@ async function circleRequest(method, path, body) {
39
52
  }
40
53
  return res.json();
41
54
  }
42
- const server = new Server({ name: "mcp-circle", version: "0.1.0" }, { capabilities: { tools: {} } });
55
+ const server = new Server({ name: "mcp-circle", version: "0.2.1" }, { capabilities: { tools: {} } });
56
+ const amountSchema = {
57
+ type: "object",
58
+ properties: {
59
+ amount: { type: "string", description: "Amount (e.g. '10.00')" },
60
+ currency: { type: "string", description: "Currency (USD)" },
61
+ },
62
+ required: ["amount", "currency"],
63
+ };
64
+ const sourceDestSchema = (label) => ({
65
+ type: "object",
66
+ properties: {
67
+ id: { type: "string", description: `${label} ID` },
68
+ type: { type: "string", description: `${label} type (e.g. wallet, card, ach, wire, blockchain)` },
69
+ },
70
+ required: ["id", "type"],
71
+ });
43
72
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
44
73
  tools: [
74
+ // Wallets
45
75
  {
46
76
  name: "create_wallet",
47
- description: "Create a new Circle wallet",
77
+ description: "Create a new Circle business-account wallet",
48
78
  inputSchema: {
49
79
  type: "object",
50
80
  properties: {
@@ -57,14 +87,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
57
87
  {
58
88
  name: "get_wallet",
59
89
  description: "Get wallet details by ID",
90
+ inputSchema: {
91
+ type: "object",
92
+ properties: { id: { type: "string", description: "Wallet ID" } },
93
+ required: ["id"],
94
+ },
95
+ },
96
+ {
97
+ name: "list_wallets",
98
+ description: "List all Circle wallets",
60
99
  inputSchema: {
61
100
  type: "object",
62
101
  properties: {
63
- id: { type: "string", description: "Wallet ID" },
102
+ pageSize: { type: "number", description: "Number of results per page" },
103
+ pageBefore: { type: "string", description: "Cursor for previous page" },
104
+ pageAfter: { type: "string", description: "Cursor for next page" },
64
105
  },
65
- required: ["id"],
66
106
  },
67
107
  },
108
+ // Payments
68
109
  {
69
110
  name: "create_payment",
70
111
  description: "Accept a USDC payment via Circle",
@@ -72,9 +113,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
72
113
  type: "object",
73
114
  properties: {
74
115
  idempotencyKey: { type: "string", description: "Unique idempotency key (UUID)" },
75
- amount: { type: "object", properties: { amount: { type: "string", description: "Amount (e.g. '10.00')" }, currency: { type: "string", description: "Currency (USD)" } }, required: ["amount", "currency"], description: "Payment amount" },
76
- source: { type: "object", properties: { id: { type: "string", description: "Source ID" }, type: { type: "string", description: "Source type (e.g. card, ach)" } }, required: ["id", "type"], description: "Payment source" },
116
+ amount: { ...amountSchema, description: "Payment amount" },
117
+ source: { ...sourceDestSchema("Source"), description: "Payment source" },
77
118
  description: { type: "string", description: "Payment description" },
119
+ verification: { type: "string", description: "Verification method (cvv, three_d_secure, none)" },
120
+ metadata: { type: "object", description: "Payment metadata (email, phoneNumber, sessionId, ipAddress)" },
78
121
  },
79
122
  required: ["idempotencyKey", "amount", "source"],
80
123
  },
@@ -84,12 +127,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
84
127
  description: "Get payment details by ID",
85
128
  inputSchema: {
86
129
  type: "object",
87
- properties: {
88
- id: { type: "string", description: "Payment ID" },
89
- },
130
+ properties: { id: { type: "string", description: "Payment ID" } },
90
131
  required: ["id"],
91
132
  },
92
133
  },
134
+ // Payouts
93
135
  {
94
136
  name: "create_payout",
95
137
  description: "Create a payout from Circle (USDC to fiat)",
@@ -97,8 +139,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
97
139
  type: "object",
98
140
  properties: {
99
141
  idempotencyKey: { type: "string", description: "Unique idempotency key (UUID)" },
100
- amount: { type: "object", properties: { amount: { type: "string", description: "Amount" }, currency: { type: "string", description: "Currency (USD)" } }, required: ["amount", "currency"], description: "Payout amount" },
101
- destination: { type: "object", properties: { id: { type: "string", description: "Destination ID (bank account)" }, type: { type: "string", description: "Destination type (e.g. wire)" } }, required: ["id", "type"], description: "Payout destination" },
142
+ amount: { ...amountSchema, description: "Payout amount" },
143
+ destination: { ...sourceDestSchema("Destination"), description: "Payout destination (bank account)" },
144
+ metadata: { type: "object", description: "Payout metadata (beneficiaryEmail)" },
102
145
  },
103
146
  required: ["idempotencyKey", "amount", "destination"],
104
147
  },
@@ -106,24 +149,39 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
106
149
  {
107
150
  name: "get_payout",
108
151
  description: "Get payout details by ID",
152
+ inputSchema: {
153
+ type: "object",
154
+ properties: { id: { type: "string", description: "Payout ID" } },
155
+ required: ["id"],
156
+ },
157
+ },
158
+ {
159
+ name: "list_payouts",
160
+ description: "List payouts with optional filters",
109
161
  inputSchema: {
110
162
  type: "object",
111
163
  properties: {
112
- id: { type: "string", description: "Payout ID" },
164
+ source: { type: "string", description: "Filter by source wallet ID" },
165
+ destination: { type: "string", description: "Filter by destination ID" },
166
+ type: { type: "string", description: "Filter by type (wire, ach, sen)" },
167
+ status: { type: "string", description: "Filter by status (pending, complete, failed)" },
168
+ from: { type: "string", description: "Start date (ISO 8601)" },
169
+ to: { type: "string", description: "End date (ISO 8601)" },
170
+ pageSize: { type: "number", description: "Results per page" },
113
171
  },
114
- required: ["id"],
115
172
  },
116
173
  },
174
+ // Transfers
117
175
  {
118
176
  name: "create_transfer",
119
- description: "Create a USDC transfer between Circle wallets",
177
+ description: "Create a USDC transfer between Circle wallets (or to blockchain address)",
120
178
  inputSchema: {
121
179
  type: "object",
122
180
  properties: {
123
181
  idempotencyKey: { type: "string", description: "Unique idempotency key (UUID)" },
124
- amount: { type: "object", properties: { amount: { type: "string", description: "Amount" }, currency: { type: "string", description: "Currency (USD)" } }, required: ["amount", "currency"], description: "Transfer amount" },
125
- source: { type: "object", properties: { id: { type: "string", description: "Source wallet ID" }, type: { type: "string", description: "Source type (wallet)" } }, required: ["id", "type"], description: "Transfer source" },
126
- destination: { type: "object", properties: { id: { type: "string", description: "Destination wallet ID" }, type: { type: "string", description: "Destination type (wallet, blockchain)" } }, required: ["id", "type"], description: "Transfer destination" },
182
+ amount: { ...amountSchema, description: "Transfer amount" },
183
+ source: { ...sourceDestSchema("Source"), description: "Transfer source (wallet)" },
184
+ destination: { ...sourceDestSchema("Destination"), description: "Transfer destination (wallet or blockchain)" },
127
185
  },
128
186
  required: ["idempotencyKey", "amount", "source", "destination"],
129
187
  },
@@ -131,17 +189,143 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
131
189
  {
132
190
  name: "get_transfer",
133
191
  description: "Get transfer details by ID",
192
+ inputSchema: {
193
+ type: "object",
194
+ properties: { id: { type: "string", description: "Transfer ID" } },
195
+ required: ["id"],
196
+ },
197
+ },
198
+ {
199
+ name: "list_transfers",
200
+ description: "List transfers with optional filters",
201
+ inputSchema: {
202
+ type: "object",
203
+ properties: {
204
+ walletId: { type: "string", description: "Filter by wallet ID" },
205
+ sourceWalletId: { type: "string", description: "Filter by source wallet ID" },
206
+ destinationWalletId: { type: "string", description: "Filter by destination wallet ID" },
207
+ from: { type: "string", description: "Start date (ISO 8601)" },
208
+ to: { type: "string", description: "End date (ISO 8601)" },
209
+ pageSize: { type: "number", description: "Results per page" },
210
+ },
211
+ },
212
+ },
213
+ // Cards
214
+ {
215
+ name: "create_card",
216
+ description: "Register card data for on-ramp payments",
217
+ inputSchema: {
218
+ type: "object",
219
+ properties: {
220
+ idempotencyKey: { type: "string", description: "Unique idempotency key (UUID)" },
221
+ keyId: { type: "string", description: "Public key ID used to encrypt card data" },
222
+ encryptedData: { type: "string", description: "PGP-encrypted card number + CVV" },
223
+ billingDetails: { type: "object", description: "Billing address details (name, city, country, line1, postalCode, district)" },
224
+ expMonth: { type: "number", description: "Card expiration month (1-12)" },
225
+ expYear: { type: "number", description: "Card expiration year (4-digit)" },
226
+ metadata: { type: "object", description: "Card metadata (email, phoneNumber, sessionId, ipAddress)" },
227
+ },
228
+ required: ["idempotencyKey", "keyId", "encryptedData", "billingDetails", "expMonth", "expYear"],
229
+ },
230
+ },
231
+ {
232
+ name: "get_card",
233
+ description: "Get card details by ID",
234
+ inputSchema: {
235
+ type: "object",
236
+ properties: { id: { type: "string", description: "Card ID" } },
237
+ required: ["id"],
238
+ },
239
+ },
240
+ {
241
+ name: "list_cards",
242
+ description: "List registered cards",
243
+ inputSchema: {
244
+ type: "object",
245
+ properties: {
246
+ pageSize: { type: "number", description: "Results per page" },
247
+ pageBefore: { type: "string", description: "Cursor for previous page" },
248
+ pageAfter: { type: "string", description: "Cursor for next page" },
249
+ },
250
+ },
251
+ },
252
+ // Settlements
253
+ {
254
+ name: "list_settlements",
255
+ description: "List settlements (card payment batches)",
256
+ inputSchema: {
257
+ type: "object",
258
+ properties: {
259
+ from: { type: "string", description: "Start date (ISO 8601)" },
260
+ to: { type: "string", description: "End date (ISO 8601)" },
261
+ pageSize: { type: "number", description: "Results per page" },
262
+ pageBefore: { type: "string", description: "Cursor for previous page" },
263
+ pageAfter: { type: "string", description: "Cursor for next page" },
264
+ },
265
+ },
266
+ },
267
+ {
268
+ name: "get_settlement",
269
+ description: "Get settlement details by ID",
270
+ inputSchema: {
271
+ type: "object",
272
+ properties: { id: { type: "string", description: "Settlement ID" } },
273
+ required: ["id"],
274
+ },
275
+ },
276
+ // Chargebacks
277
+ {
278
+ name: "list_chargebacks",
279
+ description: "List chargebacks",
280
+ inputSchema: {
281
+ type: "object",
282
+ properties: {
283
+ paymentId: { type: "string", description: "Filter by payment ID" },
284
+ from: { type: "string", description: "Start date (ISO 8601)" },
285
+ to: { type: "string", description: "End date (ISO 8601)" },
286
+ pageSize: { type: "number", description: "Results per page" },
287
+ },
288
+ },
289
+ },
290
+ {
291
+ name: "get_chargeback",
292
+ description: "Get chargeback details by ID",
293
+ inputSchema: {
294
+ type: "object",
295
+ properties: { id: { type: "string", description: "Chargeback ID" } },
296
+ required: ["id"],
297
+ },
298
+ },
299
+ // Notification subscriptions (webhooks)
300
+ {
301
+ name: "create_subscription",
302
+ description: "Register a notification subscription (webhook)",
134
303
  inputSchema: {
135
304
  type: "object",
136
305
  properties: {
137
- id: { type: "string", description: "Transfer ID" },
306
+ endpoint: { type: "string", description: "HTTPS webhook endpoint URL" },
138
307
  },
308
+ required: ["endpoint"],
309
+ },
310
+ },
311
+ {
312
+ name: "list_subscriptions",
313
+ description: "List notification subscriptions (webhooks)",
314
+ inputSchema: { type: "object", properties: {} },
315
+ },
316
+ {
317
+ name: "delete_subscription",
318
+ description: "Delete a notification subscription",
319
+ inputSchema: {
320
+ type: "object",
321
+ properties: { id: { type: "string", description: "Subscription ID" } },
139
322
  required: ["id"],
140
323
  },
141
324
  },
325
+ // Balance + transactions
142
326
  {
143
327
  name: "get_balance",
144
- description: "Get account balance",
328
+ description: "Get business-account balance",
145
329
  inputSchema: { type: "object", properties: {} },
146
330
  },
147
331
  {
@@ -162,46 +346,78 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
162
346
  },
163
347
  ],
164
348
  }));
349
+ function buildQuery(args, keys) {
350
+ const params = new URLSearchParams();
351
+ if (args) {
352
+ for (const k of keys) {
353
+ const v = args[k];
354
+ if (v !== undefined && v !== null && v !== "")
355
+ params.set(k, String(v));
356
+ }
357
+ }
358
+ const q = params.toString();
359
+ return q ? `?${q}` : "";
360
+ }
165
361
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
166
362
  const { name, arguments: args } = request.params;
167
363
  try {
168
364
  switch (name) {
365
+ // Wallets
169
366
  case "create_wallet":
170
- return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/wallets", args), null, 2) }] };
367
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/businessAccount/wallets", args), null, 2) }] };
171
368
  case "get_wallet":
172
369
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/wallets/${args?.id}`), null, 2) }] };
370
+ case "list_wallets":
371
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/wallets${buildQuery(args, ["pageSize", "pageBefore", "pageAfter"])}`), null, 2) }] };
372
+ // Payments
173
373
  case "create_payment":
174
374
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/payments", args), null, 2) }] };
175
375
  case "get_payment":
176
376
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/payments/${args?.id}`), null, 2) }] };
377
+ // Payouts
177
378
  case "create_payout":
178
379
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/payouts", args), null, 2) }] };
179
380
  case "get_payout":
180
381
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/payouts/${args?.id}`), null, 2) }] };
382
+ case "list_payouts":
383
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/payouts${buildQuery(args, ["source", "destination", "type", "status", "from", "to", "pageSize"])}`), null, 2) }] };
384
+ // Transfers
181
385
  case "create_transfer":
182
386
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/transfers", args), null, 2) }] };
183
387
  case "get_transfer":
184
388
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/transfers/${args?.id}`), null, 2) }] };
389
+ case "list_transfers":
390
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/transfers${buildQuery(args, ["walletId", "sourceWalletId", "destinationWalletId", "from", "to", "pageSize"])}`), null, 2) }] };
391
+ // Cards
392
+ case "create_card":
393
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/cards", args), null, 2) }] };
394
+ case "get_card":
395
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/cards/${args?.id}`), null, 2) }] };
396
+ case "list_cards":
397
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/cards${buildQuery(args, ["pageSize", "pageBefore", "pageAfter"])}`), null, 2) }] };
398
+ // Settlements
399
+ case "list_settlements":
400
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/settlements${buildQuery(args, ["from", "to", "pageSize", "pageBefore", "pageAfter"])}`), null, 2) }] };
401
+ case "get_settlement":
402
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/settlements/${args?.id}`), null, 2) }] };
403
+ // Chargebacks
404
+ case "list_chargebacks":
405
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/chargebacks${buildQuery(args, ["paymentId", "from", "to", "pageSize"])}`), null, 2) }] };
406
+ case "get_chargeback":
407
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/chargebacks/${args?.id}`), null, 2) }] };
408
+ // Notification subscriptions
409
+ case "create_subscription":
410
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/notifications/subscriptions", args), null, 2) }] };
411
+ case "list_subscriptions":
412
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", "/notifications/subscriptions"), null, 2) }] };
413
+ case "delete_subscription":
414
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("DELETE", `/notifications/subscriptions/${args?.id}`), null, 2) }] };
415
+ // Balance
185
416
  case "get_balance":
186
- return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", "/balances"), null, 2) }] };
187
- case "list_transactions": {
188
- const params = new URLSearchParams();
189
- if (args?.type)
190
- params.set("type", String(args.type));
191
- if (args?.status)
192
- params.set("status", String(args.status));
193
- if (args?.from)
194
- params.set("from", String(args.from));
195
- if (args?.to)
196
- params.set("to", String(args.to));
197
- if (args?.pageSize)
198
- params.set("pageSize", String(args.pageSize));
199
- if (args?.pageBefore)
200
- params.set("pageBefore", String(args.pageBefore));
201
- if (args?.pageAfter)
202
- params.set("pageAfter", String(args.pageAfter));
203
- return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/transactions?${params}`), null, 2) }] };
204
- }
417
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", "/businessAccount/balances"), null, 2) }] };
418
+ // Transactions
419
+ case "list_transactions":
420
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/transactions${buildQuery(args, ["type", "status", "from", "to", "pageSize", "pageBefore", "pageAfter"])}`), null, 2) }] };
205
421
  default:
206
422
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
207
423
  }
@@ -228,7 +444,7 @@ async function main() {
228
444
  const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
229
445
  t.onclose = () => { if (t.sessionId)
230
446
  transports.delete(t.sessionId); };
231
- const s = new Server({ name: "mcp-circle", version: "0.1.0" }, { capabilities: { tools: {} } });
447
+ const s = new Server({ name: "mcp-circle", version: "0.2.1" }, { capabilities: { tools: {} } });
232
448
  server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
233
449
  server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
234
450
  await s.connect(t);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codespar/mcp-circle",
3
- "version": "0.1.2",
4
- "description": "MCP server for Circle USDC payments, wallets, payouts, transfers",
3
+ "version": "0.2.1",
4
+ "description": "MCP server for Circle \u2014 USDC payments, wallets, payouts, transfers, cards, settlements, chargebacks, webhooks",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "bin": {
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "source": "github",
8
8
  "subfolder": "packages/crypto/circle"
9
9
  },
10
- "version": "0.1.2",
10
+ "version": "0.2.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@codespar/mcp-circle",
15
- "version": "0.1.2",
15
+ "version": "0.2.1",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },
package/src/index.ts CHANGED
@@ -4,15 +4,28 @@
4
4
  * MCP Server for Circle — USDC stablecoin infrastructure.
5
5
  *
6
6
  * Tools:
7
- * - create_wallet: Create a new Circle wallet
7
+ * - create_wallet: Create a new Circle business-account wallet
8
8
  * - get_wallet: Get wallet details by ID
9
+ * - list_wallets: List all wallets
9
10
  * - create_payment: Accept a USDC payment
10
11
  * - get_payment: Get payment details by ID
11
12
  * - create_payout: Create a payout (USDC to fiat)
12
13
  * - get_payout: Get payout details by ID
14
+ * - list_payouts: List payouts with filters
13
15
  * - create_transfer: Create a USDC transfer between wallets
14
16
  * - get_transfer: Get transfer details by ID
15
- * - get_balance: Get wallet balance
17
+ * - list_transfers: List transfers with filters
18
+ * - create_card: Register card data for on-ramp
19
+ * - get_card: Get card details by ID
20
+ * - list_cards: List cards
21
+ * - list_settlements: List settlements
22
+ * - get_settlement: Get settlement details by ID
23
+ * - list_chargebacks: List chargebacks
24
+ * - get_chargeback: Get chargeback by ID
25
+ * - create_subscription: Register a notification subscription (webhook)
26
+ * - list_subscriptions: List notification subscriptions
27
+ * - delete_subscription: Remove a notification subscription
28
+ * - get_balance: Get business-account balance
16
29
  * - list_transactions: List transactions with filters
17
30
  *
18
31
  * Environment:
@@ -48,15 +61,34 @@ async function circleRequest(method: string, path: string, body?: unknown): Prom
48
61
  }
49
62
 
50
63
  const server = new Server(
51
- { name: "mcp-circle", version: "0.1.0" },
64
+ { name: "mcp-circle", version: "0.2.1" },
52
65
  { capabilities: { tools: {} } }
53
66
  );
54
67
 
68
+ const amountSchema = {
69
+ type: "object",
70
+ properties: {
71
+ amount: { type: "string", description: "Amount (e.g. '10.00')" },
72
+ currency: { type: "string", description: "Currency (USD)" },
73
+ },
74
+ required: ["amount", "currency"],
75
+ };
76
+
77
+ const sourceDestSchema = (label: string) => ({
78
+ type: "object",
79
+ properties: {
80
+ id: { type: "string", description: `${label} ID` },
81
+ type: { type: "string", description: `${label} type (e.g. wallet, card, ach, wire, blockchain)` },
82
+ },
83
+ required: ["id", "type"],
84
+ });
85
+
55
86
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
56
87
  tools: [
88
+ // Wallets
57
89
  {
58
90
  name: "create_wallet",
59
- description: "Create a new Circle wallet",
91
+ description: "Create a new Circle business-account wallet",
60
92
  inputSchema: {
61
93
  type: "object",
62
94
  properties: {
@@ -69,14 +101,26 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
69
101
  {
70
102
  name: "get_wallet",
71
103
  description: "Get wallet details by ID",
104
+ inputSchema: {
105
+ type: "object",
106
+ properties: { id: { type: "string", description: "Wallet ID" } },
107
+ required: ["id"],
108
+ },
109
+ },
110
+ {
111
+ name: "list_wallets",
112
+ description: "List all Circle wallets",
72
113
  inputSchema: {
73
114
  type: "object",
74
115
  properties: {
75
- id: { type: "string", description: "Wallet ID" },
116
+ pageSize: { type: "number", description: "Number of results per page" },
117
+ pageBefore: { type: "string", description: "Cursor for previous page" },
118
+ pageAfter: { type: "string", description: "Cursor for next page" },
76
119
  },
77
- required: ["id"],
78
120
  },
79
121
  },
122
+
123
+ // Payments
80
124
  {
81
125
  name: "create_payment",
82
126
  description: "Accept a USDC payment via Circle",
@@ -84,9 +128,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
84
128
  type: "object",
85
129
  properties: {
86
130
  idempotencyKey: { type: "string", description: "Unique idempotency key (UUID)" },
87
- amount: { type: "object", properties: { amount: { type: "string", description: "Amount (e.g. '10.00')" }, currency: { type: "string", description: "Currency (USD)" } }, required: ["amount", "currency"], description: "Payment amount" },
88
- source: { type: "object", properties: { id: { type: "string", description: "Source ID" }, type: { type: "string", description: "Source type (e.g. card, ach)" } }, required: ["id", "type"], description: "Payment source" },
131
+ amount: { ...amountSchema, description: "Payment amount" },
132
+ source: { ...sourceDestSchema("Source"), description: "Payment source" },
89
133
  description: { type: "string", description: "Payment description" },
134
+ verification: { type: "string", description: "Verification method (cvv, three_d_secure, none)" },
135
+ metadata: { type: "object", description: "Payment metadata (email, phoneNumber, sessionId, ipAddress)" },
90
136
  },
91
137
  required: ["idempotencyKey", "amount", "source"],
92
138
  },
@@ -96,12 +142,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
96
142
  description: "Get payment details by ID",
97
143
  inputSchema: {
98
144
  type: "object",
99
- properties: {
100
- id: { type: "string", description: "Payment ID" },
101
- },
145
+ properties: { id: { type: "string", description: "Payment ID" } },
102
146
  required: ["id"],
103
147
  },
104
148
  },
149
+
150
+ // Payouts
105
151
  {
106
152
  name: "create_payout",
107
153
  description: "Create a payout from Circle (USDC to fiat)",
@@ -109,8 +155,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
109
155
  type: "object",
110
156
  properties: {
111
157
  idempotencyKey: { type: "string", description: "Unique idempotency key (UUID)" },
112
- amount: { type: "object", properties: { amount: { type: "string", description: "Amount" }, currency: { type: "string", description: "Currency (USD)" } }, required: ["amount", "currency"], description: "Payout amount" },
113
- destination: { type: "object", properties: { id: { type: "string", description: "Destination ID (bank account)" }, type: { type: "string", description: "Destination type (e.g. wire)" } }, required: ["id", "type"], description: "Payout destination" },
158
+ amount: { ...amountSchema, description: "Payout amount" },
159
+ destination: { ...sourceDestSchema("Destination"), description: "Payout destination (bank account)" },
160
+ metadata: { type: "object", description: "Payout metadata (beneficiaryEmail)" },
114
161
  },
115
162
  required: ["idempotencyKey", "amount", "destination"],
116
163
  },
@@ -118,24 +165,40 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
118
165
  {
119
166
  name: "get_payout",
120
167
  description: "Get payout details by ID",
168
+ inputSchema: {
169
+ type: "object",
170
+ properties: { id: { type: "string", description: "Payout ID" } },
171
+ required: ["id"],
172
+ },
173
+ },
174
+ {
175
+ name: "list_payouts",
176
+ description: "List payouts with optional filters",
121
177
  inputSchema: {
122
178
  type: "object",
123
179
  properties: {
124
- id: { type: "string", description: "Payout ID" },
180
+ source: { type: "string", description: "Filter by source wallet ID" },
181
+ destination: { type: "string", description: "Filter by destination ID" },
182
+ type: { type: "string", description: "Filter by type (wire, ach, sen)" },
183
+ status: { type: "string", description: "Filter by status (pending, complete, failed)" },
184
+ from: { type: "string", description: "Start date (ISO 8601)" },
185
+ to: { type: "string", description: "End date (ISO 8601)" },
186
+ pageSize: { type: "number", description: "Results per page" },
125
187
  },
126
- required: ["id"],
127
188
  },
128
189
  },
190
+
191
+ // Transfers
129
192
  {
130
193
  name: "create_transfer",
131
- description: "Create a USDC transfer between Circle wallets",
194
+ description: "Create a USDC transfer between Circle wallets (or to blockchain address)",
132
195
  inputSchema: {
133
196
  type: "object",
134
197
  properties: {
135
198
  idempotencyKey: { type: "string", description: "Unique idempotency key (UUID)" },
136
- amount: { type: "object", properties: { amount: { type: "string", description: "Amount" }, currency: { type: "string", description: "Currency (USD)" } }, required: ["amount", "currency"], description: "Transfer amount" },
137
- source: { type: "object", properties: { id: { type: "string", description: "Source wallet ID" }, type: { type: "string", description: "Source type (wallet)" } }, required: ["id", "type"], description: "Transfer source" },
138
- destination: { type: "object", properties: { id: { type: "string", description: "Destination wallet ID" }, type: { type: "string", description: "Destination type (wallet, blockchain)" } }, required: ["id", "type"], description: "Transfer destination" },
199
+ amount: { ...amountSchema, description: "Transfer amount" },
200
+ source: { ...sourceDestSchema("Source"), description: "Transfer source (wallet)" },
201
+ destination: { ...sourceDestSchema("Destination"), description: "Transfer destination (wallet or blockchain)" },
139
202
  },
140
203
  required: ["idempotencyKey", "amount", "source", "destination"],
141
204
  },
@@ -143,17 +206,148 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
143
206
  {
144
207
  name: "get_transfer",
145
208
  description: "Get transfer details by ID",
209
+ inputSchema: {
210
+ type: "object",
211
+ properties: { id: { type: "string", description: "Transfer ID" } },
212
+ required: ["id"],
213
+ },
214
+ },
215
+ {
216
+ name: "list_transfers",
217
+ description: "List transfers with optional filters",
218
+ inputSchema: {
219
+ type: "object",
220
+ properties: {
221
+ walletId: { type: "string", description: "Filter by wallet ID" },
222
+ sourceWalletId: { type: "string", description: "Filter by source wallet ID" },
223
+ destinationWalletId: { type: "string", description: "Filter by destination wallet ID" },
224
+ from: { type: "string", description: "Start date (ISO 8601)" },
225
+ to: { type: "string", description: "End date (ISO 8601)" },
226
+ pageSize: { type: "number", description: "Results per page" },
227
+ },
228
+ },
229
+ },
230
+
231
+ // Cards
232
+ {
233
+ name: "create_card",
234
+ description: "Register card data for on-ramp payments",
146
235
  inputSchema: {
147
236
  type: "object",
148
237
  properties: {
149
- id: { type: "string", description: "Transfer ID" },
238
+ idempotencyKey: { type: "string", description: "Unique idempotency key (UUID)" },
239
+ keyId: { type: "string", description: "Public key ID used to encrypt card data" },
240
+ encryptedData: { type: "string", description: "PGP-encrypted card number + CVV" },
241
+ billingDetails: { type: "object", description: "Billing address details (name, city, country, line1, postalCode, district)" },
242
+ expMonth: { type: "number", description: "Card expiration month (1-12)" },
243
+ expYear: { type: "number", description: "Card expiration year (4-digit)" },
244
+ metadata: { type: "object", description: "Card metadata (email, phoneNumber, sessionId, ipAddress)" },
150
245
  },
246
+ required: ["idempotencyKey", "keyId", "encryptedData", "billingDetails", "expMonth", "expYear"],
247
+ },
248
+ },
249
+ {
250
+ name: "get_card",
251
+ description: "Get card details by ID",
252
+ inputSchema: {
253
+ type: "object",
254
+ properties: { id: { type: "string", description: "Card ID" } },
151
255
  required: ["id"],
152
256
  },
153
257
  },
258
+ {
259
+ name: "list_cards",
260
+ description: "List registered cards",
261
+ inputSchema: {
262
+ type: "object",
263
+ properties: {
264
+ pageSize: { type: "number", description: "Results per page" },
265
+ pageBefore: { type: "string", description: "Cursor for previous page" },
266
+ pageAfter: { type: "string", description: "Cursor for next page" },
267
+ },
268
+ },
269
+ },
270
+
271
+ // Settlements
272
+ {
273
+ name: "list_settlements",
274
+ description: "List settlements (card payment batches)",
275
+ inputSchema: {
276
+ type: "object",
277
+ properties: {
278
+ from: { type: "string", description: "Start date (ISO 8601)" },
279
+ to: { type: "string", description: "End date (ISO 8601)" },
280
+ pageSize: { type: "number", description: "Results per page" },
281
+ pageBefore: { type: "string", description: "Cursor for previous page" },
282
+ pageAfter: { type: "string", description: "Cursor for next page" },
283
+ },
284
+ },
285
+ },
286
+ {
287
+ name: "get_settlement",
288
+ description: "Get settlement details by ID",
289
+ inputSchema: {
290
+ type: "object",
291
+ properties: { id: { type: "string", description: "Settlement ID" } },
292
+ required: ["id"],
293
+ },
294
+ },
295
+
296
+ // Chargebacks
297
+ {
298
+ name: "list_chargebacks",
299
+ description: "List chargebacks",
300
+ inputSchema: {
301
+ type: "object",
302
+ properties: {
303
+ paymentId: { type: "string", description: "Filter by payment ID" },
304
+ from: { type: "string", description: "Start date (ISO 8601)" },
305
+ to: { type: "string", description: "End date (ISO 8601)" },
306
+ pageSize: { type: "number", description: "Results per page" },
307
+ },
308
+ },
309
+ },
310
+ {
311
+ name: "get_chargeback",
312
+ description: "Get chargeback details by ID",
313
+ inputSchema: {
314
+ type: "object",
315
+ properties: { id: { type: "string", description: "Chargeback ID" } },
316
+ required: ["id"],
317
+ },
318
+ },
319
+
320
+ // Notification subscriptions (webhooks)
321
+ {
322
+ name: "create_subscription",
323
+ description: "Register a notification subscription (webhook)",
324
+ inputSchema: {
325
+ type: "object",
326
+ properties: {
327
+ endpoint: { type: "string", description: "HTTPS webhook endpoint URL" },
328
+ },
329
+ required: ["endpoint"],
330
+ },
331
+ },
332
+ {
333
+ name: "list_subscriptions",
334
+ description: "List notification subscriptions (webhooks)",
335
+ inputSchema: { type: "object", properties: {} },
336
+ },
337
+ {
338
+ name: "delete_subscription",
339
+ description: "Delete a notification subscription",
340
+ inputSchema: {
341
+ type: "object",
342
+ properties: { id: { type: "string", description: "Subscription ID" } },
343
+ required: ["id"],
344
+ },
345
+ },
346
+
347
+ // Balance + transactions
154
348
  {
155
349
  name: "get_balance",
156
- description: "Get account balance",
350
+ description: "Get business-account balance",
157
351
  inputSchema: { type: "object", properties: {} },
158
352
  },
159
353
  {
@@ -175,40 +369,89 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
175
369
  ],
176
370
  }));
177
371
 
372
+ function buildQuery(args: Record<string, unknown> | undefined, keys: string[]): string {
373
+ const params = new URLSearchParams();
374
+ if (args) {
375
+ for (const k of keys) {
376
+ const v = args[k];
377
+ if (v !== undefined && v !== null && v !== "") params.set(k, String(v));
378
+ }
379
+ }
380
+ const q = params.toString();
381
+ return q ? `?${q}` : "";
382
+ }
383
+
178
384
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
179
385
  const { name, arguments: args } = request.params;
180
386
 
181
387
  try {
182
388
  switch (name) {
389
+ // Wallets
183
390
  case "create_wallet":
184
- return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/wallets", args), null, 2) }] };
391
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/businessAccount/wallets", args), null, 2) }] };
185
392
  case "get_wallet":
186
393
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/wallets/${args?.id}`), null, 2) }] };
394
+ case "list_wallets":
395
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/wallets${buildQuery(args as any, ["pageSize", "pageBefore", "pageAfter"])}`), null, 2) }] };
396
+
397
+ // Payments
187
398
  case "create_payment":
188
399
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/payments", args), null, 2) }] };
189
400
  case "get_payment":
190
401
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/payments/${args?.id}`), null, 2) }] };
402
+
403
+ // Payouts
191
404
  case "create_payout":
192
405
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/payouts", args), null, 2) }] };
193
406
  case "get_payout":
194
407
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/payouts/${args?.id}`), null, 2) }] };
408
+ case "list_payouts":
409
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/payouts${buildQuery(args as any, ["source", "destination", "type", "status", "from", "to", "pageSize"])}`), null, 2) }] };
410
+
411
+ // Transfers
195
412
  case "create_transfer":
196
413
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/transfers", args), null, 2) }] };
197
414
  case "get_transfer":
198
415
  return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/transfers/${args?.id}`), null, 2) }] };
416
+ case "list_transfers":
417
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/transfers${buildQuery(args as any, ["walletId", "sourceWalletId", "destinationWalletId", "from", "to", "pageSize"])}`), null, 2) }] };
418
+
419
+ // Cards
420
+ case "create_card":
421
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/cards", args), null, 2) }] };
422
+ case "get_card":
423
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/cards/${args?.id}`), null, 2) }] };
424
+ case "list_cards":
425
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/cards${buildQuery(args as any, ["pageSize", "pageBefore", "pageAfter"])}`), null, 2) }] };
426
+
427
+ // Settlements
428
+ case "list_settlements":
429
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/settlements${buildQuery(args as any, ["from", "to", "pageSize", "pageBefore", "pageAfter"])}`), null, 2) }] };
430
+ case "get_settlement":
431
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/settlements/${args?.id}`), null, 2) }] };
432
+
433
+ // Chargebacks
434
+ case "list_chargebacks":
435
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/chargebacks${buildQuery(args as any, ["paymentId", "from", "to", "pageSize"])}`), null, 2) }] };
436
+ case "get_chargeback":
437
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/chargebacks/${args?.id}`), null, 2) }] };
438
+
439
+ // Notification subscriptions
440
+ case "create_subscription":
441
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("POST", "/notifications/subscriptions", args), null, 2) }] };
442
+ case "list_subscriptions":
443
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", "/notifications/subscriptions"), null, 2) }] };
444
+ case "delete_subscription":
445
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("DELETE", `/notifications/subscriptions/${args?.id}`), null, 2) }] };
446
+
447
+ // Balance
199
448
  case "get_balance":
200
- return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", "/balances"), null, 2) }] };
201
- case "list_transactions": {
202
- const params = new URLSearchParams();
203
- if (args?.type) params.set("type", String(args.type));
204
- if (args?.status) params.set("status", String(args.status));
205
- if (args?.from) params.set("from", String(args.from));
206
- if (args?.to) params.set("to", String(args.to));
207
- if (args?.pageSize) params.set("pageSize", String(args.pageSize));
208
- if (args?.pageBefore) params.set("pageBefore", String(args.pageBefore));
209
- if (args?.pageAfter) params.set("pageAfter", String(args.pageAfter));
210
- return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/transactions?${params}`), null, 2) }] };
211
- }
449
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", "/businessAccount/balances"), null, 2) }] };
450
+
451
+ // Transactions
452
+ case "list_transactions":
453
+ return { content: [{ type: "text", text: JSON.stringify(await circleRequest("GET", `/transactions${buildQuery(args as any, ["type", "status", "from", "to", "pageSize", "pageBefore", "pageAfter"])}`), null, 2) }] };
454
+
212
455
  default:
213
456
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
214
457
  }
@@ -231,7 +474,7 @@ async function main() {
231
474
  if (!sid && isInitializeRequest(req.body)) {
232
475
  const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
233
476
  t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
234
- const s = new Server({ name: "mcp-circle", version: "0.1.0" }, { capabilities: { tools: {} } }); (server as any)._requestHandlers.forEach((v: any, k: any) => (s as any)._requestHandlers.set(k, v)); (server as any)._notificationHandlers?.forEach((v: any, k: any) => (s as any)._notificationHandlers.set(k, v)); await s.connect(t);
477
+ const s = new Server({ name: "mcp-circle", version: "0.2.1" }, { capabilities: { tools: {} } }); (server as any)._requestHandlers.forEach((v: any, k: any) => (s as any)._requestHandlers.set(k, v)); (server as any)._notificationHandlers?.forEach((v: any, k: any) => (s as any)._notificationHandlers.set(k, v)); await s.connect(t);
235
478
  await t.handleRequest(req, res, req.body); return;
236
479
  }
237
480
  res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });