opf-br-mcp 0.7.0 → 0.9.3
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 +26 -3
- package/dist/index.js +204 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,10 @@ acesso token-eficiente às regras do Open Finance Brasil.
|
|
|
21
21
|
| `payments-common-rules` | Confluence público OFB (Serviços - SV) | Conteúdo comum aos produtos de Iniciação de Pagamentos (atores, Idempotência, Como Assinar o Payload, Convenções de data/fuso, Polling) — item por seção |
|
|
22
22
|
| `payments-implementation-guides` | Confluence público OFB (Serviços - SV) | Guias de Implementação (Pix Automático, Agendamento Recorrente, Transferências Inteligentes, Liquidação de QR Codes) — item por seção |
|
|
23
23
|
| `consents-v3-openapi` | GitHub Pages openbanking-brasil.github.io | Spec OpenAPI 3.3.1 da API de Consentimentos (Dados Cadastrais e Transacionais) |
|
|
24
|
+
| `resources-v3-openapi` | GitHub Pages openbanking-brasil.github.io | Spec OpenAPI 3.1.0 da API de Recursos (`GET /resources`: status dos recursos compartilhados) |
|
|
25
|
+
| `resources-v3-business-rules` | Confluence público OFB (Dados - DC) | Regras de negócio da API de Recursos 3.1.0 (Informações Gerais, Orientações) — item por seção |
|
|
26
|
+
| `accounts-v2-openapi` | GitHub Pages openbanking-brasil.github.io | Spec OpenAPI 2.4.2 da API de Contas (listagem, saldos, transações, limites de cheque especial) |
|
|
27
|
+
| `accounts-v2-business-rules` | Confluence público OFB (Dados - DC) | Regras de negócio da API de Contas 2.4.2 (Informações Gerais, Orientações — contraparte/IN BCB nº 371) — item por seção |
|
|
24
28
|
| `pcm-openapi` | GitHub OpenBanking-Brasil/pcm-specs | Spec OpenAPI da PCM (reportes, hybrid-flow, opendata, consents/stock, credit-portabilities, payments/status) |
|
|
25
29
|
| `pcm-business-rules` | Confluence público OFB | Regras de negócio da PCM (Reporte, Processamento, Divergências, Especificação Técnica, Manual de Integração) — item por seção |
|
|
26
30
|
| `jornada-otimizada` | Confluence público OFB | Regras da Jornada Otimizada (Orientações Gerais, Transferências Inteligentes, Jornada sem Redirecionamento) — item por seção |
|
|
@@ -32,13 +36,27 @@ acesso token-eficiente às regras do Open Finance Brasil.
|
|
|
32
36
|
|
|
33
37
|
## Tools
|
|
34
38
|
|
|
35
|
-
- `list_domains()` — descoberta: domínios, filtros e estado do cache
|
|
39
|
+
- `list_domains()` — descoberta: domínios, filtros, versão da spec de origem e estado do cache
|
|
36
40
|
- `search(domain, query?, filters?, limit?, offset?)` — busca filtrada, retorno compacto
|
|
37
41
|
- `get_item(domain, id)` — registro completo
|
|
38
|
-
- `refresh(domain?)` — força re-extração das fontes
|
|
42
|
+
- `refresh(domain?)` — força re-extração das fontes. Prefira passar `domain`:
|
|
43
|
+
sem ele o server atualiza o que couber em 45s (o timeout padrão do cliente MCP
|
|
44
|
+
é 60s) e devolve o restante em `pendentes`, para o agente retomar um a um
|
|
39
45
|
|
|
40
46
|
Fluxo recomendado para o agente: `list_domains` → `search` → `get_item`.
|
|
41
47
|
|
|
48
|
+
### Qual versão estou usando?
|
|
49
|
+
|
|
50
|
+
`list_domains` devolve `server: { name, version }` junto do catálogo, então o
|
|
51
|
+
agente descobre a versão do server na mesma chamada com que descobre os domínios.
|
|
52
|
+
Cada domínio que embrulha uma spec traz também o seu `specVersion`.
|
|
53
|
+
|
|
54
|
+
Pela linha de comando:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx opf-br-mcp --version
|
|
58
|
+
```
|
|
59
|
+
|
|
42
60
|
Domínios marcados como `live` (ex.: `portal`) consultam a fonte a cada chamada:
|
|
43
61
|
não têm cache nem `refresh`, e `search` exige `query`. Quando um `search` em
|
|
44
62
|
domínio comum retorna 0 resultados, a resposta inclui um `hint` sugerindo o
|
|
@@ -51,7 +69,12 @@ bem na janela de contexto de um agente, e despejá-la desperdiça tokens. A solu
|
|
|
51
69
|
é **revelação progressiva** — o agente nunca recebe a spec completa de uma vez,
|
|
52
70
|
apenas o mínimo necessário em cada etapa do funil:
|
|
53
71
|
|
|
54
|
-
1. **`list_domains`** — catálogo barato: quais domínios e filtros existem.
|
|
72
|
+
1. **`list_domains`** — catálogo barato: quais domínios e filtros existem. Os
|
|
73
|
+
filtros idênticos a uma família inteira de domínios (os 8 `*-openapi`, os 11
|
|
74
|
+
de seções do Confluence) saem uma única vez em `filterSets`; cada domínio
|
|
75
|
+
traz `filterSet` e só lista inline o que é próprio dele (em `*-openapi`,
|
|
76
|
+
apenas `path`, cujo exemplo varia por API). Os filtros aceitos por um domínio
|
|
77
|
+
são a união dos dois.
|
|
55
78
|
2. **`search`** — índice **pesquisável e resumido**. Cada resultado traz só os
|
|
56
79
|
campos leves (`id`, `type`, `path`, `method`, `summary`/`name`, `required`,
|
|
57
80
|
`in`); o nó pesado da spec (`detail`) e a lista `refs` são removidos do
|
package/dist/index.js
CHANGED
|
@@ -18,14 +18,24 @@ function cacheDir() {
|
|
|
18
18
|
function cachePath(domainId) {
|
|
19
19
|
return join(cacheDir(), `${domainId}.json`);
|
|
20
20
|
}
|
|
21
|
+
function isCacheEntry(value) {
|
|
22
|
+
const entry = value;
|
|
23
|
+
return typeof entry === "object" && entry !== null && typeof entry.extractedAt === "string" && typeof entry.packageVersion === "string" && typeof entry.data === "object" && entry.data !== null && Array.isArray(entry.data.items);
|
|
24
|
+
}
|
|
21
25
|
function readCache(domainId) {
|
|
22
26
|
const path = cachePath(domainId);
|
|
23
27
|
if (!existsSync(path)) return null;
|
|
28
|
+
let parsed;
|
|
24
29
|
try {
|
|
25
|
-
|
|
30
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
26
31
|
} catch {
|
|
27
32
|
return null;
|
|
28
33
|
}
|
|
34
|
+
if (!isCacheEntry(parsed)) {
|
|
35
|
+
console.error(`[opf-br-mcp] cache de ${domainId} tem forma inesperada; ignorando (${path})`);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return parsed;
|
|
29
39
|
}
|
|
30
40
|
function writeCache(domainId, data, packageVersion) {
|
|
31
41
|
const entry = {
|
|
@@ -46,7 +56,7 @@ function isFresh(entry, ttlHours, now = /* @__PURE__ */ new Date()) {
|
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
// src/core/version.ts
|
|
49
|
-
var PACKAGE_VERSION = "0.
|
|
59
|
+
var PACKAGE_VERSION = "0.9.3";
|
|
50
60
|
|
|
51
61
|
// src/core/data.ts
|
|
52
62
|
async function getDomainData(domain, force = false, ctx) {
|
|
@@ -66,15 +76,52 @@ async function getDomainData(domain, force = false, ctx) {
|
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
|
|
79
|
+
// src/core/filter-sets.ts
|
|
80
|
+
var FILTER_SETS = {
|
|
81
|
+
openapi: [
|
|
82
|
+
{ name: "method", description: "Verbo HTTP exato (ex.: POST)" },
|
|
83
|
+
{ name: "schema", description: "Substring no nome do schema (case-insensitive)" },
|
|
84
|
+
{
|
|
85
|
+
name: "response",
|
|
86
|
+
description: "Substring no nome do components.responses, a chave usada no $ref (ex.: 202Webhook)"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "parameter",
|
|
90
|
+
description: "Substring no nome do components.parameters, a chave usada no $ref (ex.: xFapiInteractionId)"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "header",
|
|
94
|
+
description: "Substring no nome do components.headers, a chave usada no $ref (ex.: X-V)"
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
sections: [
|
|
98
|
+
{ name: "page", description: "Substring no t\xEDtulo da p\xE1gina Confluence" },
|
|
99
|
+
{ name: "heading", description: "Substring no t\xEDtulo da se\xE7\xE3o (heading)" },
|
|
100
|
+
{ name: "contains", description: "Substring no conte\xFAdo da se\xE7\xE3o" }
|
|
101
|
+
]
|
|
102
|
+
};
|
|
103
|
+
|
|
69
104
|
// src/core/http.ts
|
|
70
|
-
function sleep(ms) {
|
|
71
|
-
|
|
105
|
+
function sleep(ms, signal) {
|
|
106
|
+
if (signal?.aborted) return Promise.resolve();
|
|
107
|
+
return new Promise((resolve) => {
|
|
108
|
+
const done = () => {
|
|
109
|
+
clearTimeout(timer);
|
|
110
|
+
signal?.removeEventListener("abort", done);
|
|
111
|
+
resolve();
|
|
112
|
+
};
|
|
113
|
+
const timer = setTimeout(done, ms);
|
|
114
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
115
|
+
});
|
|
72
116
|
}
|
|
73
117
|
async function fetchWithRetry(url, opts) {
|
|
74
118
|
let lastError;
|
|
75
119
|
for (let attempt = 0; attempt <= opts.retryDelaysMs.length; attempt++) {
|
|
76
120
|
if (opts.signal?.aborted) break;
|
|
77
|
-
if (attempt > 0)
|
|
121
|
+
if (attempt > 0) {
|
|
122
|
+
await sleep(opts.retryDelaysMs[attempt - 1], opts.signal);
|
|
123
|
+
if (opts.signal?.aborted) break;
|
|
124
|
+
}
|
|
78
125
|
const timeout = AbortSignal.timeout(3e4);
|
|
79
126
|
const signal = opts.signal ? AbortSignal.any([opts.signal, timeout]) : timeout;
|
|
80
127
|
let response;
|
|
@@ -134,8 +181,14 @@ async function fetchConfluencePage(baseUrl, pageId, retryDelaysMs, signal) {
|
|
|
134
181
|
const apiUrl = `${baseUrl}/wiki/rest/api/content/${pageId}?expand=body.view`;
|
|
135
182
|
const response = await fetchWithRetry(apiUrl, { retryDelaysMs, headers: HEADERS, signal });
|
|
136
183
|
const json = await response.json();
|
|
184
|
+
const html = json?.body?.view?.value;
|
|
185
|
+
if (typeof html !== "string") {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`resposta da p\xE1gina ${pageId} n\xE3o traz body.view.value; n\xE3o \xE9 conte\xFAdo Confluence expandido`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
137
190
|
return {
|
|
138
|
-
html
|
|
191
|
+
html,
|
|
139
192
|
title: json.title,
|
|
140
193
|
url: `${baseUrl}/wiki${json._links?.webui ?? ""}`
|
|
141
194
|
};
|
|
@@ -310,8 +363,8 @@ var pcmDomain = {
|
|
|
310
363
|
const total = pcmConfig.pages.length;
|
|
311
364
|
const pages = [];
|
|
312
365
|
for (const [i, page] of pcmConfig.pages.entries()) {
|
|
366
|
+
if (i > 0) await sleep(pcmConfig.interRequestDelayMs, ctx?.signal);
|
|
313
367
|
if (ctx?.signal?.aborted) throw new Error("Extra\xE7\xE3o cancelada pelo cliente");
|
|
314
|
-
if (i > 0) await sleep(pcmConfig.interRequestDelayMs);
|
|
315
368
|
ctx?.onProgress?.(i, total, `Extraindo "${page.title}"`);
|
|
316
369
|
const { html, url } = await fetchConfluencePage(
|
|
317
370
|
pcmConfig.confluenceBaseUrl,
|
|
@@ -380,7 +433,13 @@ function refsOf(node, specName) {
|
|
|
380
433
|
return [...out];
|
|
381
434
|
}
|
|
382
435
|
function parseOpenApiSpec(yamlText, specName) {
|
|
383
|
-
const
|
|
436
|
+
const parsed = parse(yamlText);
|
|
437
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed) || !("paths" in parsed) && !("components" in parsed)) {
|
|
438
|
+
throw new Error(
|
|
439
|
+
`resposta de ${specName} n\xE3o \xE9 uma spec OpenAPI (sem "paths" nem "components"); in\xEDcio do conte\xFAdo: ${JSON.stringify(yamlText.slice(0, 80))}`
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
const spec = parsed;
|
|
384
443
|
const items = [];
|
|
385
444
|
for (const [path, pathItem] of Object.entries(spec.paths ?? {})) {
|
|
386
445
|
for (const method of HTTP_METHODS) {
|
|
@@ -433,23 +492,12 @@ function createOpenApiDomain(config) {
|
|
|
433
492
|
id: config.id,
|
|
434
493
|
title: config.title,
|
|
435
494
|
description: config.description,
|
|
495
|
+
specVersion: config.specVersion,
|
|
436
496
|
ttlHours: 72,
|
|
497
|
+
filterSet: "openapi",
|
|
437
498
|
filters: [
|
|
438
499
|
{ name: "path", description: `Substring no path do endpoint (ex.: ${config.pathExample})` },
|
|
439
|
-
|
|
440
|
-
{ name: "schema", description: "Substring no nome do schema (case-insensitive)" },
|
|
441
|
-
{
|
|
442
|
-
name: "response",
|
|
443
|
-
description: "Substring no nome do components.responses, a chave usada no $ref (ex.: 202Webhook)"
|
|
444
|
-
},
|
|
445
|
-
{
|
|
446
|
-
name: "parameter",
|
|
447
|
-
description: "Substring no nome do components.parameters, a chave usada no $ref (ex.: xFapiInteractionId)"
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
name: "header",
|
|
451
|
-
description: "Substring no nome do components.headers, a chave usada no $ref (ex.: X-V)"
|
|
452
|
-
}
|
|
500
|
+
...FILTER_SETS.openapi
|
|
453
501
|
],
|
|
454
502
|
async extract(ctx) {
|
|
455
503
|
if (ctx?.signal?.aborted) throw new Error("Extra\xE7\xE3o cancelada pelo cliente");
|
|
@@ -589,18 +637,16 @@ function createConfluenceSectionsDomain(config) {
|
|
|
589
637
|
id: config.id,
|
|
590
638
|
title: config.title,
|
|
591
639
|
description: config.description,
|
|
640
|
+
specVersion: config.specVersion,
|
|
592
641
|
ttlHours: 72,
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
{ name: "heading", description: "Substring no t\xEDtulo da se\xE7\xE3o (heading)" },
|
|
596
|
-
{ name: "contains", description: "Substring no conte\xFAdo da se\xE7\xE3o" }
|
|
597
|
-
],
|
|
642
|
+
filterSet: "sections",
|
|
643
|
+
filters: [...FILTER_SETS.sections],
|
|
598
644
|
async extract(ctx) {
|
|
599
645
|
const total = config.pages.length;
|
|
600
646
|
const pages = [];
|
|
601
647
|
for (const [i, page] of config.pages.entries()) {
|
|
648
|
+
if (i > 0) await sleep(config.interRequestDelayMs, ctx?.signal);
|
|
602
649
|
if (ctx?.signal?.aborted) throw new Error("Extra\xE7\xE3o cancelada pelo cliente");
|
|
603
|
-
if (i > 0) await sleep(config.interRequestDelayMs);
|
|
604
650
|
ctx?.onProgress?.(i, total, `Extraindo "${page.title}"`);
|
|
605
651
|
const { html, url } = await fetchConfluencePage(
|
|
606
652
|
config.confluenceBaseUrl,
|
|
@@ -637,6 +683,7 @@ function createConfluenceSectionsDomain(config) {
|
|
|
637
683
|
var paymentsV5BusinessRulesConfig = {
|
|
638
684
|
id: "payments-v5-business-rules",
|
|
639
685
|
title: "API de Pagamentos v5 \u2014 Regras de neg\xF3cio (SV)",
|
|
686
|
+
specVersion: "5.0.0",
|
|
640
687
|
description: "Regras de neg\xF3cio da API de Inicia\xE7\xE3o de Pagamentos (Pix) vers\xE3o 5.0.0 do Open Finance Brasil, extra\xEDdas das p\xE1ginas Confluence de Servi\xE7os (SV): Informa\xE7\xF5es Gerais (vis\xE3o geral dos endpoints e dicion\xE1rio de dados), Escopo da API (formas de inicia\xE7\xE3o suportadas: Pix imediato, agendado, agendado recorrente, QR Code est\xE1tico/din\xE2mico, Pix Saque, Pix Troco, Pix Composto), M\xE1quina de Estados (status do consentimento e do pagamento), Diagrama de Sequ\xEAncia, Valida\xE7\xE3o de informa\xE7\xF5es no DICT e Adapta\xE7\xF5es para consultas de recursos entre 4.0.1 e 5.0.0. Complementa payments-v5-openapi, que traz a spec (endpoints e schemas) da mesma vers\xE3o. Conte\xFAdo que vale para todos os produtos de inicia\xE7\xE3o (idempot\xEAncia, assinatura de payload, polling, formatos de data) est\xE1 em payments-common-rules. Cada item \xE9 uma se\xE7\xE3o (heading) da p\xE1gina. search devolve um snippet do conte\xFAdo; use get_item para o texto completo da se\xE7\xE3o.",
|
|
641
688
|
confluenceBaseUrl: "https://openfinancebrasil.atlassian.net",
|
|
642
689
|
interRequestDelayMs: 2e3,
|
|
@@ -673,6 +720,7 @@ var enrollmentsV2Domain = createOpenApiDomain(enrollmentsV2Config);
|
|
|
673
720
|
var enrollmentsV2BusinessRulesConfig = {
|
|
674
721
|
id: "enrollments-v2-business-rules",
|
|
675
722
|
title: "API de V\xEDnculo de Dispositivo v2 \u2014 Regras de neg\xF3cio (SV)",
|
|
723
|
+
specVersion: "2.2.0",
|
|
676
724
|
description: "Regras de neg\xF3cio da API de V\xEDnculo de Dispositivo (Enrollments / Jornada Sem Redirecionamento) vers\xE3o 2.2.0 do Open Finance Brasil, extra\xEDdas das p\xE1ginas Confluence de Servi\xE7os (SV): Informa\xE7\xF5es Gerais, M\xE1quina de estados (vincula\xE7\xE3o de conta, autoriza\xE7\xE3o de consentimento e do consentimento recorrente), Edi\xE7\xE3o do v\xEDnculo de dispositivo (campos edit\xE1veis e condi\xE7\xF5es) e FAQ - JSR (perguntas e respostas por categoria). Complementa enrollments-v2-openapi, que traz a spec (endpoints e schemas) do mesmo major \u2014 hoje na 2.3.0, enquanto estas p\xE1ginas do Confluence ainda s\xE3o as da 2.2.0. Cada item \xE9 uma se\xE7\xE3o (heading) da p\xE1gina. search devolve um snippet do conte\xFAdo; use get_item para o texto completo da se\xE7\xE3o.",
|
|
677
725
|
confluenceBaseUrl: "https://openfinancebrasil.atlassian.net",
|
|
678
726
|
interRequestDelayMs: 2e3,
|
|
@@ -709,6 +757,7 @@ var automaticPaymentsV2Domain = createOpenApiDomain(automaticPaymentsV2Config);
|
|
|
709
757
|
var automaticPaymentsV2BusinessRulesConfig = {
|
|
710
758
|
id: "automatic-payments-v2-business-rules",
|
|
711
759
|
title: "API de Pagamentos Autom\xE1ticos v2 \u2014 Regras de neg\xF3cio (SV)",
|
|
760
|
+
specVersion: "2.2.0",
|
|
712
761
|
description: "Regras de neg\xF3cio da API de Pagamentos Autom\xE1ticos (Pix Autom\xE1tico e Transfer\xEAncias Inteligentes) vers\xE3o 2.2.0 do Open Finance Brasil, extra\xEDdas das p\xE1ginas Confluence de Servi\xE7os (SV): Informa\xE7\xF5es Gerais (vis\xE3o geral dos endpoints e dicion\xE1rio de dados), M\xE1quina de Estados (status do consentimento recorrente e do pagamento), Edi\xE7\xE3o do consentimento (campos edit\xE1veis e condi\xE7\xF5es), Tentativas Intradia e Extradia para Pix Autom\xE1tico (janelas de liquida\xE7\xE3o, retentativas, e2eID) e Adapta\xE7\xF5es para consultas de recursos entre 1.0.0 e 2.2.0. Complementa automatic-payments-v2-openapi, que traz a spec (endpoints e schemas) da mesma vers\xE3o. O guia de implementa\xE7\xE3o passo a passo do Pix Autom\xE1tico est\xE1 em payments-implementation-guides. Cada item \xE9 uma se\xE7\xE3o (heading) da p\xE1gina. search devolve um snippet do conte\xFAdo; use get_item para o texto completo da se\xE7\xE3o.",
|
|
713
762
|
confluenceBaseUrl: "https://openfinancebrasil.atlassian.net",
|
|
714
763
|
interRequestDelayMs: 2e3,
|
|
@@ -784,18 +833,91 @@ var consentsV3Config = {
|
|
|
784
833
|
// src/domains/consents-v3-openapi/index.ts
|
|
785
834
|
var consentsV3Domain = createOpenApiDomain(consentsV3Config);
|
|
786
835
|
|
|
836
|
+
// src/domains/resources-v3-openapi/config.ts
|
|
837
|
+
var resourcesV3Config = {
|
|
838
|
+
id: "resources-v3-openapi",
|
|
839
|
+
title: "API de Recursos \u2014 spec OpenAPI 3.1.0",
|
|
840
|
+
description: "Spec OpenAPI oficial da API de Recursos (Resources) do Open Finance Brasil, vers\xE3o 3.1.0. Endpoint \xFAnico (GET /resources) que lista os recursos compartilhados pelo cliente e o status de cada um, considerando a disponibilidade do recurso e o status do consentimento relacionado \u2014 \xE9 o ponto de partida de qualquer jornada de Dados Cadastrais e Transacionais, pois diz o que a receptora pode de fato consultar. O consentimento que a habilita fica em consents-v3-openapi; as contas em si, em accounts-v2-openapi. Itens type=operation, type=schema (ex.: ResponseResourceList, ResourceData com enum de status) e os components alvos dos $ref: type=response e type=parameter. Regras de neg\xF3cio da mesma API em resources-v3-business-rules. search devolve resumos; use get_item para o n\xF3 completo da spec \u2014 nas opera\xE7\xF5es o campo `refs` traz os ids dos components referenciados, prontos para get_item.",
|
|
841
|
+
pathExample: "/resources",
|
|
842
|
+
specName: "resources",
|
|
843
|
+
specVersion: "3.1.0",
|
|
844
|
+
url: "https://openbanking-brasil.github.io/openapi/swagger-apis/resources/3.1.0.yml",
|
|
845
|
+
retryDelaysMs: [2e3, 4e3, 8e3, 16e3]
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
// src/domains/resources-v3-openapi/index.ts
|
|
849
|
+
var resourcesV3Domain = createOpenApiDomain(resourcesV3Config);
|
|
850
|
+
|
|
851
|
+
// src/domains/resources-v3-business-rules/config.ts
|
|
852
|
+
var resourcesV3BusinessRulesConfig = {
|
|
853
|
+
id: "resources-v3-business-rules",
|
|
854
|
+
title: "API de Recursos v3 \u2014 Regras de neg\xF3cio (DC)",
|
|
855
|
+
specVersion: "3.1.0",
|
|
856
|
+
description: "Regras de neg\xF3cio da API de Recursos (Resources) vers\xE3o 3.1.0 do Open Finance Brasil, extra\xEDdas das p\xE1ginas Confluence de Dados Cadastrais e Transacionais (DC): Informa\xE7\xF5es Gerais (vis\xE3o geral do GET /resources/v3/resources, status do recurso versus status do consentimento) e Orienta\xE7\xF5es - [DC] Recursos (quando um recurso volta a ficar dispon\xEDvel, uso do status code de erro apropriado quando a transmissora n\xE3o retorna as informa\xE7\xF5es) \u2014 esta \xFAltima n\xE3o \xE9 versionada e vale para todo o major v3. Complementa resources-v3-openapi, que traz a spec (endpoints e schemas) do mesmo major. Cada item \xE9 uma se\xE7\xE3o (heading) da p\xE1gina. search devolve um snippet do conte\xFAdo; use get_item para o texto completo da se\xE7\xE3o.",
|
|
857
|
+
confluenceBaseUrl: "https://openfinancebrasil.atlassian.net",
|
|
858
|
+
interRequestDelayMs: 2e3,
|
|
859
|
+
retryDelaysMs: [2e3, 4e3, 8e3, 16e3],
|
|
860
|
+
pages: [
|
|
861
|
+
{ pageId: "1267990545", title: "Informa\xE7\xF5es Gerais - [DC] Recursos - v3.1.0" },
|
|
862
|
+
{ pageId: "219512943", title: "Orienta\xE7\xF5es - [DC] Recursos" }
|
|
863
|
+
]
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
// src/domains/resources-v3-business-rules/index.ts
|
|
867
|
+
var resourcesV3BusinessRulesDomain = createConfluenceSectionsDomain(
|
|
868
|
+
resourcesV3BusinessRulesConfig
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
// src/domains/accounts-v2-openapi/config.ts
|
|
872
|
+
var accountsV2Config = {
|
|
873
|
+
id: "accounts-v2-openapi",
|
|
874
|
+
title: "API de Contas \u2014 spec OpenAPI 2.4.2",
|
|
875
|
+
description: "Spec OpenAPI oficial da API de Contas (Accounts) do Open Finance Brasil, vers\xE3o 2.4.2 \u2014 compartilhamento de contas de dep\xF3sito \xE0 vista, poupan\xE7a e pagamento pr\xE9-paga. Cobre listagem (GET /accounts), identifica\xE7\xE3o (GET /accounts/{accountId}), saldos (GET /accounts/{accountId}/balances), transa\xE7\xF5es (GET /accounts/{accountId}/transactions e /transactions-current) e limites de cheque especial (GET /accounts/{accountId}/overdraft-limits). \xC9 API de Dados Cadastrais e Transacionais: o consentimento que a habilita fica em consents-v3-openapi, e a listagem de recursos compartilhados em resources-v3-openapi. Itens type=operation (um por m\xE9todo+path), type=schema (payloads, ex.: AccountData, AccountBalancesData, AccountTransactionsData) e os components alvos dos $ref: type=response e type=parameter. Regras de neg\xF3cio da mesma API em accounts-v2-business-rules. search devolve resumos; use get_item para o n\xF3 completo da spec \u2014 nas opera\xE7\xF5es o campo `refs` traz os ids dos components referenciados, prontos para get_item.",
|
|
876
|
+
pathExample: "/accounts/{accountId}/transactions",
|
|
877
|
+
specName: "accounts",
|
|
878
|
+
specVersion: "2.4.2",
|
|
879
|
+
url: "https://openbanking-brasil.github.io/openapi/swagger-apis/accounts/2.4.2.yml",
|
|
880
|
+
retryDelaysMs: [2e3, 4e3, 8e3, 16e3]
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
// src/domains/accounts-v2-openapi/index.ts
|
|
884
|
+
var accountsV2Domain = createOpenApiDomain(accountsV2Config);
|
|
885
|
+
|
|
886
|
+
// src/domains/accounts-v2-business-rules/config.ts
|
|
887
|
+
var accountsV2BusinessRulesConfig = {
|
|
888
|
+
id: "accounts-v2-business-rules",
|
|
889
|
+
title: "API de Contas v2 \u2014 Regras de neg\xF3cio (DC)",
|
|
890
|
+
specVersion: "2.4.2",
|
|
891
|
+
description: "Regras de neg\xF3cio da API de Contas (Accounts) vers\xE3o 2.4.2 do Open Finance Brasil, extra\xEDdas das p\xE1ginas Confluence de Dados Cadastrais e Transacionais (DC): Informa\xE7\xF5es Gerais (vis\xE3o geral dos endpoints, contas de dep\xF3sito \xE0 vista/poupan\xE7a/pagamento pr\xE9-paga, saldos, transa\xE7\xF5es e limites) e Orienta\xE7\xF5es - [DC] Contas (obrigatoriedade de informa\xE7\xF5es de contraparte pela IN BCB n\xBA 371, tratamento de transa\xE7\xF5es e casos de borda) \u2014 esta \xFAltima n\xE3o \xE9 versionada e vale para todo o major v2. Complementa accounts-v2-openapi, que traz a spec (endpoints e schemas) do mesmo major. Cada item \xE9 uma se\xE7\xE3o (heading) da p\xE1gina. search devolve um snippet do conte\xFAdo; use get_item para o texto completo da se\xE7\xE3o.",
|
|
892
|
+
confluenceBaseUrl: "https://openfinancebrasil.atlassian.net",
|
|
893
|
+
interRequestDelayMs: 2e3,
|
|
894
|
+
retryDelaysMs: [2e3, 4e3, 8e3, 16e3],
|
|
895
|
+
pages: [
|
|
896
|
+
{ pageId: "979566625", title: "Informa\xE7\xF5es Gerais - [DC] Contas - v2.4.2" },
|
|
897
|
+
{ pageId: "1739096252", title: "Orienta\xE7\xF5es - [DC] Contas" }
|
|
898
|
+
]
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
// src/domains/accounts-v2-business-rules/index.ts
|
|
902
|
+
var accountsV2BusinessRulesDomain = createConfluenceSectionsDomain(
|
|
903
|
+
accountsV2BusinessRulesConfig
|
|
904
|
+
);
|
|
905
|
+
|
|
787
906
|
// src/domains/pcm-openapi/config.ts
|
|
788
907
|
var pcmOpenapiConfig = {
|
|
789
908
|
specName: "pcm",
|
|
790
|
-
|
|
791
|
-
|
|
909
|
+
// A spec da PCM não declara `info.version` (o campo `openapi:` traz um valor
|
|
910
|
+
// fora do padrão). Identificar pelo commit é o único rótulo verificável — e
|
|
911
|
+
// deixa explícito, em list_domains, que este domínio está congelado no SHA.
|
|
912
|
+
specVersion: "commit 8a7f7b0",
|
|
913
|
+
url: "https://raw.githubusercontent.com/OpenBanking-Brasil/pcm-specs/8a7f7b03ddfab7eaf97bcfe68e67fac22a5eeb68/PCM-current.openapi.yaml",
|
|
792
914
|
retryDelaysMs: [2e3, 4e3, 8e3, 16e3]
|
|
793
915
|
};
|
|
794
916
|
|
|
795
917
|
// src/domains/pcm-openapi/index.ts
|
|
796
918
|
var pcmOpenapiDomain = createOpenApiDomain({
|
|
797
919
|
id: "pcm-openapi",
|
|
798
|
-
title: `PCM \u2014 spec OpenAPI ${pcmOpenapiConfig.specVersion}`,
|
|
920
|
+
title: `PCM \u2014 spec OpenAPI (${pcmOpenapiConfig.specVersion})`,
|
|
799
921
|
description: "Spec OpenAPI oficial da PCM (Plataforma de Coleta de M\xE9tricas) do Open Finance Brasil \u2014 a faceta swagger. Cobre os endpoints de reporte (report-api v1/v2), hybrid-flow, opendata, consents/stock, credit-portabilities, payments/status e token. Itens type=operation (um por m\xE9todo+path), type=schema (payloads) e type=response (components reutiliz\xE1veis, alvos dos $ref). Outras facetas da PCM: tabelas de obrigatoriedade do campo additionalInfo em pcm-additional-info; regras de neg\xF3cio (reporte, processamento, diverg\xEAncias) em pcm-business-rules. search devolve resumos; use get_item para o n\xF3 completo da spec (opera\xE7\xF5es trazem `refs` com os ids dos components que referenciam).",
|
|
800
922
|
pathExample: "/report-api/v1/private/report",
|
|
801
923
|
...pcmOpenapiConfig
|
|
@@ -922,6 +1044,11 @@ var participantesConfig = {
|
|
|
922
1044
|
// src/domains/participantes/parser.ts
|
|
923
1045
|
function parseParticipants(jsonText) {
|
|
924
1046
|
const orgs = JSON.parse(jsonText);
|
|
1047
|
+
if (!Array.isArray(orgs) || orgs.length === 0) {
|
|
1048
|
+
throw new Error(
|
|
1049
|
+
`resposta do diret\xF3rio n\xE3o \xE9 uma lista de organiza\xE7\xF5es n\xE3o-vazia; in\xEDcio do conte\xFAdo: ${JSON.stringify(jsonText.slice(0, 80))}`
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
925
1052
|
return orgs.map((org) => {
|
|
926
1053
|
const servers = org.AuthorisationServers ?? [];
|
|
927
1054
|
const families = /* @__PURE__ */ new Set();
|
|
@@ -1069,6 +1196,10 @@ var domains = [
|
|
|
1069
1196
|
paymentsCommonRulesDomain,
|
|
1070
1197
|
paymentsImplementationGuidesDomain,
|
|
1071
1198
|
consentsV3Domain,
|
|
1199
|
+
resourcesV3Domain,
|
|
1200
|
+
resourcesV3BusinessRulesDomain,
|
|
1201
|
+
accountsV2Domain,
|
|
1202
|
+
accountsV2BusinessRulesDomain,
|
|
1072
1203
|
pcmOpenapiDomain,
|
|
1073
1204
|
pcmBusinessRulesDomain,
|
|
1074
1205
|
jornadaOtimizadaDomain,
|
|
@@ -1099,6 +1230,7 @@ function findDomain(id) {
|
|
|
1099
1230
|
return domains.find((d) => d.id === id);
|
|
1100
1231
|
}
|
|
1101
1232
|
var validIds = () => domains.map((d) => d.id).join(", ");
|
|
1233
|
+
var REFRESH_BUDGET_MS = 45e3;
|
|
1102
1234
|
var domainIdSchema = z.enum(domains.map((d) => d.id)).describe("Id do dom\xEDnio (ver list_domains)");
|
|
1103
1235
|
function extractContext(extra) {
|
|
1104
1236
|
const progressToken = extra._meta?.progressToken;
|
|
@@ -1113,7 +1245,7 @@ function extractContext(extra) {
|
|
|
1113
1245
|
}
|
|
1114
1246
|
};
|
|
1115
1247
|
}
|
|
1116
|
-
function createServer() {
|
|
1248
|
+
function createServer(refreshBudgetMs = REFRESH_BUDGET_MS) {
|
|
1117
1249
|
const server2 = new McpServer(
|
|
1118
1250
|
{ name: "opf-br-mcp", version: PACKAGE_VERSION },
|
|
1119
1251
|
{
|
|
@@ -1124,7 +1256,7 @@ function createServer() {
|
|
|
1124
1256
|
"list_domains",
|
|
1125
1257
|
{
|
|
1126
1258
|
title: "Listar dom\xEDnios",
|
|
1127
|
-
description: "Lista os dom\xEDnios de conhecimento do Open Finance Brasil dispon\xEDveis neste server, com os filtros aceitos por cada um e o estado do cache local. Comece por aqui; depois use search(domain, ...) e get_item(domain, id).",
|
|
1259
|
+
description: "Lista os dom\xEDnios de conhecimento do Open Finance Brasil dispon\xEDveis neste server, com os filtros aceitos por cada um, a vers\xE3o da spec de origem e o estado do cache local. Devolve tamb\xE9m a vers\xE3o deste server em `server.version`. Os filtros aceitos por um dom\xEDnio s\xE3o a uni\xE3o de `filters` (os pr\xF3prios dele, pode vir ausente) com `filterSets[<filterSet>]` (o conjunto comum \xE0 fam\xEDlia, listado uma vez no topo). Comece por aqui; depois use search(domain, ...) e get_item(domain, id).",
|
|
1128
1260
|
annotations: {
|
|
1129
1261
|
readOnlyHint: true,
|
|
1130
1262
|
destructiveHint: false,
|
|
@@ -1134,20 +1266,34 @@ function createServer() {
|
|
|
1134
1266
|
},
|
|
1135
1267
|
async () => {
|
|
1136
1268
|
const out = domains.map((d) => {
|
|
1269
|
+
const shared = d.filterSet ? FILTER_SETS[d.filterSet] : void 0;
|
|
1270
|
+
const own = shared ? d.filters.filter((f) => !shared.some((s) => s.name === f.name && s.description === f.description)) : d.filters;
|
|
1271
|
+
const base = {
|
|
1272
|
+
id: d.id,
|
|
1273
|
+
title: d.title,
|
|
1274
|
+
description: d.description,
|
|
1275
|
+
...d.filterSet ? { filterSet: d.filterSet } : {},
|
|
1276
|
+
...own.length > 0 ? { filters: own } : {},
|
|
1277
|
+
...d.specVersion ? { specVersion: d.specVersion } : {}
|
|
1278
|
+
};
|
|
1137
1279
|
if (d.live) {
|
|
1138
|
-
return {
|
|
1280
|
+
return { ...base, live: true };
|
|
1139
1281
|
}
|
|
1140
1282
|
const cached = readCache(d.id);
|
|
1141
1283
|
return {
|
|
1142
|
-
|
|
1143
|
-
title: d.title,
|
|
1144
|
-
description: d.description,
|
|
1145
|
-
filters: d.filters,
|
|
1284
|
+
...base,
|
|
1146
1285
|
cachedItems: cached?.data.items.length ?? 0,
|
|
1147
1286
|
extractedAt: cached?.extractedAt ?? null
|
|
1148
1287
|
};
|
|
1149
1288
|
});
|
|
1150
|
-
|
|
1289
|
+
const usedSets = Object.fromEntries(
|
|
1290
|
+
Object.entries(FILTER_SETS).filter(([name]) => domains.some((d) => d.filterSet === name))
|
|
1291
|
+
);
|
|
1292
|
+
return text({
|
|
1293
|
+
server: { name: "opf-br-mcp", version: PACKAGE_VERSION },
|
|
1294
|
+
filterSets: usedSets,
|
|
1295
|
+
domains: out
|
|
1296
|
+
});
|
|
1151
1297
|
}
|
|
1152
1298
|
);
|
|
1153
1299
|
server2.registerTool(
|
|
@@ -1263,7 +1409,7 @@ function createServer() {
|
|
|
1263
1409
|
"refresh",
|
|
1264
1410
|
{
|
|
1265
1411
|
title: "Re-extrair fontes",
|
|
1266
|
-
description: "For\xE7a re-extra\xE7\xE3o das fontes p\xFAblicas (ignora o TTL de 72h do cache).
|
|
1412
|
+
description: "For\xE7a re-extra\xE7\xE3o das fontes p\xFAblicas (ignora o TTL de 72h do cache). Use quando suspeitar de dados desatualizados. Prefira passar `domain`: sem ele o server atualiza o que couber em 45s e devolve o restante em `pendentes`, que voc\xEA deve refazer chamando refresh(domain) para cada id.",
|
|
1267
1413
|
inputSchema: {
|
|
1268
1414
|
domain: domainIdSchema.optional().describe("Id do dom\xEDnio; omita para todos")
|
|
1269
1415
|
},
|
|
@@ -1279,7 +1425,13 @@ function createServer() {
|
|
|
1279
1425
|
(d) => !d.live && (!domain || d.id === domain)
|
|
1280
1426
|
);
|
|
1281
1427
|
const report = {};
|
|
1428
|
+
const pendentes = [];
|
|
1429
|
+
const inicio = Date.now();
|
|
1282
1430
|
for (const d of targets) {
|
|
1431
|
+
if (!domain && Date.now() - inicio > refreshBudgetMs) {
|
|
1432
|
+
pendentes.push(d.id);
|
|
1433
|
+
continue;
|
|
1434
|
+
}
|
|
1283
1435
|
try {
|
|
1284
1436
|
const { data } = await getDomainData(d, true, extractContext(extra));
|
|
1285
1437
|
report[d.id] = `ok: ${data.items.length} itens`;
|
|
@@ -1287,13 +1439,23 @@ function createServer() {
|
|
|
1287
1439
|
report[d.id] = `erro: ${err.message}`;
|
|
1288
1440
|
}
|
|
1289
1441
|
}
|
|
1290
|
-
return text(
|
|
1442
|
+
return text({
|
|
1443
|
+
atualizados: report,
|
|
1444
|
+
...pendentes.length > 0 ? {
|
|
1445
|
+
pendentes,
|
|
1446
|
+
nota: `Parei em ${refreshBudgetMs / 1e3}s para n\xE3o estourar o timeout do cliente. Chame refresh(domain) para cada id em pendentes.`
|
|
1447
|
+
} : {}
|
|
1448
|
+
});
|
|
1291
1449
|
}
|
|
1292
1450
|
);
|
|
1293
1451
|
return server2;
|
|
1294
1452
|
}
|
|
1295
1453
|
|
|
1296
1454
|
// src/index.ts
|
|
1455
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
1456
|
+
console.log(PACKAGE_VERSION);
|
|
1457
|
+
process.exit(0);
|
|
1458
|
+
}
|
|
1297
1459
|
var server = createServer();
|
|
1298
1460
|
await server.connect(new StdioServerTransport());
|
|
1299
|
-
console.error(
|
|
1461
|
+
console.error(`[opf-br-mcp] server pronto (stdio) \u2014 v${PACKAGE_VERSION}`);
|