opf-br-mcp 0.9.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/dist/index.js +172 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -31,6 +31,8 @@ acesso token-eficiente às regras do Open Finance Brasil.
31
31
  | `mqd` | Confluence público OFB | Motor de Qualidade de Dados (Especificação Técnica, Arquitetura, Documentação da API, Manual de Instalação, Endpoints Validados, FAQ, Troubleshooting) — item por seção |
32
32
  | `webhook-v1-openapi` | GitHub OpenBanking-Brasil/all-services-repo | Spec OpenAPI 1.3.0 da API de Webhook (notificações de mudança de estado: pagamentos, enrollments, pagamentos automáticos) |
33
33
  | `seguranca` | Confluence público OFB | Segurança do Open Finance Brasil (Perfil de Segurança, FAPI, DCR, CIBA, Padrão de Certificados, Assinaturas, Casos de Erro, Redirecionamento App-to-App, Glossário, Versionamento) — item por seção |
34
+ | `requisitos-nao-funcionais` | Confluence público OFB (Manual de APIs) | Requisitos não funcionais de todas as APIs (Desempenho, Disponibilidade, Timeout, Limites de tráfego, Limites operacionais, Indisponibilidade Programada) — item por seção |
35
+ | `limites-por-endpoint` | Confluence público OFB (Manual de APIs) | SLA (p95), timeout, TPM, TPS e limite operacional de cada endpoint de todas as famílias de API — um item por endpoint |
34
36
  | `participantes` | Diretório OFB (data.directory.openbankingbrasil.org.br) | Organizações participantes, marcas (authorisation servers) e famílias de API suportadas com versões — um item por organização |
35
37
  | `portal` | Confluence público OFB (busca ao vivo) | Busca CQL em todo o Portal do Desenvolvedor (espaço OF) — sem cache, `query` obrigatória; fallback quando os domínios específicos não cobrem o assunto |
36
38
 
@@ -70,7 +72,7 @@ bem na janela de contexto de um agente, e despejá-la desperdiça tokens. A solu
70
72
  apenas o mínimo necessário em cada etapa do funil:
71
73
 
72
74
  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
75
+ filtros idênticos a uma família inteira de domínios (os 9 `*-openapi`, os 12
74
76
  de seções do Confluence) saem uma única vez em `filterSets`; cada domínio
75
77
  traz `filterSet` e só lista inline o que é próprio dele (em `*-openapi`,
76
78
  apenas `path`, cujo exemplo varia por API). Os filtros aceitos por um domínio
package/dist/index.js CHANGED
@@ -56,7 +56,7 @@ function isFresh(entry, ttlHours, now = /* @__PURE__ */ new Date()) {
56
56
  }
57
57
 
58
58
  // src/core/version.ts
59
- var PACKAGE_VERSION = "0.9.3";
59
+ var PACKAGE_VERSION = "1.0.0";
60
60
 
61
61
  // src/core/data.ts
62
62
  async function getDomainData(domain, force = false, ctx) {
@@ -1032,6 +1032,175 @@ var segurancaConfig = {
1032
1032
  // src/domains/seguranca/index.ts
1033
1033
  var segurancaDomain = createConfluenceSectionsDomain(segurancaConfig);
1034
1034
 
1035
+ // src/domains/requisitos-nao-funcionais/config.ts
1036
+ var requisitosNaoFuncionaisConfig = {
1037
+ id: "requisitos-nao-funcionais",
1038
+ title: "Requisitos n\xE3o Funcionais \u2014 SLA, disponibilidade e limites (Manual de APIs)",
1039
+ description: "Requisitos n\xE3o funcionais que valem para todas as APIs do Open Finance Brasil, extra\xEDdos das p\xE1ginas Confluence do Manual de APIs: Desempenho (SLA do percentil 95 do tempo de resposta por faixa de frequ\xEAncia do endpoint), Disponibilidade (m\xEDnimos por 24h e por trimestre), Timeout, Limites de tr\xE1fego (TPM por origem e TPS globais), Limites operacionais (teto de consultas por m\xEAs, por endpoint e por recurso, e pagina\xE7\xE3o com pagination-key) e Indisponibilidade Programada. Os n\xFAmeros por endpoint (SLA, timeout, TPM, TPS e limite operacional de cada rota) est\xE3o em limites-por-endpoint, um registro por endpoint. Cada item aqui \xE9 uma se\xE7\xE3o (heading) da p\xE1gina \u2014 as p\xE1ginas Desempenho e Timeout n\xE3o t\xEAm headings e viram um \xFAnico item `intro` cada, ent\xE3o nelas filtre por page ou contains, n\xE3o por heading. search devolve um snippet do conte\xFAdo; use get_item para o texto completo da se\xE7\xE3o.",
1040
+ confluenceBaseUrl: "https://openfinancebrasil.atlassian.net",
1041
+ interRequestDelayMs: 2e3,
1042
+ retryDelaysMs: [2e3, 4e3, 8e3, 16e3],
1043
+ // Fora da lista: a página-mãe "Requisitos não Funcionais" (17956981), que só
1044
+ // nomeia as quatro faixas de frequência sem defini-las (página índice), e a
1045
+ // página "Referência" (17957025), cujas tabelas de limites por endpoint têm
1046
+ // parser próprio no domínio limites-por-endpoint.
1047
+ pages: [
1048
+ { pageId: "17891396", title: "Desempenho" },
1049
+ { pageId: "17891406", title: "Disponibilidade" },
1050
+ { pageId: "17891413", title: "Timeout" },
1051
+ { pageId: "17989722", title: "Limites de tr\xE1fego" },
1052
+ { pageId: "17924220", title: "Limites operacionais" },
1053
+ { pageId: "1808269313", title: "Indisponibilidade Programada" }
1054
+ ]
1055
+ };
1056
+
1057
+ // src/domains/requisitos-nao-funcionais/index.ts
1058
+ var requisitosNaoFuncionaisDomain = createConfluenceSectionsDomain(
1059
+ requisitosNaoFuncionaisConfig
1060
+ );
1061
+
1062
+ // src/domains/limites-por-endpoint/config.ts
1063
+ var limitesPorEndpointConfig = {
1064
+ confluenceBaseUrl: "https://openfinancebrasil.atlassian.net",
1065
+ retryDelaysMs: [2e3, 4e3, 8e3, 16e3],
1066
+ // Página única: a tabela "Referência" do Manual de APIs, com uma tabela por
1067
+ // família de API e uma linha por endpoint.
1068
+ pageId: "17957025",
1069
+ pageTitle: "Refer\xEAncia"
1070
+ };
1071
+
1072
+ // src/domains/limites-por-endpoint/parser.ts
1073
+ import * as cheerio3 from "cheerio";
1074
+ var HTTP_METHODS2 = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
1075
+ function cellText3($, el) {
1076
+ const clone = $(el).clone();
1077
+ clone.find("br").replaceWith("\n");
1078
+ return clone.text().replace(/[​‌‍]/g, "").replace(/ /g, " ").replace(/\s+/g, " ").trim();
1079
+ }
1080
+ function headerKey(raw) {
1081
+ const h = raw.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim();
1082
+ if (h.startsWith("endpoint")) return "path";
1083
+ if (h.startsWith("frequencia")) return "frequencia";
1084
+ if (h.startsWith("sla")) return "slaMs";
1085
+ if (h.startsWith("timeout")) return "timeoutS";
1086
+ if (h.startsWith("limite operacional")) return "limiteOperacional";
1087
+ if (h === "tpm") return "tpm";
1088
+ if (h === "tps") return "tps";
1089
+ return null;
1090
+ }
1091
+ function splitEndpoint(cell) {
1092
+ const [first, ...rest] = cell.split(" ");
1093
+ if (HTTP_METHODS2.has(first.toUpperCase()) && rest.length > 0) {
1094
+ return { metodo: first.toUpperCase(), path: rest.join(" ") };
1095
+ }
1096
+ return { metodo: null, path: cell };
1097
+ }
1098
+ function parseEndpointLimits(html) {
1099
+ const $ = cheerio3.load(html);
1100
+ $("style, script").remove();
1101
+ const limits = [];
1102
+ for (const table of $("table").toArray()) {
1103
+ const rows = $(table).find("tr").toArray();
1104
+ const cellsOf = (tr) => $(tr).find("th, td").toArray().map((c) => cellText3($, c));
1105
+ const headerIdx = rows.findIndex(
1106
+ (tr) => cellsOf(tr).some((c) => headerKey(c) === "path")
1107
+ );
1108
+ if (headerIdx === -1) continue;
1109
+ const api = rows.slice(0, headerIdx).map((tr) => cellsOf(tr).filter(Boolean).join(" ")).filter(Boolean).join(" ").trim();
1110
+ const keys = cellsOf(rows[headerIdx]).map(headerKey);
1111
+ for (const tr of rows.slice(headerIdx + 1)) {
1112
+ const cells = cellsOf(tr);
1113
+ const limit = {
1114
+ api,
1115
+ metodo: null,
1116
+ path: "",
1117
+ frequencia: null,
1118
+ slaMs: null,
1119
+ timeoutS: null,
1120
+ tpm: null,
1121
+ tps: null,
1122
+ limiteOperacional: null
1123
+ };
1124
+ cells.forEach((text2, i) => {
1125
+ const key = keys[i];
1126
+ if (!key || !text2) return;
1127
+ if (key === "path") {
1128
+ const { metodo, path } = splitEndpoint(text2);
1129
+ limit.metodo = metodo;
1130
+ limit.path = path;
1131
+ } else {
1132
+ limit[key] = text2;
1133
+ }
1134
+ });
1135
+ if (limit.path) limits.push(limit);
1136
+ }
1137
+ }
1138
+ return limits;
1139
+ }
1140
+
1141
+ // src/domains/limites-por-endpoint/index.ts
1142
+ function slugify3(text2) {
1143
+ return text2.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
1144
+ }
1145
+ function buildItems3(page, limits) {
1146
+ const items = [];
1147
+ const seen = /* @__PURE__ */ new Map();
1148
+ for (const limit of limits) {
1149
+ const base = `${page.pageId}:${slugify3(`${limit.api} ${limit.metodo ?? ""} ${limit.path}`)}`;
1150
+ const count = (seen.get(base) ?? 0) + 1;
1151
+ seen.set(base, count);
1152
+ items.push({ ...limit, id: count > 1 ? `${base}-${count}` : base, page });
1153
+ }
1154
+ return items;
1155
+ }
1156
+ var limitesPorEndpointDomain = {
1157
+ id: "limites-por-endpoint",
1158
+ title: "Limites por endpoint \u2014 SLA, timeout, TPM, TPS e limite operacional",
1159
+ description: 'Tabela de refer\xEAncia do Manual de APIs do Open Finance Brasil com os limites de cada endpoint de todas as fam\xEDlias de API (Dados Abertos, Dados Cadastrais e Transacionais, Servi\xE7os de Inicia\xE7\xE3o, Portabilidade de Cr\xE9dito, Webhook, Admin e Discovery). Um item por endpoint, com a faixa de frequ\xEAncia (alta, m\xE9dia-alta, m\xE9dia ou baixa), o SLA em ms do percentil 95 do tempo de resposta, o timeout em segundos, os limites de tr\xE1fego TPM e TPS e o limite operacional de consultas por m\xEAs. Responde perguntas do tipo "qual o SLA e o timeout do GET /resources". O texto normativo por tr\xE1s desses n\xFAmeros (como o SLA \xE9 medido e apurado, disponibilidade m\xEDnima, regras de limites de tr\xE1fego e operacionais) est\xE1 em requisitos-nao-funcionais. search devolve o registro completo \u2014 em TPM algumas linhas trazem a tabela de faixas QCA\u2192TPM como texto.',
1160
+ ttlHours: 72,
1161
+ filters: [
1162
+ { name: "endpoint", description: "Substring na rota (ex.: /resources)" },
1163
+ { name: "api", description: "Substring no nome da fam\xEDlia de API (ex.: Recursos, [SV] API Pagamentos)" },
1164
+ { name: "method", description: "Verbo HTTP exato (ex.: GET)" },
1165
+ { name: "frequencia", description: "Substring na faixa de frequ\xEAncia (ex.: Alta, Baixa)" }
1166
+ ],
1167
+ async extract(ctx) {
1168
+ ctx?.onProgress?.(0, 1, `Extraindo "${limitesPorEndpointConfig.pageTitle}"`);
1169
+ const { html, url } = await fetchConfluencePage(
1170
+ limitesPorEndpointConfig.confluenceBaseUrl,
1171
+ limitesPorEndpointConfig.pageId,
1172
+ limitesPorEndpointConfig.retryDelaysMs,
1173
+ ctx?.signal
1174
+ );
1175
+ ctx?.onProgress?.(1, 1);
1176
+ const page = {
1177
+ pageId: limitesPorEndpointConfig.pageId,
1178
+ title: limitesPorEndpointConfig.pageTitle,
1179
+ url
1180
+ };
1181
+ return { items: buildItems3(page, parseEndpointLimits(html)) };
1182
+ },
1183
+ search(data, query, filters = {}) {
1184
+ const endpoint = filters.endpoint ? normalize(filters.endpoint) : void 0;
1185
+ const api = filters.api ? normalize(filters.api) : void 0;
1186
+ const metodo = filters.method?.trim().toUpperCase();
1187
+ const frequencia = filters.frequencia ? normalize(filters.frequencia) : void 0;
1188
+ return data.items.filter((item) => {
1189
+ if (endpoint && !normalize(item.path).includes(endpoint)) return false;
1190
+ if (api && !normalize(item.api).includes(api)) return false;
1191
+ if (metodo && item.metodo !== metodo) return false;
1192
+ if (frequencia && !normalize(item.frequencia ?? "").includes(frequencia)) return false;
1193
+ if (query?.trim() && !matchesQuery(`${item.api} ${item.metodo ?? ""} ${item.path}`, query)) {
1194
+ return false;
1195
+ }
1196
+ return true;
1197
+ });
1198
+ },
1199
+ getItem(data, id) {
1200
+ return data.items.find((i) => i.id === id) ?? null;
1201
+ }
1202
+ };
1203
+
1035
1204
  // src/domains/participantes/config.ts
1036
1205
  var participantesConfig = {
1037
1206
  id: "participantes",
@@ -1206,6 +1375,8 @@ var domains = [
1206
1375
  mqdDomain,
1207
1376
  webhookDomain,
1208
1377
  segurancaDomain,
1378
+ requisitosNaoFuncionaisDomain,
1379
+ limitesPorEndpointDomain,
1209
1380
  participantesDomain,
1210
1381
  portalDomain
1211
1382
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opf-br-mcp",
3
- "version": "0.9.3",
3
+ "version": "1.0.0",
4
4
  "description": "MCP server local com as regras do Open Finance Brasil (PCM additionalInfo, specs OpenAPI de Pagamentos)",
5
5
  "type": "module",
6
6
  "license": "MIT",