@codespar/mcp-matera 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 +44 -18
- package/dist/index.js +427 -17
- package/package.json +3 -2
- package/server.json +2 -2
- package/src/index.ts +401 -17
package/README.md
CHANGED
|
@@ -63,26 +63,36 @@ Add to `.cursor/mcp.json` or `.vscode/mcp.json`:
|
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
## Tools
|
|
67
|
-
|
|
68
|
-
| Tool |
|
|
69
|
-
|
|
70
|
-
| `create_pix_charge_static` |
|
|
71
|
-
| `create_pix_charge_dynamic` |
|
|
72
|
-
| `get_pix_charge` | Retrieve a Pix charge by txid |
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
66
|
+
## Tools (22)
|
|
67
|
+
|
|
68
|
+
| Tool | Purpose |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `create_pix_charge_static` | Create a static Pix charge (reusable QR code tied to a merchant Pix key). |
|
|
71
|
+
| `create_pix_charge_dynamic` | Create a dynamic Pix charge (single-use QR with expiration). |
|
|
72
|
+
| `get_pix_charge` | Retrieve a Pix charge (static or dynamic) by txid. |
|
|
73
|
+
| `list_pix_charges` | List immediate Pix charges (BCB /cob) with date and status filters. |
|
|
74
|
+
| `update_pix_charge` | Update an immediate Pix charge (BCB PATCH /cob/{txid}). |
|
|
75
|
+
| `create_pix_charge_due` | Create a due-dated Pix charge (BCB /cobv — Pix com Vencimento). |
|
|
76
|
+
| `get_pix_charge_due` | Retrieve a due-dated Pix charge (BCB GET /cobv/{txid}). |
|
|
77
|
+
| `create_pix_payment` | Initiate an outbound Pix transfer (ordem de pagamento). |
|
|
78
|
+
| `get_pix_payment` | Retrieve an outbound Pix payment by endToEndId. |
|
|
79
|
+
| `refund_pix_payment` | Refund (devolução) a Pix payment. |
|
|
80
|
+
| `list_pix_payments` | List outbound Pix payments with optional filters. |
|
|
81
|
+
| `list_pix_received` | List inbound Pix (Pix recebidos) credited to merchant accounts in a date range. |
|
|
82
|
+
| `resolve_pix_key` | Resolve a Pix DICT key to the account holder's identity and ISPB/branch/account. |
|
|
83
|
+
| `list_dict_keys` | List DICT keys registered to the merchant's accounts on Matera. |
|
|
84
|
+
| `register_dict_key` | Register (claim) a DICT key for a merchant account on Matera. |
|
|
85
|
+
| `delete_dict_key` | Delete a DICT key the merchant owns. |
|
|
86
|
+
| `create_pix_automatico` | Register a Pix Automático recurrence (BCB 2025 recurring Pix product, /rec). |
|
|
87
|
+
| `get_pix_automatico` | Retrieve a Pix Automático recurrence by idRec. |
|
|
88
|
+
| `cancel_pix_automatico` | Cancel an active Pix Automático recurrence. |
|
|
89
|
+
| `get_account_balance` | Get the current balance of a Matera-managed account. |
|
|
90
|
+
| `get_account_statement` | Get the statement (extrato) of a Matera-managed account in a date range. |
|
|
91
|
+
| `internal_transfer` | Book a transfer between two accounts both held on Matera (TED-interno / transferência interna). |
|
|
80
92
|
|
|
81
93
|
## Authentication
|
|
82
94
|
|
|
83
|
-
Matera uses **
|
|
84
|
-
|
|
85
|
-
Matera also supports `secret-key` + `data-signature` headers for signed server-to-server calls. That path is not implemented in v0.1; OAuth2 is sufficient for every tool above.
|
|
95
|
+
Matera's public doc index states that **server integration uses `secret-key` + `data-signature` headers**, while **OAuth2 is scoped to mobile / web-UI integrations** ([doc-api.matera.com](https://doc-api.matera.com/)). The v0.1-alpha scaffold currently implements the OAuth2 client-credentials path against `POST /auth/token`. That pairing is almost certainly wrong for server-to-server use and will be replaced with the signed-request scheme once we can validate against a live sandbox — see the "Status" section below.
|
|
86
96
|
|
|
87
97
|
## Environment Variables
|
|
88
98
|
|
|
@@ -94,7 +104,23 @@ Matera also supports `secret-key` + `data-signature` headers for signed server-t
|
|
|
94
104
|
|
|
95
105
|
## Status
|
|
96
106
|
|
|
97
|
-
v0.1 —
|
|
107
|
+
**v0.1.0-alpha.1 — tool surface is stable, wire paths are NOT yet verified.**
|
|
108
|
+
|
|
109
|
+
On 2026-04-24 we attempted to validate every endpoint path against `doc-api.matera.com`. The doc site is reachable in a browser but could not be fetched from our automation environment (DNS / network-gated; no login wall observed). Public search snippets and third-party references confirm the product surface (Pix charges, Pix payments, DICT, Pix Automático) and the authentication model (server = `secret-key` + `data-signature`; OAuth2 = end-user only) but do **not** surface the exact URL paths.
|
|
110
|
+
|
|
111
|
+
Until we can run the server against a live Matera sandbox, the following remain **assumed, not verified**:
|
|
112
|
+
|
|
113
|
+
| Area | Value in code | Why it's suspect |
|
|
114
|
+
|------|---------------|------------------|
|
|
115
|
+
| Token endpoint | `POST /auth/token` (Basic auth, `grant_type=client_credentials`) | Likely wrong for server integration — see Authentication |
|
|
116
|
+
| Pix charges | `/pix/charges/static`, `/pix/charges/dynamic`, `/pix/charges/{txid}` | Matera likely follows BCB's `/cob` (immediate) / `/cobv` (dated) naming |
|
|
117
|
+
| Pix payments | `/pix/payments`, `/pix/payments/{e2eid}`, `/pix/payments/{e2eid}/refund` | BCB spec names refunds `/pix/{e2eid}/devolucao/{id}` |
|
|
118
|
+
| DICT lookup | `GET /pix/dict/{key}`, `GET /pix/dict/keys` | BCB DICT is RSFN-gated; Matera exposes its own wrapper. Exact path unknown. |
|
|
119
|
+
| Pix Automático | `POST /pix/automatico` | Almost certainly a placeholder. BCB 2025 spec uses `POST /rec` (recurrence) + `/cobr/{txid}` (recurring charge). |
|
|
120
|
+
|
|
121
|
+
The 10 tool **names** and **input schemas** above are the public contract and will remain stable. Only the internal HTTP calls in `src/index.ts` will change when we verify against the sandbox. Using this server against a live Matera tenant today will produce 404s on most calls.
|
|
122
|
+
|
|
123
|
+
Track the verification work in the repo issues; PR welcome from anyone with a Matera sandbox.
|
|
98
124
|
|
|
99
125
|
## Roadmap
|
|
100
126
|
|
package/dist/index.js
CHANGED
|
@@ -8,24 +8,80 @@
|
|
|
8
8
|
* DICT, registering recurring Pix Automático agreements) — distinct from PSPs
|
|
9
9
|
* like Zoop/Asaas/Mercado Pago which serve merchants accepting Pix.
|
|
10
10
|
*
|
|
11
|
-
* Tools (
|
|
12
|
-
* create_pix_charge_static — static QR code (merchant Pix key, reusable)
|
|
13
|
-
* create_pix_charge_dynamic — dynamic QR code (single-use, expiring)
|
|
14
|
-
* get_pix_charge — fetch a charge by txid
|
|
15
|
-
* create_pix_payment — initiate an outbound Pix transfer
|
|
16
|
-
* get_pix_payment — fetch a payment by endToEndId
|
|
17
|
-
* refund_pix_payment — refund a Pix payment (MED / devolução)
|
|
18
|
-
* list_pix_payments — list payments with start/end/status filters
|
|
19
|
-
* resolve_pix_key — DICT lookup (CPF/CNPJ/email/phone/random → account)
|
|
20
|
-
* list_dict_keys — list DICT keys registered to merchant accounts
|
|
21
|
-
* create_pix_automatico — register a recurring Pix agreement (BCB 2025)
|
|
11
|
+
* Tools (21) — Pix + DICT + accounts for v0.2:
|
|
22
12
|
*
|
|
23
|
-
*
|
|
13
|
+
* Immediate Pix charges (BCB /cob)
|
|
14
|
+
* create_pix_charge_static — static QR code (merchant Pix key, reusable)
|
|
15
|
+
* create_pix_charge_dynamic — dynamic QR code (single-use, expiring)
|
|
16
|
+
* get_pix_charge — fetch a charge by txid
|
|
17
|
+
* list_pix_charges — list /cob charges with date+status filters
|
|
18
|
+
* update_pix_charge — PATCH a /cob (revise amount/expiration/status)
|
|
19
|
+
*
|
|
20
|
+
* Due-dated Pix charges (BCB /cobv — boleto-style with vencimento)
|
|
21
|
+
* create_pix_charge_due — cobv create
|
|
22
|
+
* get_pix_charge_due — cobv get by txid
|
|
23
|
+
*
|
|
24
|
+
* Outbound Pix payments (ordem de pagamento)
|
|
25
|
+
* create_pix_payment — initiate an outbound Pix transfer
|
|
26
|
+
* get_pix_payment — fetch a payment by endToEndId
|
|
27
|
+
* refund_pix_payment — refund a Pix payment (MED / devolução)
|
|
28
|
+
* list_pix_payments — list outbound payments with filters
|
|
29
|
+
*
|
|
30
|
+
* Inbound Pix (recebidos)
|
|
31
|
+
* list_pix_received — list received Pix in a window
|
|
32
|
+
*
|
|
33
|
+
* DICT (Pix key directory)
|
|
34
|
+
* resolve_pix_key — DICT lookup (CPF/CNPJ/email/phone/random → account)
|
|
35
|
+
* list_dict_keys — list DICT keys registered to merchant accounts
|
|
36
|
+
* register_dict_key — claim/register a DICT key for a merchant account
|
|
37
|
+
* delete_dict_key — remove a DICT key the merchant owns
|
|
38
|
+
*
|
|
39
|
+
* Pix Automático (BCB 2025 recurring Pix)
|
|
40
|
+
* create_pix_automatico — register a recurring Pix agreement
|
|
41
|
+
* get_pix_automatico — fetch a recurrence by idRec
|
|
42
|
+
* cancel_pix_automatico — cancel an active recurrence
|
|
43
|
+
*
|
|
44
|
+
* Accounts (Matera-managed core-banking accounts)
|
|
45
|
+
* get_account_balance — current balance for a Matera account
|
|
46
|
+
* get_account_statement — extrato / statement for a date range
|
|
47
|
+
* internal_transfer — book transfer between two Matera-managed accounts
|
|
48
|
+
*
|
|
49
|
+
* -------------------------------------------------------------------------
|
|
50
|
+
* ALPHA STATUS — endpoint paths below are NOT verified against a live sandbox.
|
|
51
|
+
*
|
|
52
|
+
* On 2026-04-24 we attempted to validate every path against doc-api.matera.com.
|
|
53
|
+
* The doc site was not reachable from our automation environment (DNS-gated;
|
|
54
|
+
* no login wall observed). Public search snippets and third-party references
|
|
55
|
+
* confirm Matera's product surface and auth model but do not surface exact URL
|
|
56
|
+
* paths. Where Matera-specific paths are unknown, we follow BCB Pix API v2
|
|
57
|
+
* conventions (`/cob`, `/cobv`, `/pix`, `/rec`) which Matera is required to
|
|
58
|
+
* mirror as a participating PSP. Known-suspect items are flagged inline with
|
|
59
|
+
* `TODO(verify)` comments.
|
|
60
|
+
*
|
|
61
|
+
* High-confidence corrections pending sandbox access:
|
|
62
|
+
* - Auth: Matera's server integration uses `secret-key` + `data-signature`
|
|
63
|
+
* headers, not OAuth2. OAuth2 is documented only for mobile / web-UI
|
|
64
|
+
* integrations. The code below still uses the OAuth2 client_credentials
|
|
65
|
+
* flow as a placeholder and will fail against the real server path.
|
|
66
|
+
* - Pix Automático: BCB's 2025 spec uses POST /rec (recurrence) and /cobr
|
|
67
|
+
* (recurring charge). `/pix/automatico` is a placeholder; the true Matera
|
|
68
|
+
* path is unknown.
|
|
69
|
+
* - DICT: Real DICT (RSFN-gated) sits at BCB; Matera wraps it. The wrapper
|
|
70
|
+
* path `/pix/dict/*` is a guess.
|
|
71
|
+
* - Accounts (balance/statement/internal-transfer): no public path.
|
|
72
|
+
* `/accounts/*` is a reasoned default.
|
|
73
|
+
*
|
|
74
|
+
* The 21 tool names + input schemas are the stable public contract. Only the
|
|
75
|
+
* internal `materaRequest(method, path, body)` calls will change when paths
|
|
76
|
+
* are verified.
|
|
77
|
+
* -------------------------------------------------------------------------
|
|
78
|
+
*
|
|
79
|
+
* Authentication (placeholder — see note above)
|
|
24
80
|
* OAuth 2.0 Client Credentials. POST /auth/token with Basic auth
|
|
25
81
|
* (client_id:client_secret) + grant_type=client_credentials. Bearer token
|
|
26
82
|
* cached in memory until a minute before expiry.
|
|
27
|
-
* Matera
|
|
28
|
-
*
|
|
83
|
+
* Matera's production server auth is secret-key + data-signature — NOT
|
|
84
|
+
* implemented in this alpha.
|
|
29
85
|
*
|
|
30
86
|
* Environment
|
|
31
87
|
* MATERA_CLIENT_ID OAuth2 client_id
|
|
@@ -50,6 +106,9 @@ async function getAccessToken() {
|
|
|
50
106
|
return tokenCache.accessToken;
|
|
51
107
|
}
|
|
52
108
|
const basic = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64");
|
|
109
|
+
// TODO(verify): Matera's real token path is unconfirmed. Candidates include
|
|
110
|
+
// `/auth/token`, `/oauth/token`, `/auth/v1/token`. Leaving the original
|
|
111
|
+
// placeholder until the sandbox confirms it.
|
|
53
112
|
const res = await fetch(`${BASE_URL}/auth/token`, {
|
|
54
113
|
method: "POST",
|
|
55
114
|
headers: {
|
|
@@ -85,7 +144,7 @@ async function materaRequest(method, path, body) {
|
|
|
85
144
|
const text = await res.text();
|
|
86
145
|
return text ? JSON.parse(text) : {};
|
|
87
146
|
}
|
|
88
|
-
const server = new Server({ name: "mcp-matera", version: "0.
|
|
147
|
+
const server = new Server({ name: "mcp-matera", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
89
148
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
90
149
|
tools: [
|
|
91
150
|
{
|
|
@@ -139,6 +198,85 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
139
198
|
required: ["txid"],
|
|
140
199
|
},
|
|
141
200
|
},
|
|
201
|
+
{
|
|
202
|
+
name: "list_pix_charges",
|
|
203
|
+
description: "List immediate Pix charges (BCB /cob) with date and status filters. Use for reconciling QR-driven receipts in a time window.",
|
|
204
|
+
inputSchema: {
|
|
205
|
+
type: "object",
|
|
206
|
+
properties: {
|
|
207
|
+
start: { type: "string", description: "ISO-8601 start timestamp (inclusive). BCB calls this `inicio`." },
|
|
208
|
+
end: { type: "string", description: "ISO-8601 end timestamp (exclusive). BCB calls this `fim`." },
|
|
209
|
+
status: { type: "string", description: "Filter by status (ATIVA, CONCLUIDA, REMOVIDA_PELO_USUARIO_RECEBEDOR, REMOVIDA_PELO_PSP)" },
|
|
210
|
+
cpf: { type: "string", description: "Filter charges where the payer CPF equals this value" },
|
|
211
|
+
cnpj: { type: "string", description: "Filter charges where the payer CNPJ equals this value" },
|
|
212
|
+
page: { type: "number", description: "Page number (starts at 0 per BCB convention)" },
|
|
213
|
+
limit: { type: "number", description: "Page size (BCB max 1000)" },
|
|
214
|
+
},
|
|
215
|
+
required: ["start", "end"],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "update_pix_charge",
|
|
220
|
+
description: "Update an immediate Pix charge (BCB PATCH /cob/{txid}). Use to revise amount/expiration before payment, or to mark a charge REMOVIDA_PELO_USUARIO_RECEBEDOR.",
|
|
221
|
+
inputSchema: {
|
|
222
|
+
type: "object",
|
|
223
|
+
properties: {
|
|
224
|
+
txid: { type: "string", description: "txid of the existing charge" },
|
|
225
|
+
amount: { type: "number", description: "New amount in BRL (only if charge still ATIVA)" },
|
|
226
|
+
expiration: { type: "number", description: "New expiration in seconds from creation" },
|
|
227
|
+
description: { type: "string", description: "New description shown to payer" },
|
|
228
|
+
status: { type: "string", description: "Set to REMOVIDA_PELO_USUARIO_RECEBEDOR to cancel an unpaid charge" },
|
|
229
|
+
},
|
|
230
|
+
required: ["txid"],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: "create_pix_charge_due",
|
|
235
|
+
description: "Create a due-dated Pix charge (BCB /cobv — Pix com Vencimento). Boleto-style charge with due date, late fee (multa), interest (juros), and discount fields. Returns txid + QR payload.",
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {
|
|
239
|
+
pix_key: { type: "string", description: "Merchant Pix key the charge settles to" },
|
|
240
|
+
amount: { type: "number", description: "Original amount in BRL (valor.original)" },
|
|
241
|
+
description: { type: "string", description: "Description shown to payer (solicitacaoPagador)" },
|
|
242
|
+
due_date: { type: "string", description: "ISO-8601 date when the charge becomes due (data.dataDeVencimento)" },
|
|
243
|
+
validity_days_after_due: { type: "number", description: "How many calendar days after due date the QR remains payable (validadeAposVencimento). Default 30." },
|
|
244
|
+
debtor: {
|
|
245
|
+
type: "object",
|
|
246
|
+
description: "Payer (devedor). BCB requires CPF or CNPJ + name for cobv.",
|
|
247
|
+
properties: {
|
|
248
|
+
cpf: { type: "string" },
|
|
249
|
+
cnpj: { type: "string" },
|
|
250
|
+
name: { type: "string" },
|
|
251
|
+
address: { type: "string", description: "Logradouro" },
|
|
252
|
+
city: { type: "string", description: "Cidade" },
|
|
253
|
+
state: { type: "string", description: "UF (2-letter)" },
|
|
254
|
+
zip: { type: "string", description: "CEP" },
|
|
255
|
+
},
|
|
256
|
+
required: ["name"],
|
|
257
|
+
},
|
|
258
|
+
fine: { type: "number", description: "Late fee, BRL or % depending on `fine_mode` (multa.valor or multa.modalidade)" },
|
|
259
|
+
fine_mode: { type: "number", enum: [1, 2], description: "1=fixed amount, 2=percent (BCB modalidade)" },
|
|
260
|
+
interest: { type: "number", description: "Interest after due date" },
|
|
261
|
+
interest_mode: { type: "number", description: "BCB juros.modalidade (1..7). 2=percent per day, 5=percent per month, etc." },
|
|
262
|
+
discount: { type: "number", description: "Discount value if paid before due date" },
|
|
263
|
+
discount_mode: { type: "number", description: "BCB desconto.modalidade (1..6)" },
|
|
264
|
+
txid: { type: "string", description: "Optional merchant-side txid (26-35 alphanumerics)" },
|
|
265
|
+
},
|
|
266
|
+
required: ["pix_key", "amount", "description", "due_date", "debtor"],
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "get_pix_charge_due",
|
|
271
|
+
description: "Retrieve a due-dated Pix charge (BCB GET /cobv/{txid}).",
|
|
272
|
+
inputSchema: {
|
|
273
|
+
type: "object",
|
|
274
|
+
properties: {
|
|
275
|
+
txid: { type: "string", description: "txid of the cobv charge" },
|
|
276
|
+
},
|
|
277
|
+
required: ["txid"],
|
|
278
|
+
},
|
|
279
|
+
},
|
|
142
280
|
{
|
|
143
281
|
name: "create_pix_payment",
|
|
144
282
|
description: "Initiate an outbound Pix transfer (ordem de pagamento). Moves money from a debtor account held on Matera to any Pix key in BR. Returns endToEndId once the BCB SPI confirms.",
|
|
@@ -202,6 +340,23 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
202
340
|
},
|
|
203
341
|
},
|
|
204
342
|
},
|
|
343
|
+
{
|
|
344
|
+
name: "list_pix_received",
|
|
345
|
+
description: "List inbound Pix (Pix recebidos) credited to merchant accounts in a date range. Mirrors BCB GET /pix. Filter by txid to find the receipt that closed a specific cob.",
|
|
346
|
+
inputSchema: {
|
|
347
|
+
type: "object",
|
|
348
|
+
properties: {
|
|
349
|
+
start: { type: "string", description: "ISO-8601 start timestamp (inclusive)" },
|
|
350
|
+
end: { type: "string", description: "ISO-8601 end timestamp (exclusive)" },
|
|
351
|
+
txid: { type: "string", description: "Only return Pix receipts that closed this txid" },
|
|
352
|
+
cpf: { type: "string", description: "Filter by payer CPF" },
|
|
353
|
+
cnpj: { type: "string", description: "Filter by payer CNPJ" },
|
|
354
|
+
page: { type: "number", description: "Page number (BCB starts at 0)" },
|
|
355
|
+
limit: { type: "number", description: "Page size" },
|
|
356
|
+
},
|
|
357
|
+
required: ["start", "end"],
|
|
358
|
+
},
|
|
359
|
+
},
|
|
205
360
|
{
|
|
206
361
|
name: "resolve_pix_key",
|
|
207
362
|
description: "Resolve a Pix DICT key to the account holder's identity and ISPB/branch/account. Use before sending large transfers to verify the counterparty. Note: DICT queries are rate-limited and logged by BCB.",
|
|
@@ -223,9 +378,44 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
223
378
|
},
|
|
224
379
|
},
|
|
225
380
|
},
|
|
381
|
+
{
|
|
382
|
+
name: "register_dict_key",
|
|
383
|
+
description: "Register (claim) a DICT key for a merchant account on Matera. For email/phone keys this triggers BCB's confirmation flow; for CPF/CNPJ/random it claims immediately. Subject to BCB DICT rate limits and ownership rules (max 5 keys per CPF, 20 per CNPJ).",
|
|
384
|
+
inputSchema: {
|
|
385
|
+
type: "object",
|
|
386
|
+
properties: {
|
|
387
|
+
key_type: { type: "string", enum: ["CPF", "CNPJ", "EMAIL", "PHONE", "RANDOM"], description: "Type of key to register" },
|
|
388
|
+
key_value: { type: "string", description: "Value (omit for RANDOM — Matera will generate the UUID)" },
|
|
389
|
+
account: {
|
|
390
|
+
type: "object",
|
|
391
|
+
description: "Matera-managed account that will own the key",
|
|
392
|
+
properties: {
|
|
393
|
+
branch: { type: "string", description: "Branch (agência)" },
|
|
394
|
+
account: { type: "string", description: "Account number" },
|
|
395
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"], description: "ISO 20022 account type" },
|
|
396
|
+
ispb: { type: "string", description: "ISPB of the holding institution (Matera-issued)" },
|
|
397
|
+
},
|
|
398
|
+
required: ["branch", "account", "account_type"],
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
required: ["key_type", "account"],
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
name: "delete_dict_key",
|
|
406
|
+
description: "Delete a DICT key the merchant owns. After delete, the key is held in BCB's quarentena window before another holder can claim it.",
|
|
407
|
+
inputSchema: {
|
|
408
|
+
type: "object",
|
|
409
|
+
properties: {
|
|
410
|
+
key: { type: "string", description: "Pix key value to delete" },
|
|
411
|
+
reason: { type: "string", description: "Optional reason (BCB MotivoExclusao). Default: USER_REQUESTED." },
|
|
412
|
+
},
|
|
413
|
+
required: ["key"],
|
|
414
|
+
},
|
|
415
|
+
},
|
|
226
416
|
{
|
|
227
417
|
name: "create_pix_automatico",
|
|
228
|
-
description: "Register a Pix Automático
|
|
418
|
+
description: "Register a Pix Automático recurrence (BCB 2025 recurring Pix product, /rec). The payer authorizes the merchant to pull recurring amounts on a schedule. Matera is one of the few providers live with this.",
|
|
229
419
|
inputSchema: {
|
|
230
420
|
type: "object",
|
|
231
421
|
properties: {
|
|
@@ -250,6 +440,90 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
250
440
|
required: ["payer", "merchant_pix_key", "frequency", "amount", "first_charge_date", "description"],
|
|
251
441
|
},
|
|
252
442
|
},
|
|
443
|
+
{
|
|
444
|
+
name: "get_pix_automatico",
|
|
445
|
+
description: "Retrieve a Pix Automático recurrence by idRec. Returns current status (CRIADA, APROVADA, REJEITADA, CANCELADA), schedule, and last charge attempt.",
|
|
446
|
+
inputSchema: {
|
|
447
|
+
type: "object",
|
|
448
|
+
properties: {
|
|
449
|
+
id_rec: { type: "string", description: "Recurrence id (idRec) returned by create_pix_automatico" },
|
|
450
|
+
},
|
|
451
|
+
required: ["id_rec"],
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
name: "cancel_pix_automatico",
|
|
456
|
+
description: "Cancel an active Pix Automático recurrence. Future charges stop after BCB confirms the cancellation. Past charges are unaffected.",
|
|
457
|
+
inputSchema: {
|
|
458
|
+
type: "object",
|
|
459
|
+
properties: {
|
|
460
|
+
id_rec: { type: "string", description: "Recurrence id (idRec) to cancel" },
|
|
461
|
+
reason: { type: "string", description: "Optional reason recorded on the cancellation" },
|
|
462
|
+
},
|
|
463
|
+
required: ["id_rec"],
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: "get_account_balance",
|
|
468
|
+
description: "Get the current balance of a Matera-managed account. Returns available, blocked, and total balance in BRL.",
|
|
469
|
+
inputSchema: {
|
|
470
|
+
type: "object",
|
|
471
|
+
properties: {
|
|
472
|
+
branch: { type: "string", description: "Branch (agência)" },
|
|
473
|
+
account: { type: "string", description: "Account number" },
|
|
474
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"], description: "ISO 20022 account type. Defaults to CACC." },
|
|
475
|
+
},
|
|
476
|
+
required: ["branch", "account"],
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: "get_account_statement",
|
|
481
|
+
description: "Get the statement (extrato) of a Matera-managed account in a date range. Returns one entry per credit/debit with counterparty, endToEndId (when applicable), and running balance.",
|
|
482
|
+
inputSchema: {
|
|
483
|
+
type: "object",
|
|
484
|
+
properties: {
|
|
485
|
+
branch: { type: "string", description: "Branch (agência)" },
|
|
486
|
+
account: { type: "string", description: "Account number" },
|
|
487
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"], description: "ISO 20022 account type. Defaults to CACC." },
|
|
488
|
+
start: { type: "string", description: "ISO-8601 start date (inclusive)" },
|
|
489
|
+
end: { type: "string", description: "ISO-8601 end date (exclusive)" },
|
|
490
|
+
page: { type: "number", description: "Page number" },
|
|
491
|
+
limit: { type: "number", description: "Page size" },
|
|
492
|
+
},
|
|
493
|
+
required: ["branch", "account", "start", "end"],
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
name: "internal_transfer",
|
|
498
|
+
description: "Book a transfer between two accounts both held on Matera (TED-interno / transferência interna). Settles instantly without touching SPI/Pix rails — no endToEndId, no DICT lookup. Use for moving funds across a fintech's own customer accounts.",
|
|
499
|
+
inputSchema: {
|
|
500
|
+
type: "object",
|
|
501
|
+
properties: {
|
|
502
|
+
debtor_account: {
|
|
503
|
+
type: "object",
|
|
504
|
+
properties: {
|
|
505
|
+
branch: { type: "string" },
|
|
506
|
+
account: { type: "string" },
|
|
507
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"] },
|
|
508
|
+
},
|
|
509
|
+
required: ["branch", "account", "account_type"],
|
|
510
|
+
},
|
|
511
|
+
creditor_account: {
|
|
512
|
+
type: "object",
|
|
513
|
+
properties: {
|
|
514
|
+
branch: { type: "string" },
|
|
515
|
+
account: { type: "string" },
|
|
516
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"] },
|
|
517
|
+
},
|
|
518
|
+
required: ["branch", "account", "account_type"],
|
|
519
|
+
},
|
|
520
|
+
amount: { type: "number", description: "Amount in BRL (decimal)" },
|
|
521
|
+
description: { type: "string", description: "Free-text description recorded on both ledger entries" },
|
|
522
|
+
idempotency_key: { type: "string", description: "Merchant-side unique id to prevent double-debit on retry" },
|
|
523
|
+
},
|
|
524
|
+
required: ["debtor_account", "creditor_account", "amount"],
|
|
525
|
+
},
|
|
526
|
+
},
|
|
253
527
|
],
|
|
254
528
|
}));
|
|
255
529
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -257,6 +531,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
257
531
|
const a = (args ?? {});
|
|
258
532
|
try {
|
|
259
533
|
switch (name) {
|
|
534
|
+
// TODO(verify): BCB canonical charge paths are `/cob` (immediate) and
|
|
535
|
+
// `/cobv` (dated). Matera may expose them at `/pix/charges/*` per the
|
|
536
|
+
// original guess, but this has NOT been confirmed against the sandbox.
|
|
260
537
|
case "create_pix_charge_static":
|
|
261
538
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/charges/static", a), null, 2) }] };
|
|
262
539
|
case "create_pix_charge_dynamic":
|
|
@@ -265,6 +542,53 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
265
542
|
const txid = encodeURIComponent(String(a.txid ?? ""));
|
|
266
543
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/charges/${txid}`), null, 2) }] };
|
|
267
544
|
}
|
|
545
|
+
case "list_pix_charges": {
|
|
546
|
+
// TODO(verify): BCB Pix v2 lists /cob via `?inicio=&fim=`. Path is a
|
|
547
|
+
// wrapped Matera guess.
|
|
548
|
+
const params = new URLSearchParams();
|
|
549
|
+
if (a.start)
|
|
550
|
+
params.set("inicio", String(a.start));
|
|
551
|
+
if (a.end)
|
|
552
|
+
params.set("fim", String(a.end));
|
|
553
|
+
if (a.status)
|
|
554
|
+
params.set("status", String(a.status));
|
|
555
|
+
if (a.cpf)
|
|
556
|
+
params.set("cpf", String(a.cpf));
|
|
557
|
+
if (a.cnpj)
|
|
558
|
+
params.set("cnpj", String(a.cnpj));
|
|
559
|
+
if (a.page !== undefined)
|
|
560
|
+
params.set("paginacao.paginaAtual", String(a.page));
|
|
561
|
+
if (a.limit !== undefined)
|
|
562
|
+
params.set("paginacao.itensPorPagina", String(a.limit));
|
|
563
|
+
const qs = params.toString();
|
|
564
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/charges${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
565
|
+
}
|
|
566
|
+
case "update_pix_charge": {
|
|
567
|
+
// TODO(verify): BCB spec uses PATCH /cob/{txid}. Wrapper path guessed.
|
|
568
|
+
const txid = encodeURIComponent(String(a.txid ?? ""));
|
|
569
|
+
const body = {};
|
|
570
|
+
if (a.amount !== undefined)
|
|
571
|
+
body.amount = a.amount;
|
|
572
|
+
if (a.expiration !== undefined)
|
|
573
|
+
body.expiration = a.expiration;
|
|
574
|
+
if (a.description !== undefined)
|
|
575
|
+
body.description = a.description;
|
|
576
|
+
if (a.status !== undefined)
|
|
577
|
+
body.status = a.status;
|
|
578
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("PATCH", `/pix/charges/${txid}`, body), null, 2) }] };
|
|
579
|
+
}
|
|
580
|
+
case "create_pix_charge_due": {
|
|
581
|
+
// TODO(verify): BCB canonical path is PUT /cobv/{txid}. Matera wrapper
|
|
582
|
+
// path unknown — using `/pix/charges/due` for symmetry with /charges.
|
|
583
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/charges/due", a), null, 2) }] };
|
|
584
|
+
}
|
|
585
|
+
case "get_pix_charge_due": {
|
|
586
|
+
const txid = encodeURIComponent(String(a.txid ?? ""));
|
|
587
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/charges/due/${txid}`), null, 2) }] };
|
|
588
|
+
}
|
|
589
|
+
// TODO(verify): BCB spec names outbound Pix refunds
|
|
590
|
+
// `PUT /pix/{e2eid}/devolucao/{id}`. The `POST /pix/payments/{e2eid}/refund`
|
|
591
|
+
// path below is a plausible Matera wrapper but NOT confirmed.
|
|
268
592
|
case "create_pix_payment":
|
|
269
593
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/payments", a), null, 2) }] };
|
|
270
594
|
case "get_pix_payment": {
|
|
@@ -291,6 +615,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
291
615
|
const qs = params.toString();
|
|
292
616
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/payments${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
293
617
|
}
|
|
618
|
+
case "list_pix_received": {
|
|
619
|
+
// TODO(verify): BCB canonical is GET /pix?inicio=&fim=. Wrapper path
|
|
620
|
+
// assumed at `/pix/received`.
|
|
621
|
+
const params = new URLSearchParams();
|
|
622
|
+
if (a.start)
|
|
623
|
+
params.set("inicio", String(a.start));
|
|
624
|
+
if (a.end)
|
|
625
|
+
params.set("fim", String(a.end));
|
|
626
|
+
if (a.txid)
|
|
627
|
+
params.set("txid", String(a.txid));
|
|
628
|
+
if (a.cpf)
|
|
629
|
+
params.set("cpf", String(a.cpf));
|
|
630
|
+
if (a.cnpj)
|
|
631
|
+
params.set("cnpj", String(a.cnpj));
|
|
632
|
+
if (a.page !== undefined)
|
|
633
|
+
params.set("paginacao.paginaAtual", String(a.page));
|
|
634
|
+
if (a.limit !== undefined)
|
|
635
|
+
params.set("paginacao.itensPorPagina", String(a.limit));
|
|
636
|
+
const qs = params.toString();
|
|
637
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/received${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
638
|
+
}
|
|
639
|
+
// TODO(verify): BCB DICT is RSFN-gated and typically lives under
|
|
640
|
+
// `/api/v2/entries/*` on the Central Bank side. Matera wraps it in its
|
|
641
|
+
// own API — path `/pix/dict/*` below is a guess.
|
|
294
642
|
case "resolve_pix_key": {
|
|
295
643
|
const key = encodeURIComponent(String(a.key ?? ""));
|
|
296
644
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/dict/${key}`), null, 2) }] };
|
|
@@ -302,8 +650,70 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
302
650
|
const qs = params.toString();
|
|
303
651
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/dict/keys${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
304
652
|
}
|
|
653
|
+
case "register_dict_key": {
|
|
654
|
+
// TODO(verify): BCB DICT register is POST /api/v2/entries with an XML
|
|
655
|
+
// signed payload. Matera wraps it; path `/pix/dict/keys` is a guess.
|
|
656
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/dict/keys", a), null, 2) }] };
|
|
657
|
+
}
|
|
658
|
+
case "delete_dict_key": {
|
|
659
|
+
// TODO(verify): BCB uses DELETE /api/v2/entries/{key}. Wrapper path
|
|
660
|
+
// assumed.
|
|
661
|
+
const key = encodeURIComponent(String(a.key ?? ""));
|
|
662
|
+
const params = new URLSearchParams();
|
|
663
|
+
if (a.reason)
|
|
664
|
+
params.set("reason", String(a.reason));
|
|
665
|
+
const qs = params.toString();
|
|
666
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("DELETE", `/pix/dict/${key}${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
667
|
+
}
|
|
668
|
+
// TODO(verify): `/pix/automatico` is almost certainly wrong. BCB's
|
|
669
|
+
// 2025 Pix Automático spec uses `POST /rec` (recorrência — payer
|
|
670
|
+
// authorization) and `/cobr/{txid}` (cobrança recorrente — each
|
|
671
|
+
// recurring charge). Matera likely mirrors this. The body shape here
|
|
672
|
+
// also needs to change (idRec, expiracaoSolicitacao, vinculo, etc.)
|
|
305
673
|
case "create_pix_automatico":
|
|
306
674
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/automatico", a), null, 2) }] };
|
|
675
|
+
case "get_pix_automatico": {
|
|
676
|
+
const id = encodeURIComponent(String(a.id_rec ?? ""));
|
|
677
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/automatico/${id}`), null, 2) }] };
|
|
678
|
+
}
|
|
679
|
+
case "cancel_pix_automatico": {
|
|
680
|
+
const id = encodeURIComponent(String(a.id_rec ?? ""));
|
|
681
|
+
const body = { status: "CANCELADA" };
|
|
682
|
+
if (a.reason)
|
|
683
|
+
body.reason = a.reason;
|
|
684
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("PATCH", `/pix/automatico/${id}`, body), null, 2) }] };
|
|
685
|
+
}
|
|
686
|
+
// TODO(verify): No public Matera path for accounts. `/accounts/*` is a
|
|
687
|
+
// reasoned default. Real path may live under `/baas/accounts/*` or
|
|
688
|
+
// `/core/accounts/*`.
|
|
689
|
+
case "get_account_balance": {
|
|
690
|
+
const params = new URLSearchParams();
|
|
691
|
+
params.set("branch", String(a.branch ?? ""));
|
|
692
|
+
params.set("account", String(a.account ?? ""));
|
|
693
|
+
if (a.account_type)
|
|
694
|
+
params.set("account_type", String(a.account_type));
|
|
695
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/accounts/balance?${params.toString()}`), null, 2) }] };
|
|
696
|
+
}
|
|
697
|
+
case "get_account_statement": {
|
|
698
|
+
const params = new URLSearchParams();
|
|
699
|
+
params.set("branch", String(a.branch ?? ""));
|
|
700
|
+
params.set("account", String(a.account ?? ""));
|
|
701
|
+
if (a.account_type)
|
|
702
|
+
params.set("account_type", String(a.account_type));
|
|
703
|
+
if (a.start)
|
|
704
|
+
params.set("start", String(a.start));
|
|
705
|
+
if (a.end)
|
|
706
|
+
params.set("end", String(a.end));
|
|
707
|
+
if (a.page !== undefined)
|
|
708
|
+
params.set("page", String(a.page));
|
|
709
|
+
if (a.limit !== undefined)
|
|
710
|
+
params.set("limit", String(a.limit));
|
|
711
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/accounts/statement?${params.toString()}`), null, 2) }] };
|
|
712
|
+
}
|
|
713
|
+
case "internal_transfer": {
|
|
714
|
+
// TODO(verify): book transfer endpoint unknown. Using `/accounts/transfers`.
|
|
715
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/accounts/transfers", a), null, 2) }] };
|
|
716
|
+
}
|
|
307
717
|
default:
|
|
308
718
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
309
719
|
}
|
|
@@ -330,7 +740,7 @@ async function main() {
|
|
|
330
740
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
331
741
|
t.onclose = () => { if (t.sessionId)
|
|
332
742
|
transports.delete(t.sessionId); };
|
|
333
|
-
const s = new Server({ name: "mcp-matera", version: "0.
|
|
743
|
+
const s = new Server({ name: "mcp-matera", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
334
744
|
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
335
745
|
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
336
746
|
await s.connect(t);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codespar/mcp-matera",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for Matera
|
|
3
|
+
"version": "0.2.0-alpha.2",
|
|
4
|
+
"description": "MCP server for Matera \u2014 Brazilian core-banking infrastructure (BaaS) for fintechs building on top of Pix, DICT, and Pix Autom\u00e1tico",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
+
"@types/express": "^5.0.6",
|
|
18
19
|
"@types/node": "^25.5.0",
|
|
19
20
|
"typescript": "^5.8.0"
|
|
20
21
|
},
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/banking/matera"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.2.0-alpha.2",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@codespar/mcp-matera",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.2.0-alpha.2",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
package/src/index.ts
CHANGED
|
@@ -8,24 +8,80 @@
|
|
|
8
8
|
* DICT, registering recurring Pix Automático agreements) — distinct from PSPs
|
|
9
9
|
* like Zoop/Asaas/Mercado Pago which serve merchants accepting Pix.
|
|
10
10
|
*
|
|
11
|
-
* Tools (
|
|
12
|
-
* create_pix_charge_static — static QR code (merchant Pix key, reusable)
|
|
13
|
-
* create_pix_charge_dynamic — dynamic QR code (single-use, expiring)
|
|
14
|
-
* get_pix_charge — fetch a charge by txid
|
|
15
|
-
* create_pix_payment — initiate an outbound Pix transfer
|
|
16
|
-
* get_pix_payment — fetch a payment by endToEndId
|
|
17
|
-
* refund_pix_payment — refund a Pix payment (MED / devolução)
|
|
18
|
-
* list_pix_payments — list payments with start/end/status filters
|
|
19
|
-
* resolve_pix_key — DICT lookup (CPF/CNPJ/email/phone/random → account)
|
|
20
|
-
* list_dict_keys — list DICT keys registered to merchant accounts
|
|
21
|
-
* create_pix_automatico — register a recurring Pix agreement (BCB 2025)
|
|
11
|
+
* Tools (21) — Pix + DICT + accounts for v0.2:
|
|
22
12
|
*
|
|
23
|
-
*
|
|
13
|
+
* Immediate Pix charges (BCB /cob)
|
|
14
|
+
* create_pix_charge_static — static QR code (merchant Pix key, reusable)
|
|
15
|
+
* create_pix_charge_dynamic — dynamic QR code (single-use, expiring)
|
|
16
|
+
* get_pix_charge — fetch a charge by txid
|
|
17
|
+
* list_pix_charges — list /cob charges with date+status filters
|
|
18
|
+
* update_pix_charge — PATCH a /cob (revise amount/expiration/status)
|
|
19
|
+
*
|
|
20
|
+
* Due-dated Pix charges (BCB /cobv — boleto-style with vencimento)
|
|
21
|
+
* create_pix_charge_due — cobv create
|
|
22
|
+
* get_pix_charge_due — cobv get by txid
|
|
23
|
+
*
|
|
24
|
+
* Outbound Pix payments (ordem de pagamento)
|
|
25
|
+
* create_pix_payment — initiate an outbound Pix transfer
|
|
26
|
+
* get_pix_payment — fetch a payment by endToEndId
|
|
27
|
+
* refund_pix_payment — refund a Pix payment (MED / devolução)
|
|
28
|
+
* list_pix_payments — list outbound payments with filters
|
|
29
|
+
*
|
|
30
|
+
* Inbound Pix (recebidos)
|
|
31
|
+
* list_pix_received — list received Pix in a window
|
|
32
|
+
*
|
|
33
|
+
* DICT (Pix key directory)
|
|
34
|
+
* resolve_pix_key — DICT lookup (CPF/CNPJ/email/phone/random → account)
|
|
35
|
+
* list_dict_keys — list DICT keys registered to merchant accounts
|
|
36
|
+
* register_dict_key — claim/register a DICT key for a merchant account
|
|
37
|
+
* delete_dict_key — remove a DICT key the merchant owns
|
|
38
|
+
*
|
|
39
|
+
* Pix Automático (BCB 2025 recurring Pix)
|
|
40
|
+
* create_pix_automatico — register a recurring Pix agreement
|
|
41
|
+
* get_pix_automatico — fetch a recurrence by idRec
|
|
42
|
+
* cancel_pix_automatico — cancel an active recurrence
|
|
43
|
+
*
|
|
44
|
+
* Accounts (Matera-managed core-banking accounts)
|
|
45
|
+
* get_account_balance — current balance for a Matera account
|
|
46
|
+
* get_account_statement — extrato / statement for a date range
|
|
47
|
+
* internal_transfer — book transfer between two Matera-managed accounts
|
|
48
|
+
*
|
|
49
|
+
* -------------------------------------------------------------------------
|
|
50
|
+
* ALPHA STATUS — endpoint paths below are NOT verified against a live sandbox.
|
|
51
|
+
*
|
|
52
|
+
* On 2026-04-24 we attempted to validate every path against doc-api.matera.com.
|
|
53
|
+
* The doc site was not reachable from our automation environment (DNS-gated;
|
|
54
|
+
* no login wall observed). Public search snippets and third-party references
|
|
55
|
+
* confirm Matera's product surface and auth model but do not surface exact URL
|
|
56
|
+
* paths. Where Matera-specific paths are unknown, we follow BCB Pix API v2
|
|
57
|
+
* conventions (`/cob`, `/cobv`, `/pix`, `/rec`) which Matera is required to
|
|
58
|
+
* mirror as a participating PSP. Known-suspect items are flagged inline with
|
|
59
|
+
* `TODO(verify)` comments.
|
|
60
|
+
*
|
|
61
|
+
* High-confidence corrections pending sandbox access:
|
|
62
|
+
* - Auth: Matera's server integration uses `secret-key` + `data-signature`
|
|
63
|
+
* headers, not OAuth2. OAuth2 is documented only for mobile / web-UI
|
|
64
|
+
* integrations. The code below still uses the OAuth2 client_credentials
|
|
65
|
+
* flow as a placeholder and will fail against the real server path.
|
|
66
|
+
* - Pix Automático: BCB's 2025 spec uses POST /rec (recurrence) and /cobr
|
|
67
|
+
* (recurring charge). `/pix/automatico` is a placeholder; the true Matera
|
|
68
|
+
* path is unknown.
|
|
69
|
+
* - DICT: Real DICT (RSFN-gated) sits at BCB; Matera wraps it. The wrapper
|
|
70
|
+
* path `/pix/dict/*` is a guess.
|
|
71
|
+
* - Accounts (balance/statement/internal-transfer): no public path.
|
|
72
|
+
* `/accounts/*` is a reasoned default.
|
|
73
|
+
*
|
|
74
|
+
* The 21 tool names + input schemas are the stable public contract. Only the
|
|
75
|
+
* internal `materaRequest(method, path, body)` calls will change when paths
|
|
76
|
+
* are verified.
|
|
77
|
+
* -------------------------------------------------------------------------
|
|
78
|
+
*
|
|
79
|
+
* Authentication (placeholder — see note above)
|
|
24
80
|
* OAuth 2.0 Client Credentials. POST /auth/token with Basic auth
|
|
25
81
|
* (client_id:client_secret) + grant_type=client_credentials. Bearer token
|
|
26
82
|
* cached in memory until a minute before expiry.
|
|
27
|
-
* Matera
|
|
28
|
-
*
|
|
83
|
+
* Matera's production server auth is secret-key + data-signature — NOT
|
|
84
|
+
* implemented in this alpha.
|
|
29
85
|
*
|
|
30
86
|
* Environment
|
|
31
87
|
* MATERA_CLIENT_ID OAuth2 client_id
|
|
@@ -56,6 +112,9 @@ async function getAccessToken(): Promise<string> {
|
|
|
56
112
|
return tokenCache.accessToken;
|
|
57
113
|
}
|
|
58
114
|
const basic = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64");
|
|
115
|
+
// TODO(verify): Matera's real token path is unconfirmed. Candidates include
|
|
116
|
+
// `/auth/token`, `/oauth/token`, `/auth/v1/token`. Leaving the original
|
|
117
|
+
// placeholder until the sandbox confirms it.
|
|
59
118
|
const res = await fetch(`${BASE_URL}/auth/token`, {
|
|
60
119
|
method: "POST",
|
|
61
120
|
headers: {
|
|
@@ -94,7 +153,7 @@ async function materaRequest(method: string, path: string, body?: unknown): Prom
|
|
|
94
153
|
}
|
|
95
154
|
|
|
96
155
|
const server = new Server(
|
|
97
|
-
{ name: "mcp-matera", version: "0.
|
|
156
|
+
{ name: "mcp-matera", version: "0.2.0" },
|
|
98
157
|
{ capabilities: { tools: {} } },
|
|
99
158
|
);
|
|
100
159
|
|
|
@@ -151,6 +210,85 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
151
210
|
required: ["txid"],
|
|
152
211
|
},
|
|
153
212
|
},
|
|
213
|
+
{
|
|
214
|
+
name: "list_pix_charges",
|
|
215
|
+
description: "List immediate Pix charges (BCB /cob) with date and status filters. Use for reconciling QR-driven receipts in a time window.",
|
|
216
|
+
inputSchema: {
|
|
217
|
+
type: "object",
|
|
218
|
+
properties: {
|
|
219
|
+
start: { type: "string", description: "ISO-8601 start timestamp (inclusive). BCB calls this `inicio`." },
|
|
220
|
+
end: { type: "string", description: "ISO-8601 end timestamp (exclusive). BCB calls this `fim`." },
|
|
221
|
+
status: { type: "string", description: "Filter by status (ATIVA, CONCLUIDA, REMOVIDA_PELO_USUARIO_RECEBEDOR, REMOVIDA_PELO_PSP)" },
|
|
222
|
+
cpf: { type: "string", description: "Filter charges where the payer CPF equals this value" },
|
|
223
|
+
cnpj: { type: "string", description: "Filter charges where the payer CNPJ equals this value" },
|
|
224
|
+
page: { type: "number", description: "Page number (starts at 0 per BCB convention)" },
|
|
225
|
+
limit: { type: "number", description: "Page size (BCB max 1000)" },
|
|
226
|
+
},
|
|
227
|
+
required: ["start", "end"],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: "update_pix_charge",
|
|
232
|
+
description: "Update an immediate Pix charge (BCB PATCH /cob/{txid}). Use to revise amount/expiration before payment, or to mark a charge REMOVIDA_PELO_USUARIO_RECEBEDOR.",
|
|
233
|
+
inputSchema: {
|
|
234
|
+
type: "object",
|
|
235
|
+
properties: {
|
|
236
|
+
txid: { type: "string", description: "txid of the existing charge" },
|
|
237
|
+
amount: { type: "number", description: "New amount in BRL (only if charge still ATIVA)" },
|
|
238
|
+
expiration: { type: "number", description: "New expiration in seconds from creation" },
|
|
239
|
+
description: { type: "string", description: "New description shown to payer" },
|
|
240
|
+
status: { type: "string", description: "Set to REMOVIDA_PELO_USUARIO_RECEBEDOR to cancel an unpaid charge" },
|
|
241
|
+
},
|
|
242
|
+
required: ["txid"],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: "create_pix_charge_due",
|
|
247
|
+
description: "Create a due-dated Pix charge (BCB /cobv — Pix com Vencimento). Boleto-style charge with due date, late fee (multa), interest (juros), and discount fields. Returns txid + QR payload.",
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: "object",
|
|
250
|
+
properties: {
|
|
251
|
+
pix_key: { type: "string", description: "Merchant Pix key the charge settles to" },
|
|
252
|
+
amount: { type: "number", description: "Original amount in BRL (valor.original)" },
|
|
253
|
+
description: { type: "string", description: "Description shown to payer (solicitacaoPagador)" },
|
|
254
|
+
due_date: { type: "string", description: "ISO-8601 date when the charge becomes due (data.dataDeVencimento)" },
|
|
255
|
+
validity_days_after_due: { type: "number", description: "How many calendar days after due date the QR remains payable (validadeAposVencimento). Default 30." },
|
|
256
|
+
debtor: {
|
|
257
|
+
type: "object",
|
|
258
|
+
description: "Payer (devedor). BCB requires CPF or CNPJ + name for cobv.",
|
|
259
|
+
properties: {
|
|
260
|
+
cpf: { type: "string" },
|
|
261
|
+
cnpj: { type: "string" },
|
|
262
|
+
name: { type: "string" },
|
|
263
|
+
address: { type: "string", description: "Logradouro" },
|
|
264
|
+
city: { type: "string", description: "Cidade" },
|
|
265
|
+
state: { type: "string", description: "UF (2-letter)" },
|
|
266
|
+
zip: { type: "string", description: "CEP" },
|
|
267
|
+
},
|
|
268
|
+
required: ["name"],
|
|
269
|
+
},
|
|
270
|
+
fine: { type: "number", description: "Late fee, BRL or % depending on `fine_mode` (multa.valor or multa.modalidade)" },
|
|
271
|
+
fine_mode: { type: "number", enum: [1, 2], description: "1=fixed amount, 2=percent (BCB modalidade)" },
|
|
272
|
+
interest: { type: "number", description: "Interest after due date" },
|
|
273
|
+
interest_mode: { type: "number", description: "BCB juros.modalidade (1..7). 2=percent per day, 5=percent per month, etc." },
|
|
274
|
+
discount: { type: "number", description: "Discount value if paid before due date" },
|
|
275
|
+
discount_mode: { type: "number", description: "BCB desconto.modalidade (1..6)" },
|
|
276
|
+
txid: { type: "string", description: "Optional merchant-side txid (26-35 alphanumerics)" },
|
|
277
|
+
},
|
|
278
|
+
required: ["pix_key", "amount", "description", "due_date", "debtor"],
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: "get_pix_charge_due",
|
|
283
|
+
description: "Retrieve a due-dated Pix charge (BCB GET /cobv/{txid}).",
|
|
284
|
+
inputSchema: {
|
|
285
|
+
type: "object",
|
|
286
|
+
properties: {
|
|
287
|
+
txid: { type: "string", description: "txid of the cobv charge" },
|
|
288
|
+
},
|
|
289
|
+
required: ["txid"],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
154
292
|
{
|
|
155
293
|
name: "create_pix_payment",
|
|
156
294
|
description: "Initiate an outbound Pix transfer (ordem de pagamento). Moves money from a debtor account held on Matera to any Pix key in BR. Returns endToEndId once the BCB SPI confirms.",
|
|
@@ -214,6 +352,23 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
214
352
|
},
|
|
215
353
|
},
|
|
216
354
|
},
|
|
355
|
+
{
|
|
356
|
+
name: "list_pix_received",
|
|
357
|
+
description: "List inbound Pix (Pix recebidos) credited to merchant accounts in a date range. Mirrors BCB GET /pix. Filter by txid to find the receipt that closed a specific cob.",
|
|
358
|
+
inputSchema: {
|
|
359
|
+
type: "object",
|
|
360
|
+
properties: {
|
|
361
|
+
start: { type: "string", description: "ISO-8601 start timestamp (inclusive)" },
|
|
362
|
+
end: { type: "string", description: "ISO-8601 end timestamp (exclusive)" },
|
|
363
|
+
txid: { type: "string", description: "Only return Pix receipts that closed this txid" },
|
|
364
|
+
cpf: { type: "string", description: "Filter by payer CPF" },
|
|
365
|
+
cnpj: { type: "string", description: "Filter by payer CNPJ" },
|
|
366
|
+
page: { type: "number", description: "Page number (BCB starts at 0)" },
|
|
367
|
+
limit: { type: "number", description: "Page size" },
|
|
368
|
+
},
|
|
369
|
+
required: ["start", "end"],
|
|
370
|
+
},
|
|
371
|
+
},
|
|
217
372
|
{
|
|
218
373
|
name: "resolve_pix_key",
|
|
219
374
|
description: "Resolve a Pix DICT key to the account holder's identity and ISPB/branch/account. Use before sending large transfers to verify the counterparty. Note: DICT queries are rate-limited and logged by BCB.",
|
|
@@ -235,9 +390,44 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
235
390
|
},
|
|
236
391
|
},
|
|
237
392
|
},
|
|
393
|
+
{
|
|
394
|
+
name: "register_dict_key",
|
|
395
|
+
description: "Register (claim) a DICT key for a merchant account on Matera. For email/phone keys this triggers BCB's confirmation flow; for CPF/CNPJ/random it claims immediately. Subject to BCB DICT rate limits and ownership rules (max 5 keys per CPF, 20 per CNPJ).",
|
|
396
|
+
inputSchema: {
|
|
397
|
+
type: "object",
|
|
398
|
+
properties: {
|
|
399
|
+
key_type: { type: "string", enum: ["CPF", "CNPJ", "EMAIL", "PHONE", "RANDOM"], description: "Type of key to register" },
|
|
400
|
+
key_value: { type: "string", description: "Value (omit for RANDOM — Matera will generate the UUID)" },
|
|
401
|
+
account: {
|
|
402
|
+
type: "object",
|
|
403
|
+
description: "Matera-managed account that will own the key",
|
|
404
|
+
properties: {
|
|
405
|
+
branch: { type: "string", description: "Branch (agência)" },
|
|
406
|
+
account: { type: "string", description: "Account number" },
|
|
407
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"], description: "ISO 20022 account type" },
|
|
408
|
+
ispb: { type: "string", description: "ISPB of the holding institution (Matera-issued)" },
|
|
409
|
+
},
|
|
410
|
+
required: ["branch", "account", "account_type"],
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
required: ["key_type", "account"],
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "delete_dict_key",
|
|
418
|
+
description: "Delete a DICT key the merchant owns. After delete, the key is held in BCB's quarentena window before another holder can claim it.",
|
|
419
|
+
inputSchema: {
|
|
420
|
+
type: "object",
|
|
421
|
+
properties: {
|
|
422
|
+
key: { type: "string", description: "Pix key value to delete" },
|
|
423
|
+
reason: { type: "string", description: "Optional reason (BCB MotivoExclusao). Default: USER_REQUESTED." },
|
|
424
|
+
},
|
|
425
|
+
required: ["key"],
|
|
426
|
+
},
|
|
427
|
+
},
|
|
238
428
|
{
|
|
239
429
|
name: "create_pix_automatico",
|
|
240
|
-
description: "Register a Pix Automático
|
|
430
|
+
description: "Register a Pix Automático recurrence (BCB 2025 recurring Pix product, /rec). The payer authorizes the merchant to pull recurring amounts on a schedule. Matera is one of the few providers live with this.",
|
|
241
431
|
inputSchema: {
|
|
242
432
|
type: "object",
|
|
243
433
|
properties: {
|
|
@@ -262,6 +452,90 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
262
452
|
required: ["payer", "merchant_pix_key", "frequency", "amount", "first_charge_date", "description"],
|
|
263
453
|
},
|
|
264
454
|
},
|
|
455
|
+
{
|
|
456
|
+
name: "get_pix_automatico",
|
|
457
|
+
description: "Retrieve a Pix Automático recurrence by idRec. Returns current status (CRIADA, APROVADA, REJEITADA, CANCELADA), schedule, and last charge attempt.",
|
|
458
|
+
inputSchema: {
|
|
459
|
+
type: "object",
|
|
460
|
+
properties: {
|
|
461
|
+
id_rec: { type: "string", description: "Recurrence id (idRec) returned by create_pix_automatico" },
|
|
462
|
+
},
|
|
463
|
+
required: ["id_rec"],
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: "cancel_pix_automatico",
|
|
468
|
+
description: "Cancel an active Pix Automático recurrence. Future charges stop after BCB confirms the cancellation. Past charges are unaffected.",
|
|
469
|
+
inputSchema: {
|
|
470
|
+
type: "object",
|
|
471
|
+
properties: {
|
|
472
|
+
id_rec: { type: "string", description: "Recurrence id (idRec) to cancel" },
|
|
473
|
+
reason: { type: "string", description: "Optional reason recorded on the cancellation" },
|
|
474
|
+
},
|
|
475
|
+
required: ["id_rec"],
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
name: "get_account_balance",
|
|
480
|
+
description: "Get the current balance of a Matera-managed account. Returns available, blocked, and total balance in BRL.",
|
|
481
|
+
inputSchema: {
|
|
482
|
+
type: "object",
|
|
483
|
+
properties: {
|
|
484
|
+
branch: { type: "string", description: "Branch (agência)" },
|
|
485
|
+
account: { type: "string", description: "Account number" },
|
|
486
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"], description: "ISO 20022 account type. Defaults to CACC." },
|
|
487
|
+
},
|
|
488
|
+
required: ["branch", "account"],
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: "get_account_statement",
|
|
493
|
+
description: "Get the statement (extrato) of a Matera-managed account in a date range. Returns one entry per credit/debit with counterparty, endToEndId (when applicable), and running balance.",
|
|
494
|
+
inputSchema: {
|
|
495
|
+
type: "object",
|
|
496
|
+
properties: {
|
|
497
|
+
branch: { type: "string", description: "Branch (agência)" },
|
|
498
|
+
account: { type: "string", description: "Account number" },
|
|
499
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"], description: "ISO 20022 account type. Defaults to CACC." },
|
|
500
|
+
start: { type: "string", description: "ISO-8601 start date (inclusive)" },
|
|
501
|
+
end: { type: "string", description: "ISO-8601 end date (exclusive)" },
|
|
502
|
+
page: { type: "number", description: "Page number" },
|
|
503
|
+
limit: { type: "number", description: "Page size" },
|
|
504
|
+
},
|
|
505
|
+
required: ["branch", "account", "start", "end"],
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
name: "internal_transfer",
|
|
510
|
+
description: "Book a transfer between two accounts both held on Matera (TED-interno / transferência interna). Settles instantly without touching SPI/Pix rails — no endToEndId, no DICT lookup. Use for moving funds across a fintech's own customer accounts.",
|
|
511
|
+
inputSchema: {
|
|
512
|
+
type: "object",
|
|
513
|
+
properties: {
|
|
514
|
+
debtor_account: {
|
|
515
|
+
type: "object",
|
|
516
|
+
properties: {
|
|
517
|
+
branch: { type: "string" },
|
|
518
|
+
account: { type: "string" },
|
|
519
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"] },
|
|
520
|
+
},
|
|
521
|
+
required: ["branch", "account", "account_type"],
|
|
522
|
+
},
|
|
523
|
+
creditor_account: {
|
|
524
|
+
type: "object",
|
|
525
|
+
properties: {
|
|
526
|
+
branch: { type: "string" },
|
|
527
|
+
account: { type: "string" },
|
|
528
|
+
account_type: { type: "string", enum: ["CACC", "SLRY", "SVGS", "TRAN"] },
|
|
529
|
+
},
|
|
530
|
+
required: ["branch", "account", "account_type"],
|
|
531
|
+
},
|
|
532
|
+
amount: { type: "number", description: "Amount in BRL (decimal)" },
|
|
533
|
+
description: { type: "string", description: "Free-text description recorded on both ledger entries" },
|
|
534
|
+
idempotency_key: { type: "string", description: "Merchant-side unique id to prevent double-debit on retry" },
|
|
535
|
+
},
|
|
536
|
+
required: ["debtor_account", "creditor_account", "amount"],
|
|
537
|
+
},
|
|
538
|
+
},
|
|
265
539
|
],
|
|
266
540
|
}));
|
|
267
541
|
|
|
@@ -270,6 +544,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
270
544
|
const a = (args ?? {}) as Record<string, unknown>;
|
|
271
545
|
try {
|
|
272
546
|
switch (name) {
|
|
547
|
+
// TODO(verify): BCB canonical charge paths are `/cob` (immediate) and
|
|
548
|
+
// `/cobv` (dated). Matera may expose them at `/pix/charges/*` per the
|
|
549
|
+
// original guess, but this has NOT been confirmed against the sandbox.
|
|
273
550
|
case "create_pix_charge_static":
|
|
274
551
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/charges/static", a), null, 2) }] };
|
|
275
552
|
case "create_pix_charge_dynamic":
|
|
@@ -278,6 +555,42 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
278
555
|
const txid = encodeURIComponent(String(a.txid ?? ""));
|
|
279
556
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/charges/${txid}`), null, 2) }] };
|
|
280
557
|
}
|
|
558
|
+
case "list_pix_charges": {
|
|
559
|
+
// TODO(verify): BCB Pix v2 lists /cob via `?inicio=&fim=`. Path is a
|
|
560
|
+
// wrapped Matera guess.
|
|
561
|
+
const params = new URLSearchParams();
|
|
562
|
+
if (a.start) params.set("inicio", String(a.start));
|
|
563
|
+
if (a.end) params.set("fim", String(a.end));
|
|
564
|
+
if (a.status) params.set("status", String(a.status));
|
|
565
|
+
if (a.cpf) params.set("cpf", String(a.cpf));
|
|
566
|
+
if (a.cnpj) params.set("cnpj", String(a.cnpj));
|
|
567
|
+
if (a.page !== undefined) params.set("paginacao.paginaAtual", String(a.page));
|
|
568
|
+
if (a.limit !== undefined) params.set("paginacao.itensPorPagina", String(a.limit));
|
|
569
|
+
const qs = params.toString();
|
|
570
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/charges${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
571
|
+
}
|
|
572
|
+
case "update_pix_charge": {
|
|
573
|
+
// TODO(verify): BCB spec uses PATCH /cob/{txid}. Wrapper path guessed.
|
|
574
|
+
const txid = encodeURIComponent(String(a.txid ?? ""));
|
|
575
|
+
const body: Record<string, unknown> = {};
|
|
576
|
+
if (a.amount !== undefined) body.amount = a.amount;
|
|
577
|
+
if (a.expiration !== undefined) body.expiration = a.expiration;
|
|
578
|
+
if (a.description !== undefined) body.description = a.description;
|
|
579
|
+
if (a.status !== undefined) body.status = a.status;
|
|
580
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("PATCH", `/pix/charges/${txid}`, body), null, 2) }] };
|
|
581
|
+
}
|
|
582
|
+
case "create_pix_charge_due": {
|
|
583
|
+
// TODO(verify): BCB canonical path is PUT /cobv/{txid}. Matera wrapper
|
|
584
|
+
// path unknown — using `/pix/charges/due` for symmetry with /charges.
|
|
585
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/charges/due", a), null, 2) }] };
|
|
586
|
+
}
|
|
587
|
+
case "get_pix_charge_due": {
|
|
588
|
+
const txid = encodeURIComponent(String(a.txid ?? ""));
|
|
589
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/charges/due/${txid}`), null, 2) }] };
|
|
590
|
+
}
|
|
591
|
+
// TODO(verify): BCB spec names outbound Pix refunds
|
|
592
|
+
// `PUT /pix/{e2eid}/devolucao/{id}`. The `POST /pix/payments/{e2eid}/refund`
|
|
593
|
+
// path below is a plausible Matera wrapper but NOT confirmed.
|
|
281
594
|
case "create_pix_payment":
|
|
282
595
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/payments", a), null, 2) }] };
|
|
283
596
|
case "get_pix_payment": {
|
|
@@ -299,6 +612,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
299
612
|
const qs = params.toString();
|
|
300
613
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/payments${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
301
614
|
}
|
|
615
|
+
case "list_pix_received": {
|
|
616
|
+
// TODO(verify): BCB canonical is GET /pix?inicio=&fim=. Wrapper path
|
|
617
|
+
// assumed at `/pix/received`.
|
|
618
|
+
const params = new URLSearchParams();
|
|
619
|
+
if (a.start) params.set("inicio", String(a.start));
|
|
620
|
+
if (a.end) params.set("fim", String(a.end));
|
|
621
|
+
if (a.txid) params.set("txid", String(a.txid));
|
|
622
|
+
if (a.cpf) params.set("cpf", String(a.cpf));
|
|
623
|
+
if (a.cnpj) params.set("cnpj", String(a.cnpj));
|
|
624
|
+
if (a.page !== undefined) params.set("paginacao.paginaAtual", String(a.page));
|
|
625
|
+
if (a.limit !== undefined) params.set("paginacao.itensPorPagina", String(a.limit));
|
|
626
|
+
const qs = params.toString();
|
|
627
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/received${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
628
|
+
}
|
|
629
|
+
// TODO(verify): BCB DICT is RSFN-gated and typically lives under
|
|
630
|
+
// `/api/v2/entries/*` on the Central Bank side. Matera wraps it in its
|
|
631
|
+
// own API — path `/pix/dict/*` below is a guess.
|
|
302
632
|
case "resolve_pix_key": {
|
|
303
633
|
const key = encodeURIComponent(String(a.key ?? ""));
|
|
304
634
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/dict/${key}`), null, 2) }] };
|
|
@@ -309,8 +639,62 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
309
639
|
const qs = params.toString();
|
|
310
640
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/dict/keys${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
311
641
|
}
|
|
642
|
+
case "register_dict_key": {
|
|
643
|
+
// TODO(verify): BCB DICT register is POST /api/v2/entries with an XML
|
|
644
|
+
// signed payload. Matera wraps it; path `/pix/dict/keys` is a guess.
|
|
645
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/dict/keys", a), null, 2) }] };
|
|
646
|
+
}
|
|
647
|
+
case "delete_dict_key": {
|
|
648
|
+
// TODO(verify): BCB uses DELETE /api/v2/entries/{key}. Wrapper path
|
|
649
|
+
// assumed.
|
|
650
|
+
const key = encodeURIComponent(String(a.key ?? ""));
|
|
651
|
+
const params = new URLSearchParams();
|
|
652
|
+
if (a.reason) params.set("reason", String(a.reason));
|
|
653
|
+
const qs = params.toString();
|
|
654
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("DELETE", `/pix/dict/${key}${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
655
|
+
}
|
|
656
|
+
// TODO(verify): `/pix/automatico` is almost certainly wrong. BCB's
|
|
657
|
+
// 2025 Pix Automático spec uses `POST /rec` (recorrência — payer
|
|
658
|
+
// authorization) and `/cobr/{txid}` (cobrança recorrente — each
|
|
659
|
+
// recurring charge). Matera likely mirrors this. The body shape here
|
|
660
|
+
// also needs to change (idRec, expiracaoSolicitacao, vinculo, etc.)
|
|
312
661
|
case "create_pix_automatico":
|
|
313
662
|
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/pix/automatico", a), null, 2) }] };
|
|
663
|
+
case "get_pix_automatico": {
|
|
664
|
+
const id = encodeURIComponent(String(a.id_rec ?? ""));
|
|
665
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/pix/automatico/${id}`), null, 2) }] };
|
|
666
|
+
}
|
|
667
|
+
case "cancel_pix_automatico": {
|
|
668
|
+
const id = encodeURIComponent(String(a.id_rec ?? ""));
|
|
669
|
+
const body: Record<string, unknown> = { status: "CANCELADA" };
|
|
670
|
+
if (a.reason) body.reason = a.reason;
|
|
671
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("PATCH", `/pix/automatico/${id}`, body), null, 2) }] };
|
|
672
|
+
}
|
|
673
|
+
// TODO(verify): No public Matera path for accounts. `/accounts/*` is a
|
|
674
|
+
// reasoned default. Real path may live under `/baas/accounts/*` or
|
|
675
|
+
// `/core/accounts/*`.
|
|
676
|
+
case "get_account_balance": {
|
|
677
|
+
const params = new URLSearchParams();
|
|
678
|
+
params.set("branch", String(a.branch ?? ""));
|
|
679
|
+
params.set("account", String(a.account ?? ""));
|
|
680
|
+
if (a.account_type) params.set("account_type", String(a.account_type));
|
|
681
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/accounts/balance?${params.toString()}`), null, 2) }] };
|
|
682
|
+
}
|
|
683
|
+
case "get_account_statement": {
|
|
684
|
+
const params = new URLSearchParams();
|
|
685
|
+
params.set("branch", String(a.branch ?? ""));
|
|
686
|
+
params.set("account", String(a.account ?? ""));
|
|
687
|
+
if (a.account_type) params.set("account_type", String(a.account_type));
|
|
688
|
+
if (a.start) params.set("start", String(a.start));
|
|
689
|
+
if (a.end) params.set("end", String(a.end));
|
|
690
|
+
if (a.page !== undefined) params.set("page", String(a.page));
|
|
691
|
+
if (a.limit !== undefined) params.set("limit", String(a.limit));
|
|
692
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("GET", `/accounts/statement?${params.toString()}`), null, 2) }] };
|
|
693
|
+
}
|
|
694
|
+
case "internal_transfer": {
|
|
695
|
+
// TODO(verify): book transfer endpoint unknown. Using `/accounts/transfers`.
|
|
696
|
+
return { content: [{ type: "text", text: JSON.stringify(await materaRequest("POST", "/accounts/transfers", a), null, 2) }] };
|
|
697
|
+
}
|
|
314
698
|
default:
|
|
315
699
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
316
700
|
}
|
|
@@ -336,7 +720,7 @@ async function main() {
|
|
|
336
720
|
if (!sid && isInitializeRequest(req.body)) {
|
|
337
721
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
338
722
|
t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
|
|
339
|
-
const s = new Server({ name: "mcp-matera", version: "0.
|
|
723
|
+
const s = new Server({ name: "mcp-matera", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
340
724
|
(server as any)._requestHandlers.forEach((v: any, k: any) => (s as any)._requestHandlers.set(k, v));
|
|
341
725
|
(server as any)._notificationHandlers?.forEach((v: any, k: any) => (s as any)._notificationHandlers.set(k, v));
|
|
342
726
|
await s.connect(t);
|