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/dist/auth.js ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Bearer token authentication for the MCP server.
3
+ *
4
+ * - Uses constant-time comparison to prevent timing attacks.
5
+ * - When no API_KEY is configured, all requests pass through (open access).
6
+ */
7
+ const encoder = new TextEncoder();
8
+ /** Constant-time string comparison using crypto.subtle.timingSafeEqual. */
9
+ export async function timingSafeEqual(a, b) {
10
+ const aBuf = encoder.encode(a);
11
+ const bBuf = encoder.encode(b);
12
+ if (aBuf.byteLength !== bBuf.byteLength)
13
+ return false;
14
+ return crypto.subtle.timingSafeEqual(aBuf, bBuf);
15
+ }
16
+ /**
17
+ * Check Bearer token authentication.
18
+ *
19
+ * @returns `null` if auth passes, or a `Response` (401/403) if it fails.
20
+ */
21
+ export async function checkAuth(request, apiKey) {
22
+ // No API_KEY configured — open access (backward compatible)
23
+ if (!apiKey)
24
+ return null;
25
+ const authHeader = request.headers.get("Authorization");
26
+ if (!authHeader) {
27
+ return new Response("Missing Authorization header", {
28
+ status: 401,
29
+ headers: { "WWW-Authenticate": "Bearer" },
30
+ });
31
+ }
32
+ // Accept "Bearer <token>" (case-insensitive scheme)
33
+ const match = authHeader.match(/^bearer\s+(.+)$/i);
34
+ if (!match) {
35
+ return new Response("Invalid Authorization format, expected: Bearer <token>", {
36
+ status: 401,
37
+ headers: { "WWW-Authenticate": "Bearer" },
38
+ });
39
+ }
40
+ const token = match[1];
41
+ const valid = await timingSafeEqual(token, apiKey);
42
+ if (!valid) {
43
+ return new Response("Invalid token", { status: 403 });
44
+ }
45
+ return null;
46
+ }
@@ -0,0 +1,30 @@
1
+ /** L0 — In-memory isolate cache with TTL. Zero external operations. */
2
+ const store = new Map();
3
+ const MAX_ENTRIES = 500;
4
+ export function l0Get(key) {
5
+ const entry = store.get(key);
6
+ if (!entry)
7
+ return undefined;
8
+ if (Date.now() > entry.expiresAt) {
9
+ store.delete(key);
10
+ return undefined;
11
+ }
12
+ return entry.value;
13
+ }
14
+ export const MAX_L0_ENTRIES = MAX_ENTRIES;
15
+ /** Reset the in-memory store. For testing only. */
16
+ export function _resetStore() {
17
+ store.clear();
18
+ }
19
+ export function l0Set(key, value, ttlSeconds) {
20
+ // Evict oldest entries if at capacity
21
+ if (store.size >= MAX_ENTRIES) {
22
+ const firstKey = store.keys().next().value;
23
+ if (firstKey !== undefined)
24
+ store.delete(firstKey);
25
+ }
26
+ store.set(key, {
27
+ value,
28
+ expiresAt: Date.now() + ttlSeconds * 1000,
29
+ });
30
+ }
@@ -0,0 +1,36 @@
1
+ /** L1 — Cloudflare Cache API (caches.default). Per-datacenter, best-effort. */
2
+ const CACHE_HOST = "https://senado-br-mcp.internal";
3
+ function buildCacheKey(tool, paramsHash) {
4
+ return new Request(`${CACHE_HOST}/__cache/${tool}/${paramsHash}`, {
5
+ method: "GET",
6
+ });
7
+ }
8
+ export async function l1Get(tool, paramsHash) {
9
+ try {
10
+ const cache = caches.default;
11
+ const key = buildCacheKey(tool, paramsHash);
12
+ const response = await cache.match(key);
13
+ if (!response)
14
+ return undefined;
15
+ return await response.text();
16
+ }
17
+ catch {
18
+ return undefined;
19
+ }
20
+ }
21
+ export async function l1Set(tool, paramsHash, body, ttlSeconds) {
22
+ try {
23
+ const cache = caches.default;
24
+ const key = buildCacheKey(tool, paramsHash);
25
+ const response = new Response(body, {
26
+ headers: {
27
+ "Cache-Control": `public, max-age=${ttlSeconds}`,
28
+ "Content-Type": "application/json",
29
+ },
30
+ });
31
+ await cache.put(key, response);
32
+ }
33
+ catch {
34
+ // Cache put failures are non-fatal
35
+ }
36
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Cache orchestrator: L0 (memory) → L1 (Cache API) → miss → fetch upstream.
3
+ * L2 (KV) is used only for rare, low-write items (see specific tool impls).
4
+ *
5
+ * Entries are persisted wrapped in a CacheEnvelope that carries `fetchedAt` — the ISO
6
+ * timestamp of the upstream extraction — so provenance (Vetor A) can report a faithful
7
+ * `retrieved_at` even when a response is served from cache (the level-1 vs level-2
8
+ * differentiator). `cachedFetchWithMeta` exposes that timestamp + a `fromCache` flag;
9
+ * `cachedFetch` stays data-only for the tools that don't need it. Reads are backward
10
+ * compatible: a non-enveloped (legacy/in-flight) entry is treated as the raw value.
11
+ */
12
+ import { l0Get, l0Set } from "./l0-memory.js";
13
+ import { l1Get, l1Set } from "./l1-cache-api.js";
14
+ import { logger } from "../utils/logger.js";
15
+ import { incr } from "../metrics.js";
16
+ import { recordFetch } from "../observability/call-context.js";
17
+ /** Compute a stable hash for cache keying. */
18
+ export async function hashParams(params) {
19
+ const sorted = JSON.stringify(params, Object.keys(params).sort());
20
+ const data = new TextEncoder().encode(sorted);
21
+ const hash = await crypto.subtle.digest("SHA-256", data);
22
+ const arr = new Uint8Array(hash);
23
+ return Array.from(arr)
24
+ .map((b) => b.toString(16).padStart(2, "0"))
25
+ .join("");
26
+ }
27
+ function isEnvelope(x) {
28
+ return (typeof x === "object" &&
29
+ x !== null &&
30
+ !Array.isArray(x) &&
31
+ "_provMeta" in x &&
32
+ "v" in x &&
33
+ typeof x._provMeta?.fetchedAt === "string");
34
+ }
35
+ /**
36
+ * Multi-layer cache-through fetch, returning the value plus the upstream-fetch timestamp
37
+ * and a cache-hit flag. On a miss, `fetchedAt` is stamped at the moment the fetcher resolves
38
+ * and persisted alongside the value; on a hit it is read back from the envelope. Legacy
39
+ * (non-enveloped) cache entries fall back to the current time, marked `fromCache: true`.
40
+ *
41
+ * Designed for graceful degradation: if hashing or cache operations fail, falls through
42
+ * to the fetcher so the tool still works.
43
+ */
44
+ async function runCachedFetch(toolName, params, category, fetcher) {
45
+ let paramsHash;
46
+ try {
47
+ paramsHash = await hashParams(params);
48
+ }
49
+ catch {
50
+ // crypto.subtle failure (unlikely but possible in some runtimes) — skip cache
51
+ const value = await fetcher();
52
+ return { value, fetchedAt: new Date().toISOString(), fromCache: false };
53
+ }
54
+ const cacheKey = `${toolName}:${paramsHash}`;
55
+ incr("toolCalls");
56
+ // L0: in-memory
57
+ const l0 = l0Get(cacheKey);
58
+ if (l0 !== undefined) {
59
+ incr("cacheL0Hits");
60
+ logger.info("cache_hit", { tool: toolName, layer: "L0" });
61
+ if (isEnvelope(l0))
62
+ return { value: l0.v, fetchedAt: l0._provMeta.fetchedAt, fromCache: true };
63
+ return { value: l0, fetchedAt: new Date().toISOString(), fromCache: true };
64
+ }
65
+ // L1: Cache API
66
+ const l1 = await l1Get(toolName, paramsHash);
67
+ if (l1 !== undefined) {
68
+ try {
69
+ const parsed = JSON.parse(l1);
70
+ l0Set(cacheKey, parsed, category.l0Ttl);
71
+ incr("cacheL1Hits");
72
+ logger.info("cache_hit", { tool: toolName, layer: "L1" });
73
+ if (isEnvelope(parsed)) {
74
+ return { value: parsed.v, fetchedAt: parsed._provMeta.fetchedAt, fromCache: true };
75
+ }
76
+ return { value: parsed, fetchedAt: new Date().toISOString(), fromCache: true };
77
+ }
78
+ catch {
79
+ // corrupted cache entry, fall through
80
+ }
81
+ }
82
+ // Miss: fetch upstream and stamp the extraction time.
83
+ incr("cacheMisses");
84
+ logger.info("cache_miss", { tool: toolName });
85
+ const value = await fetcher();
86
+ const fetchedAt = new Date().toISOString();
87
+ const envelope = { _provMeta: { fetchedAt }, v: value };
88
+ // Populate caches — failures here are non-fatal
89
+ try {
90
+ const serialized = JSON.stringify(envelope);
91
+ l0Set(cacheKey, envelope, category.l0Ttl);
92
+ await l1Set(toolName, paramsHash, serialized, category.l1Ttl);
93
+ }
94
+ catch {
95
+ // Serialization or L1 put failure — result is still valid
96
+ }
97
+ return { value, fetchedAt, fromCache: false };
98
+ }
99
+ /**
100
+ * Public entry point. Records the cache outcome of this call into the per-call
101
+ * observability accumulator (single point, so cache hits/misses are counted exactly
102
+ * once regardless of which return path `runCachedFetch` took — see call-context.ts).
103
+ */
104
+ export async function cachedFetchWithMeta(toolName, params, category, fetcher) {
105
+ const result = await runCachedFetch(toolName, params, category, fetcher);
106
+ recordFetch(result.fromCache);
107
+ return result;
108
+ }
109
+ /**
110
+ * Data-only cache-through fetch. Thin wrapper over `cachedFetchWithMeta` — keeps the
111
+ * return shape the 19 existing tool call sites rely on. Use `cachedFetchWithMeta` when
112
+ * you need the upstream `fetchedAt` (provenance) or the `fromCache` flag.
113
+ */
114
+ export async function cachedFetch(toolName, params, category, fetcher) {
115
+ return (await cachedFetchWithMeta(toolName, params, category, fetcher)).value;
116
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * npm/stdio channel entrypoint — runs the same `createServer` as the hosted
4
+ * Worker, but over the stdio transport so it can be launched via `npx senado-br-mcp`.
5
+ *
6
+ * Parity note: this binary reaches the official government APIs directly (no
7
+ * third-party hop). Everything Workers-specific degrades on its own — the L1
8
+ * Cloudflare Cache API becomes a no-op (L0 in-memory Map still works), D1 /
9
+ * Analytics / version_metadata bindings are simply absent, and `ctx.waitUntil`
10
+ * write-through is skipped (no `ctx` passed). The only behavioural gap is the
11
+ * e-Cidadania list/corpus tools: without D1 they fall back to the existing live
12
+ * scrape (~5 REST highlights), flagged via `meta.fonte` / `possivelDesatualizacao`.
13
+ *
14
+ * This file is NOT imported by the Worker entrypoint (`src/index.ts`) and never
15
+ * touches `agents/mcp`. It only depends on `createServer` + the stdio transport.
16
+ */
17
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18
+ import { createServer } from "./server.js";
19
+ // No Cloudflare bindings in Node: createServer defaults the base URLs when these
20
+ // are empty, and every binding-dependent path is guarded. CACHE_KV is declared on
21
+ // Env but never read in src/, so the cast is safe.
22
+ const env = {
23
+ SENADO_BASE_URL: process.env.SENADO_BASE_URL,
24
+ SENADO_ADM_BASE_URL: process.env.SENADO_ADM_BASE_URL,
25
+ API_KEY: process.env.API_KEY,
26
+ };
27
+ async function main() {
28
+ // No `ctx` → e-Cidadania detail write-through is a no-op (fire-and-forget skipped).
29
+ const server = createServer(env);
30
+ await server.connect(new StdioServerTransport());
31
+ // Logs go to stderr (see src/utils/logger.ts); stdout carries only JSON-RPC.
32
+ console.error(JSON.stringify({ level: "info", msg: "stdio_ready", ts: new Date().toISOString() }));
33
+ }
34
+ main().catch((err) => {
35
+ console.error(err);
36
+ process.exit(1);
37
+ });
package/dist/icon.js ADDED
@@ -0,0 +1,3 @@
1
+ /** Server logo — Congresso Nacional (Brasília) photo, 512x512 JPEG, base64-inlined.
2
+ * Served by the /icon.jpg route and referenced by serverInfo.icons. */
3
+ export const ICON_JPEG_BASE64 = "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";
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Per-tool-call instrumentation.
3
+ *
4
+ * Goal: measure *which tool the agent selected* and whether it succeeded, so the
5
+ * later tool-consolidation decision (P1) rests on real usage instead of guesses.
6
+ *
7
+ * Two sinks:
8
+ * - In-memory per-tool tallies (metrics.ts) — a live smoke test at /metrics for
9
+ * the current isolate only.
10
+ * - Analytics Engine — durable, queryable via SQL; the decision-grade signal.
11
+ *
12
+ * Instrumentation is observability, never the critical path: a failure here must
13
+ * not add latency nor alter a tool's response. `writeDataPoint` is synchronous and
14
+ * fire-and-forget in the Workers runtime (returns void, flushed out of band), so
15
+ * there is nothing to await and no need for ctx.waitUntil — a try/catch is the
16
+ * correct and sufficient guard.
17
+ *
18
+ * Privacy: only the tool name, a coarse ok/error status, and cache-outcome counts are
19
+ * recorded. No user query content, tool parameters, or PII ever reach Analytics Engine.
20
+ */
21
+ import { incr, incrTool } from "./metrics.js";
22
+ import { callCache, cacheClass } from "./observability/call-context.js";
23
+ export function instrumentTool(name, cb, analytics) {
24
+ return async (...args) => {
25
+ incr("toolCalls");
26
+ let isError = false;
27
+ // Per-call store the cache layer increments per upstream fetch (see call-context.ts).
28
+ const stats = { fetches: 0, hits: 0 };
29
+ try {
30
+ const result = await callCache.run(stats, () => cb(...args));
31
+ isError =
32
+ typeof result === "object" && result !== null && result.isError === true;
33
+ return result;
34
+ }
35
+ catch (e) {
36
+ // A thrown error is also a failed tool call — record it, then rethrow so the
37
+ // SDK still produces the normal error response.
38
+ isError = true;
39
+ throw e;
40
+ }
41
+ finally {
42
+ recordToolCall(name, isError, stats, analytics);
43
+ }
44
+ };
45
+ }
46
+ function recordToolCall(name, isError, stats, analytics) {
47
+ incrTool(name, isError);
48
+ if (!analytics)
49
+ return;
50
+ try {
51
+ analytics.writeDataPoint({
52
+ // Low-cardinality index → cheap GROUP BY in SQL. The tool name only.
53
+ indexes: [name],
54
+ // blob1 = tool name (for GROUP BY without relying on the index), blob2 = outcome,
55
+ // blob3 = cache class of the call (cached | live | partial | none).
56
+ blobs: [name, isError ? "error" : "ok", cacheClass(stats)],
57
+ // double1 = error flag (error rate via avg); double2 = upstream fetches in the call;
58
+ // double3 = how many were cache hits (fetch-level cache-hit ratio via sum/sum).
59
+ doubles: [isError ? 1 : 0, stats.fetches, stats.hits],
60
+ });
61
+ }
62
+ catch {
63
+ // Swallow: a telemetry failure must never break or slow a tool response.
64
+ }
65
+ }
@@ -0,0 +1,40 @@
1
+ /** In-memory counters accumulated per isolate lifetime. */
2
+ const counters = {
3
+ requests: 0,
4
+ errors: 0,
5
+ authFailures: 0,
6
+ cacheL0Hits: 0,
7
+ cacheL1Hits: 0,
8
+ cacheMisses: 0,
9
+ upstreamCalls: 0,
10
+ upstreamErrors: 0,
11
+ upstreamRetries: 0,
12
+ toolCalls: 0,
13
+ toolErrors: 0,
14
+ };
15
+ export function incr(name, n = 1) {
16
+ counters[name] += n;
17
+ }
18
+ /**
19
+ * Per-tool call/error tallies for the current isolate. This is a live smoke test
20
+ * of the instrumentation path (visible at /metrics) — it resets when the isolate
21
+ * recycles, so it is NOT the durable measurement. Decision-grade tool-call data
22
+ * lives in Analytics Engine, queried via SQL.
23
+ */
24
+ const perTool = {};
25
+ export function incrTool(name, isError) {
26
+ const entry = (perTool[name] ??= { calls: 0, errors: 0 });
27
+ entry.calls += 1;
28
+ if (isError)
29
+ entry.errors += 1;
30
+ }
31
+ export function getMetrics() {
32
+ return { ...counters, perTool: { ...perTool }, ts: new Date().toISOString() };
33
+ }
34
+ /** Reset all counters — test-only. */
35
+ export function _resetMetrics() {
36
+ for (const k of Object.keys(counters))
37
+ counters[k] = 0;
38
+ for (const k of Object.keys(perTool))
39
+ delete perTool[k];
40
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Per-tool-call cache accumulator (Vetor B — cache-vs-live signal).
3
+ *
4
+ * `cachedFetchWithMeta` (bottom of the stack) knows whether each upstream fetch was
5
+ * served from cache, but the instrumentation wrapper (top of the stack) only sees the
6
+ * final MCP result. This AsyncLocalStorage bridges them without polluting the response
7
+ * or touching the 19 tool call sites: `instrumentTool` runs the callback inside a fresh
8
+ * store, the cache layer increments it per fetch, and the wrapper reads it back to derive
9
+ * the analytics datapoint. Concurrency-safe (one store per async context) and entirely
10
+ * off the critical path — a missing store is a no-op.
11
+ *
12
+ * Records counts only (no PII): how many upstream fetches a call made and how many came
13
+ * from cache. See docs/observability-queries.md.
14
+ */
15
+ import { AsyncLocalStorage } from "node:async_hooks";
16
+ export const callCache = new AsyncLocalStorage();
17
+ /** Record one cache-through fetch outcome into the current call's store, if any. */
18
+ export function recordFetch(fromCache) {
19
+ const s = callCache.getStore();
20
+ if (!s)
21
+ return;
22
+ s.fetches += 1;
23
+ if (fromCache)
24
+ s.hits += 1;
25
+ }
26
+ /** Derive the categorical cache class for a finished call (blob3 in Analytics Engine). */
27
+ export function cacheClass(stats) {
28
+ if (stats.fetches === 0)
29
+ return "none";
30
+ if (stats.hits === stats.fetches)
31
+ return "cached";
32
+ if (stats.hits === 0)
33
+ return "live";
34
+ return "partial";
35
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * MCP Prompts — reusable pt-BR instruction templates that guide an agent through
3
+ * common Senate-data workflows, chaining the right tools in order.
4
+ *
5
+ * Registered per-request in server.ts via registerPrompts(server). The text builders
6
+ * are exported as pure functions so tests can target them without a transport.
7
+ */
8
+ import { z } from "zod";
9
+ /** Workflow: despesas CEAPS de um senador num ano. */
10
+ export function buildGastosSenador(senador, ano) {
11
+ return [
12
+ `Tarefa: analisar os gastos da cota parlamentar (CEAPS) do senador "${senador}" no ano de ${ano}.`,
13
+ "",
14
+ "Passos:",
15
+ `1. Resolva o senador com \`senado_listar_senadores\` (parâmetro \`nome: "${senador}"\`) e pegue o \`codigo\`.`,
16
+ `2. Chame \`senado_ceaps\` com \`ano: ${ano}\` e \`codSenador\` do passo 1 no modo \`por-tipo\` para ver a distribuição das despesas por categoria.`,
17
+ "3. Para o maior tipo de despesa, chame `senado_ceaps` novamente com `modo: \"detalhe\"` e `tipoDespesa` correspondente para listar os lançamentos e fornecedores.",
18
+ "4. Apresente um resumo: valor total no ano, top categorias, e quaisquer fornecedores recorrentes. Cite valores em reais.",
19
+ ].join("\n");
20
+ }
21
+ /** Workflow: situação e histórico de tramitação de uma matéria. */
22
+ export function buildTramitacaoMateria(sigla, numero, ano) {
23
+ const id = `${sigla} ${numero}/${ano}`;
24
+ return [
25
+ `Tarefa: descrever a situação atual e a tramitação da matéria ${id}.`,
26
+ "",
27
+ "Passos:",
28
+ `1. Localize a matéria com \`senado_buscar_materias\` (\`sigla: "${sigla}"\`, \`numero: ${numero}\`, \`ano: ${ano}\`) e pegue o \`codigo\` (codigoMateria).`,
29
+ "2. Chame `senado_obter_materia` com `secao: \"detalhe\"` para situação atual, autor, relator e ementa.",
30
+ "3. Chame `senado_obter_materia` com `secao: \"tramitacao\"` para o histórico cronológico de movimentações.",
31
+ "4. (Opcional) `secao: \"textos\"` para os documentos/pareceres.",
32
+ "5. Resuma: o que a matéria propõe, onde está hoje, relator, e os marcos recentes da tramitação.",
33
+ ].join("\n");
34
+ }
35
+ /** Workflow: como um senador votou num período. */
36
+ export function buildVotosSenador(senador, periodo) {
37
+ const janela = periodo && periodo.trim()
38
+ ? `no período ${periodo}`
39
+ : "no ano corrente";
40
+ return [
41
+ `Tarefa: mostrar como o senador "${senador}" votou ${janela}.`,
42
+ "",
43
+ "Passos:",
44
+ `1. Resolva o senador com \`senado_listar_senadores\` (\`nome: "${senador}"\`) e pegue o \`codigo\`.`,
45
+ `2. Chame \`senado_votacoes_senador\` com esse \`codigoSenador\`${periodo && periodo.trim() ? ` e o período (\`dataInicio\`/\`dataFim\` em YYYYMMDD cobrindo ${periodo})` : ""}.`,
46
+ "3. (Opcional) Para detalhar uma votação específica, use `senado_obter_votacao` com o `codigoVotacao`.",
47
+ "4. Resuma o posicionamento: total de votos Sim/Não/Abstenção, e destaque votações relevantes com a matéria e o resultado.",
48
+ ].join("\n");
49
+ }
50
+ /** Workflow: panorama da participação cidadã no e-Cidadania. */
51
+ export function buildPanoramaEcidadania() {
52
+ return [
53
+ "Tarefa: montar um panorama da participação cidadã atual no portal e-Cidadania do Senado.",
54
+ "",
55
+ "Passos:",
56
+ "1. `senado_ecidadania_listar_consultas` para as consultas públicas abertas (votos sim/não).",
57
+ "2. `senado_ecidadania_consultas_analise` com `modo: \"consenso\"` e depois `modo: \"polarizada\"` para os temas de maior concordância e os mais divididos.",
58
+ "3. `senado_ecidadania_listar_ideias` com `ordenarPor: \"apoios\"`, `ordem: \"desc\"`, `status: \"aberta\"` para as ideias legislativas mais apoiadas.",
59
+ "4. `senado_ecidadania_listar_eventos` com `ordenarPor: \"comentarios\"`, `ordem: \"desc\"` para os eventos com mais participação.",
60
+ "5. Sintetize: temas de consenso, temas polêmicos, ideias em destaque e eventos relevantes — com os números de participação.",
61
+ ].join("\n");
62
+ }
63
+ export function registerPrompts(server) {
64
+ server.registerPrompt("senado_gastos_senador", {
65
+ title: "Gastos do senador (CEAPS)",
66
+ description: "Guia o passo a passo para analisar as despesas da cota parlamentar (CEAPS) de um senador num ano, usando senado_listar_senadores e senado_ceaps.",
67
+ argsSchema: {
68
+ senador: z.string().describe("Nome (ou parte) do senador"),
69
+ ano: z.string().describe("Ano das despesas (ex: 2025)"),
70
+ },
71
+ }, (args) => ({
72
+ messages: [{ role: "user", content: { type: "text", text: buildGastosSenador(args.senador, args.ano) } }],
73
+ }));
74
+ server.registerPrompt("senado_tramitacao_materia", {
75
+ title: "Tramitação de uma matéria",
76
+ description: "Guia o passo a passo para obter a situação atual e o histórico de tramitação de uma proposição, usando senado_buscar_materias e senado_obter_materia.",
77
+ argsSchema: {
78
+ sigla: z.string().describe("Tipo da proposição (ex: PEC, PL, PLP, MPV)"),
79
+ numero: z.string().describe("Número da proposição"),
80
+ ano: z.string().describe("Ano da proposição"),
81
+ },
82
+ }, (args) => ({
83
+ messages: [{ role: "user", content: { type: "text", text: buildTramitacaoMateria(args.sigla, args.numero, args.ano) } }],
84
+ }));
85
+ server.registerPrompt("senado_votos_senador", {
86
+ title: "Como votou um senador",
87
+ description: "Guia o passo a passo para listar os votos nominais de um senador num período, usando senado_listar_senadores e senado_votacoes_senador.",
88
+ argsSchema: {
89
+ senador: z.string().describe("Nome (ou parte) do senador"),
90
+ periodo: z.string().optional().describe("Período em texto livre (ex: '2024' ou 'jan a jun 2025'); vazio = ano corrente"),
91
+ },
92
+ }, (args) => ({
93
+ messages: [{ role: "user", content: { type: "text", text: buildVotosSenador(args.senador, args.periodo) } }],
94
+ }));
95
+ server.registerPrompt("senado_panorama_ecidadania", {
96
+ title: "Panorama do e-Cidadania",
97
+ description: "Guia o passo a passo para consolidar consultas (consenso/polarização), ideias e eventos populares do portal e-Cidadania.",
98
+ }, () => ({
99
+ messages: [{ role: "user", content: { type: "text", text: buildPanoramaEcidadania() } }],
100
+ }));
101
+ }