senado-br-mcp 3.10.0 → 3.12.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 +2 -1
- package/dist/call-shape.js +62 -0
- package/dist/data/estrutura-organizacional.js +9 -21
- package/dist/instrument.js +11 -3
- package/dist/scraper/ecidadania.js +62 -63
- package/dist/server.js +8 -8
- package/dist/throttle/upstream.js +248 -140
- package/dist/tools/comissoes.js +28 -47
- package/dist/tools/contratacoes.js +5 -5
- package/dist/tools/deep-research.js +19 -5
- package/dist/tools/discursos.js +13 -40
- package/dist/tools/ecidadania.js +4 -10
- package/dist/tools/estrutura.js +1 -1
- package/dist/tools/legislacao.js +2 -2
- package/dist/tools/materias.js +1 -1
- package/dist/tools/plenario.js +1 -1
- package/dist/tools/processos.js +2 -2
- package/dist/tools/resolver-reuniao.js +2 -0
- package/dist/tools/servidores.js +1 -1
- package/dist/tools/votacao-comissao.js +3 -3
- package/dist/tools/votacoes.js +1 -1
- package/dist/utils/provenance.js +2 -0
- package/dist/utils/validation.js +31 -5
- package/dist/version.js +1 -1
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
[](https://lobehub.com/mcp/sidneybissoli-senado-br-mcp-cloudflare)
|
|
9
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
|
+
[](https://smithery.ai/servers/sidneybissoli/senado-br-mcp-cloudflare)
|
|
11
12
|
[](https://github.com/SidneyBissoli/senado-br-mcp-cloudflare)
|
|
12
13
|
[](https://github.com/sponsors/SidneyBissoli)
|
|
13
14
|
[](LICENSE)
|
|
@@ -153,7 +154,7 @@ Claude Code auto-discovers it when you work in this repo. To use it elsewhere, c
|
|
|
153
154
|
- **Caching:** 2-layer (L0 memory + L1 Cache API) with SHA-256 keying
|
|
154
155
|
- **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))
|
|
155
156
|
- **Rate limiting:** Token bucket — global (8 req/s) + per-client (2 req/s)
|
|
156
|
-
- **Upstream throttle:** Max 6 concurrent requests, 10s
|
|
157
|
+
- **Upstream throttle:** Max 6 concurrent requests, 10s budget per trip, retry on 429/5xx/network with exponential backoff and `Retry-After` (portfolio shared fetch `@sbissoli/mcp-upstream`); every response's provenance carries the measured `retrieval` diagnostics (trips, attempts, anomalies)
|
|
157
158
|
- **Auth:** Optional Bearer token (set the `API_KEY` secret; open access when unset). Constant-time comparison.
|
|
158
159
|
- **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
|
|
159
160
|
- **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
|
package/dist/call-shape.js
CHANGED
|
@@ -171,8 +171,70 @@ export function classifyThrown(error) {
|
|
|
171
171
|
error instanceof SyntaxError) {
|
|
172
172
|
return "defeito";
|
|
173
173
|
}
|
|
174
|
+
const declarada = classeDeclarada(error);
|
|
175
|
+
if (declarada)
|
|
176
|
+
return declarada;
|
|
174
177
|
return classifyError(error instanceof Error ? error.message : String(error));
|
|
175
178
|
}
|
|
179
|
+
const CLASSES = new Set(["contrato", "nao_encontrado", "fonte", "defeito", "outro"]);
|
|
180
|
+
function ehClasse(x) {
|
|
181
|
+
return typeof x === "string" && CLASSES.has(x);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* A classe com que o erro NASCEU (`UpstreamError.classe`, o erro do
|
|
185
|
+
* e-Cidadania), ou `undefined` quando ele não declara nenhuma — aí vale a
|
|
186
|
+
* frase, como sempre.
|
|
187
|
+
*/
|
|
188
|
+
export function classeDeclarada(error) {
|
|
189
|
+
const c = error?.classe;
|
|
190
|
+
return ehClasse(c) ? c : undefined;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Onde um resultado de erro leva a classe decidida pelo TIPO da exceção.
|
|
194
|
+
*
|
|
195
|
+
* Por que existe. Medido em 30/09/2026, rodando este classificador sobre as
|
|
196
|
+
* mensagens de falha da origem: "Erro de rede: ..." (base legislativa), o 429
|
|
197
|
+
* do nosso próprio balde ("Taxa de requisições excedida") e, no e-Cidadania,
|
|
198
|
+
* "falha de rede ao acessar" e "retornou HTTP 429/400/403" caíam em `outro`.
|
|
199
|
+
* O tipo da falha existia no `catch` e só o texto chegava ao `instrumentTool`.
|
|
200
|
+
*
|
|
201
|
+
* O mesmo defeito de fundo foi consertado no bcb-br-mcp (#45), no
|
|
202
|
+
* ilo-mcp-server (#24), no uis-mcp-server (#22), no ibge-br-mcp (#62) e no
|
|
203
|
+
* medical-terminologies-mcp (#58). O conserto não reescreve frase nem mexe em
|
|
204
|
+
* regex: a classe viaja AO LADO do texto, nesta chave-símbolo não enumerável,
|
|
205
|
+
* que o `JSON.stringify` não vê — o que o cliente recebe não muda.
|
|
206
|
+
* `instrumentTool` a lê antes de cair na frase.
|
|
207
|
+
*/
|
|
208
|
+
export const CLASSE_DO_ERRO = Symbol.for("br.com.sidneybissoli.mcp/classe-do-erro");
|
|
209
|
+
/** A classe anexada a um resultado de erro, ou `undefined` quando não há. */
|
|
210
|
+
export function classeAnexada(result) {
|
|
211
|
+
if (!result || typeof result !== "object")
|
|
212
|
+
return undefined;
|
|
213
|
+
const c = result[CLASSE_DO_ERRO];
|
|
214
|
+
return ehClasse(c) ? c : undefined;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Anexa ao resultado, fora do fio, a classe que o erro declara — ou `defeito`,
|
|
218
|
+
* quando é bug nosso (`TypeError` & cia., como em `classifyThrown`). Devolve o
|
|
219
|
+
* próprio resultado.
|
|
220
|
+
*
|
|
221
|
+
* Medido em 30/09/2026 na varredura da frota: os ~60 `catch` que passam por
|
|
222
|
+
* `errorFrom` engolem a exceção antes do hook, e um "Cannot read properties of
|
|
223
|
+
* undefined" saía `outro`. A falha de rede crua da undici também é `TypeError`
|
|
224
|
+
* ("fetch failed"); toda ida passa por throttle/upstream.ts, que a tipa — a
|
|
225
|
+
* exclusão é guarda, para uma ida crua futura não virar `defeito`.
|
|
226
|
+
*/
|
|
227
|
+
export function anexarClasse(result, error) {
|
|
228
|
+
const classe = classeDeclarada(error) ?? (ehBugNosso(error) ? "defeito" : undefined);
|
|
229
|
+
if (classe !== undefined)
|
|
230
|
+
Object.defineProperty(result, CLASSE_DO_ERRO, { value: classe, enumerable: false });
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
function ehBugNosso(error) {
|
|
234
|
+
if (error instanceof TypeError)
|
|
235
|
+
return error.message !== "fetch failed";
|
|
236
|
+
return error instanceof RangeError || error instanceof ReferenceError || error instanceof SyntaxError;
|
|
237
|
+
}
|
|
176
238
|
/**
|
|
177
239
|
* Classe de um erro JSON-RPC do PROTOCOLO, pelo código antes da mensagem.
|
|
178
240
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const ESTRUTURA_ORGANIZACIONAL = {
|
|
2
|
-
"extraidoEm": "2026-
|
|
2
|
+
"extraidoEm": "2026-10-01T12:13:07.708Z",
|
|
3
3
|
"fonteUrl": "https://www12.senado.leg.br/institucional/estrutura/orgaosenado",
|
|
4
|
-
"total":
|
|
4
|
+
"total": 821,
|
|
5
5
|
"orgaos": [
|
|
6
6
|
{
|
|
7
7
|
"cod": -2072346465,
|
|
@@ -3129,12 +3129,6 @@ export const ESTRUTURA_ORGANIZACIONAL = {
|
|
|
3129
3129
|
"nome": "Escritório de Apoio Nº 01 do Senador Chico Rodrigues",
|
|
3130
3130
|
"codSuperior": 10057
|
|
3131
3131
|
},
|
|
3132
|
-
{
|
|
3133
|
-
"cod": 10156,
|
|
3134
|
-
"sigla": null,
|
|
3135
|
-
"nome": "Escritório de Apoio Nº 01 do Senador Angelo Coronel",
|
|
3136
|
-
"codSuperior": 10046
|
|
3137
|
-
},
|
|
3138
3132
|
{
|
|
3139
3133
|
"cod": 10157,
|
|
3140
3134
|
"sigla": null,
|
|
@@ -4631,7 +4625,7 @@ export const ESTRUTURA_ORGANIZACIONAL = {
|
|
|
4631
4625
|
},
|
|
4632
4626
|
{
|
|
4633
4627
|
"cod": 10749,
|
|
4634
|
-
"sigla":
|
|
4628
|
+
"sigla": "CSADVOSF",
|
|
4635
4629
|
"nome": "Conselho Superior da Advocacia do Senado Federal",
|
|
4636
4630
|
"codSuperior": 67
|
|
4637
4631
|
},
|
|
@@ -4887,18 +4881,6 @@ export const ESTRUTURA_ORGANIZACIONAL = {
|
|
|
4887
4881
|
"nome": "Gabinete da Liderança do Avante",
|
|
4888
4882
|
"codSuperior": 109
|
|
4889
4883
|
},
|
|
4890
|
-
{
|
|
4891
|
-
"cod": 10847,
|
|
4892
|
-
"sigla": "GSHKLANN",
|
|
4893
|
-
"nome": "Gabinete do Senador Hermes Klann",
|
|
4894
|
-
"codSuperior": 108
|
|
4895
|
-
},
|
|
4896
|
-
{
|
|
4897
|
-
"cod": 10850,
|
|
4898
|
-
"sigla": null,
|
|
4899
|
-
"nome": "Escritório de Apoio Nº 01 do Senador Hermes Klann",
|
|
4900
|
-
"codSuperior": 10847
|
|
4901
|
-
},
|
|
4902
4884
|
{
|
|
4903
4885
|
"cod": 10857,
|
|
4904
4886
|
"sigla": null,
|
|
@@ -4940,6 +4922,12 @@ export const ESTRUTURA_ORGANIZACIONAL = {
|
|
|
4940
4922
|
"sigla": null,
|
|
4941
4923
|
"nome": "Escritório de Apoio Nº 01 do Senador Sargento Reginauro",
|
|
4942
4924
|
"codSuperior": 10862
|
|
4925
|
+
},
|
|
4926
|
+
{
|
|
4927
|
+
"cod": 10866,
|
|
4928
|
+
"sigla": "GSRBRAZ",
|
|
4929
|
+
"nome": "Gabinete do Senador Renzo Braz",
|
|
4930
|
+
"codSuperior": 108
|
|
4943
4931
|
}
|
|
4944
4932
|
]
|
|
4945
4933
|
};
|
package/dist/instrument.js
CHANGED
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
* (SELF_HEADER, value = the SELF_MARKER Wrangler secret).
|
|
30
30
|
*/
|
|
31
31
|
import { resolveDesfecho } from "./envelope.js";
|
|
32
|
-
import { classifyError, classifyThrown, errorText, paramNames } from "./call-shape.js";
|
|
32
|
+
import { classeAnexada, classifyError, classifyThrown, errorText, paramNames } from "./call-shape.js";
|
|
33
33
|
import { incr, incrTool } from "./metrics.js";
|
|
34
34
|
import { callCache, cacheClass } from "./observability/call-context.js";
|
|
35
|
+
import { withUpstreamCall } from "./throttle/upstream.js";
|
|
35
36
|
/** Header the owner's MCP clients send (value = the SELF_MARKER secret). */
|
|
36
37
|
export const SELF_HEADER = "x-mcp-self";
|
|
37
38
|
/**
|
|
@@ -71,11 +72,18 @@ export function instrumentTool(name, cb, analytics, tag, gravados) {
|
|
|
71
72
|
// Per-call store the cache layer increments per upstream fetch (see call-context.ts).
|
|
72
73
|
const stats = { fetches: 0, hits: 0 };
|
|
73
74
|
try {
|
|
74
|
-
|
|
75
|
+
// Dois contextos por chamada, aninhados: o coletor de REDE do fetch comum
|
|
76
|
+
// (idas, tentativas, anomalias → `retrieval` da proveniência; ver
|
|
77
|
+
// src/throttle/upstream.ts) e o acumulador de CACHE (acertos → blob3).
|
|
78
|
+
// Este é o único lugar em que uma chamada de tool começa — stdio e Worker
|
|
79
|
+
// passam pelo mesmo funil (`host.tool` em src/server.ts).
|
|
80
|
+
const result = await withUpstreamCall(() => callCache.run(stats, () => Promise.resolve(cb(...args))));
|
|
75
81
|
isError =
|
|
76
82
|
typeof result === "object" && result !== null && result.isError === true;
|
|
83
|
+
// A classe que o handler anexou pelo TIPO do erro vence a frase; a frase
|
|
84
|
+
// fica para o erro que nasceu sem tipo. Ver CLASSE_DO_ERRO.
|
|
77
85
|
if (isError)
|
|
78
|
-
classe = classifyError(errorText(result));
|
|
86
|
+
classe = classeAnexada(result) ?? classifyError(errorText(result));
|
|
79
87
|
return result;
|
|
80
88
|
}
|
|
81
89
|
catch (e) {
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
* Everything here is pure parsing + fetch, with no MCP/tool/cache dependency, so it can be
|
|
10
10
|
* driven by the Cron pipeline (step 3) and covered by fixture-based contract tests.
|
|
11
11
|
*/
|
|
12
|
+
import { UpstreamError as PkgUpstreamError } from "@sbissoli/mcp-upstream";
|
|
12
13
|
import { UPSTREAM_TIMEOUT_MS } from "../types.js";
|
|
13
14
|
import { USER_AGENT } from "../version.js";
|
|
15
|
+
import { upstreamCall } from "../throttle/upstream.js";
|
|
14
16
|
export const ECIDADANIA_BASE = "https://www12.senado.leg.br/ecidadania";
|
|
15
17
|
/**
|
|
16
18
|
* Error carrying a `retryable` flag, mirroring UpstreamError so the tool layer
|
|
@@ -18,8 +20,14 @@ export const ECIDADANIA_BASE = "https://www12.senado.leg.br/ecidadania";
|
|
|
18
20
|
* (not `upstreamFetch`), so transient conditions are classified here: 5xx / 429 /
|
|
19
21
|
* timeout / network → retryable; 4xx → not.
|
|
20
22
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
/**
|
|
24
|
+
* `classe`, quando dada, é a classe de telemetria decidida pelo TIPO da falha
|
|
25
|
+
* (ver `CLASSE_DO_ERRO` em src/call-shape.ts). Medido em 30/09/2026: pela
|
|
26
|
+
* frase, "falha de rede ao acessar" e "retornou HTTP 429/400/403" caíam em
|
|
27
|
+
* `outro`. Sem ela, vale a frase, como sempre.
|
|
28
|
+
*/
|
|
29
|
+
function ecidadaniaFetchError(message, retryable, classe) {
|
|
30
|
+
return Object.assign(new Error(message), classe ? { retryable, classe } : { retryable });
|
|
23
31
|
}
|
|
24
32
|
const isTransientStatus = (status) => status >= 500 || status === 429;
|
|
25
33
|
/**
|
|
@@ -28,78 +36,69 @@ const isTransientStatus = (status) => status >= 500 || status === 429;
|
|
|
28
36
|
* (`X-Requested-With: XMLHttpRequest`, wider `Accept`).
|
|
29
37
|
*/
|
|
30
38
|
export async function fetchPage(path, opts = {}) {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
signal: controller.signal,
|
|
40
|
-
});
|
|
41
|
-
clearTimeout(timeout);
|
|
42
|
-
if (!resp.ok) {
|
|
43
|
-
throw ecidadaniaFetchError(`e-Cidadania retornou HTTP ${resp.status} para ${path}`, isTransientStatus(resp.status));
|
|
44
|
-
}
|
|
45
|
-
const text = await resp.text();
|
|
46
|
-
// AJAX comment fragments are legitimately empty for events with zero comments — allowEmpty
|
|
47
|
-
// lets the caller treat "" as a valid (empty) result instead of a transient failure.
|
|
48
|
-
if (!opts.allowEmpty && !text.trim()) {
|
|
49
|
-
throw ecidadaniaFetchError(`e-Cidadania retornou página vazia para ${path}`, true);
|
|
50
|
-
}
|
|
51
|
-
return text;
|
|
52
|
-
}
|
|
53
|
-
catch (e) {
|
|
54
|
-
clearTimeout(timeout);
|
|
55
|
-
if (e.name === "AbortError") {
|
|
56
|
-
throw ecidadaniaFetchError(`e-Cidadania: timeout (${UPSTREAM_TIMEOUT_MS / 1000}s) ao acessar ${path}`, true);
|
|
57
|
-
}
|
|
58
|
-
if (e instanceof Error && "retryable" in e)
|
|
59
|
-
throw e; // already classified above
|
|
60
|
-
throw ecidadaniaFetchError(`e-Cidadania: falha de rede ao acessar ${path} (${e.message})`, true);
|
|
39
|
+
const resp = await idaEcidadania(path, opts.ajax
|
|
40
|
+
? { Accept: "text/html,*/*", "User-Agent": USER_AGENT, "X-Requested-With": "XMLHttpRequest" }
|
|
41
|
+
: { Accept: "text/html", "User-Agent": USER_AGENT }, "e-Cidadania");
|
|
42
|
+
const text = await resp.text();
|
|
43
|
+
// AJAX comment fragments are legitimately empty for events with zero comments — allowEmpty
|
|
44
|
+
// lets the caller treat "" as a valid (empty) result instead of a transient failure.
|
|
45
|
+
if (!opts.allowEmpty && !text.trim()) {
|
|
46
|
+
throw ecidadaniaFetchError(`e-Cidadania retornou página vazia para ${path}`, true);
|
|
61
47
|
}
|
|
48
|
+
return text;
|
|
62
49
|
}
|
|
63
50
|
/**
|
|
64
51
|
* Fetch JSON from an e-Cidadania REST endpoint with validation.
|
|
65
52
|
* Ensures the response is valid JSON and an array.
|
|
66
53
|
*/
|
|
67
54
|
export async function fetchEcidadaniaJson(endpoint) {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
const timeout = setTimeout(() => controller.abort(), UPSTREAM_TIMEOUT_MS);
|
|
55
|
+
const resp = await idaEcidadania(endpoint, { Accept: "application/json", "User-Agent": USER_AGENT }, "e-Cidadania REST API");
|
|
56
|
+
let data;
|
|
71
57
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
58
|
+
data = await resp.json();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
throw ecidadaniaFetchError(`e-Cidadania REST API retornou JSON inválido para ${endpoint}`, true);
|
|
62
|
+
}
|
|
63
|
+
if (!Array.isArray(data)) {
|
|
64
|
+
// API returned an object or other non-array — wrap or return empty
|
|
65
|
+
if (data && typeof data === "object")
|
|
66
|
+
return [data];
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A ida ao portal, pelo fetch comum do portfólio (desde a 3.11.0; antes um
|
|
73
|
+
* `fetch` cru com timeout de 10 s e sem retry). Mesma política da API do Senado
|
|
74
|
+
* (`upstreamSenado`: teto 10 s, 5xx/429/rede repetem, timeout não) e mesmo
|
|
75
|
+
* coletor da chamada de tool — o detalhe raspado ao vivo entra no `retrieval`
|
|
76
|
+
* do bloco e-Cidadania. No cron (`scheduled`, fora de uma chamada) o coletor é
|
|
77
|
+
* descartável e a política vale igual. O token bucket NÃO se aplica: é outro
|
|
78
|
+
* host, e o portal nunca passou por ele. As mensagens e o `retryable` são os
|
|
79
|
+
* de antes — `ecidadaniaError` (tools) e o pipeline os leem.
|
|
80
|
+
*/
|
|
81
|
+
async function idaEcidadania(path, headers, rotulo) {
|
|
82
|
+
const url = `${ECIDADANIA_BASE}${path}`;
|
|
83
|
+
try {
|
|
84
|
+
return await upstreamCall().response(url, { headers });
|
|
94
85
|
}
|
|
95
86
|
catch (e) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
87
|
+
if (!(e instanceof PkgUpstreamError))
|
|
88
|
+
throw e;
|
|
89
|
+
switch (e.kind) {
|
|
90
|
+
case "timeout":
|
|
91
|
+
case "aborted":
|
|
92
|
+
throw ecidadaniaFetchError(`${rotulo}: timeout (${UPSTREAM_TIMEOUT_MS / 1000}s) ao acessar ${path}`, true, "fonte");
|
|
93
|
+
case "network": {
|
|
94
|
+
const causa = e.cause instanceof Error ? e.cause.message : String(e.cause ?? "desconhecido");
|
|
95
|
+
throw ecidadaniaFetchError(`${rotulo}: falha de rede ao acessar ${path} (${causa})`, true, "fonte");
|
|
96
|
+
}
|
|
97
|
+
default: {
|
|
98
|
+
const status = e.status ?? 502;
|
|
99
|
+
throw ecidadaniaFetchError(`${rotulo} retornou HTTP ${status} para ${path}`, isTransientStatus(status), status === 404 ? "nao_encontrado" : "fonte");
|
|
100
|
+
}
|
|
99
101
|
}
|
|
100
|
-
if (e instanceof Error && "retryable" in e)
|
|
101
|
-
throw e; // already classified above
|
|
102
|
-
throw ecidadaniaFetchError(`e-Cidadania REST API: falha de rede ao acessar ${endpoint} (${e.message})`, true);
|
|
103
102
|
}
|
|
104
103
|
}
|
|
105
104
|
/** Parse Brazilian number: 1.234.567 -> 1234567 */
|
package/dist/server.js
CHANGED
|
@@ -50,14 +50,14 @@ import { announceServedVersions } from "./discover.js";
|
|
|
50
50
|
* `.strict()` publica `additionalProperties: false` e faz o SDK responder
|
|
51
51
|
* `Unrecognized key: "<nome>"`, que NOMEIA a chave.
|
|
52
52
|
*
|
|
53
|
-
* `search` e `fetch`
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* Vale para TODAS, `search` e `fetch` inclusive. Até a 3.11.0 as duas ficavam
|
|
54
|
+
* abertas por serem contrato da OpenAI; desde o `@sbissoli/mcp-search` 0.9.0
|
|
55
|
+
* o próprio contrato do portfólio é estrito (`z.strictObject`), e as duas
|
|
56
|
+
* entram por este funil (registerDeepResearchToolsSenado passa a shape) —
|
|
57
|
+
* sem o `.strict()` aqui, a estrição do pacote se perderia na passagem.
|
|
56
58
|
*/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const objeto = z.object(shape);
|
|
60
|
-
return TOOLS_DE_CONTRATO_ALHEIO.has(name) ? objeto : objeto.strict();
|
|
59
|
+
function esquemaDeEntrada(shape) {
|
|
60
|
+
return z.object(shape).strict();
|
|
61
61
|
}
|
|
62
62
|
export function createServer(env, ctx, options = {}) {
|
|
63
63
|
const toolProfile = options.toolProfile ?? "full";
|
|
@@ -102,7 +102,7 @@ export function createServer(env, ctx, options = {}) {
|
|
|
102
102
|
return registerTool(name, {
|
|
103
103
|
title: titleForTool(name),
|
|
104
104
|
description,
|
|
105
|
-
inputSchema: esquemaDeEntrada(
|
|
105
|
+
inputSchema: esquemaDeEntrada(shape),
|
|
106
106
|
outputSchema: outputSchema,
|
|
107
107
|
annotations: {
|
|
108
108
|
title: titleForTool(name),
|