senado-br-mcp 1.2.0 → 3.2.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 (49) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +810 -408
  3. package/README.pt-BR.md +788 -0
  4. package/dist/auth.js +46 -0
  5. package/dist/cache/l0-memory.js +30 -0
  6. package/dist/cache/l1-cache-api.js +36 -0
  7. package/dist/cache/manager.js +116 -0
  8. package/dist/cli.js +37 -0
  9. package/dist/icon.js +3 -0
  10. package/dist/instrument.js +65 -0
  11. package/dist/metrics.js +40 -0
  12. package/dist/observability/call-context.js +35 -0
  13. package/dist/prompts.js +101 -0
  14. package/dist/resources.js +141 -0
  15. package/dist/scraper/anomaly.js +34 -0
  16. package/dist/scraper/ecidadania.js +442 -0
  17. package/dist/scraper/pipeline.js +180 -0
  18. package/dist/scraper/store.js +147 -0
  19. package/dist/server.js +101 -3383
  20. package/dist/status.js +21 -0
  21. package/dist/throttle/adm.js +26 -0
  22. package/dist/throttle/token-bucket.js +46 -0
  23. package/dist/throttle/upstream.js +151 -0
  24. package/dist/tools/comissoes.js +407 -0
  25. package/dist/tools/composicao.js +137 -0
  26. package/dist/tools/contratacoes.js +263 -0
  27. package/dist/tools/discursos.js +141 -0
  28. package/dist/tools/ecidadania.js +344 -0
  29. package/dist/tools/legislacao.js +92 -0
  30. package/dist/tools/materias.js +241 -0
  31. package/dist/tools/orcamento-senado.js +155 -0
  32. package/dist/tools/orcamento.js +70 -0
  33. package/dist/tools/plenario.js +344 -0
  34. package/dist/tools/processos.js +330 -0
  35. package/dist/tools/referencia.js +178 -0
  36. package/dist/tools/senadores-admin.js +193 -0
  37. package/dist/tools/senadores.js +287 -0
  38. package/dist/tools/servidores.js +219 -0
  39. package/dist/tools/supridos.js +58 -0
  40. package/dist/tools/taquigrafia.js +127 -0
  41. package/dist/tools/votacao-comissao.js +122 -0
  42. package/dist/tools/votacoes.js +143 -0
  43. package/dist/types.js +10 -0
  44. package/dist/utils/logger.js +23 -0
  45. package/dist/utils/provenance.js +160 -0
  46. package/dist/utils/validation.js +69 -0
  47. package/dist/version.js +2 -0
  48. package/package.json +42 -75
  49. package/dist/index.js +0 -3196
package/README.md CHANGED
@@ -1,408 +1,810 @@
1
- # Senado Brasil MCP Server
2
-
3
- MCP server for Brazilian Federal Senate open data (legislators, bills, votes, committees).
4
-
5
- Servidor MCP (Model Context Protocol) que permite acesso estruturado aos dados abertos do Senado Federal do Brasil, democratizando o acesso a informações legislativas através de conversas com IA.
6
-
7
- ## Features
8
-
9
- - **33 tools** for accessing Senate data
10
- - Real-time data from official API
11
- - **e-Cidadania integration**: citizen participation data via web scraping
12
- - Structured JSON responses
13
- - Error handling with actionable suggestions
14
- - TypeScript with full type safety
15
- - **Two access modes:** stdio (npm) and HTTP remote
16
-
17
- ## Two Ways to Use
18
-
19
- | Mode | Installation | Best For |
20
- |------|--------------|----------|
21
- | **stdio/npm** | `npx senado-br-mcp` | Technical users, local usage |
22
- | **HTTP remote** | None required | Non-technical users, cloud access |
23
-
24
- Both modes provide access to the same 33 tools.
25
-
26
- ---
27
-
28
- ## Option 1: stdio/npm (Local)
29
-
30
- ### Installation
31
-
32
- **Using npx (Recommended):**
33
- ```bash
34
- npx senado-br-mcp
35
- ```
36
-
37
- **Global Installation:**
38
- ```bash
39
- npm install -g senado-br-mcp
40
- senado-br-mcp
41
- ```
42
-
43
- **From Source:**
44
- ```bash
45
- git clone https://github.com/SidneyBissoli/senado-br-mcp.git
46
- cd senado-br-mcp
47
- npm install
48
- npm run build
49
- npm start
50
- ```
51
-
52
- ### Claude Desktop Configuration
53
-
54
- Add to your `claude_desktop_config.json`:
55
-
56
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
57
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
58
-
59
- ```json
60
- {
61
- "mcpServers": {
62
- "senado-br-mcp": {
63
- "command": "npx",
64
- "args": ["-y", "senado-br-mcp"]
65
- }
66
- }
67
- }
68
- ```
69
-
70
- ---
71
-
72
- ## Option 2: HTTP Remote (Cloud)
73
-
74
- No installation required. Connect directly via URL.
75
-
76
- ### Claude Desktop Configuration
77
-
78
- In Claude Desktop settings, go to **"Add Custom Connector"** and enter:
79
-
80
- - **Name:** senado-br-mcp
81
- - **URL:** `https://senado-br-mcp.up.railway.app/mcp`
82
-
83
- ### Endpoints
84
-
85
- | Endpoint | Description |
86
- |----------|-------------|
87
- | `GET /` | Server info and documentation |
88
- | `GET /health` | Health check |
89
- | `GET /stats` | Usage statistics |
90
- | `POST /mcp` | MCP protocol endpoint |
91
-
92
- ### Rate Limiting
93
-
94
- The HTTP server has a monthly request limit to stay within free tier:
95
- - Alerts are sent at 50%, 80%, and 100% usage
96
- - When limit is reached, use the npm version instead
97
- - Limit resets on the 1st of each month
98
-
99
- ## Available Tools
100
-
101
- ### Senators (Senadores)
102
-
103
- | Tool | Description |
104
- |------|-------------|
105
- | `senado_listar_senadores` | List senators in office or by legislature. Filters: state (UF), party |
106
- | `senado_obter_senador` | Get detailed info about a senator (biography, mandates, committees) |
107
- | `senado_buscar_senador_por_nome` | Search senators by name |
108
- | `senado_votacoes_senador` | List how a senator voted. Filters: year, date range |
109
-
110
- ### Legislative Matters (Matérias)
111
-
112
- | Tool | Description |
113
- |------|-------------|
114
- | `senado_buscar_materias` | Search bills by type (PEC, PL, PLP, MPV), number, year, keyword, author |
115
- | `senado_obter_materia` | Get bill details (summary, author, status, rapporteur) |
116
- | `senado_tramitacao_materia` | Get bill's procedural history |
117
- | `senado_textos_materia` | Get bill texts (original, substitute, final) with download URLs |
118
- | `senado_votos_materia` | Get voting results for a bill |
119
-
120
- ### Votes (Votações)
121
-
122
- | Tool | Description |
123
- |------|-------------|
124
- | `senado_listar_votacoes` | List plenary votes by year. Filters: month, date range |
125
- | `senado_obter_votacao` | Get vote details with nominal votes by senator |
126
- | `senado_votacoes_recentes` | Get recent votes (last N days) |
127
-
128
- ### Committees (Comissões)
129
-
130
- | Tool | Description |
131
- |------|-------------|
132
- | `senado_listar_comissoes` | List committees. Filters: type (permanent, temporary, CPI), active |
133
- | `senado_obter_comissao` | Get committee details (president, vice-president, purpose) |
134
- | `senado_membros_comissao` | List committee members with roles |
135
- | `senado_reunioes_comissao` | List committee meetings with agenda |
136
-
137
- ### Agenda
138
-
139
- | Tool | Description |
140
- |------|-------------|
141
- | `senado_agenda_plenario` | Get plenary session schedule with voting agenda |
142
- | `senado_agenda_comissoes` | Get committee meeting schedule |
143
-
144
- ### Lookup/Auxiliary (Auxiliares)
145
-
146
- | Tool | Description |
147
- |------|-------------|
148
- | `senado_legislatura_atual` | Get current legislature info (number, period, dates) |
149
- | `senado_tipos_materia` | List valid bill types (PEC, PL, PLP, MPV, etc.) |
150
- | `senado_partidos` | List parties with senator count |
151
- | `senado_ufs` | List states with senator count |
152
-
153
- ### e-Cidadania (Citizen Participation)
154
-
155
- Tools for accessing e-Cidadania data - the Senate's citizen participation platform.
156
-
157
- #### Public Consultations (Consultas Públicas)
158
-
159
- | Tool | Description |
160
- |------|-------------|
161
- | `senado_ecidadania_listar_consultas` | List public consultations with citizen voting on pending bills |
162
- | `senado_ecidadania_obter_consulta` | Get consultation details including votes, author, and comments |
163
- | `senado_ecidadania_consultas_polarizadas` | Get polarized consultations (~50/50 votes) - useful for identifying divisive issues |
164
- | `senado_ecidadania_consultas_consensuais` | Get consensual consultations (>85% one way) - identifies broad agreement |
165
-
166
- #### Legislative Ideas (Ideias Legislativas)
167
-
168
- | Tool | Description |
169
- |------|-------------|
170
- | `senado_ecidadania_listar_ideias` | List citizen-proposed legislative ideas |
171
- | `senado_ecidadania_obter_ideia` | Get idea details including full description and conversion to bill |
172
- | `senado_ecidadania_ideias_populares` | Get most supported legislative ideas |
173
-
174
- #### Interactive Events (Eventos Interativos)
175
-
176
- | Tool | Description |
177
- |------|-------------|
178
- | `senado_ecidadania_listar_eventos` | List interactive events (hearings, confirmations, livestreams) |
179
- | `senado_ecidadania_obter_evento` | Get event details including agenda, guests, and video link |
180
- | `senado_ecidadania_eventos_populares` | Get events with most citizen comments and questions |
181
-
182
- #### Analysis Tools
183
-
184
- | Tool | Description |
185
- |------|-------------|
186
- | `senado_ecidadania_sugerir_tema_enquete` | AI-assisted analysis to suggest topics for monthly surveys based on participation metrics |
187
-
188
- > **Note:** e-Cidadania tools use web scraping with rate limiting and caching. If the e-Cidadania website is temporarily unavailable, API tools (senators, bills, votes) remain operational.
189
-
190
- ## Usage Examples
191
-
192
- ### List senators from São Paulo
193
-
194
- ```
195
- Use senado_listar_senadores with uf: "SP"
196
- ```
197
-
198
- ### Search for Constitutional Amendments in 2024
199
-
200
- ```
201
- Use senado_buscar_materias with sigla: "PEC", ano: 2024
202
- ```
203
-
204
- ### Get recent votes
205
-
206
- ```
207
- Use senado_votacoes_recentes with dias: 7
208
- ```
209
-
210
- ### Find a senator by name
211
-
212
- ```
213
- Use senado_buscar_senador_por_nome with nome: "Randolfe"
214
- ```
215
-
216
- ### Get CCJ committee members
217
-
218
- ```
219
- Use senado_membros_comissao with sigla: "CCJ"
220
- ```
221
-
222
- ### Get polarized public consultations
223
-
224
- ```
225
- Use senado_ecidadania_consultas_polarizadas with minimoVotos: 5000
226
- ```
227
-
228
- ### Get most supported citizen ideas
229
-
230
- ```
231
- Use senado_ecidadania_ideias_populares with limite: 5
232
- ```
233
-
234
- ### Find upcoming interactive events
235
-
236
- ```
237
- Use senado_ecidadania_listar_eventos with status: "agendado"
238
- ```
239
-
240
- ## Response Format
241
-
242
- All tools return structured JSON responses:
243
-
244
- ### Success Response
245
-
246
- ```json
247
- {
248
- "success": true,
249
- "data": { ... },
250
- "metadata": {
251
- "fonte": "Senado Federal - Dados Abertos",
252
- "dataConsulta": "2024-01-15T10:30:00Z",
253
- "endpoint": "/senador/lista/atual"
254
- }
255
- }
256
- ```
257
-
258
- ### Error Response
259
-
260
- ```json
261
- {
262
- "success": false,
263
- "error": {
264
- "code": "SENADOR_NAO_ENCONTRADO",
265
- "message": "Senator with code 99999 was not found",
266
- "suggestion": "Use senado_buscar_senador_por_nome to find the correct code"
267
- }
268
- }
269
- ```
270
-
271
- ## Development
272
-
273
- ### Prerequisites
274
-
275
- - Node.js 18+
276
- - npm
277
-
278
- ### Setup
279
-
280
- ```bash
281
- npm install
282
- ```
283
-
284
- ### Build
285
-
286
- ```bash
287
- # Build stdio version (npm package)
288
- npm run build
289
-
290
- # Build HTTP server version
291
- npm run build:server
292
-
293
- # Build both
294
- npm run build:all
295
- ```
296
-
297
- ### Development Mode
298
-
299
- ```bash
300
- # stdio mode
301
- npm run dev
302
-
303
- # HTTP server mode
304
- npm run dev:server
305
- ```
306
-
307
- ### Type Check
308
-
309
- ```bash
310
- npm run typecheck
311
- ```
312
-
313
- ### Test with MCP Inspector
314
-
315
- ```bash
316
- npm run inspect
317
- ```
318
-
319
- ---
320
-
321
- ## Self-Hosting HTTP Server
322
-
323
- You can host your own HTTP server instance.
324
-
325
- ### Local
326
-
327
- ```bash
328
- npm run build:server
329
- npm run start:server
330
- # Server runs on http://localhost:3000
331
- ```
332
-
333
- ### Railway
334
-
335
- 1. Fork this repository
336
- 2. Connect Railway to your GitHub
337
- 3. Deploy (auto-detects configuration from `railway.json`)
338
- 4. Set environment variables:
339
- - `MONTHLY_REQUEST_LIMIT` (default: 10000)
340
- - `ALERT_WEBHOOK_URL` (optional, for notifications)
341
-
342
- ### Environment Variables
343
-
344
- | Variable | Description | Default |
345
- |----------|-------------|---------|
346
- | `PORT` | Server port | 3000 |
347
- | `MONTHLY_REQUEST_LIMIT` | Monthly request limit | 10000 |
348
- | `ALERT_WEBHOOK_URL` | Webhook for alerts | - |
349
- | `LOG_LEVEL` | Logging level | info |
350
-
351
- ## Data Sources
352
-
353
- ### Official API
354
- - **API**: [Senado Federal - Dados Abertos](https://legis.senado.leg.br/dadosabertos)
355
- - **Documentation**: https://legis.senado.leg.br/dadosabertos/docs/
356
- - **Format**: JSON
357
- - **Authentication**: None (public data)
358
-
359
- ### e-Cidadania (Web Scraping)
360
- - **Website**: [e-Cidadania](https://www12.senado.leg.br/ecidadania)
361
- - **Content**: Public consultations, legislative ideas, interactive events
362
- - **Method**: HTML scraping with rate limiting (1 req/sec) and caching (15min-24h)
363
- - **Fallback**: If e-Cidadania is unavailable, API tools continue working
364
-
365
- ## Bill Types (Tipos de Matéria)
366
-
367
- | Code | Name | Description |
368
- |------|------|-------------|
369
- | PEC | Proposta de Emenda à Constituição | Constitutional Amendment |
370
- | PL | Projeto de Lei | Ordinary Law Bill |
371
- | PLP | Projeto de Lei Complementar | Complementary Law Bill |
372
- | MPV | Medida Provisória | Provisional Measure |
373
- | PDL | Projeto de Decreto Legislativo | Legislative Decree Bill |
374
- | PRS | Projeto de Resolução do Senado | Senate Resolution Bill |
375
- | PLC | Projeto de Lei da Câmara | Chamber of Deputies Bill |
376
-
377
- ## License
378
-
379
- MIT
380
-
381
- ## Author
382
-
383
- Sidney da Silva Pereira Bissoli
384
-
385
- ## Repository
386
-
387
- https://github.com/SidneyBissoli/senado-br-mcp
388
-
389
- ## Contributing
390
-
391
- Contributions are welcome! Please feel free to submit a Pull Request.
392
-
393
- ## Changelog
394
-
395
- ### 1.1.0
396
-
397
- - Added e-Cidadania integration (11 new tools)
398
- - Web scraping infrastructure with rate limiting and caching
399
- - Public consultations analysis (polarized/consensual)
400
- - Legislative ideas tracking
401
- - Interactive events monitoring
402
- - Survey topic suggestion tool
403
-
404
- ### 1.0.0
405
-
406
- - Initial release
407
- - 22 tools for Senate data access
408
- - Senators, bills, votes, committees, agenda, and lookup tools
1
+ # Senado Brasil MCP Server
2
+
3
+ ![Cloudflare Workers](https://img.shields.io/badge/Cloudflare%20Workers-F38020?logo=cloudflare&logoColor=white)
4
+ ![MCP](https://img.shields.io/badge/MCP-Streamable%20HTTP-1f6feb)
5
+ ![Tools](https://img.shields.io/badge/tools-66-2ea44f)
6
+ [![MCP Registry](https://img.shields.io/badge/MCP-Registry-blue)](https://registry.modelcontextprotocol.io)
7
+ [![LobeHub](https://lobehub.com/badge/mcp/sidneybissoli-senado-br-mcp-cloudflare)](https://lobehub.com/mcp/sidneybissoli-senado-br-mcp-cloudflare)
8
+ [![smithery badge](https://smithery.ai/badge/sidneybissoli/senado-br-mcp-cloudflare)](https://smithery.ai/servers/sidneybissoli/senado-br-mcp-cloudflare)
9
+ [![GitHub stars](https://img.shields.io/github/stars/SidneyBissoli/senado-br-mcp-cloudflare?style=flat&logo=github)](https://github.com/SidneyBissoli/senado-br-mcp-cloudflare)
10
+ [![GitHub Sponsors](https://img.shields.io/github/sponsors/SidneyBissoli?logo=githubsponsors&label=Sponsor&color=db61a2)](https://github.com/sponsors/SidneyBissoli)
11
+ [![License: MIT](https://img.shields.io/github/license/SidneyBissoli/senado-br-mcp-cloudflare)](LICENSE)
12
+ [![Status](https://img.shields.io/website?url=https%3A%2F%2Fsenado.sidneybissoli.com%2Fhealth&up_message=online&down_message=offline&label=status)](https://senado.sidneybissoli.com/status)
13
+
14
+ 🇧🇷 [Leia em Português](README.pt-BR.md)
15
+
16
+ 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.
17
+
18
+ It exposes **66 tools**, **4 prompts**, and **5 resources** across two domains:
19
+
20
+ - **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.
21
+ - **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.
22
+
23
+ Data comes from three official sources — the [legislative open-data API](https://legis.senado.leg.br/dadosabertos/), the [administrative open-data API](https://adm.senado.gov.br/adm-dadosabertos/swagger-ui/index.html), and the e-Cidadania portal. All tool responses are in Portuguese (pt-BR). See [CHANGELOG.md](CHANGELOG.md) for release history.
24
+
25
+ ## See it in action
26
+
27
+ Point a client at the endpoint and ask, in plain Portuguese:
28
+
29
+ - *"Como os senadores de São Paulo votaram nas últimas votações do plenário?"* → `senado_search_votacoes`
30
+ - *"Mostre a tramitação da PEC 45/2019."* → `senado_buscar_materias` + `senado_obter_materia`
31
+ - *"Quanto foi gasto com a cota parlamentar (CEAPS) em 2024, por tipo de despesa?"* → `senado_ceaps`
32
+
33
+ The answers come live from the Senate's official open-data APIs — exact figures with provenance, not numbers guessed from training data.
34
+
35
+ ## Use it (hosted — no setup)
36
+
37
+ This is a **remote, hosted, open-access** server. To use it, point any MCP client at the Streamable
38
+ HTTP endpoint — **no install, no account, no API key, no configuration**:
39
+
40
+ ```
41
+ https://senado.sidneybissoli.com/mcp
42
+ ```
43
+
44
+ ### Install (any client)
45
+
46
+ For clients that launch MCP servers as a command — and for one-command setup — use the
47
+ [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) bridge. **No build, no config, no key:**
48
+
49
+ ```bash
50
+ npx -y mcp-remote https://senado.sidneybissoli.com/mcp
51
+ ```
52
+
53
+ - **One-click (LobeHub):** open the [server page](https://lobehub.com/mcp/sidneybissoli-senado-br-mcp-cloudflare) and click **Install**.
54
+ - **Native remote URL** (Claude Desktop/Code and other Streamable-HTTP clients): see [Connecting MCP Clients](#connecting-mcp-clients).
55
+
56
+ Everything below *Architecture* (Prerequisites, Setup, Deploy) is **only for optionally self-hosting your
57
+ own instance** — it is **not** required to use this public server.
58
+
59
+ ## Run locally (npx · stdio)
60
+
61
+ Prefer not to route queries through a third-party host (e.g. a newsroom policy)? The **same server**
62
+ also runs as a **local stdio process** that talks **directly to the official government APIs** — same
63
+ 66 tools, same provenance envelope, no Cloudflare in the loop. This is the npm/stdio channel, published
64
+ as [`senado-br-mcp`](https://www.npmjs.com/package/senado-br-mcp).
65
+
66
+ Point a command-based client (Claude Desktop/Code, etc.) at the package — npm fetches and runs it,
67
+ no clone or build:
68
+
69
+ ```json
70
+ {
71
+ "mcpServers": {
72
+ "senado-br": {
73
+ "command": "npx",
74
+ "args": ["-y", "senado-br-mcp"]
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ To run it directly or hack on it, use the source checkout instead:
81
+
82
+ ```bash
83
+ git clone https://github.com/SidneyBissoli/senado-br-mcp-cloudflare
84
+ cd senado-br-mcp-cloudflare
85
+ npm install
86
+ npm run build
87
+ node dist/cli.js # serves MCP over stdio (Ctrl+C to stop)
88
+ ```
89
+
90
+ **Parity with the hosted server:** the legislative and administrative tools are **identical** (same
91
+ upstream APIs, same throttle/cache/provenance) — locally the L1 Cloudflare cache is a no-op, but the L0
92
+ in-memory cache still works, so results are the same. The **only** difference is the e-Cidadania
93
+ list/corpus tools: without D1 they fall back to a live scrape of the ~5 REST highlights, flagged via
94
+ `meta.fonte` / `possivelDesatualizacao`; the detail tools (`obter_*`) are identical. Logs go to
95
+ **stderr** — stdout carries only the JSON-RPC protocol stream.
96
+
97
+ ## Architecture
98
+
99
+ - **Runtime:** Cloudflare Workers (ESM)
100
+ - **Transport:** Streamable HTTP (MCP spec 2025-03-26) via `createMcpHandler` from `agents/mcp`
101
+ - **Protocol:** MCP over JSON-RPC — single `/mcp` endpoint handles POST, GET, DELETE
102
+ - **SDK:** `@modelcontextprotocol/sdk` 1.26.0+ (per-request McpServer instances)
103
+ - **Validation:** Zod schemas for all tool inputs
104
+ - **Caching:** 2-layer (L0 memory + L1 Cache API) with SHA-256 keying
105
+ - **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))
106
+ - **Rate limiting:** Token bucket — global (8 req/s) + per-client (2 req/s)
107
+ - **Upstream throttle:** Max 6 concurrent requests, 10s timeout, retry with exponential backoff
108
+ - **Auth:** Optional Bearer token (set the `API_KEY` secret; open access when unset). Constant-time comparison.
109
+ - **Observability:** Structured JSON logging + in-memory counters at `/metrics`; per-tool call telemetry (selection, error rate, cache-vs-live) in Cloudflare Analytics Engine, PII-free
110
+ - **Liveness:** Runs on Cloudflare's own global network behind a custom domain — no third-party host that can go dark. Public `/health` and `/status` (version + last-deploy id/timestamp) make uptime and the current build verifiable; the **status** badge above pings the live endpoint
111
+ - **Tests:** Vitest unit tests for parsers, helpers, cache, throttle, and auth
112
+
113
+ ## Self-hosting (optional)
114
+
115
+ > **Not needed to use the server** — it is already hosted at `https://senado.sidneybissoli.com/mcp`
116
+ > (open access). Follow this section only if you want to run your **own** private instance.
117
+
118
+ ### Prerequisites
119
+
120
+ - Node.js 18+
121
+ - [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) v4+
122
+ - Cloudflare account
123
+
124
+ ### Setup
125
+
126
+ #### 1. Install dependencies
127
+
128
+ ```bash
129
+ npm install
130
+ ```
131
+
132
+ #### 2. Create KV namespace
133
+
134
+ ```bash
135
+ # Create the KV namespace
136
+ wrangler kv namespace create CACHE_KV
137
+
138
+ # Note the ID from the output, e.g.:
139
+ # { binding = "CACHE_KV", id = "abc123..." }
140
+ ```
141
+
142
+ #### 3. Configure wrangler.toml
143
+
144
+ Replace the placeholder KV namespace ID:
145
+
146
+ ```toml
147
+ [[kv_namespaces]]
148
+ binding = "CACHE_KV"
149
+ id = "YOUR_KV_NAMESPACE_ID_HERE"
150
+ ```
151
+
152
+ Optionally set `ALLOWED_ORIGIN` to restrict CORS:
153
+
154
+ ```toml
155
+ [vars]
156
+ ALLOWED_ORIGIN = "https://your-app.example.com"
157
+ ```
158
+
159
+ The e-Cidadania pipeline needs a **D1 database** and a **Cron Trigger** (both already declared in `wrangler.toml` — replace the database ID):
160
+
161
+ ```toml
162
+ [[d1_databases]]
163
+ binding = "ECIDADANIA_DB"
164
+ database_name = "senado-ecidadania"
165
+ database_id = "YOUR_D1_DATABASE_ID_HERE"
166
+
167
+ [triggers]
168
+ crons = ["0 */2 * * *"]
169
+ ```
170
+
171
+ Create the database (paste the returned ID above) and apply the schema:
172
+
173
+ ```bash
174
+ npx wrangler d1 create senado-ecidadania
175
+ npx wrangler d1 migrations apply senado-ecidadania --remote
176
+ ```
177
+
178
+ The list tools fall back to live scraping when D1 is empty, so the server works before the first Cron run.
179
+
180
+ #### 4. (Optional) Enable authentication
181
+
182
+ ```bash
183
+ wrangler secret put API_KEY
184
+ # Clients must then send: Authorization: Bearer <key>
185
+ # When API_KEY is not set, the server is open access.
186
+ ```
187
+
188
+ #### 5. Local development
189
+
190
+ ```bash
191
+ npm run dev
192
+ # Dev server runs locally on port 8787 (local only).
193
+ # The public MCP endpoint is https://senado.sidneybissoli.com/mcp
194
+ ```
195
+
196
+ #### 6. Tests and typecheck
197
+
198
+ ```bash
199
+ npm test # run all tests once
200
+ npm run test:watch # watch mode
201
+ npm run typecheck # tsc --noEmit
202
+ ```
203
+
204
+ #### 7. Deploy
205
+
206
+ ```bash
207
+ npm run deploy
208
+ # Serves at https://senado.sidneybissoli.com (custom domain) and
209
+ # https://senado-br-mcp.sidneybissoli.workers.dev (workers.dev fallback)
210
+ ```
211
+
212
+ ## Endpoints
213
+
214
+ | Path | Methods | Description |
215
+ |------|---------|-------------|
216
+ | `/mcp` | POST, GET, DELETE, OPTIONS | MCP Streamable HTTP endpoint (managed by `createMcpHandler`) |
217
+ | `/health` | GET | Health check — returns `ok` (always public) |
218
+ | `/status` | GET | JSON: `status`, `version`, and last-deploy metadata (`deploy.id`/`tag`/`timestamp`) — liveness + current build, no MCP handshake needed (always public) |
219
+ | `/metrics` | GET | JSON counters: requests, tool calls, cache hits/misses, upstream calls/retries/errors, auth failures (always public) |
220
+
221
+ ## MCP Request Examples
222
+
223
+ All requests go to `POST /mcp` with JSON-RPC 2.0 format.
224
+
225
+ ### List available tools
226
+
227
+ ```json
228
+ {
229
+ "jsonrpc": "2.0",
230
+ "id": 1,
231
+ "method": "tools/list"
232
+ }
233
+ ```
234
+
235
+ ### Call a tool — List senators from SP
236
+
237
+ ```json
238
+ {
239
+ "jsonrpc": "2.0",
240
+ "id": 2,
241
+ "method": "tools/call",
242
+ "params": {
243
+ "name": "senado_listar_senadores",
244
+ "arguments": {
245
+ "uf": "SP",
246
+ "emExercicio": true
247
+ }
248
+ }
249
+ }
250
+ ```
251
+
252
+ ### Call a tool — Search bills by keyword
253
+
254
+ ```json
255
+ {
256
+ "jsonrpc": "2.0",
257
+ "id": 3,
258
+ "method": "tools/call",
259
+ "params": {
260
+ "name": "senado_buscar_materias",
261
+ "arguments": {
262
+ "palavraChave": "inteligência artificial",
263
+ "tramitando": true
264
+ }
265
+ }
266
+ }
267
+ ```
268
+
269
+ ### Call a tool — Get recent plenary votes
270
+
271
+ ```json
272
+ {
273
+ "jsonrpc": "2.0",
274
+ "id": 4,
275
+ "method": "tools/call",
276
+ "params": {
277
+ "name": "senado_search_votacoes",
278
+ "arguments": {
279
+ "dias": 7
280
+ }
281
+ }
282
+ }
283
+ ```
284
+
285
+ ### Call a tool — Most popular citizen ideas
286
+
287
+ ```json
288
+ {
289
+ "jsonrpc": "2.0",
290
+ "id": 5,
291
+ "method": "tools/call",
292
+ "params": {
293
+ "name": "senado_ecidadania_listar_ideias",
294
+ "arguments": {
295
+ "ordenarPor": "apoios",
296
+ "ordem": "desc",
297
+ "status": "aberta"
298
+ }
299
+ }
300
+ }
301
+ ```
302
+
303
+ ## Upstream API Endpoints
304
+
305
+ The server consumes two classes of upstream endpoints from the Senado API:
306
+
307
+ ### Legacy endpoints (`.json` suffix, PascalCase responses)
308
+
309
+ Used by Groups A, E, F, H, I, J, K, L, M, N. The `.json` suffix is appended automatically by `upstream.ts`. None of these is marked deprecated upstream.
310
+
311
+ | Upstream path | Used by |
312
+ |---------------|---------|
313
+ | `/senador/lista/atual` | `senado_listar_senadores` |
314
+ | `/senador/lista/legislatura/{legislatura}` | `senado_listar_senadores` (param `legislatura`) |
315
+ | `/senador/{codigo}` | `senado_obter_senador` |
316
+ | `/senador/{codigo}/licencas`, `/comissoes`, `/cargos`, `/historicoAcademico`, `/filiacoes`, `/profissao` | `senado_senador_historico` (`tipo` enum) |
317
+ | `/senador/afastados` | `senado_senadores_afastados` |
318
+ | `/senador/{codigo}/apartes` | `senado_discursos_senador` (`tipo=apartes`) |
319
+ | `/comissao/lista/colegiados` | `senado_listar_comissoes` (+ sigla-to-code resolution) |
320
+ | `/comissao/{codigo}` | `senado_obter_comissao` (`secao=resumo`; numeric code, not sigla) |
321
+ | `/composicao/comissao/{codigo}` | `senado_obter_comissao` (`secao=membros`) |
322
+ | `/comissao/agenda/{data}` | `senado_agenda_comissoes` |
323
+ | `/comissao/agenda/{dataInicio}/{dataFim}` | `senado_reunioes_comissao` |
324
+ | `/comissao/reuniao/{codigoReuniao}` | `senado_reuniao_comissao` |
325
+ | `/comissao/cpi/{sigla}/requerimentos` | `senado_requerimentos_cpi` (empty body = no requests) |
326
+ | `/materia/distribuicao/autoria`, `/distribuicao/relatoria/{sigla}` | `senado_distribuicao_materias` |
327
+ | `/plenario/agenda/dia/{data}`, `/agenda/mes/{data}`, `/agenda/cn/...` | `senado_agenda_plenario` |
328
+ | `/plenario/resultado/{data}`, `/resultado/cn/{data}`, `/resultado/mes/{data}` | `senado_resultado_plenario` |
329
+ | `/plenario/resultado/veto/{codigo}` (+ `/materia/`, `/dispositivo/`) | `senado_resultado_veto` |
330
+ | `/plenario/votacao/orientacaoBancada/{data}` (+ período) | `senado_orientacao_bancada` |
331
+ | `/plenario/encontro/{codigo}` (+ `/pauta`, `/resultado`, `/resumo`) | `senado_encontro_plenario` |
332
+ | `/plenario/tiposSessao`, `/lista/tiposComparecimento`, `/lista/legislaturas` | `senado_tabelas_plenario` |
333
+ | `/materia/vetos/{ano}`, `/vetos/aposrcn`, `/vetos/antesrcn`, `/vetos/encerrados` | `senado_vetos` |
334
+ | `/taquigrafia/notas/{sessao\|reuniao}/{id}` | `senado_notas_taquigraficas` |
335
+ | `/taquigrafia/videos/{sessao\|reuniao}/{id}` | `senado_videos_taquigrafia` |
336
+ | `/senador/{codigo}/discursos` | `senado_discursos_senador` |
337
+ | `/plenario/lista/discursos/{dataInicio}/{dataFim}` | `senado_discursos_plenario` |
338
+ | `/discurso/texto-integral/{codigo}` | `senado_discurso_texto` (plain text, fetched directly) |
339
+ | `/senador/lista/tiposUsoPalavra` | `senado_tabelas_referencia` (`tabela=tipos-uso-palavra`) |
340
+ | `/composicao/lista/blocos` | `senado_listar_blocos` |
341
+ | `/composicao/bloco/{codigo}` | `senado_obter_bloco` |
342
+ | `/composicao/lideranca` | `senado_liderancas` |
343
+ | `/composicao/mesaSF` | `senado_mesa` (`casa=senado`) |
344
+ | `/composicao/mesaCN` | `senado_mesa` (`casa=congresso`) |
345
+ | `/orcamento/lista` | `senado_orcamento_parlamentar` (`tipo=emendas`) |
346
+ | `/orcamento/oficios` | `senado_orcamento_parlamentar` (`tipo=oficios`) |
347
+ | `/legislacao/lista` | `senado_buscar_legislacao` |
348
+ | `/legislacao/{codigo}` | `senado_obter_legislacao` |
349
+ | `/legislacao/tiposNorma` | `senado_tabelas_referencia` (`tabela=tipos-norma`) |
350
+ | `/votacaoComissao/comissao/{sigla}` | `senado_votacao_comissao` (`por=comissao`) |
351
+ | `/votacaoComissao/parlamentar/{codigo}` | `senado_votacao_comissao` (`por=senador`) |
352
+ | `/votacaoComissao/materia/{sigla}/{numero}/{ano}` | `senado_votacao_comissao` (`por=materia`) |
353
+ | `/autor/lista/atual` | `senado_autores_atuais` |
354
+
355
+ ### v3 endpoints (flat JSON arrays/objects, camelCase)
356
+
357
+ Used by Groups B, C, D. Dates must be in **ISO format** (`YYYY-MM-DD`) — tools accept `YYYYMMDD` and convert. The `codigoMateria` query param bridges legacy matéria codes to v3 processes.
358
+
359
+ | Upstream path | Used by |
360
+ |---------------|---------|
361
+ | `/votacao` | `senado_obter_votacao`, `senado_search_votacoes`, `senado_votos_materia`, `senado_votacoes_senador` |
362
+ | `/processo` | `senado_search_processos`, `senado_buscar_materias` |
363
+ | `/processo/{id}` | `senado_obter_processo`, `senado_obter_materia` (`secao=detalhe`/`tramitacao`) |
364
+ | `/processo/documento` | `senado_obter_materia` (`secao=textos`) |
365
+ | `/processo/emenda` | `senado_processo_detalhe` (`secao=emendas`) |
366
+ | `/processo/relatoria` | `senado_processo_detalhe` (`secao=relatorias`), `senado_obter_materia` (rapporteur) |
367
+ | `/processo/prazo` | `senado_processo_detalhe` (`secao=prazos`) |
368
+ | `/processo/{siglas,assuntos,classes,destinos,entes,tipos-*}` | `senado_tabelas_processo` (12 reference tables) |
369
+
370
+ ### Administrative API (adm.senado.gov.br/adm-dadosabertos, flat snake_case JSON)
371
+
372
+ Used by Groups O, P, Q, R via `admFetch` (no `.json` suffix; HTTP 404 treated as empty collection). Base URL configurable via `SENADO_ADM_BASE_URL`.
373
+
374
+ | Upstream path | Used by |
375
+ |---------------|---------|
376
+ | `/api/v1/senadores/despesas_ceaps/{ano}` | `senado_ceaps` (~10 MB/year, cached + aggregated in-Worker) |
377
+ | `/api/v1/senadores/{auxilio-moradia,escritorios,aposentados}` | `senado_senadores_admin` (`tipo` enum) |
378
+ | `/api/v1/servidores/servidores/{ativos,efetivos,comissionados,inativos}` | `senado_servidores` |
379
+ | `/api/v1/servidores/remuneracoes/{ano}/{mes}` | `senado_remuneracoes_servidores` (~5.5 MB/month) |
380
+ | `/api/v1/servidores/horas-extras/{ano}/{mes}` | `senado_horas_extras` |
381
+ | `/api/v1/servidores/quantitativos/*`, `/previsao-aposentadoria`, `/api/v1/senadores/quantitativos/senadores` | `senado_pessoal_tabelas` (quantitativos) |
382
+ | `/api/v1/servidores/{estagiarios,pensionistas,lotacoes,cargos}` | `senado_pessoal_tabelas` (listas nominais) |
383
+ | `/api/v1/contratacoes/contratos` (+ `/{id}/aditivos`) | `senado_contratos`, `senado_contratacao_detalhe` |
384
+ | `/api/v1/contratacoes/{tipo}/{id}/{itens,pagamentos,garantias}` | `senado_contratacao_detalhe` |
385
+ | `/api/v1/contratacoes/licitacoes` | `senado_licitacoes` |
386
+ | `/api/v1/contratacoes/terceirizados` | `senado_terceirizados` |
387
+ | `/api/v1/contratacoes/empresas` | `senado_empresas_contratadas` (~13 MB, requires filter) |
388
+ | `/api/v1/contratacoes/{atas_registro_preco,notas_empenho,menores_aprendizes}` | `senado_contratacoes_lista` |
389
+ | `/api/v1/supridos/{ano}` (+ atosConcessao, empenhos, movimentacoes, transacoes) | `senado_suprimento_fundos` |
390
+ | `senado.gov.br/bi-arqs/Arquimedes/Financeiro/{Despesa,Receitas}SenadoDadosAbertos.json` | `senado_execucao_orcamentaria` (daily JSON feeds, Brazilian decimal strings normalized) |
391
+
392
+ ### e-Cidadania (D1-backed, Cron-refreshed)
393
+
394
+ The e-Cidadania **list** data is persisted in a **D1 database** (`ecidadania_current/_history/_scrape_runs`, discriminated by `entidade`) and read from there instead of being scraped on every call. **Two cadences** write into it:
395
+
396
+ - a **weekly off-Worker GitHub Action** owns the **full corpus** of each entity (see below) — the source of truth;
397
+ - an in-Worker **Cron Trigger** (`0 */2 * * *`, `src/scraper/pipeline.ts → refreshEcidadania`) does only a **targeted metric splice** of the ~5 REST highlights per live entity (`restcolecaomaismateria/ideia/audiencia` — votos/comentários/apoios), recorded as `ok-metrica` so it never re-breaks the corpus baseline and never touches the long tail.
398
+
399
+ Both writers build payloads through the canonical `buildXResumo` builders + shared `contentHash`, so their rows are byte-identical. Each write:
400
+
401
+ - **upserts** `ecidadania_current` (one row per item — what the tools read),
402
+ - **appends** `ecidadania_history` only when an item's `content_hash` changes (time-series-ready),
403
+ - records each run in `ecidadania_scrape_runs`.
404
+
405
+ An **anomaly guard** (`src/scraper/anomaly.ts`, `classifyRun`) ensures a failed or anomalous corpus run (zero rows, or fewer than `ECIDADANIA_CORPUS_MIN_PCT`% of the last good run) **never overwrites** the last good state.
406
+
407
+ The **list / analysis tools** (`listar_*`, `consultas_analise`, `sugerir_tema_enquete`, `consultas_votos`) read from D1 via `resolveList` (`src/scraper/store.ts`): D1-first. Because every entity is now a full corpus, a stale corpus is served from D1 **flagged** (`possivelDesatualizacao: true`) rather than collapsing to the ~5-item live highlights (the original coverage bug); the live scrape is reserved for an empty D1 (cold start, before the first weekly run). Staleness uses `ECIDADANIA_CORPUS_STALE_MAX_MIN` (~10 days). Every list response carries an additive `meta` (`fonte`, `lastScrapedAt`, `possivelDesatualizacao`) so callers always see the data's real age and never get stale data silently.
408
+
409
+ The **detail tools** (`obter_*`) stay **live** (HTML scraped with CSS-class-targeted regex) for freshness, and write their richer payload through to `ecidadania_detalhe` fire-and-forget (deduped by `content_hash`), so detail history accrues without adding latency to the response.
410
+
411
+ #### Full-corpus ingestion (off-Worker, weekly)
412
+
413
+ All four e-Cidadania entities are full corpora owned by the weekly Action (`.github/workflows/ingest-ecidadania.yml`), each with its own `scripts/ingest-ecidadania/index-*.ts` orchestrator emitting batched `out-*.sql` the apply step bulk-loads:
414
+
415
+ - **`consultas`** — open consultations (detailed below).
416
+ - **`eventos`** — audiências/eventos from the `principalaudiencia?p=N` HTML listing; status comes straight from the listing block (no `/processo` bridge).
417
+ - **`ideias`** — ideias legislativas (~150k) from `pesquisaideia?situacao=N&p=M`, crawled **per `situacao` bucket** (the listing has no inline status) and emitted in ~10k-statement batches.
418
+ - **`consultas_votos`** — a separate **historical** acervo of votes-by-UF parsed from the ~33 MB Arquimedes CSV (`Proposições-com-votos.csv`), aggregated to one record per matéria with a `votosPorUf` breakdown. The CSV's "dados atualizados até" stamp becomes the provenance `reference_period`; it is excluded from the row hash (`consultaVotoCore`) so a weekly stamp bump on these frozen votes doesn't churn `_history`. `STATUS ATUAL` is uniformly "Descontinuado", hence archival, not a migration of the open consultations. Served by `senado_ecidadania_consultas_votos` with provenance pointing at the CSV (`ECIDADANIA_ARQUIMEDES`).
419
+
420
+ The `consultas` job is the reference implementation:
421
+
422
+ `consultas` covers the **full set of OPEN consultations** — every matter currently in tramitação (~7.7k), not just the ~5 highlights. Confirmed on the first run: the `pesquisamateria` listing is **in-tramitação-only**, so closed/historical consultations are **not** captured by this source (a pre-ingestion historical backfill is out of scope). Three settled design decisions:
423
+
424
+ 1. **Decoupled ingestion.** The open set is acquired by an **off-Worker TypeScript job** (`scripts/ingest-ecidadania/`, run by a weekly GitHub Action — `.github/workflows/ingest-ecidadania.yml`) that paginates the HTML listing (`pesquisamateria?p=1..N`, the only full-coverage source for open consultations) for ids + vote counts and **bulk-loads D1**; the Worker only reads. The brittle, long crawl is kept out of the request/Cron path.
425
+ 2. **Status from `/processo`, not HTML.** A consultation runs from presentation until the end of tramitação, so `status` is a function of the matter: **aberta ⟺ the `codigoMateria` is in the `/processo` `tramitando=S` set**, derived from robust JSON (never scraped). Every consultation enters as `aberta` (the listing only yields in-tramitação matters); on each **complete** run the job re-derives status for **all stored rows** by `/processo` membership (not by listing-absence, which can be transient), so a consultation whose matter leaves tramitação flips to `encerrada`. The `encerrada`/`todas` sets therefore grow over time; consultations that closed **before** the first ingestion aren't captured (out of scope). The list/analysis tools default to `status: aberta`.
426
+ 3. **Two reconciled cadences (one shared writer contract).** The job **reuses** `contentHash` + the `ConsultaResumo` builder + `classifyRun` from `src/scraper/`, so its rows are byte-identical to the Cron's. The weekly job owns the long tail; the **2h Cron** keeps the ~5 hot/open highlights fresh via a *targeted metric splice* (recorded as `ok-metrica`, bypassing the corpus `classifyRun` baseline). Corpus freshness (`possivelDesatualizacao`) is computed from the last `status='ok'` run and uses a larger window (`ECIDADANIA_CORPUS_STALE_MAX_MIN`), and a stale consultas corpus is served from D1 flagged rather than collapsing back to the live highlights.
427
+
428
+ Write guards on the load: an **incomplete crawl** (any page failed) or an incomplete `/processo` status universe writes only an `erro` run row; even a complete crawl is rejected by a **catastrophic floor** (`ECIDADANIA_CORPUS_MIN_PCT`, default 80% of the last good corpus) to guard against a degraded page — overridable with `--force` / `INGEST_FORCE=1` for a legitimate large shrink. Run weekly via the Action, or manually:
429
+
430
+ ```bash
431
+ CLOUDFLARE_API_TOKEN=… npm run ingest:ecidadania # writes scripts/ingest-ecidadania/out.sql
432
+ npx wrangler d1 execute senado-ecidadania --remote --file=scripts/ingest-ecidadania/out.sql
433
+ ```
434
+
435
+ ## Caching
436
+
437
+ ### Layer architecture
438
+
439
+ | Layer | Storage | Scope | TTL range | Purpose |
440
+ |-------|---------|-------|-----------|---------|
441
+ | **L0** | In-memory `Map` | Per-isolate | 30-300s | Ultra-fast, eliminates redundant requests within a Worker isolate |
442
+ | **L1** | Cloudflare Cache API (`caches.default`) | Per-colo (PoP) | 60-600s | Shared across requests at the same edge location |
443
+ | **L2** | KV (optional) | Global | Variable | Reserved for rare, low-write data |
444
+
445
+ ### Cache categories
446
+
447
+ | Category | L0 TTL | L1 TTL | Used for |
448
+ |----------|--------|--------|----------|
449
+ | **STATIC** | 300s | 600s | Legislation types, static reference |
450
+ | **SEMI_STATIC** | 120s | 300s | Party list, UF list, committee details |
451
+ | **DYNAMIC** | 30s | 60s | Agendas, recent votes, meeting lists |
452
+ | **ON_DEMAND** | 30s | 120s | Specific bill/senator/vote lookups |
453
+
454
+ ### POST caching approach
455
+
456
+ MCP uses POST for all `tools/call` requests. Caching POST responses is not natively supported by the Cache API, which requires GET requests. The solution:
457
+
458
+ 1. **Hash parameters** — Tool name + sorted parameters are hashed with SHA-256
459
+ 2. **Synthetic GET key** — A synthetic URL `https://senado-br-mcp.internal/__cache/{tool}/{hash}` is constructed
460
+ 3. **Cache API match/put** — The synthetic GET URL is used with `caches.default.match()` and `caches.default.put()`, allowing standard Cache API operations on POST-originated data
461
+
462
+ This caching happens at the **tool level** (inside each tool's callback), not at the MCP transport level.
463
+
464
+ ## Provenance
465
+
466
+ **Every tool** attaches a **provenance envelope** so a result is traceable back to its official source — provenance is treated as a first-class part of the answer, not an optional extra (the audience is journalists and political-science researchers, for whom an un-sourced figure is unusable). The envelope lives in `structuredContent.provenance` (parseable by clients, validated by the tool's output schema) and is mirrored as a one-line source footer in the text content for clients that only render text — the data JSON itself is **not** duplicated with the envelope, to keep the per-response token cost low (a fixed ≈170 chars).
467
+
468
+ Coverage spans all four upstream sources, each with its own `source`/`attribution`/`license` (in `src/utils/provenance.ts`):
469
+
470
+ - **Senado Federal — Dados Abertos (Legislativo)** — `legis.senado.leg.br/dadosabertos`
471
+ - **Senado Federal — Dados Abertos (Administrativo)** — `adm.senado.gov.br/adm-dadosabertos`
472
+ - **Senado Federal — Execução Orçamentária e Financeira** — Arquimedes/Financeiro feed at `senado.gov.br`
473
+ - **Senado Federal — Portal e-Cidadania** — `www12.senado.leg.br/ecidadania`
474
+
475
+ Fields (per response — one tool, one source):
476
+
477
+ | Field | Meaning |
478
+ |-------|---------|
479
+ | `source` | Official source name (e.g. *Senado Federal — Dados Abertos (Legislativo)*) |
480
+ | `source_url` | Canonical endpoint/item URL consulted (e.g. `…/processo/{id}`) |
481
+ | `dataset_id` | Item/series identifier (e.g. `codigoMateria=137808`) |
482
+ | `reference_period` | Vintage/competência of the data (e.g. `2024-03-15`, `2019`) |
483
+ | `retrieved_at` | ISO-8601 of the **upstream extraction** — carried through the cache, so it reflects when the data was actually fetched, not the build or the cache-hit time |
484
+ | `attribution` | Ready-to-use citation string |
485
+ | `license` | Source terms (Dados Abertos do Senado Federal) |
486
+
487
+ `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.
488
+
489
+ Coverage is **universal**: all 66 tools carry the envelope (verify with `grep -c 'resultWithProvenance(' src/tools/*.ts`). 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.
490
+
491
+ ## Tool Inventory
492
+
493
+ ### Group H — Reference/Metadata (1 tool)
494
+
495
+ | Tool | Description |
496
+ |------|-------------|
497
+ | `senado_tabelas_referencia` | Tabelas de referência via `tabela` enum: tipos-materia, partidos, ufs, legislatura-atual, tipos-norma, tipos-uso-palavra |
498
+
499
+ ### Group A — Senators (5 tools)
500
+
501
+ | Tool | Description |
502
+ |------|-------------|
503
+ | `senado_listar_senadores` | Lista senadores em exercício/por legislatura, com filtros `nome` (busca parcial sem acento), `uf` e `partido` |
504
+ | `senado_obter_senador` | Detalhe biográfico de um senador: bio, mandatos, partido, contato |
505
+ | `senado_votacoes_senador` ⊕ | Como um senador votou em cada matéria (via v3 `/votacao`) |
506
+ | `senado_senador_historico` | Histórico funcional via `tipo` enum: licencas, comissoes, cargos, historico-academico, filiacoes, profissoes |
507
+ | `senado_senadores_afastados` | Senadores atualmente afastados (fora de exercício) |
508
+
509
+ ### Group B — Bills/Matters (2 tools, v3 backend)
510
+
511
+ | Tool | Description |
512
+ |------|-------------|
513
+ | `senado_buscar_materias` ⊕ | Busca matérias por tipo, número, ano, palavra-chave, autor ou tramitação (via v3 `/processo`) |
514
+ | `senado_obter_materia` ⊕ | Dados de uma matéria via `secao` enum: detalhe (situação/relator), tramitacao (histórico) ou textos (documentos) |
515
+
516
+ ### Group C — Processes (5 tools)
517
+
518
+ | Tool | Description |
519
+ |------|-------------|
520
+ | `senado_search_processos` ⊕ | Busca processos legislativos (complementar à busca de matérias) |
521
+ | `senado_obter_processo` ⊕ | Detalhes completos de um processo legislativo específico |
522
+ | `senado_processo_detalhe` | Aspecto de um processo via `secao` enum: emendas, relatorias ou prazos |
523
+ | `senado_autores_atuais` | Parlamentares autores de processos em tramitação, ordenados por produção |
524
+ | `senado_tabelas_processo` | 12 tabelas de referência (siglas, assuntos, classes, tipos-*) via `tabela` enum |
525
+
526
+ ### Group D — Votes (3 tools)
527
+
528
+ | Tool | Description |
529
+ |------|-------------|
530
+ | `senado_obter_votacao` ⊕ | Detalhes de uma votação com votos nominais. Aceita `codigoVotacao` (codigoSessao da sessão plenária). |
531
+ | `senado_votos_materia` ⊕ | Votações de uma matéria (via v3 `/votacao?codigoMateria`), com votos nominais opcionais |
532
+ | `senado_search_votacoes` ⊕ | Busca/listagem flexível de votações do plenário por `dias`, período, processo, matéria ou senador |
533
+
534
+ ### Group E — Committees (7 tools)
535
+
536
+ | Tool | Description |
537
+ |------|-------------|
538
+ | `senado_listar_comissoes` | Lista comissões (colegiados) ativas, filtráveis por tipo |
539
+ | `senado_obter_comissao` | Dados de uma comissão via `secao` enum: resumo (mesa/totais) ou membros (composição). Resolve sigla para código internamente. |
540
+ | `senado_reunioes_comissao` | Reuniões de uma comissão num período (lida com intervalos entre anos) |
541
+ | `senado_agenda_comissoes` | Agenda de reuniões de todas as comissões numa data |
542
+ | `senado_reuniao_comissao` | Detalhe completo de uma reunião: partes, itens, convidados, resultados, links pauta/ata |
543
+ | `senado_requerimentos_cpi` | Requerimentos protocolados numa CPI em atividade, paginados |
544
+ | `senado_distribuicao_materias` | Estatísticas de carga por senador numa comissão: autoria ou relatoria |
545
+
546
+ ### Group F — Plenary (7 tools)
547
+
548
+ | Tool | Description |
549
+ |------|-------------|
550
+ | `senado_agenda_plenario` | Plenary schedule — by day, month or Congress (escopo dia/mes/cn) |
551
+ | `senado_resultado_plenario` | Session results: items deliberated, opinions, outcomes (SF/CN/month) |
552
+ | `senado_orientacao_bancada` | Party leadership voting instructions per vote, with tallies |
553
+ | `senado_vetos` | Presidential vetoes by year or tramitation status |
554
+ | `senado_resultado_veto` | Nominal veto vote results (by veto, vetoed bill or device) |
555
+ | `senado_encontro_plenario` | Legislative session detail, agenda items, results or summary |
556
+ | `senado_tabelas_plenario` | Session types, attendance types, legislatures list |
557
+
558
+ ### Group G — e-Cidadania (9 tools)
559
+
560
+ | Tool | Description |
561
+ |------|-------------|
562
+ | `senado_ecidadania_listar_consultas` | Consultas públicas (conjunto completo das **abertas** — matérias em tramitação) com votação sim/não; filtro `status` (padrão `aberta`) |
563
+ | `senado_ecidadania_obter_consulta` | Detalhe de uma consulta: votos, autor, relator, comentários |
564
+ | `senado_ecidadania_consultas_analise` | Analisa o conjunto completo de consultas **abertas** via `modo` (consenso/polarizada); `status` padrão `aberta` |
565
+ | `senado_ecidadania_listar_ideias` | Ideias legislativas de cidadãos; ranking das mais apoiadas via `ordenarPor: apoios` |
566
+ | `senado_ecidadania_obter_ideia` | Detalhe de uma ideia: texto, apoios, status de conversão em projeto |
567
+ | `senado_ecidadania_listar_eventos` | Eventos interativos (audiências, sabatinas, lives); ranking dos mais comentados via `ordenarPor` |
568
+ | `senado_ecidadania_obter_evento` | Detalhe de um evento: pauta, convidados, link de vídeo |
569
+ | `senado_ecidadania_sugerir_tema_enquete` | Sugere temas para enquete mensal a partir de critérios configuráveis |
570
+ | `senado_ecidadania_consultas_votos` | Acervo **histórico** de votos das consultas com quebra **por UF** (CSV Arquimedes); ranking por `total`/`sim`/`nao`, filtro `uf`/`materia` |
571
+
572
+ ### Group I — Speeches (3 tools)
573
+
574
+ | Tool | Description |
575
+ |------|-------------|
576
+ | `senado_discursos_senador` | Pronunciamentos de um senador via `tipo` enum: discursos (próprios) ou apartes (intervenções) |
577
+ | `senado_discursos_plenario` | Todos os discursos em plenário num intervalo de datas |
578
+ | `senado_discurso_texto` | Texto integral de um pronunciamento/discurso específico |
579
+
580
+ ### Group J — Blocs & Leadership (4 tools)
581
+
582
+ | Tool | Description |
583
+ |------|-------------|
584
+ | `senado_listar_blocos` | Blocos parlamentares do Senado e seus partidos membros |
585
+ | `senado_obter_bloco` | Detalhes de um bloco parlamentar específico |
586
+ | `senado_liderancas` | Lideranças do Senado/Congresso (líderes, vice-líderes), filtráveis |
587
+ | `senado_mesa` | Membros da Mesa Diretora via `casa` enum: senado (Mesa do SF) ou congresso (Mesa do CN) |
588
+
589
+ ### Group K — Budget (1 tool)
590
+
591
+ | Tool | Description |
592
+ |------|-------------|
593
+ | `senado_orcamento_parlamentar` | Dados de emendas orçamentárias via `tipo` enum: emendas (lotes) ou oficios (ofícios de apoio) |
594
+
595
+ ### Group L — Federal Law (2 tools)
596
+
597
+ | Tool | Description |
598
+ |------|-------------|
599
+ | `senado_buscar_legislacao` | Busca normas jurídicas federais por tipo, número, ano ou data (ao menos um obrigatório) |
600
+ | `senado_obter_legislacao` | Detalhes de uma norma jurídica federal específica |
601
+
602
+ ### Group M — Committee Voting (1 tool)
603
+
604
+ | Tool | Description |
605
+ |------|-------------|
606
+ | `senado_votacao_comissao` | Votações em comissões via `por` enum: comissao, senador ou materia; período opcional |
607
+
608
+ ### Group N — Taquigrafia (2 tools)
609
+
610
+ | Tool | Description |
611
+ |------|-------------|
612
+ | `senado_notas_taquigraficas` | Official transcripts of plenary sessions or committee meetings — summary mode with excerpts, full-text mode paginated in blocks, speaker filter |
613
+ | `senado_videos_taquigrafia` | Video/audio units per session or meeting, with speaker and media links |
614
+
615
+ ### Group O — Senadores/Administrativo (2 tools)
616
+
617
+ | Tool | Description |
618
+ |------|-------------|
619
+ | `senado_ceaps` | CEAPS parliamentary quota expenses by year — aggregated by senator, expense type, month or supplier, or itemized detail; filters by senator/month/type/supplier |
620
+ | `senado_senadores_admin` | Dados administrativos dos senadores via `tipo` enum: auxilio-moradia, escritorios-apoio ou aposentados |
621
+
622
+ ### Group P — Servidores / Gestão de Pessoas (4 tools)
623
+
624
+ | Tool | Description |
625
+ |------|-------------|
626
+ | `senado_servidores` | Civil servants by status (active/effective/commissioned/inactive), filterable by name, unit, position |
627
+ | `senado_remuneracoes_servidores` | Monthly payroll — summary by payroll type or per-person composition with computed gross |
628
+ | `senado_horas_extras` | Overtime payments by month with totals |
629
+ | `senado_pessoal_tabelas` | Tabelas de pessoal via `tabela` enum: quantitativos (pessoal, cargos-funcoes, previsao-aposentadoria, senadores) e listas (estagiarios, pensionistas, lotacoes, cargos) |
630
+
631
+ ### Group Q — Contratações (6 tools)
632
+
633
+ | Tool | Description |
634
+ |------|-------------|
635
+ | `senado_contratos` | Contracts with server-side filters: supplier, CNPJ, year, number, object, labor |
636
+ | `senado_contratacao_detalhe` | Items, payments, guarantees, amendments or activations of a contract/ata/empenho |
637
+ | `senado_licitacoes` | Biddings by number or object text |
638
+ | `senado_terceirizados` | Outsourced collaborators by name, company or unit |
639
+ | `senado_empresas_contratadas` | Companies contracting with the Senate (requires name/CNPJ filter) |
640
+ | `senado_contratacoes_lista` | Price-registration atas, commitment notes, young apprentices |
641
+
642
+ ### Group R — Suprimento de Fundos (1 tool)
643
+
644
+ | Tool | Description |
645
+ |------|-------------|
646
+ | `senado_suprimento_fundos` | Petty-cash advances by year: recipients, concession acts, commitments, movements, card transactions |
647
+
648
+ ### Group S — Orçamento do Senado (1 tool)
649
+
650
+ | Tool | Description |
651
+ |------|-------------|
652
+ | `senado_execucao_orcamentaria` | Budget execution since 2013 (allocation, committed/settled/paid) and own revenues since 2012 (forecast vs collected) — aggregated by year, action, expense group, source or revenue origin |
653
+
654
+ **Total: 66 tools**
655
+
656
+ ### Prompts (4)
657
+
658
+ Reusable pt-BR workflow templates (MCP `prompts` capability), defined in `src/prompts.ts`:
659
+
660
+ | Prompt | Args | What it guides |
661
+ | --- | --- | --- |
662
+ | `senado_gastos_senador` | `senador`, `ano` | Resolve o senador e agrega/detalha despesas CEAPS. |
663
+ | `senado_tramitacao_materia` | `sigla`, `numero`, `ano` | Obtém situação atual + histórico de tramitação da matéria. |
664
+ | `senado_votos_senador` | `senador`, `periodo?` | Lista os votos nominais do senador no período. |
665
+ | `senado_panorama_ecidadania` | — | Consolida consultas (consenso/polarização), ideias e eventos populares. |
666
+
667
+ ### Resources (5)
668
+
669
+ Static context documents/tables (MCP `resources` capability), defined in `src/resources.ts`:
670
+
671
+ | URI | Type | Content |
672
+ | --- | --- | --- |
673
+ | `senado://guia` | markdown | Visão geral e qual ferramenta usar por objetivo. |
674
+ | `senado://catalogo` | markdown | As 66 ferramentas agrupadas por domínio. |
675
+ | `senado://glossario` | markdown | Siglas e termos do Senado (PEC, CEAPS, CCJ, RCN…). |
676
+ | `senado://tabelas/tipos-materia` | json | Tipos de proposição (sigla/nome/descrição). |
677
+ | `senado://tabelas/ufs` | json | As 27 unidades federativas. |
678
+
679
+ ## Project Structure
680
+
681
+ ```
682
+ src/
683
+ ├── index.ts # Worker entrypoint (fetch handler + scheduled/Cron handler)
684
+ ├── server.ts # McpServer factory (creates per-request instance)
685
+ ├── auth.ts # Optional Bearer token auth (constant-time compare)
686
+ ├── metrics.ts # In-memory counters served at /metrics
687
+ ├── types.ts # Env, cache categories, safeguard constants
688
+ ├── cache/
689
+ │ ├── l0-memory.ts # In-memory Map cache with TTL + LRU eviction
690
+ │ ├── l1-cache-api.ts # Cloudflare Cache API wrapper (synthetic GET keys)
691
+ │ └── manager.ts # Cache orchestrator (L0 → L1 → upstream)
692
+ ├── throttle/
693
+ │ ├── token-bucket.ts # Token bucket rate limiter (global + per-client)
694
+ │ └── upstream.ts # Upstream fetch with concurrency limit, retry, timeout
695
+ ├── scraper/
696
+ │ ├── ecidadania.ts # Isolated e-Cidadania scraper (REST lists + regex HTML detail; buildConsultaResumo)
697
+ │ ├── pipeline.ts # 2h Cron: targeted highlight metric splice (consultas/eventos/ideias); corpora owned by the weekly jobs
698
+ │ ├── anomaly.ts # Run classification (anomalous run never overwrites current)
699
+ │ └── store.ts # D1 reads (resolveList + per-entity staleness, lastGoodRunAt) + detail write-through
700
+ ├── instrument.ts # Per-tool call telemetry (in-memory + Analytics Engine)
701
+ ├── utils/
702
+ │ ├── logger.ts # Structured JSON logging
703
+ │ └── validation.ts # toolResult, toolError, errorFrom, buildParams, ensureArray helpers
704
+ └── tools/
705
+ ├── referencia.ts # Group H — 1 reference/metadata tool
706
+ ├── senadores.ts # Group A — 5 senator tools
707
+ ├── materias.ts # Group B — 2 bill/matter tools (v3 backend)
708
+ ├── processos.ts # Group C — 5 process tools
709
+ ├── votacoes.ts # Group D — 3 vote tools
710
+ ├── comissoes.ts # Group E — 7 committee tools
711
+ ├── plenario.ts # Group F — 7 plenary tools
712
+ ├── ecidadania.ts # Group G — 8 e-Cidadania tools (read from D1; see scraper/)
713
+ ├── discursos.ts # Group I — 3 speech tools
714
+ ├── composicao.ts # Group J — 4 bloc/leadership tools
715
+ ├── orcamento.ts # Group K — 1 budget tool
716
+ ├── legislacao.ts # Group L — 2 federal law tools
717
+ ├── votacao-comissao.ts # Group M — 1 committee voting tool
718
+ ├── taquigrafia.ts # Group N — 2 stenographic record tools
719
+ ├── senadores-admin.ts # Group O — 2 admin senator tools (CEAPS, housing)
720
+ ├── servidores.ts # Group P — 4 personnel tools
721
+ ├── contratacoes.ts # Group Q — 6 procurement tools
722
+ ├── supridos.ts # Group R — 1 petty-cash tool
723
+ └── orcamento-senado.ts # Group S — 1 budget execution tool
724
+ scripts/
725
+ └── ingest-ecidadania/ # Off-Worker weekly full-corpus consultas ingestion (run via `npm run ingest:ecidadania`)
726
+ ├── index.ts # Orchestrator: crawl → status (/processo) → normalize → guards → out.sql
727
+ ├── listing.ts # Pure listing parser (parseConsultaListingPage, findLastPage)
728
+ ├── status.ts # tramitando=S set from /processo → aberta/encerrada (deriveStatus)
729
+ ├── restatus.ts # Linger fix: re-status stored rows by /processo membership (close zombies)
730
+ ├── http.ts # Polite fetch (retry/backoff) for the unattended crawl
731
+ ├── d1.ts # D1 pre-reads (existing meta, payloads, last good rows) via wrangler
732
+ └── sql.ts # out.sql generation (mirrors SQL.upsert/SQL.history; reuses SyncRecord)
733
+ .github/workflows/ # publish-mcp.yml (registry) + ingest-ecidadania.yml (weekly D1 corpus load)
734
+ migrations/ # D1 schema (0001 tables, 0002 indexes) for the e-Cidadania pipeline
735
+ tests/ # Vitest unit tests mirroring src/ (parsers, cache, throttle, auth, scraper,
736
+ # pipeline/anomaly/store, listing/sql/highlights, plus e-Cidadania contract tests)
737
+ ```
738
+
739
+ ## Environment Variables
740
+
741
+ | Variable | Required | Default | Description |
742
+ |----------|----------|---------|-------------|
743
+ | `SENADO_BASE_URL` | No | `https://legis.senado.leg.br/dadosabertos` | Legislative API base URL |
744
+ | `SENADO_ADM_BASE_URL` | No | `https://adm.senado.gov.br/adm-dadosabertos` | Administrative API base URL |
745
+ | `ALLOWED_ORIGIN` | No | `*` | CORS allowed origin |
746
+ | `API_KEY` | No (secret) | — | When set, requires `Authorization: Bearer <key>` on all requests except `/health`, `/metrics`, and CORS preflight |
747
+ | `CACHE_KV` | Yes (binding) | — | KV namespace for L2 cache |
748
+ | `ECIDADANIA_DB` | Yes (binding) | — | D1 database for the e-Cidadania pipeline (list persistence + history) |
749
+ | `ECIDADANIA_CORPUS_STALE_MAX_MIN` | No | `14400` | Staleness window (minutes, ~10d) for the weekly full corpora (all e-Cidadania entities) — served flagged, never collapsed to highlights |
750
+ | `ECIDADANIA_CORPUS_MIN_PCT` | No | `80` | Catastrophic floor for the off-Worker corpus jobs: a complete crawl/parse below this % of the last good corpus is rejected |
751
+ | `CLOUDFLARE_API_TOKEN` | No (secret) | — | GitHub Actions secret (D1 edit scope) for the weekly corpus ingestion job; not used by the Worker |
752
+ | `CLOUDFLARE_ACCOUNT_ID` | No (Actions var) | — | GitHub Actions repo variable so wrangler skips `/memberships` account auto-discovery (a D1-scoped token can't read it); required alongside `CLOUDFLARE_API_TOKEN` in the ingestion job |
753
+ | `SENADO_ANALYTICS` | No (binding) | — | Analytics Engine dataset for per-tool call telemetry |
754
+
755
+ ## Connecting MCP Clients
756
+
757
+ This is a **remote** server (Streamable HTTP, no install, open access) — point any MCP client at
758
+ `https://senado.sidneybissoli.com/mcp`. Besides 66 tools, it exposes **prompts** (ready-made pt-BR
759
+ workflows: `senado_gastos_senador`, `senado_tramitacao_materia`, `senado_votos_senador`,
760
+ `senado_panorama_ecidadania`) and **resources** (`senado://guia`, `senado://catalogo`,
761
+ `senado://glossario`, `senado://tabelas/tipos-materia`, `senado://tabelas/ufs`).
762
+
763
+ ### One-click (LobeHub)
764
+
765
+ Install from the LobeHub marketplace — open the
766
+ [server page](https://lobehub.com/mcp/sidneybissoli-senado-br-mcp-cloudflare) and click **Install**
767
+ (it pre-fills the remote endpoint, no config needed).
768
+
769
+ ### Claude Desktop / Claude Code
770
+
771
+ Add to your MCP configuration:
772
+
773
+ ```json
774
+ {
775
+ "mcpServers": {
776
+ "senado-br": {
777
+ "url": "https://senado.sidneybissoli.com/mcp"
778
+ }
779
+ }
780
+ }
781
+ ```
782
+
783
+ For command-based clients (or any client without native remote support), use the `mcp-remote` bridge:
784
+
785
+ ```json
786
+ {
787
+ "mcpServers": {
788
+ "senado-br": {
789
+ "command": "npx",
790
+ "args": ["-y", "mcp-remote", "https://senado.sidneybissoli.com/mcp"]
791
+ }
792
+ }
793
+ }
794
+ ```
795
+
796
+ ### MCP Inspector
797
+
798
+ ```bash
799
+ npx @modelcontextprotocol/inspector https://senado.sidneybissoli.com/mcp
800
+ ```
801
+
802
+ ## License
803
+
804
+ MIT
805
+
806
+ ## Credits
807
+
808
+ Icon: *"Amanhecer no Congresso Nacional"* — photograph of the Brazilian National
809
+ Congress, used under a Creative Commons license. (If you are the author, open an
810
+ issue so we can add full attribution / the license link.)