@codespar/mcp-dock 0.1.0-alpha.1 → 0.2.0-alpha.2

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
@@ -73,20 +73,30 @@ Add to `.cursor/mcp.json` or `.vscode/mcp.json`:
73
73
  }
74
74
  ```
75
75
 
76
- ## Tools
77
-
78
- | Tool | Description |
79
- |------|-------------|
80
- | `create_account` | Open a digital account for an end user |
81
- | `get_account` | Retrieve account balance, status, coordinates |
82
- | `send_pix` | Outbound Pix transfer from a Dock account |
83
- | `get_pix` | Retrieve Pix payment by endToEndId |
84
- | `create_pix_qr_static` | Reusable Pix QR (points-of-sale, donations) |
85
- | `create_pix_qr_dynamic` | Single-use expiring Pix QR (e-commerce checkouts) |
86
- | `refund_pix` | Refund (devolução) a Pix payment |
87
- | `resolve_dict_key` | DICT lookup resolve Pix key to account holder |
88
- | `issue_card` | Issue debit / credit / prepaid / virtual card Dock's differentiator |
89
- | `get_card` | Retrieve card status, masked PAN, limits |
76
+ ## Tools (20)
77
+
78
+ | Tool | Purpose |
79
+ |---|---|
80
+ | `create_account` | Create a digital account for an end user (CPF holder) on Dock. |
81
+ | `get_account` | Retrieve a Dock account by id. |
82
+ | `send_pix` | Initiate an outbound Pix transfer from a Dock account to any Pix key in BR. |
83
+ | `get_pix` | Retrieve an outbound Pix payment by endToEndId. |
84
+ | `create_pix_qr_static` | Create a static Pix QR (reusable, tied to a merchant Pix key). |
85
+ | `create_pix_qr_dynamic` | Create a dynamic Pix QR (single-use, expiring). |
86
+ | `refund_pix` | Refund (devolução) a Pix payment. |
87
+ | `resolve_dict_key` | Resolve a Pix DICT key to the account holder's identity and ISPB/branch/account. |
88
+ | `issue_card` | Issue a card (debit / credit / prepaid / virtual) against a Dock account. |
89
+ | `get_card` | Retrieve a card by id. |
90
+ | `list_accounts` | List Dock accounts under the merchant. |
91
+ | `freeze_account` | Freeze (block) a Dock account. |
92
+ | `unfreeze_account` | Unfreeze a previously frozen Dock account, restoring Pix and card operations. |
93
+ | `block_card` | Block a card temporarily (reversible). |
94
+ | `unblock_card` | Unblock a card that was previously blocked (reversible). |
95
+ | `change_card_status` | Change a card's lifecycle status: ACTIVE / BLOCKED / CANCELED. |
96
+ | `list_transactions` | List transactions on a Dock account (Pix in/out, card auths, fees, transfers). |
97
+ | `get_transaction` | Retrieve a single transaction by id. |
98
+ | `create_webhook` | Register a webhook endpoint to receive Dock event notifications (account.*, pix.*, card.*, transaction.*). |
99
+ | `list_webhooks` | List all webhook endpoints registered for the merchant. |
90
100
 
91
101
  ## Authentication
92
102
 
package/dist/index.js CHANGED
@@ -8,17 +8,27 @@
8
8
  * Card issuing is the key differentiator vs Matera — Dock is historically
9
9
  * a card-issuing platform that expanded into full BaaS.
10
10
  *
11
- * Tools (10) — Banking + Pix + Card Issuing for v0.1:
12
- * create_account — POST /accounts (digital account for an end user)
13
- * get_account — GET /accounts/{id}
14
- * send_pix POST /pix/payments (outbound Pix transfer)
15
- * get_pix GET /pix/payments/{endToEndId}
16
- * create_pix_qr_static — POST /pix/qrcodes/static
17
- * create_pix_qr_dynamic — POST /pix/qrcodes/dynamic
18
- * refund_pix POST /pix/payments/{endToEndId}/refund
19
- * resolve_dict_key GET /pix/dict/{key}
20
- * issue_card — POST /cards (Dock's core differentiator)
21
- * get_card GET /cards/{id}
11
+ * Tools (20) — Banking + Pix + Card Issuing + Webhooks:
12
+ * create_account — POST /accounts (digital account for an end user)
13
+ * get_account — GET /accounts/{id}
14
+ * list_accounts GET /accounts
15
+ * freeze_account POST /accounts/{id}/freeze
16
+ * unfreeze_account — POST /accounts/{id}/unfreeze
17
+ * send_pix — POST /pix/payments (outbound Pix transfer)
18
+ * get_pix GET /pix/payments/{endToEndId}
19
+ * create_pix_qr_static POST /pix/qrcodes/static
20
+ * create_pix_qr_dynamic — POST /pix/qrcodes/dynamic
21
+ * refund_pix POST /pix/payments/{endToEndId}/refund
22
+ * resolve_dict_key — GET /pix/dict/{key}
23
+ * issue_card — POST /cards (Dock's core differentiator)
24
+ * get_card — GET /cards/{id}
25
+ * block_card — POST /cards/{id}/block
26
+ * unblock_card — POST /cards/{id}/unblock
27
+ * change_card_status — PATCH /cards/{id}/status
28
+ * list_transactions — GET /accounts/{id}/transactions
29
+ * get_transaction — GET /transactions/{id}
30
+ * create_webhook — POST /webhooks
31
+ * list_webhooks — GET /webhooks
22
32
  *
23
33
  * -------------------------------------------------------------------------
24
34
  * ALPHA STATUS — endpoint paths below are NOT verified against a live sandbox.
@@ -99,7 +109,7 @@ async function dockRequest(method, path, body) {
99
109
  const text = await res.text();
100
110
  return text ? JSON.parse(text) : {};
101
111
  }
102
- const server = new Server({ name: "mcp-dock", version: "0.1.0" }, { capabilities: { tools: {} } });
112
+ const server = new Server({ name: "mcp-dock", version: "0.2.0" }, { capabilities: { tools: {} } });
103
113
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
104
114
  tools: [
105
115
  {
@@ -278,6 +288,136 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
278
288
  required: ["card_id"],
279
289
  },
280
290
  },
291
+ {
292
+ name: "list_accounts",
293
+ description: "List Dock accounts under the merchant. Supports pagination and filtering by holder document or status.",
294
+ inputSchema: {
295
+ type: "object",
296
+ properties: {
297
+ cpf: { type: "string", description: "Filter by holder CPF (11 digits)" },
298
+ status: { type: "string", enum: ["ACTIVE", "FROZEN", "CLOSED"], description: "Filter by account status" },
299
+ page: { type: "number", description: "Page number (1-based)" },
300
+ page_size: { type: "number", description: "Results per page (default 20, max 100)" },
301
+ },
302
+ },
303
+ },
304
+ {
305
+ name: "freeze_account",
306
+ description: "Freeze (block) a Dock account. Pix outflows and card spend are halted but balance is preserved. Used for fraud holds, KYC re-verification, or judicial blocks.",
307
+ inputSchema: {
308
+ type: "object",
309
+ properties: {
310
+ account_id: { type: "string", description: "Dock account id" },
311
+ reason: { type: "string", description: "Reason code or free-text justification (logged for audit)" },
312
+ },
313
+ required: ["account_id"],
314
+ },
315
+ },
316
+ {
317
+ name: "unfreeze_account",
318
+ description: "Unfreeze a previously frozen Dock account, restoring Pix and card operations.",
319
+ inputSchema: {
320
+ type: "object",
321
+ properties: {
322
+ account_id: { type: "string", description: "Dock account id" },
323
+ reason: { type: "string", description: "Reason for the unblock (optional, logged)" },
324
+ },
325
+ required: ["account_id"],
326
+ },
327
+ },
328
+ {
329
+ name: "block_card",
330
+ description: "Block a card temporarily (reversible). Use for lost-card or fraud-suspected flows. Card status goes to BLOCKED — declines all authorizations until unblocked. Different from change_card_status which permanently cancels.",
331
+ inputSchema: {
332
+ type: "object",
333
+ properties: {
334
+ card_id: { type: "string", description: "Dock card id" },
335
+ reason: { type: "string", description: "Reason: LOST, STOLEN, SUSPECTED_FRAUD, CARDHOLDER_REQUEST" },
336
+ },
337
+ required: ["card_id"],
338
+ },
339
+ },
340
+ {
341
+ name: "unblock_card",
342
+ description: "Unblock a card that was previously blocked (reversible). Restores card to ACTIVE status. Cannot be used on permanently CANCELED cards.",
343
+ inputSchema: {
344
+ type: "object",
345
+ properties: {
346
+ card_id: { type: "string", description: "Dock card id" },
347
+ },
348
+ required: ["card_id"],
349
+ },
350
+ },
351
+ {
352
+ name: "change_card_status",
353
+ description: "Change a card's lifecycle status: ACTIVE / BLOCKED / CANCELED. Use CANCELED for permanent termination (replacement card, account closure). Status transitions are constrained — see Dock issuing docs.",
354
+ inputSchema: {
355
+ type: "object",
356
+ properties: {
357
+ card_id: { type: "string", description: "Dock card id" },
358
+ status: {
359
+ type: "string",
360
+ enum: ["ACTIVE", "BLOCKED", "CANCELED"],
361
+ description: "Target status",
362
+ },
363
+ reason: { type: "string", description: "Reason code or free text" },
364
+ },
365
+ required: ["card_id", "status"],
366
+ },
367
+ },
368
+ {
369
+ name: "list_transactions",
370
+ description: "List transactions on a Dock account (Pix in/out, card auths, fees, transfers). Supports date range and pagination. Returns ordered by timestamp desc.",
371
+ inputSchema: {
372
+ type: "object",
373
+ properties: {
374
+ account_id: { type: "string", description: "Dock account id" },
375
+ start_date: { type: "string", description: "ISO-8601 date or datetime (inclusive)" },
376
+ end_date: { type: "string", description: "ISO-8601 date or datetime (inclusive)" },
377
+ type: { type: "string", description: "Filter by transaction type (PIX_IN, PIX_OUT, CARD_AUTH, FEE, etc.)" },
378
+ page: { type: "number", description: "Page number (1-based)" },
379
+ page_size: { type: "number", description: "Results per page (default 50, max 200)" },
380
+ },
381
+ required: ["account_id"],
382
+ },
383
+ },
384
+ {
385
+ name: "get_transaction",
386
+ description: "Retrieve a single transaction by id. Returns full detail including counterparty, fees, and originating event (Pix endToEndId, card auth code, etc.).",
387
+ inputSchema: {
388
+ type: "object",
389
+ properties: {
390
+ transaction_id: { type: "string", description: "Dock transaction id" },
391
+ },
392
+ required: ["transaction_id"],
393
+ },
394
+ },
395
+ {
396
+ name: "create_webhook",
397
+ description: "Register a webhook endpoint to receive Dock event notifications (account.*, pix.*, card.*, transaction.*). Dock signs payloads with HMAC; verify the signature on receipt.",
398
+ inputSchema: {
399
+ type: "object",
400
+ properties: {
401
+ url: { type: "string", description: "HTTPS endpoint that will receive POSTed events" },
402
+ events: {
403
+ type: "array",
404
+ items: { type: "string" },
405
+ description: "Event types to subscribe to (e.g. ['pix.received','card.authorized']). Use ['*'] for all.",
406
+ },
407
+ secret: { type: "string", description: "Optional shared secret used for HMAC signature verification" },
408
+ description: { type: "string", description: "Free-text label" },
409
+ },
410
+ required: ["url", "events"],
411
+ },
412
+ },
413
+ {
414
+ name: "list_webhooks",
415
+ description: "List all webhook endpoints registered for the merchant. Returns id, url, subscribed events, and active status.",
416
+ inputSchema: {
417
+ type: "object",
418
+ properties: {},
419
+ },
420
+ },
281
421
  ],
282
422
  }));
283
423
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -322,6 +462,77 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
322
462
  const id = encodeURIComponent(String(a.card_id ?? ""));
323
463
  return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", `/cards/${id}`), null, 2) }] };
324
464
  }
465
+ // TODO(verify): list_accounts wrapper path; `/accounts?cpf=&status=&page=` is the standard shape.
466
+ case "list_accounts": {
467
+ const qs = new URLSearchParams();
468
+ if (a.cpf)
469
+ qs.set("cpf", String(a.cpf));
470
+ if (a.status)
471
+ qs.set("status", String(a.status));
472
+ if (a.page)
473
+ qs.set("page", String(a.page));
474
+ if (a.page_size)
475
+ qs.set("page_size", String(a.page_size));
476
+ const q = qs.toString();
477
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", `/accounts${q ? `?${q}` : ""}`), null, 2) }] };
478
+ }
479
+ case "freeze_account": {
480
+ const id = encodeURIComponent(String(a.account_id ?? ""));
481
+ const body = {};
482
+ if (a.reason)
483
+ body.reason = a.reason;
484
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", `/accounts/${id}/freeze`, body), null, 2) }] };
485
+ }
486
+ case "unfreeze_account": {
487
+ const id = encodeURIComponent(String(a.account_id ?? ""));
488
+ const body = {};
489
+ if (a.reason)
490
+ body.reason = a.reason;
491
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", `/accounts/${id}/unfreeze`, body), null, 2) }] };
492
+ }
493
+ case "block_card": {
494
+ const id = encodeURIComponent(String(a.card_id ?? ""));
495
+ const body = {};
496
+ if (a.reason)
497
+ body.reason = a.reason;
498
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", `/cards/${id}/block`, body), null, 2) }] };
499
+ }
500
+ case "unblock_card": {
501
+ const id = encodeURIComponent(String(a.card_id ?? ""));
502
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", `/cards/${id}/unblock`, {}), null, 2) }] };
503
+ }
504
+ case "change_card_status": {
505
+ const id = encodeURIComponent(String(a.card_id ?? ""));
506
+ const body = { status: a.status };
507
+ if (a.reason)
508
+ body.reason = a.reason;
509
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("PATCH", `/cards/${id}/status`, body), null, 2) }] };
510
+ }
511
+ // TODO(verify): transactions list path; `/accounts/{id}/transactions` is conventional but Dock may use a top-level `/transactions?account_id=`.
512
+ case "list_transactions": {
513
+ const id = encodeURIComponent(String(a.account_id ?? ""));
514
+ const qs = new URLSearchParams();
515
+ if (a.start_date)
516
+ qs.set("start_date", String(a.start_date));
517
+ if (a.end_date)
518
+ qs.set("end_date", String(a.end_date));
519
+ if (a.type)
520
+ qs.set("type", String(a.type));
521
+ if (a.page)
522
+ qs.set("page", String(a.page));
523
+ if (a.page_size)
524
+ qs.set("page_size", String(a.page_size));
525
+ const q = qs.toString();
526
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", `/accounts/${id}/transactions${q ? `?${q}` : ""}`), null, 2) }] };
527
+ }
528
+ case "get_transaction": {
529
+ const id = encodeURIComponent(String(a.transaction_id ?? ""));
530
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", `/transactions/${id}`), null, 2) }] };
531
+ }
532
+ case "create_webhook":
533
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", "/webhooks", a), null, 2) }] };
534
+ case "list_webhooks":
535
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", "/webhooks"), null, 2) }] };
325
536
  default:
326
537
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
327
538
  }
@@ -348,7 +559,7 @@ async function main() {
348
559
  const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
349
560
  t.onclose = () => { if (t.sessionId)
350
561
  transports.delete(t.sessionId); };
351
- const s = new Server({ name: "mcp-dock", version: "0.1.0" }, { capabilities: { tools: {} } });
562
+ const s = new Server({ name: "mcp-dock", version: "0.2.0" }, { capabilities: { tools: {} } });
352
563
  server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
353
564
  server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
354
565
  await s.connect(t);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codespar/mcp-dock",
3
- "version": "0.1.0-alpha.1",
4
- "description": "MCP server for Dock Brazilian Banking-as-a-Service (accounts, Pix, card issuing) for fintechs and embedded-finance products",
3
+ "version": "0.2.0-alpha.2",
4
+ "description": "MCP server for Dock \u2014 Brazilian Banking-as-a-Service (accounts, Pix, card issuing) for fintechs and embedded-finance products",
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/banking/dock"
9
9
  },
10
- "version": "0.1.0",
10
+ "version": "0.2.0-alpha.2",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@codespar/mcp-dock",
15
- "version": "0.1.0",
15
+ "version": "0.2.0-alpha.2",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },
package/src/index.ts CHANGED
@@ -8,17 +8,27 @@
8
8
  * Card issuing is the key differentiator vs Matera — Dock is historically
9
9
  * a card-issuing platform that expanded into full BaaS.
10
10
  *
11
- * Tools (10) — Banking + Pix + Card Issuing for v0.1:
12
- * create_account — POST /accounts (digital account for an end user)
13
- * get_account — GET /accounts/{id}
14
- * send_pix POST /pix/payments (outbound Pix transfer)
15
- * get_pix GET /pix/payments/{endToEndId}
16
- * create_pix_qr_static — POST /pix/qrcodes/static
17
- * create_pix_qr_dynamic — POST /pix/qrcodes/dynamic
18
- * refund_pix POST /pix/payments/{endToEndId}/refund
19
- * resolve_dict_key GET /pix/dict/{key}
20
- * issue_card — POST /cards (Dock's core differentiator)
21
- * get_card GET /cards/{id}
11
+ * Tools (20) — Banking + Pix + Card Issuing + Webhooks:
12
+ * create_account — POST /accounts (digital account for an end user)
13
+ * get_account — GET /accounts/{id}
14
+ * list_accounts GET /accounts
15
+ * freeze_account POST /accounts/{id}/freeze
16
+ * unfreeze_account — POST /accounts/{id}/unfreeze
17
+ * send_pix — POST /pix/payments (outbound Pix transfer)
18
+ * get_pix GET /pix/payments/{endToEndId}
19
+ * create_pix_qr_static POST /pix/qrcodes/static
20
+ * create_pix_qr_dynamic — POST /pix/qrcodes/dynamic
21
+ * refund_pix POST /pix/payments/{endToEndId}/refund
22
+ * resolve_dict_key — GET /pix/dict/{key}
23
+ * issue_card — POST /cards (Dock's core differentiator)
24
+ * get_card — GET /cards/{id}
25
+ * block_card — POST /cards/{id}/block
26
+ * unblock_card — POST /cards/{id}/unblock
27
+ * change_card_status — PATCH /cards/{id}/status
28
+ * list_transactions — GET /accounts/{id}/transactions
29
+ * get_transaction — GET /transactions/{id}
30
+ * create_webhook — POST /webhooks
31
+ * list_webhooks — GET /webhooks
22
32
  *
23
33
  * -------------------------------------------------------------------------
24
34
  * ALPHA STATUS — endpoint paths below are NOT verified against a live sandbox.
@@ -109,7 +119,7 @@ async function dockRequest(method: string, path: string, body?: unknown): Promis
109
119
  }
110
120
 
111
121
  const server = new Server(
112
- { name: "mcp-dock", version: "0.1.0" },
122
+ { name: "mcp-dock", version: "0.2.0" },
113
123
  { capabilities: { tools: {} } },
114
124
  );
115
125
 
@@ -291,6 +301,136 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
291
301
  required: ["card_id"],
292
302
  },
293
303
  },
304
+ {
305
+ name: "list_accounts",
306
+ description: "List Dock accounts under the merchant. Supports pagination and filtering by holder document or status.",
307
+ inputSchema: {
308
+ type: "object",
309
+ properties: {
310
+ cpf: { type: "string", description: "Filter by holder CPF (11 digits)" },
311
+ status: { type: "string", enum: ["ACTIVE", "FROZEN", "CLOSED"], description: "Filter by account status" },
312
+ page: { type: "number", description: "Page number (1-based)" },
313
+ page_size: { type: "number", description: "Results per page (default 20, max 100)" },
314
+ },
315
+ },
316
+ },
317
+ {
318
+ name: "freeze_account",
319
+ description: "Freeze (block) a Dock account. Pix outflows and card spend are halted but balance is preserved. Used for fraud holds, KYC re-verification, or judicial blocks.",
320
+ inputSchema: {
321
+ type: "object",
322
+ properties: {
323
+ account_id: { type: "string", description: "Dock account id" },
324
+ reason: { type: "string", description: "Reason code or free-text justification (logged for audit)" },
325
+ },
326
+ required: ["account_id"],
327
+ },
328
+ },
329
+ {
330
+ name: "unfreeze_account",
331
+ description: "Unfreeze a previously frozen Dock account, restoring Pix and card operations.",
332
+ inputSchema: {
333
+ type: "object",
334
+ properties: {
335
+ account_id: { type: "string", description: "Dock account id" },
336
+ reason: { type: "string", description: "Reason for the unblock (optional, logged)" },
337
+ },
338
+ required: ["account_id"],
339
+ },
340
+ },
341
+ {
342
+ name: "block_card",
343
+ description: "Block a card temporarily (reversible). Use for lost-card or fraud-suspected flows. Card status goes to BLOCKED — declines all authorizations until unblocked. Different from change_card_status which permanently cancels.",
344
+ inputSchema: {
345
+ type: "object",
346
+ properties: {
347
+ card_id: { type: "string", description: "Dock card id" },
348
+ reason: { type: "string", description: "Reason: LOST, STOLEN, SUSPECTED_FRAUD, CARDHOLDER_REQUEST" },
349
+ },
350
+ required: ["card_id"],
351
+ },
352
+ },
353
+ {
354
+ name: "unblock_card",
355
+ description: "Unblock a card that was previously blocked (reversible). Restores card to ACTIVE status. Cannot be used on permanently CANCELED cards.",
356
+ inputSchema: {
357
+ type: "object",
358
+ properties: {
359
+ card_id: { type: "string", description: "Dock card id" },
360
+ },
361
+ required: ["card_id"],
362
+ },
363
+ },
364
+ {
365
+ name: "change_card_status",
366
+ description: "Change a card's lifecycle status: ACTIVE / BLOCKED / CANCELED. Use CANCELED for permanent termination (replacement card, account closure). Status transitions are constrained — see Dock issuing docs.",
367
+ inputSchema: {
368
+ type: "object",
369
+ properties: {
370
+ card_id: { type: "string", description: "Dock card id" },
371
+ status: {
372
+ type: "string",
373
+ enum: ["ACTIVE", "BLOCKED", "CANCELED"],
374
+ description: "Target status",
375
+ },
376
+ reason: { type: "string", description: "Reason code or free text" },
377
+ },
378
+ required: ["card_id", "status"],
379
+ },
380
+ },
381
+ {
382
+ name: "list_transactions",
383
+ description: "List transactions on a Dock account (Pix in/out, card auths, fees, transfers). Supports date range and pagination. Returns ordered by timestamp desc.",
384
+ inputSchema: {
385
+ type: "object",
386
+ properties: {
387
+ account_id: { type: "string", description: "Dock account id" },
388
+ start_date: { type: "string", description: "ISO-8601 date or datetime (inclusive)" },
389
+ end_date: { type: "string", description: "ISO-8601 date or datetime (inclusive)" },
390
+ type: { type: "string", description: "Filter by transaction type (PIX_IN, PIX_OUT, CARD_AUTH, FEE, etc.)" },
391
+ page: { type: "number", description: "Page number (1-based)" },
392
+ page_size: { type: "number", description: "Results per page (default 50, max 200)" },
393
+ },
394
+ required: ["account_id"],
395
+ },
396
+ },
397
+ {
398
+ name: "get_transaction",
399
+ description: "Retrieve a single transaction by id. Returns full detail including counterparty, fees, and originating event (Pix endToEndId, card auth code, etc.).",
400
+ inputSchema: {
401
+ type: "object",
402
+ properties: {
403
+ transaction_id: { type: "string", description: "Dock transaction id" },
404
+ },
405
+ required: ["transaction_id"],
406
+ },
407
+ },
408
+ {
409
+ name: "create_webhook",
410
+ description: "Register a webhook endpoint to receive Dock event notifications (account.*, pix.*, card.*, transaction.*). Dock signs payloads with HMAC; verify the signature on receipt.",
411
+ inputSchema: {
412
+ type: "object",
413
+ properties: {
414
+ url: { type: "string", description: "HTTPS endpoint that will receive POSTed events" },
415
+ events: {
416
+ type: "array",
417
+ items: { type: "string" },
418
+ description: "Event types to subscribe to (e.g. ['pix.received','card.authorized']). Use ['*'] for all.",
419
+ },
420
+ secret: { type: "string", description: "Optional shared secret used for HMAC signature verification" },
421
+ description: { type: "string", description: "Free-text label" },
422
+ },
423
+ required: ["url", "events"],
424
+ },
425
+ },
426
+ {
427
+ name: "list_webhooks",
428
+ description: "List all webhook endpoints registered for the merchant. Returns id, url, subscribed events, and active status.",
429
+ inputSchema: {
430
+ type: "object",
431
+ properties: {},
432
+ },
433
+ },
294
434
  ],
295
435
  }));
296
436
 
@@ -336,6 +476,64 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
336
476
  const id = encodeURIComponent(String(a.card_id ?? ""));
337
477
  return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", `/cards/${id}`), null, 2) }] };
338
478
  }
479
+ // TODO(verify): list_accounts wrapper path; `/accounts?cpf=&status=&page=` is the standard shape.
480
+ case "list_accounts": {
481
+ const qs = new URLSearchParams();
482
+ if (a.cpf) qs.set("cpf", String(a.cpf));
483
+ if (a.status) qs.set("status", String(a.status));
484
+ if (a.page) qs.set("page", String(a.page));
485
+ if (a.page_size) qs.set("page_size", String(a.page_size));
486
+ const q = qs.toString();
487
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", `/accounts${q ? `?${q}` : ""}`), null, 2) }] };
488
+ }
489
+ case "freeze_account": {
490
+ const id = encodeURIComponent(String(a.account_id ?? ""));
491
+ const body: Record<string, unknown> = {};
492
+ if (a.reason) body.reason = a.reason;
493
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", `/accounts/${id}/freeze`, body), null, 2) }] };
494
+ }
495
+ case "unfreeze_account": {
496
+ const id = encodeURIComponent(String(a.account_id ?? ""));
497
+ const body: Record<string, unknown> = {};
498
+ if (a.reason) body.reason = a.reason;
499
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", `/accounts/${id}/unfreeze`, body), null, 2) }] };
500
+ }
501
+ case "block_card": {
502
+ const id = encodeURIComponent(String(a.card_id ?? ""));
503
+ const body: Record<string, unknown> = {};
504
+ if (a.reason) body.reason = a.reason;
505
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", `/cards/${id}/block`, body), null, 2) }] };
506
+ }
507
+ case "unblock_card": {
508
+ const id = encodeURIComponent(String(a.card_id ?? ""));
509
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", `/cards/${id}/unblock`, {}), null, 2) }] };
510
+ }
511
+ case "change_card_status": {
512
+ const id = encodeURIComponent(String(a.card_id ?? ""));
513
+ const body: Record<string, unknown> = { status: a.status };
514
+ if (a.reason) body.reason = a.reason;
515
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("PATCH", `/cards/${id}/status`, body), null, 2) }] };
516
+ }
517
+ // TODO(verify): transactions list path; `/accounts/{id}/transactions` is conventional but Dock may use a top-level `/transactions?account_id=`.
518
+ case "list_transactions": {
519
+ const id = encodeURIComponent(String(a.account_id ?? ""));
520
+ const qs = new URLSearchParams();
521
+ if (a.start_date) qs.set("start_date", String(a.start_date));
522
+ if (a.end_date) qs.set("end_date", String(a.end_date));
523
+ if (a.type) qs.set("type", String(a.type));
524
+ if (a.page) qs.set("page", String(a.page));
525
+ if (a.page_size) qs.set("page_size", String(a.page_size));
526
+ const q = qs.toString();
527
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", `/accounts/${id}/transactions${q ? `?${q}` : ""}`), null, 2) }] };
528
+ }
529
+ case "get_transaction": {
530
+ const id = encodeURIComponent(String(a.transaction_id ?? ""));
531
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", `/transactions/${id}`), null, 2) }] };
532
+ }
533
+ case "create_webhook":
534
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("POST", "/webhooks", a), null, 2) }] };
535
+ case "list_webhooks":
536
+ return { content: [{ type: "text", text: JSON.stringify(await dockRequest("GET", "/webhooks"), null, 2) }] };
339
537
  default:
340
538
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
341
539
  }
@@ -361,7 +559,7 @@ async function main() {
361
559
  if (!sid && isInitializeRequest(req.body)) {
362
560
  const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
363
561
  t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
364
- const s = new Server({ name: "mcp-dock", version: "0.1.0" }, { capabilities: { tools: {} } });
562
+ const s = new Server({ name: "mcp-dock", version: "0.2.0" }, { capabilities: { tools: {} } });
365
563
  (server as any)._requestHandlers.forEach((v: any, k: any) => (s as any)._requestHandlers.set(k, v));
366
564
  (server as any)._notificationHandlers?.forEach((v: any, k: any) => (s as any)._notificationHandlers.set(k, v));
367
565
  await s.connect(t);