senado-br-mcp 3.6.0 → 3.9.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.
- package/README.md +30 -11
- package/README.pt-BR.md +70 -12
- package/dist/app-surface.js +8 -1
- package/dist/call-shape.js +168 -0
- package/dist/data/estrutura-organizacional.js +26 -32
- package/dist/discovery.js +90 -0
- package/dist/instrument.js +225 -8
- package/dist/landing.js +63 -4
- package/dist/legal.js +9 -1
- package/dist/resources.js +6 -5
- package/dist/server.js +31 -1
- package/dist/throttle/upstream.js +21 -3
- package/dist/tool-titles.js +5 -1
- package/dist/tools/comissoes.js +85 -17
- package/dist/tools/deep-research.js +296 -0
- package/dist/tools/materias.js +17 -0
- package/dist/tools/resolver-reuniao.js +64 -0
- package/dist/tools/senadores.js +90 -20
- package/dist/utils/estatisticas.js +23 -6
- package/dist/utils/provenance.js +12 -0
- package/dist/version.js +1 -1
- package/package.json +13 -9
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|

|
|
5
|
-

|
|
6
6
|
[](https://github.com/SidneyBissoli/senado-br-mcp-cloudflare/actions/workflows/ci.yml)
|
|
7
7
|
[](https://registry.modelcontextprotocol.io)
|
|
8
8
|
[](https://lobehub.com/mcp/sidneybissoli-senado-br-mcp-cloudflare)
|
|
9
|
-
[](https://lightnow.ai/servers/io.github.SidneyBissoli/senado-br-mcp-cloudflare)
|
|
10
10
|
[](https://glama.ai/mcp/servers/SidneyBissoli/senado-br-mcp-cloudflare)
|
|
11
11
|
[](https://github.com/SidneyBissoli/senado-br-mcp-cloudflare)
|
|
12
12
|
[](https://github.com/sponsors/SidneyBissoli)
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
A **public, hosted** MCP server that gives AI assistants live, structured access to **Brazilian Senate open data** — **no installation, no account, no API key**. Point your MCP client at the hosted endpoint and start asking about senators, bills, votes, expenses, and more. It runs on Cloudflare Workers over Streamable HTTP.
|
|
19
19
|
|
|
20
|
-
It exposes **
|
|
20
|
+
It exposes **69 tools**, **4 prompts**, and **5 resources** across two domains:
|
|
21
21
|
|
|
22
22
|
- **Legislative** — senators; bills and their tramitation; votes; committees; plenary sessions, results and presidential vetoes; party-bloc voting orientation; speeches and stenographic transcripts; blocs and leadership; federal legislation; and citizen participation via the e-Cidadania portal.
|
|
23
23
|
- **Administrative** — CEAPS parliamentary-quota expenses; housing allowance; civil servants and payroll; overtime; interns; procurement contracts and biddings; outsourced staff; petty-cash funds; and budget execution.
|
|
@@ -68,6 +68,16 @@ Public legal URLs for app review:
|
|
|
68
68
|
- Privacy policy: `https://senado.sidneybissoli.com/privacy`
|
|
69
69
|
- Terms of use: `https://senado.sidneybissoli.com/terms`
|
|
70
70
|
|
|
71
|
+
### ChatGPT (Deep Research)
|
|
72
|
+
|
|
73
|
+
ChatGPT deep research (and company knowledge, and research workflows over the Responses API) only uses an MCP server that exposes exactly `search` and `fetch` — this server does, on top of the `senado_*` tools, on the full `/mcp` surface (not on the curated app profile). Point the connector at the hosted endpoint, no key required:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
https://senado.sidneybissoli.com/mcp
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`search` ranks the query against the senators in office and the active committees of the Senate and the National Congress and returns `{ id, title, url }` (`sen:<código>` / `com:<código>`); `fetch` returns the document as readable Markdown — the senator's biography and mandates, or the committee's summary and board — with the canonical public page (the senator's profile on www25.senado.leg.br or the committee page on legis.senado.leg.br), which is what ChatGPT cites. Both carry the same provenance block as every other tool, in `structuredContent` and `_meta` (the text channel is the contract's JSON). In ChatGPT's developer mode (Settings → Security and login → Developer mode) any tool is callable — the `senado_*` tools remain the ones to use for data.
|
|
80
|
+
|
|
71
81
|
### Install (any client)
|
|
72
82
|
|
|
73
83
|
For clients that launch MCP servers as a command — and for one-command setup — use the
|
|
@@ -87,7 +97,7 @@ own instance** — it is **not** required to use this public server.
|
|
|
87
97
|
|
|
88
98
|
Prefer not to route queries through a third-party host (e.g. a newsroom policy)? The **same server**
|
|
89
99
|
also runs as a **local stdio process** that talks **directly to the official government APIs** — same
|
|
90
|
-
|
|
100
|
+
69 tools, same provenance envelope, no Cloudflare in the loop. This is the npm/stdio channel, published
|
|
91
101
|
as [`senado-br-mcp`](https://www.npmjs.com/package/senado-br-mcp).
|
|
92
102
|
|
|
93
103
|
Point a command-based client (Claude Desktop/Code, etc.) at the package — npm fetches and runs it,
|
|
@@ -125,7 +135,7 @@ list/corpus tools: without D1 they fall back to a live scrape of the ~5 REST hig
|
|
|
125
135
|
|
|
126
136
|
This repo bundles a Claude [Agent Skill](https://platform.claude.com/docs/en/docs/agents-and-tools/agent-skills/overview)
|
|
127
137
|
at [`.claude/skills/senado-br/`](.claude/skills/senado-br/SKILL.md) that teaches Claude **when** to reach for
|
|
128
|
-
this server and **how** to use its
|
|
138
|
+
this server and **how** to use its 69 tools well — a themed tool map, common question→tool playbooks, the
|
|
129
139
|
provenance contract, and gotchas (dates, the `codigoMateria` bridge, e-Cidadania's open-set listing, pagination).
|
|
130
140
|
It points back to the server's own `senado://catalogo` / `senado://guia` resources rather than duplicating them.
|
|
131
141
|
|
|
@@ -138,7 +148,7 @@ Claude Code auto-discovers it when you work in this repo. To use it elsewhere, c
|
|
|
138
148
|
- **Runtime:** Cloudflare Workers (ESM)
|
|
139
149
|
- **Transport:** Streamable HTTP (MCP spec 2025-03-26) via `createMcpHandler` from `agents/mcp`
|
|
140
150
|
- **Protocol:** MCP over JSON-RPC — `/mcp` handles the full public server; `/mcp/openai-app-v2` exposes a curated 27-tool profile plus a shared MCP Apps widget for OpenAI app review/submission (`/mcp/openai-app` remains as a legacy alias)
|
|
141
|
-
- **SDK:** `@modelcontextprotocol/
|
|
151
|
+
- **SDK:** `@modelcontextprotocol/server` 2.x (per-request McpServer instances; the v1 `@modelcontextprotocol/sdk` remains only as a peer of `agents`, dev-time)
|
|
142
152
|
- **Validation:** Zod schemas for all tool inputs
|
|
143
153
|
- **Caching:** 2-layer (L0 memory + L1 Cache API) with SHA-256 keying
|
|
144
154
|
- **e-Cidadania store:** D1 database refreshed by a Cron Trigger (every 2h) — list tools read from D1 with a live-scrape fallback and a staleness flag; detail tools stay live with write-through (see [e-Cidadania](#e-cidadania-d1-backed-cron-refreshed))
|
|
@@ -156,7 +166,7 @@ Claude Code auto-discovers it when you work in this repo. To use it elsewhere, c
|
|
|
156
166
|
|
|
157
167
|
### Prerequisites
|
|
158
168
|
|
|
159
|
-
- Node.js
|
|
169
|
+
- Node.js 22+ (`engines.node`)
|
|
160
170
|
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) v4+
|
|
161
171
|
- Cloudflare account
|
|
162
172
|
|
|
@@ -535,7 +545,7 @@ In addition to the `provenance` envelope, `structuredContent` carries a top-leve
|
|
|
535
545
|
|
|
536
546
|
`retrieved_at` fidelity is provided by the cache layer (`cachedFetchWithMeta`), which persists the fetch timestamp alongside the value, so it reflects the real upstream extraction even on a cache hit. Two exceptions report an honest live timestamp instead: the e-Cidadania **list** tools (read from D1) use the corpus's `lastScrapedAt` — the true age of the stored data — while e-Cidadania **detail** tools, scraped live, use the fetch time and a level-3 canonical item URL. The only path that falls back to the build-time default is the in-code static reference catalog (`senado_tabelas_referencia` `tipos-materia`), which has no upstream extraction instant.
|
|
537
547
|
|
|
538
|
-
Coverage is **universal**: all
|
|
548
|
+
Coverage is **universal**: all 69 tools carry the envelope — the 67 `senado_*` tools via `resultWithProvenance(` (verify with `grep -c 'resultWithProvenance(' src/tools/*.ts`) and the two Deep Research tools via `provenanceExtras` (same block, in `structuredContent`/`_meta`, since their text channel is the contract's JSON). The **⊕** marks in the inventory below denote the original pilot tools (votes, bills, processes); the envelope now extends to every tool, so the marks are historical.
|
|
539
549
|
|
|
540
550
|
## Citable dataset (e-Cidadania participation)
|
|
541
551
|
|
|
@@ -739,7 +749,16 @@ Reads a bundled snapshot of the Senate's organizational tree (crawled from the i
|
|
|
739
749
|
|------|-------------|
|
|
740
750
|
| `senado_estrutura_organizacional` | Organizational chart (organograma) resolved for a `unidade` (sigla like `DGER` or name): returns its `caminho` (ancestors) and every subordinate unit (`subordinadas[]` — secretarias, coordenações, serviços, núcleos — with `nivel`). Pairs with `senado_servidores`'s `subordinadasA` filter, which counts/lists all servants under a whole directorate (a servant sits in a leaf serviço, so filtering `lotacao` by the parent sigla returns 0). |
|
|
741
751
|
|
|
742
|
-
|
|
752
|
+
### Group U — Deep Research (2 tools)
|
|
753
|
+
|
|
754
|
+
The [OpenAI Deep Research contract](#chatgpt-deep-research): the only two tools without the `senado_` prefix, because the names are fixed by the contract. Registered through the same shim as the others (read-only annotations, permissive `outputSchema`, per-tool telemetry) and served on `/mcp` only — the curated ChatGPT app profile does not include them. The index (senators in office + active committees, ~300 documents) is built on first use from the same two list endpoints the `senado_listar_*` tools read, and kept for 24 h.
|
|
755
|
+
|
|
756
|
+
| Tool | Description |
|
|
757
|
+
|------|-------------|
|
|
758
|
+
| `search` | Ranks the query (natural language or keywords, pt/en, accent-insensitive) against senators in office and active committees; returns up to 10 `{ id, title, url }` — `sen:<código>` with the public profile URL, `com:<código>` with the public committee page. Provenance of both lists in `structuredContent`/`_meta`. |
|
|
759
|
+
| `fetch` | Returns the document for an id from `search` as `{ id, title, text, url, metadata }`: the senator's biography and mandates (same read as `senado_obter_senador`) or the committee's summary and board (same read as `senado_obter_comissao`), as Markdown, with that read's provenance. Unknown id → error. |
|
|
760
|
+
|
|
761
|
+
**Total: 69 tools**
|
|
743
762
|
|
|
744
763
|
### Prompts (4)
|
|
745
764
|
|
|
@@ -759,7 +778,7 @@ Static context documents/tables (MCP `resources` capability), defined in `src/re
|
|
|
759
778
|
| URI | Type | Content |
|
|
760
779
|
| --- | --- | --- |
|
|
761
780
|
| `senado://guia` | markdown | Visão geral e qual ferramenta usar por objetivo. |
|
|
762
|
-
| `senado://catalogo` | markdown | As
|
|
781
|
+
| `senado://catalogo` | markdown | As 69 ferramentas agrupadas por domínio. |
|
|
763
782
|
| `senado://glossario` | markdown | Siglas e termos do Senado (PEC, CEAPS, CCJ, RCN…). |
|
|
764
783
|
| `senado://tabelas/tipos-materia` | json | Tipos de proposição (sigla/nome/descrição). |
|
|
765
784
|
| `senado://tabelas/ufs` | json | As 27 unidades federativas. |
|
|
@@ -848,7 +867,7 @@ tests/ # Vitest unit tests mirroring src/ (parsers, cache, th
|
|
|
848
867
|
## Connecting MCP Clients
|
|
849
868
|
|
|
850
869
|
This is a **remote** server (Streamable HTTP, no install, open access) — point any MCP client at
|
|
851
|
-
`https://senado.sidneybissoli.com/mcp`. Besides
|
|
870
|
+
`https://senado.sidneybissoli.com/mcp`. Besides 69 tools, it exposes **prompts** (ready-made pt-BR
|
|
852
871
|
workflows: `senado_gastos_senador`, `senado_tramitacao_materia`, `senado_votos_senador`,
|
|
853
872
|
`senado_panorama_ecidadania`) and **resources** (`senado://guia`, `senado://catalogo`,
|
|
854
873
|
`senado://glossario`, `senado://tabelas/tipos-materia`, `senado://tabelas/ufs`).
|
package/README.pt-BR.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|

|
|
5
|
-

|
|
6
6
|
[](https://github.com/SidneyBissoli/senado-br-mcp-cloudflare/actions/workflows/ci.yml)
|
|
7
7
|
[](https://registry.modelcontextprotocol.io)
|
|
8
8
|
[](https://lobehub.com/mcp/sidneybissoli-senado-br-mcp-cloudflare)
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
Um servidor MCP **público e hospedado** que dá aos assistentes de IA acesso ao vivo e estruturado aos **dados abertos do Senado Federal do Brasil** — **sem instalação, sem conta, sem chave de API**. Aponte o seu cliente MCP para o endpoint hospedado e comece a perguntar sobre senadores, matérias, votações, despesas e muito mais. Roda em Cloudflare Workers via Streamable HTTP.
|
|
18
18
|
|
|
19
|
-
Expõe **
|
|
19
|
+
Expõe **69 ferramentas**, **4 prompts** e **5 recursos** em dois domínios:
|
|
20
20
|
|
|
21
21
|
- **Legislativo** — senadores; matérias e sua tramitação; votações; comissões; sessões plenárias, resultados e vetos presidenciais; orientação de bancada nas votações; discursos e notas taquigráficas; blocos e lideranças; legislação federal; e participação cidadã pelo portal e-Cidadania.
|
|
22
22
|
- **Administrativo** — despesas da cota parlamentar (CEAPS); auxílio-moradia; servidores e remunerações; horas extras; estagiários; contratos e licitações; terceirizados; suprimento de fundos; e execução orçamentária.
|
|
@@ -50,7 +50,7 @@ Para submissao e revisao pelo OpenAI Apps SDK, o Worker tambem expoe uma superfi
|
|
|
50
50
|
https://senado.sidneybissoli.com/mcp/openai-app-v2
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
Esse endpoint preserva o servidor MCP publico completo em `/mcp`, mas limita a descoberta a
|
|
53
|
+
Esse endpoint preserva o servidor MCP publico completo em `/mcp`, mas limita a descoberta a 27
|
|
54
54
|
ferramentas de alto sinal, organizadas por intencao de usuario, para uso como app do ChatGPT.
|
|
55
55
|
`/mcp/openai-app` continua disponivel como alias legado, mas novas configuracoes do app no ChatGPT
|
|
56
56
|
devem usar `/mcp/openai-app-v2` para forcar a leitura do schema atual das ferramentas. As ferramentas
|
|
@@ -58,7 +58,7 @@ continuam chamando os mesmos handlers e retornando o mesmo envelope de provenien
|
|
|
58
58
|
anunciada fica mais estreita. Qualquer listagem de ChatGPT App deve apresentar isto como um app
|
|
59
59
|
independente de pesquisa em dados abertos, nao como conector oficial do Senado, da OpenAI ou do ChatGPT.
|
|
60
60
|
|
|
61
|
-
Para ChatGPT Apps, essas
|
|
61
|
+
Para ChatGPT Apps, essas 27 ferramentas tambem anunciam um template UI compartilhado do MCP Apps em
|
|
62
62
|
`ui://senado-br-mcp/openai-app-dashboard-v2.html`. O widget e autocontido e renderiza o
|
|
63
63
|
`structuredContent` retornado como painel compacto com metricas, registros principais e fonte/proveniencia,
|
|
64
64
|
sem adicionar outra ferramenta de dados visivel ao modelo.
|
|
@@ -68,6 +68,16 @@ URLs publicas legais para revisao do app:
|
|
|
68
68
|
- Politica de privacidade: `https://senado.sidneybissoli.com/privacy`
|
|
69
69
|
- Termos de uso: `https://senado.sidneybissoli.com/terms`
|
|
70
70
|
|
|
71
|
+
### ChatGPT (Deep Research)
|
|
72
|
+
|
|
73
|
+
O deep research do ChatGPT (e o company knowledge, e os workflows de pesquisa da API Responses) só usa um servidor MCP que exponha exatamente `search` e `fetch` — este servidor expõe, além das ferramentas `senado_*`, na superfície completa `/mcp` (não no perfil curado do app). Aponte o conector para o endpoint hospedado, sem chave:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
https://senado.sidneybissoli.com/mcp
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`search` ranqueia a consulta contra os senadores em exercício e as comissões ativas do Senado e do Congresso Nacional e devolve `{ id, title, url }` (`sen:<código>` / `com:<código>`); `fetch` devolve o documento em Markdown legível — a biografia e os mandatos do senador, ou o resumo e a mesa da comissão — com a página pública canônica (o perfil do senador em www25.senado.leg.br ou a página da comissão em legis.senado.leg.br), que é o que o ChatGPT cita. As duas carregam o mesmo bloco de proveniência das outras ferramentas, em `structuredContent` e `_meta` (o canal de texto é o JSON do contrato). No modo desenvolvedor do ChatGPT (Settings → Security and login → Developer mode) qualquer ferramenta é chamável — as `senado_*` continuam sendo as ferramentas para dados.
|
|
80
|
+
|
|
71
81
|
### Instalação (qualquer cliente)
|
|
72
82
|
|
|
73
83
|
Para clientes que iniciam servidores MCP como um comando — e para uma configuração de um único comando — use a
|
|
@@ -87,7 +97,7 @@ própria instância** — **não** é necessário para usar este servidor públi
|
|
|
87
97
|
|
|
88
98
|
Prefere não rotear suas consultas por um servidor de terceiros (ex.: política de uma redação)? O **mesmo
|
|
89
99
|
servidor** também roda como um **processo local stdio** que bate **direto nas APIs oficiais do governo** —
|
|
90
|
-
as mesmas
|
|
100
|
+
as mesmas 69 ferramentas, o mesmo envelope de proveniência, sem Cloudflare no caminho. É o canal npm/stdio,
|
|
91
101
|
publicado como [`senado-br-mcp`](https://www.npmjs.com/package/senado-br-mcp).
|
|
92
102
|
|
|
93
103
|
Aponte um cliente baseado em comando (Claude Desktop/Code, etc.) para o pacote — o npm baixa e executa,
|
|
@@ -125,7 +135,7 @@ Os logs vão para o **stderr** — o stdout carrega apenas o fluxo do protocolo
|
|
|
125
135
|
|
|
126
136
|
Este repositório inclui uma [Agent Skill](https://platform.claude.com/docs/en/docs/agents-and-tools/agent-skills/overview)
|
|
127
137
|
do Claude em [`.claude/skills/senado-br/`](.claude/skills/senado-br/SKILL.md) que ensina o Claude **quando**
|
|
128
|
-
recorrer a este servidor e **como** usar bem as
|
|
138
|
+
recorrer a este servidor e **como** usar bem as 69 ferramentas — um mapa de ferramentas por tema, playbooks de
|
|
129
139
|
pergunta→ferramenta, o contrato de proveniência e os gotchas (datas, a ponte `codigoMateria`, a listagem
|
|
130
140
|
só-abertas do e-Cidadania, paginação). Ela aponta para os recursos `senado://catalogo` / `senado://guia` do
|
|
131
141
|
próprio servidor em vez de duplicá-los.
|
|
@@ -138,8 +148,8 @@ O Claude Code a descobre automaticamente ao trabalhar neste repositório. Para u
|
|
|
138
148
|
|
|
139
149
|
- **Runtime:** Cloudflare Workers (ESM)
|
|
140
150
|
- **Transporte:** Streamable HTTP (spec MCP 2025-03-26) via `createMcpHandler` de `agents/mcp`
|
|
141
|
-
- **Protocolo:** MCP sobre JSON-RPC — `/mcp` serve o catalogo publico completo; `/mcp/openai-app-v2` expoe um perfil curado de
|
|
142
|
-
- **SDK:** `@modelcontextprotocol/
|
|
151
|
+
- **Protocolo:** MCP sobre JSON-RPC — `/mcp` serve o catalogo publico completo; `/mcp/openai-app-v2` expoe um perfil curado de 27 ferramentas mais um widget MCP Apps compartilhado para revisao/submissao como app OpenAI (`/mcp/openai-app` permanece como alias legado)
|
|
152
|
+
- **SDK:** `@modelcontextprotocol/server` 2.x (instâncias de McpServer por requisição; o `@modelcontextprotocol/sdk` v1 fica só como peer do `agents`, em tempo de dev)
|
|
143
153
|
- **Validação:** schemas Zod para todas as entradas das ferramentas
|
|
144
154
|
- **Cache:** 2 camadas (L0 memória + L1 Cache API) com chaveamento SHA-256
|
|
145
155
|
- **Store do e-Cidadania:** banco D1 atualizado por um Cron Trigger (a cada 2h) — as ferramentas de listagem leem do D1 com fallback de scraping ao vivo e flag de desatualização; as de detalhe ficam ao vivo com write-through (veja [e-Cidadania](#e-cidadania-em-d1-atualizado-por-cron))
|
|
@@ -156,7 +166,7 @@ O Claude Code a descobre automaticamente ao trabalhar neste repositório. Para u
|
|
|
156
166
|
|
|
157
167
|
### Pré-requisitos
|
|
158
168
|
|
|
159
|
-
- Node.js
|
|
169
|
+
- Node.js 22+ (`engines.node`)
|
|
160
170
|
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) v4+
|
|
161
171
|
- Conta Cloudflare
|
|
162
172
|
|
|
@@ -507,6 +517,37 @@ Campos do bloco `concise` (por resposta — uma ferramenta, uma fonte): `source`
|
|
|
507
517
|
|
|
508
518
|
A cobertura abrange as quatro fontes upstream: **Dados Abertos Legislativo** (`legis.senado.leg.br`), **Dados Abertos Administrativo** (`adm.senado.gov.br`), **Execução Orçamentária** (feed Arquimedes/Financeiro em `senado.gov.br`) e **Portal e-Cidadania** (`www12.senado.leg.br/ecidadania`). Nas listas do e-Cidadania (lidas do D1) o `retrieved_at` é o `lastScrapedAt` do corpus — a idade real do dado; nos detalhes (raspados ao vivo) é o instante da chamada, com a URL canônica do item. A fidelidade do `retrieved_at` vem da camada de cache (`cachedFetchWithMeta`), que persiste o timestamp junto ao valor.
|
|
509
519
|
|
|
520
|
+
## Conjunto de dados citável (participação no e-Cidadania)
|
|
521
|
+
|
|
522
|
+
Além do servidor ao vivo, este projeto publica um **conjunto de dados congelado, versionado e citável** da camada de participação do e-Cidadania (consultas públicas, ideias legislativas, eventos interativos + seus comentários, votos históricos por estado) — a camada que o pacote R `congressbr` nunca cobriu. Cada valor carrega um envelope de procedência por campo (`{ value, sourceEndpoint, sourceField, retrievedAt, license, schemaVersion }`); a licença dos dados (Dados Abertos do Senado Federal) é mantida **separada** da licença do código (MIT).
|
|
523
|
+
|
|
524
|
+
- **Como citar** — [`CITATION.cff`](CITATION.cff) (conjunto de dados; cite o DOI da versão do snapshot que você usou, e o DOI de conceito para o conjunto através das versões).
|
|
525
|
+
- **O que vai em cada release** — [`CHANGELOG-dataset.md`](CHANGELOG-dataset.md) (cumulativo, só acrescenta; amarra cada release ao seu `schemaVersion`).
|
|
526
|
+
- **Dicionário de variáveis e procedência de campo** — [`docs/dataset-dictionary.md`](docs/dataset-dictionary.md) (gerado de `src/dataset/schema.ts`, a fonte única de verdade).
|
|
527
|
+
- **Licença dos dados** — [`LICENSE-DATA.md`](LICENSE-DATA.md).
|
|
528
|
+
- **Como cortar uma release** (congelar → checksums → GitHub Release → DOI no Zenodo) — [`docs/release-runbook.md`](docs/release-runbook.md); a maquinaria está em `src/dataset/`, `scripts/build-dataset/` e `.github/workflows/release-dataset.yml`.
|
|
529
|
+
|
|
530
|
+
### Inventário — schema v2 (`schemaVersion` `2.0.0`)
|
|
531
|
+
|
|
532
|
+
Cada release entrega um recurso NDJSON por entidade (um `HarmonizedRecord` por linha: identidade + um envelope de procedência por campo), mais um manifesto `datapackage.json` e uma cópia do dicionário. Cinco recursos:
|
|
533
|
+
|
|
534
|
+
| Recurso (`*.ndjson`) | Grão | Variáveis principais | Fonte(s) |
|
|
535
|
+
|---|---|---|---|
|
|
536
|
+
| `consultas` | 1 consulta pública (matéria) | `materia`, `ementa`, `votosSim`/`votosNao`/`totalVotos`, `percentual*`, **`autoria`ⁿ**, **`relator`ⁿ**, `status`, `url`, `firstSeenAt` | listagem `pesquisamateria` + **detalhe** (`visualizacaomateria`)ⁿ + `/processo?tramitando=S` para o status |
|
|
537
|
+
| `ideias` | 1 ideia legislativa (~113,7 mil) | `titulo`, `apoios`, `status`, **`dataPublicacao`ⁿ**, **`autorUf`ⁿ**, **`descricao`ⁿ**, **`plConvertido`ⁿ**, `url`, `firstSeenAt` | listagem `pesquisaideia` + **detalhe** (`visualizacaoideia`) por um backfill retomávelⁿ |
|
|
538
|
+
| `eventos` | 1 evento interativo (audiência) | `titulo`, **`data`ᶜ**, **`hora`ᶜ**, `comissao`, **`comissaoNomeCompleto`ⁿ**, **`local`ⁿ**, **`descricao`ⁿ**, **`pauta`ⁿ**, **`convidados`ⁿ**, **`videoUrl`ⁿ**, **`comentarios`ᶜ**, `status`, `url`, `firstSeenAt` | listagem `principalaudiencia` + **detalhe** (`visualizacaoaudiencia`)ⁿ + **fragmento AJAX de comentários**ᶜ |
|
|
539
|
+
| **`eventos_comentarios`ⁿ** | 1 comentário (nível de comentário) | `eventoId`, `comentarioId`, `uf`, `texto`, `data`, `hora`, `momentoVideoUrl`, `convidadoAssociado` | fragmento AJAX `ajaxcolecaocomentarioaudiencia?audienciaId=` |
|
|
540
|
+
| `consultas_votos` | 1 matéria (acervo histórico) | `materia`, `ementa`, `autoria`, `votosSim`/`votosNao`/`totalVotos`, `votosPorUf`, `status`, `url`, `referencePeriod` | CSV do Arquimedes `Proposições-com-votos.csv` (reingerido semanalmente) |
|
|
541
|
+
|
|
542
|
+
**ⁿ = novo ou reaberto na v2 · ᶜ = fonte corrigida para a canônica na v2.** O que a v2 (`2.0.0`) mudou — a ingestão saiu de **só listagem** para **listagem + detalhe (+ comentários por AJAX nos eventos)**:
|
|
543
|
+
|
|
544
|
+
- **Eventos corrigidos e enriquecidos.** `data`/`hora` agora vêm da página de detalhe (a canônica — o [estudo A3](docs/estudo-a3-reconciliacao-eventos.md) achou a listagem divergente em 57% dos casos no `hora`), mais seis campos novos de detalhe (`comissaoNomeCompleto`, `local`, `descricao`, `pauta`, `convidados`, `videoUrl`). `comentarios` passou a ser a **contagem canônica do AJAX** (a contagem da listagem era `0` espúrio em 82% dos eventos, capturando só ~6,7% do engajamento).
|
|
545
|
+
- **Recurso novo em nível de comentário**, `eventos_comentarios` — uma linha por comentário de audiência, o sinal de participação que ninguém mais publica versionado.
|
|
546
|
+
- **Campos que só existem no detalhe foram reabertos** para `ideias` (`dataPublicacao`, `autorUf`, `descricao`, `plConvertido`) e `consultas` (`autoria`, `relator`) — antes sempre `null` por decisão de projeto.
|
|
547
|
+
- **Postura de privacidade pela origem do dado.** Conteúdo de cidadão (comentários de audiência, autores de ideia) guarda **apenas a UF — nunca o nome**, descartado já no parser; agente público (autoria/relatoria de consulta, convidados de evento) guarda o nome (público pela função). O racional campo a campo está em `docs/schema-v2-inventario.md` (alvo aprovado).
|
|
548
|
+
|
|
549
|
+
O NDJSON congelado **não** é commitado (é construído sob demanda a partir do corpus soberano no D1); uma release marcada `dataset-v*` anexa o tarball + `SHA256SUMS` + `release.json` e os arquiva no Zenodo.
|
|
550
|
+
|
|
510
551
|
## Inventário de ferramentas
|
|
511
552
|
|
|
512
553
|
### Grupo H — Referência/Metadados (1 ferramenta)
|
|
@@ -670,7 +711,24 @@ A cobertura abrange as quatro fontes upstream: **Dados Abertos Legislativo** (`l
|
|
|
670
711
|
|------|-------------|
|
|
671
712
|
| `senado_execucao_orcamentaria` | Execução orçamentária desde 2013 (dotação, empenhado/liquidado/pago) e receitas próprias desde 2012 (previsto vs. arrecadado) — agregadas por ano, ação, grupo de despesa, fonte ou origem de receita; `estatisticas=true` devolve a distribuição do conjunto (min/máx/média/mediana/percentis) + ranking top/bottom, ou um ranking de grupos por soma do `campo` via `agruparPor`, com `campo` (padrão pago / arrecadada) e `topN` |
|
|
672
713
|
|
|
673
|
-
|
|
714
|
+
### Grupo T — Estrutura Organizacional (1 ferramenta)
|
|
715
|
+
|
|
716
|
+
Lê um retrato embutido da árvore organizacional do Senado (varrida do portal institucional até o nível de serviço por `npm run ingest:estrutura`), porque a API de dados abertos só publica unidades até Secretaria e nunca liga uma unidade-folha ao pai dela. Unidades que o portal lista apenas por nome, sem página própria (os núcleos da CONLEG/CONORF), são capturadas como nós sintéticos a partir da listagem indentada da página. Órgãos do Congresso onde o cadastro de servidores registra lotações (CMO, CPCMS, CMMC) vêm de um complemento curado (`src/estrutura/complemento-cn.ts`, fonte pública: congressonacional.leg.br) sob uma raiz **Congresso Nacional** (`CN`) separada — nunca sob a árvore do Senado, de modo que `subordinadasA: "DGER"` os exclui e `subordinadasA: "CN"` os conta. Servidores registrados sob as pseudo-unidades situacionais "Servidores Afastados/em Trânsito - SF" são reportados à parte, como `afastadosOuEmTransito`, por `senado_servidores`.
|
|
717
|
+
|
|
718
|
+
| Ferramenta | Descrição |
|
|
719
|
+
|------|-------------|
|
|
720
|
+
| `senado_estrutura_organizacional` | Organograma resolvido para uma `unidade` (sigla como `DGER` ou nome): devolve o `caminho` (ancestrais) e todas as unidades subordinadas (`subordinadas[]` — secretarias, coordenações, serviços, núcleos — com `nivel`). Faz par com o filtro `subordinadasA` de `senado_servidores`, que conta/lista todos os servidores sob uma diretoria inteira (o servidor fica num serviço-folha, então filtrar `lotacao` pela sigla do pai devolve 0). |
|
|
721
|
+
|
|
722
|
+
### Grupo U — Deep Research (2 ferramentas)
|
|
723
|
+
|
|
724
|
+
O [contrato Deep Research da OpenAI](#chatgpt-deep-research): as únicas duas ferramentas sem o prefixo `senado_`, porque os nomes são fixados pelo contrato. Registradas pelo mesmo shim das outras (annotations somente-leitura, `outputSchema` permissivo, telemetria por ferramenta) e servidas só em `/mcp` — o perfil curado do app do ChatGPT não as inclui. O índice (senadores em exercício + comissões ativas, ~300 documentos) é construído no primeiro uso a partir dos mesmos dois endpoints de lista que as ferramentas `senado_listar_*` leem, e mantido por 24 h.
|
|
725
|
+
|
|
726
|
+
| Ferramenta | Descrição |
|
|
727
|
+
|------|-------------|
|
|
728
|
+
| `search` | Ranqueia a consulta (linguagem natural ou palavras-chave, pt/en, sem distinguir acentos) contra senadores em exercício e comissões ativas; devolve até 10 `{ id, title, url }` — `sen:<código>` com a URL do perfil público, `com:<código>` com a página pública da comissão. Proveniência das duas listas em `structuredContent`/`_meta`. |
|
|
729
|
+
| `fetch` | Devolve o documento de um id vindo de `search` como `{ id, title, text, url, metadata }`: a biografia e os mandatos do senador (a mesma leitura de `senado_obter_senador`) ou o resumo e a mesa da comissão (a mesma leitura de `senado_obter_comissao`), em Markdown, com a proveniência dessa leitura. Id desconhecido → erro. |
|
|
730
|
+
|
|
731
|
+
**Total: 69 ferramentas**
|
|
674
732
|
|
|
675
733
|
### Prompts (4)
|
|
676
734
|
|
|
@@ -690,7 +748,7 @@ Documentos/tabelas de contexto estáticos (capacidade MCP `resources`), definido
|
|
|
690
748
|
| URI | Tipo | Conteúdo |
|
|
691
749
|
| --- | --- | --- |
|
|
692
750
|
| `senado://guia` | markdown | Visão geral e qual ferramenta usar por objetivo. |
|
|
693
|
-
| `senado://catalogo` | markdown | As
|
|
751
|
+
| `senado://catalogo` | markdown | As 69 ferramentas agrupadas por domínio. |
|
|
694
752
|
| `senado://glossario` | markdown | Siglas e termos do Senado (PEC, CEAPS, CCJ, RCN…). |
|
|
695
753
|
| `senado://tabelas/tipos-materia` | json | Tipos de proposição (sigla/nome/descrição). |
|
|
696
754
|
| `senado://tabelas/ufs` | json | As 27 unidades federativas. |
|
|
@@ -774,7 +832,7 @@ tests/ # Testes unitários Vitest espelhando src/ (parsers, c
|
|
|
774
832
|
## Conectando clientes MCP
|
|
775
833
|
|
|
776
834
|
Este é um servidor **remoto** (Streamable HTTP, sem instalação, acesso aberto) — aponte qualquer cliente MCP para
|
|
777
|
-
`https://senado.sidneybissoli.com/mcp`. Além das
|
|
835
|
+
`https://senado.sidneybissoli.com/mcp`. Além das 69 ferramentas, ele expõe **prompts** (workflows prontos em pt-BR:
|
|
778
836
|
`senado_gastos_senador`, `senado_tramitacao_materia`, `senado_votos_senador`,
|
|
779
837
|
`senado_panorama_ecidadania`) e **recursos** (`senado://guia`, `senado://catalogo`,
|
|
780
838
|
`senado://glossario`, `senado://tabelas/tipos-materia`, `senado://tabelas/ufs`).
|
package/dist/app-surface.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export const DEFAULT_MCP_ROUTE = "/mcp";
|
|
2
2
|
export const OPENAI_APP_LEGACY_MCP_ROUTE = "/mcp/openai-app";
|
|
3
3
|
export const OPENAI_APP_MCP_ROUTE = "/mcp/openai-app-v2";
|
|
4
|
+
// Rota privada do dono: mesma superficie COMPLETA da rota publica, outro
|
|
5
|
+
// endereco, e a telemetria a marca como uso proprio (ver src/instrument.ts).
|
|
6
|
+
// Precisa ser reconhecida aqui, senao handlerRouteForPath cai no fallback e
|
|
7
|
+
// o handler e montado numa rota diferente da que a requisicao pediu.
|
|
8
|
+
export const SELF_MCP_ROUTE = "/mcp/uso-proprio";
|
|
4
9
|
export const OPENAI_APP_WIDGET_URI = "ui://senado-br-mcp/openai-app-dashboard-v2.html";
|
|
5
10
|
export const OPENAI_APP_WIDGET_DOMAIN = "https://senado.sidneybissoli.com";
|
|
6
11
|
const OPENAI_APP_MCP_ROUTES = new Set([OPENAI_APP_LEGACY_MCP_ROUTE, OPENAI_APP_MCP_ROUTE]);
|
|
@@ -89,7 +94,9 @@ export function mcpRouteForProfile(profile) {
|
|
|
89
94
|
}
|
|
90
95
|
export function handlerRouteForPath(pathname, profile) {
|
|
91
96
|
const normalized = normalizeMcpRoute(pathname);
|
|
92
|
-
if (OPENAI_APP_MCP_ROUTES.has(normalized) ||
|
|
97
|
+
if (OPENAI_APP_MCP_ROUTES.has(normalized) ||
|
|
98
|
+
normalized === DEFAULT_MCP_ROUTE ||
|
|
99
|
+
normalized === SELF_MCP_ROUTE) {
|
|
93
100
|
return pathname;
|
|
94
101
|
}
|
|
95
102
|
return mcpRouteForProfile(profile);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A FORMA da chamada, nunca o conteúdo dela.
|
|
3
|
+
*
|
|
4
|
+
* Por que existe. Em 10/09/2026 o painel do portfólio passou a medir erro por
|
|
5
|
+
* chamada e apontou cinco ferramentas quebradas, a pior delas
|
|
6
|
+
* `senado_reuniao_comissao`, com 74 erros em 74 chamadas. Diagnosticar custou
|
|
7
|
+
* vinte minutos de chamadas manuais porque a telemetria dizia QUE falhou e não
|
|
8
|
+
* DIZIA por quê — e a primeira hipótese (o agente escolheu a ferramenta errada,
|
|
9
|
+
* confundindo o singular com o plural `senado_reunioes_comissao`) estava
|
|
10
|
+
* ERRADA: erro de validação de esquema nem chega aqui, o SDK o responde antes
|
|
11
|
+
* do callback. As 74 falhas eram códigos bem-formados que não existem.
|
|
12
|
+
*
|
|
13
|
+
* Por que não gravar os argumentos. O dado que este servidor serve é público; a
|
|
14
|
+
* PERGUNTA não é. `senado_ceaps` com o id de um senador, repetido, é alguém
|
|
15
|
+
* investigando aquele senador, e o registro já guarda país e organização de
|
|
16
|
+
* rede. Parte dos parâmetros é texto livre (`palavraChave`), onde cabe qualquer
|
|
17
|
+
* coisa que a pessoa digitou. E ligar isso mudaria o que o serviço coleta sem
|
|
18
|
+
* mudar o que a página pública diz que ele coleta.
|
|
19
|
+
*
|
|
20
|
+
* O meio-termo: NOMES de parâmetro (que são o esquema publicado, não dado de
|
|
21
|
+
* ninguém) e a CLASSE do erro (vocabulário fechado, derivado da nossa própria
|
|
22
|
+
* mensagem). Isso separa "chamou sem o parâmetro obrigatório" de "chamou certo
|
|
23
|
+
* com um valor que não existe" — que é a bifurcação do conserto.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Classifica pela mensagem de erro, que é NOSSA. A ordem importa: "não
|
|
27
|
+
* encontrado" e "vazio" são mais específicos que "erro da fonte", e um 404
|
|
28
|
+
* casaria com os dois.
|
|
29
|
+
*
|
|
30
|
+
* O vocabulário foi ampliado depois de passar o classificador por TODAS as
|
|
31
|
+
* mensagens de erro do bcb e do senado (a varredura virou a guarda em
|
|
32
|
+
* `call-shape.test.ts`): 13 de 18 aqui e 12 de 18 lá caíam em `outro`, ou
|
|
33
|
+
* seja, a telemetria não responderia nada nestes dois servidores. As famílias
|
|
34
|
+
* que faltavam eram "Informe X ou Y", "só existe para", "desconhecido",
|
|
35
|
+
* "recusada", "não retornou dados" e "não publica" — nenhuma exótica; a versão
|
|
36
|
+
* inicial foi escrita a partir das mensagens do senado que eu já tinha lido.
|
|
37
|
+
*/
|
|
38
|
+
export function classifyError(message) {
|
|
39
|
+
const m = message.toLowerCase();
|
|
40
|
+
// A fronteira de palavra vai só no INÍCIO. Os padrões são RADICAIS
|
|
41
|
+
// ("vazi", "obrigatóri", "indisponív") justamente porque a flexão muda o
|
|
42
|
+
// fim: `\bvazi\b` não casa "vazia", e foi assim que "Resposta upstream
|
|
43
|
+
// vazia" caiu em `fonte` na primeira versão — a palavra que casava era
|
|
44
|
+
// "upstream". Ordem também importa: "não encontrado" é mais específico que
|
|
45
|
+
// "erro da fonte", e a mensagem real do senado tem sinal das duas famílias.
|
|
46
|
+
// "desconhecido"/"unknown" só é sinal de contrato quando qualifica um VALOR
|
|
47
|
+
// que o chamador passou ("Índice de preços desconhecido: X. Aceitos: ...",
|
|
48
|
+
// "Unknown dimension(s) for dataflow X"). "Erro desconhecido ao consultar o
|
|
49
|
+
// calendário do IBGE" e "Unknown error" são o oposto — são justamente o caso
|
|
50
|
+
// sem classe — e a primeira versão desta ampliação os classificava como
|
|
51
|
+
// contrato.
|
|
52
|
+
const valorDesconhecido = /\b(desconhecid|unknown)/.test(m) && !/\b(erro desconhecid|unknown error)/.test(m);
|
|
53
|
+
if (valorDesconhecido ||
|
|
54
|
+
/\b(obrigatóri|obrigatori|exige|requer|required|inválid|invalid|validation error|não aceita|nao aceita|no máximo|no maximo|só existe|so existe|recusad)/.test(m) ||
|
|
55
|
+
// Inglês, das mensagens do ilo, do uis e do medical. `empty query` fica
|
|
56
|
+
// AQUI e não em nao_encontrado: consulta vazia é parâmetro que falta. Era o
|
|
57
|
+
// radical `empty` solto que a classificava errado — ele existia para "empty
|
|
58
|
+
// response", que é outra coisa, e agora está escrito por extenso lá.
|
|
59
|
+
// "narrow it/the query" e "maximum N per call" são instruções ao chamador:
|
|
60
|
+
// a resposta não veio porque a chamada precisa mudar, que é contrato.
|
|
61
|
+
/\b(empty query|not part of|too broad|too many|maximum|narrow (it|the|your)|has no codelist|has no enumerated)/.test(m)) {
|
|
62
|
+
return "contrato";
|
|
63
|
+
}
|
|
64
|
+
if (/\b(não encontrad|nao encontrad|não existe|nao existe|does ?n[o']t exist|not.?found|inexistent|vazi|empty response|empty result|returned empty|sem registros|não retornou dados|nao retornou dados|não publica|nao publica|404)/.test(m) ||
|
|
65
|
+
// "Nenhum evento encontrado", "nenhuma reunião", "nenhum registro": a forma
|
|
66
|
+
// varia com o substantivo de cada servidor, então case pelo padrão.
|
|
67
|
+
/\bnenhum[ao]?s?\b[\s\S]{0,40}\b(encontrad|resultado|registro|dado)/.test(m)) {
|
|
68
|
+
return "nao_encontrado";
|
|
69
|
+
}
|
|
70
|
+
// "Informe X ou Y" é a mensagem canônica de parâmetro que falta — dez delas
|
|
71
|
+
// no senado, com e sem qualificador na frente ("Para por=senador, informe
|
|
72
|
+
// 'codigoSenador'"). Fica DEPOIS de "não encontrado" de propósito: é o sinal
|
|
73
|
+
// mais fraco dos dois, e um "informe um código válido" fechando uma mensagem
|
|
74
|
+
// de não encontrado não pode sequestrar a classe. Hoje nenhuma mensagem dos
|
|
75
|
+
// quatro servidores casa com as duas famílias — a ordem existe para a
|
|
76
|
+
// mensagem que alguém escrever amanhã.
|
|
77
|
+
if (/\binforme\b/.test(m))
|
|
78
|
+
return "contrato";
|
|
79
|
+
// `\b5\d\d\b` e não `5\d\d`: sem a fronteira final, qualquer número com um 5
|
|
80
|
+
// seguido de dois dígitos casava — um código de reunião "591234" citado na
|
|
81
|
+
// mensagem virava "erro 5xx". A intenção sempre foi o status HTTP.
|
|
82
|
+
// A fonte falhou ou demorou. As quatro últimas vieram da fábrica de erros do
|
|
83
|
+
// ibge, onde caíam em `outro`: ela nomeia a falha de infraestrutura em vez de
|
|
84
|
+
// usar as palavras genéricas ("Tempo de resposta excedido", não "timeout";
|
|
85
|
+
// "Serviço em manutenção"; "Erro de conexão"). `erro interno do servidor` vai
|
|
86
|
+
// com o complemento, porque "erro interno" sozinho pode ser bug nosso.
|
|
87
|
+
if (/\b(timeout|tempo esgotado|tempo de resposta excedid|excedeu o tempo|indisponív|indisponiv|manutenç|manutenc|erro de conexão|erro de conexao|erro interno do servidor|internal server error|upstream|\b5\d\d\b|payload|too large|grande demais|limite de tamanho)/.test(m)) {
|
|
88
|
+
return "fonte";
|
|
89
|
+
}
|
|
90
|
+
return "outro";
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Nomes dos parâmetros que a chamada trouxe, em ordem, separados por vírgula.
|
|
94
|
+
* Só os nomes de PRIMEIRO nível e só quando o argumento é objeto simples — o
|
|
95
|
+
* conteúdo nunca entra. Cortado em 200 caracteres porque blob do Analytics
|
|
96
|
+
* Engine tem teto de tamanho e um nome de parâmetro longo não vale a linha.
|
|
97
|
+
*/
|
|
98
|
+
export function paramNames(args) {
|
|
99
|
+
const a = Array.isArray(args) ? args[0] : args;
|
|
100
|
+
if (!a || typeof a !== "object" || Array.isArray(a))
|
|
101
|
+
return "";
|
|
102
|
+
const nomes = Object.keys(a)
|
|
103
|
+
.filter((k) => a[k] !== undefined)
|
|
104
|
+
.sort();
|
|
105
|
+
return nomes.join(",").slice(0, 200);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Texto de erro de um resultado de tool, para classificar. Vazio quando não há.
|
|
109
|
+
*
|
|
110
|
+
* Lê o CAMPO `error` do envelope, não o payload serializado inteiro. O envelope
|
|
111
|
+
* padrão é `{ error, retryable, hint }`, e o `hint` de erro não recuperável
|
|
112
|
+
* termina com "a fonte oficial pode estar indisponível" — texto de formulário,
|
|
113
|
+
* igual em todos. Classificando o payload inteiro, esse "indisponível"
|
|
114
|
+
* arrastava TODO erro não recuperável para a classe `fonte`. Visto na produção
|
|
115
|
+
* do senado em 10/09/2026: a mensagem "Não existe reunião com o código X",
|
|
116
|
+
* que é `nao_encontrado` por definição, foi gravada como `fonte`.
|
|
117
|
+
*/
|
|
118
|
+
export function errorText(result) {
|
|
119
|
+
if (!result || typeof result !== "object")
|
|
120
|
+
return "";
|
|
121
|
+
const r = result;
|
|
122
|
+
const estruturado = r.structuredContent?.error;
|
|
123
|
+
if (typeof estruturado === "string")
|
|
124
|
+
return estruturado;
|
|
125
|
+
const t = Array.isArray(r.content) ? r.content[0]?.text : undefined;
|
|
126
|
+
if (typeof t !== "string")
|
|
127
|
+
return "";
|
|
128
|
+
try {
|
|
129
|
+
const j = JSON.parse(t);
|
|
130
|
+
if (typeof j.error === "string")
|
|
131
|
+
return j.error;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Não é o envelope JSON — vale o texto cru (é o caso de outros servidores).
|
|
135
|
+
}
|
|
136
|
+
return t;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Classifica uma exceção que ESCAPOU do handler, tendo o objeto do erro em mãos
|
|
140
|
+
* e não só a mensagem.
|
|
141
|
+
*
|
|
142
|
+
* Por que existe. Em 22/09/2026 o `ibge_cnae` respondia
|
|
143
|
+
* `Cannot read properties of undefined (reading 'divisao')` a um código de
|
|
144
|
+
* classe válido — um `TypeError` nosso, nascido de um `[]` da fonte que
|
|
145
|
+
* atravessou a camada de rede. Nenhum padrão de `classifyError` casa com essa
|
|
146
|
+
* frase, então ela caía em `outro`, que já era 13 dos 23 erros da ferramenta na
|
|
147
|
+
* semana. O comentário de `outro` avisava desde o início: *se esta classe
|
|
148
|
+
* crescer, é sinal de que falta uma classe*. Faltava esta. O conserto nasceu no
|
|
149
|
+
* `ibge-br-mcp` 5.1.2 e é o mesmo em toda a frota, porque a classe é a mesma.
|
|
150
|
+
*
|
|
151
|
+
* O sinal é o TIPO, não a frase, e é por isso que esta função existe separada
|
|
152
|
+
* de `classifyError`: `TypeError`, `RangeError`, `ReferenceError` e
|
|
153
|
+
* `SyntaxError` são erros de programa. A fonte não os produz — nós os
|
|
154
|
+
* produzimos ao supor a forma do que ela respondeu. Classificá-los por
|
|
155
|
+
* mensagem seria caçar o texto do motor de JS, que muda entre versões de Node.
|
|
156
|
+
*
|
|
157
|
+
* `classifyError` fica INTACTA: ela recebe mensagem e continua sendo o caminho
|
|
158
|
+
* do erro que NÓS escrevemos.
|
|
159
|
+
*/
|
|
160
|
+
export function classifyThrown(error) {
|
|
161
|
+
if (error instanceof TypeError ||
|
|
162
|
+
error instanceof RangeError ||
|
|
163
|
+
error instanceof ReferenceError ||
|
|
164
|
+
error instanceof SyntaxError) {
|
|
165
|
+
return "defeito";
|
|
166
|
+
}
|
|
167
|
+
return classifyError(error instanceof Error ? error.message : String(error));
|
|
168
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const ESTRUTURA_ORGANIZACIONAL = {
|
|
2
|
-
"extraidoEm": "2026-
|
|
2
|
+
"extraidoEm": "2026-09-01T10:56:16.383Z",
|
|
3
3
|
"fonteUrl": "https://www12.senado.leg.br/institucional/estrutura/orgaosenado",
|
|
4
|
-
"total":
|
|
4
|
+
"total": 823,
|
|
5
5
|
"orgaos": [
|
|
6
6
|
{
|
|
7
7
|
"cod": -2072346465,
|
|
@@ -1947,12 +1947,6 @@ export const ESTRUTURA_ORGANIZACIONAL = {
|
|
|
1947
1947
|
"nome": "Ouvidoria do Senado Federal",
|
|
1948
1948
|
"codSuperior": null
|
|
1949
1949
|
},
|
|
1950
|
-
{
|
|
1951
|
-
"cod": 9426,
|
|
1952
|
-
"sigla": null,
|
|
1953
|
-
"nome": "Assessoria Técnica da Strans",
|
|
1954
|
-
"codSuperior": 8852
|
|
1955
|
-
},
|
|
1956
1950
|
{
|
|
1957
1951
|
"cod": 9430,
|
|
1958
1952
|
"sigla": null,
|
|
@@ -2217,12 +2211,6 @@ export const ESTRUTURA_ORGANIZACIONAL = {
|
|
|
2217
2211
|
"nome": "Serviço de Atendimento Eletrônico",
|
|
2218
2212
|
"codSuperior": 9600
|
|
2219
2213
|
},
|
|
2220
|
-
{
|
|
2221
|
-
"cod": 9596,
|
|
2222
|
-
"sigla": null,
|
|
2223
|
-
"nome": "Serviço de Manutenção Eletromecânica",
|
|
2224
|
-
"codSuperior": 10741
|
|
2225
|
-
},
|
|
2226
2214
|
{
|
|
2227
2215
|
"cod": 9600,
|
|
2228
2216
|
"sigla": null,
|
|
@@ -2457,12 +2445,6 @@ export const ESTRUTURA_ORGANIZACIONAL = {
|
|
|
2457
2445
|
"nome": "Serviço de Pesquisa e Análise",
|
|
2458
2446
|
"codSuperior": 9807
|
|
2459
2447
|
},
|
|
2460
|
-
{
|
|
2461
|
-
"cod": 9810,
|
|
2462
|
-
"sigla": null,
|
|
2463
|
-
"nome": "Observatório da Mulher Contra a Violência",
|
|
2464
|
-
"codSuperior": 9807
|
|
2465
|
-
},
|
|
2466
2448
|
{
|
|
2467
2449
|
"cod": 9868,
|
|
2468
2450
|
"sigla": "IFI",
|
|
@@ -4785,18 +4767,6 @@ export const ESTRUTURA_ORGANIZACIONAL = {
|
|
|
4785
4767
|
"nome": "Serviço de Suporte aos Sistemas de Saúde",
|
|
4786
4768
|
"codSuperior": 10620
|
|
4787
4769
|
},
|
|
4788
|
-
{
|
|
4789
|
-
"cod": 10801,
|
|
4790
|
-
"sigla": "GSBBONET",
|
|
4791
|
-
"nome": "Gabinete do Senador Bruno Bonetti",
|
|
4792
|
-
"codSuperior": 108
|
|
4793
|
-
},
|
|
4794
|
-
{
|
|
4795
|
-
"cod": 10803,
|
|
4796
|
-
"sigla": null,
|
|
4797
|
-
"nome": "Escritório de Apoio Nº 01 do Senador Bruno Bonetti",
|
|
4798
|
-
"codSuperior": 10801
|
|
4799
|
-
},
|
|
4800
4770
|
{
|
|
4801
4771
|
"cod": 10806,
|
|
4802
4772
|
"sigla": "GSRACIOL",
|
|
@@ -4946,6 +4916,30 @@ export const ESTRUTURA_ORGANIZACIONAL = {
|
|
|
4946
4916
|
"sigla": null,
|
|
4947
4917
|
"nome": "Coordenação do Observatório da Mulher Contra a Violência",
|
|
4948
4918
|
"codSuperior": 8852
|
|
4919
|
+
},
|
|
4920
|
+
{
|
|
4921
|
+
"cod": 10860,
|
|
4922
|
+
"sigla": "GSLPEREI",
|
|
4923
|
+
"nome": "Gabinete da Senadora Lourdinha Pereira",
|
|
4924
|
+
"codSuperior": 108
|
|
4925
|
+
},
|
|
4926
|
+
{
|
|
4927
|
+
"cod": 10861,
|
|
4928
|
+
"sigla": null,
|
|
4929
|
+
"nome": "Escritório de Apoio Nº 01 da Senadora Lourdinha Pereira",
|
|
4930
|
+
"codSuperior": 10860
|
|
4931
|
+
},
|
|
4932
|
+
{
|
|
4933
|
+
"cod": 10862,
|
|
4934
|
+
"sigla": "GSSREGIN",
|
|
4935
|
+
"nome": "Gabinete do Senador Sargento Reginauro",
|
|
4936
|
+
"codSuperior": 108
|
|
4937
|
+
},
|
|
4938
|
+
{
|
|
4939
|
+
"cod": 10863,
|
|
4940
|
+
"sigla": null,
|
|
4941
|
+
"nome": "Escritório de Apoio Nº 01 do Senador Sargento Reginauro",
|
|
4942
|
+
"codSuperior": 10862
|
|
4949
4943
|
}
|
|
4950
4944
|
]
|
|
4951
4945
|
};
|