@mcpassure/mcp-anvisa-bulario 0.0.1 → 0.1.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/LICENSE +21 -0
- package/README.en.md +196 -0
- package/README.md +192 -5
- package/dist/cache/sqlite-cache.d.ts +13 -0
- package/dist/cache/sqlite-cache.d.ts.map +1 -0
- package/dist/cache/sqlite-cache.js +58 -0
- package/dist/cache/sqlite-cache.js.map +1 -0
- package/dist/cache/types.d.ts +17 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +7 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +17 -0
- package/dist/config.js.map +1 -0
- package/dist/domain/repository.d.ts +47 -0
- package/dist/domain/repository.d.ts.map +1 -0
- package/dist/domain/repository.js +88 -0
- package/dist/domain/repository.js.map +1 -0
- package/dist/domain/types.d.ts +2 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +2 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/handlers.d.ts +24 -0
- package/dist/prompts/handlers.d.ts.map +1 -0
- package/dist/prompts/handlers.js +267 -0
- package/dist/prompts/handlers.js.map +1 -0
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +35 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/schemas.d.ts +22 -0
- package/dist/prompts/schemas.d.ts.map +1 -0
- package/dist/prompts/schemas.js +15 -0
- package/dist/prompts/schemas.js.map +1 -0
- package/dist/resources/handlers.d.ts +5 -0
- package/dist/resources/handlers.d.ts.map +1 -0
- package/dist/resources/handlers.js +145 -0
- package/dist/resources/handlers.js.map +1 -0
- package/dist/resources/index.d.ts +4 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +48 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/schemas/tools.d.ts +134 -0
- package/dist/schemas/tools.d.ts.map +1 -0
- package/dist/schemas/tools.js +106 -0
- package/dist/schemas/tools.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +25 -0
- package/dist/server.js.map +1 -0
- package/dist/sources/anvisa-api.d.ts +25 -0
- package/dist/sources/anvisa-api.d.ts.map +1 -0
- package/dist/sources/anvisa-api.js +153 -0
- package/dist/sources/anvisa-api.js.map +1 -0
- package/dist/sources/anvisa-dados-abertos.d.ts +12 -0
- package/dist/sources/anvisa-dados-abertos.d.ts.map +1 -0
- package/dist/sources/anvisa-dados-abertos.js +27 -0
- package/dist/sources/anvisa-dados-abertos.js.map +1 -0
- package/dist/sources/anvisa-portal.d.ts +12 -0
- package/dist/sources/anvisa-portal.d.ts.map +1 -0
- package/dist/sources/anvisa-portal.js +27 -0
- package/dist/sources/anvisa-portal.js.map +1 -0
- package/dist/sources/types.d.ts +56 -0
- package/dist/sources/types.d.ts.map +1 -0
- package/dist/sources/types.js +2 -0
- package/dist/sources/types.js.map +1 -0
- package/dist/tools/buscar-por-classe-terapeutica.d.ts +4 -0
- package/dist/tools/buscar-por-classe-terapeutica.d.ts.map +1 -0
- package/dist/tools/buscar-por-classe-terapeutica.js +30 -0
- package/dist/tools/buscar-por-classe-terapeutica.js.map +1 -0
- package/dist/tools/buscar-por-nome.d.ts +4 -0
- package/dist/tools/buscar-por-nome.d.ts.map +1 -0
- package/dist/tools/buscar-por-nome.js +25 -0
- package/dist/tools/buscar-por-nome.js.map +1 -0
- package/dist/tools/buscar-por-principio-ativo.d.ts +4 -0
- package/dist/tools/buscar-por-principio-ativo.d.ts.map +1 -0
- package/dist/tools/buscar-por-principio-ativo.js +25 -0
- package/dist/tools/buscar-por-principio-ativo.js.map +1 -0
- package/dist/tools/consultar-bula.d.ts +4 -0
- package/dist/tools/consultar-bula.d.ts.map +1 -0
- package/dist/tools/consultar-bula.js +42 -0
- package/dist/tools/consultar-bula.js.map +1 -0
- package/dist/tools/filtrar-por-tarja.d.ts +4 -0
- package/dist/tools/filtrar-por-tarja.d.ts.map +1 -0
- package/dist/tools/filtrar-por-tarja.js +29 -0
- package/dist/tools/filtrar-por-tarja.js.map +1 -0
- package/dist/tools/listar-apresentacoes.d.ts +4 -0
- package/dist/tools/listar-apresentacoes.d.ts.map +1 -0
- package/dist/tools/listar-apresentacoes.js +34 -0
- package/dist/tools/listar-apresentacoes.js.map +1 -0
- package/dist/tools/shared.d.ts +97 -0
- package/dist/tools/shared.d.ts.map +1 -0
- package/dist/tools/shared.js +34 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/utils/backoff.d.ts +8 -0
- package/dist/utils/backoff.d.ts.map +1 -0
- package/dist/utils/backoff.js +37 -0
- package/dist/utils/backoff.js.map +1 -0
- package/dist/utils/http.d.ts +45 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +175 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/meta.d.ts +8 -0
- package/dist/utils/meta.d.ts.map +1 -0
- package/dist/utils/meta.js +13 -0
- package/dist/utils/meta.js.map +1 -0
- package/dist/utils/playwright-http.d.ts +2 -0
- package/dist/utils/playwright-http.d.ts.map +1 -0
- package/dist/utils/playwright-http.js +4 -0
- package/dist/utils/playwright-http.js.map +1 -0
- package/package.json +68 -6
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { ApresentacoesOutput, BulaOutput, MedicamentoListaOutput } from "../schemas/tools.js";
|
|
2
|
+
export declare const TOOL_ANNOTATIONS: {
|
|
3
|
+
readonly readOnlyHint: true;
|
|
4
|
+
readonly idempotentHint: true;
|
|
5
|
+
readonly destructiveHint: false;
|
|
6
|
+
readonly openWorldHint: true;
|
|
7
|
+
};
|
|
8
|
+
export declare const DISCLAIMER: string;
|
|
9
|
+
export declare function toListResult(output: MedicamentoListaOutput): {
|
|
10
|
+
content: {
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}[];
|
|
14
|
+
structuredContent: {
|
|
15
|
+
data: {
|
|
16
|
+
total: number;
|
|
17
|
+
pagina: number;
|
|
18
|
+
medicamentos: {
|
|
19
|
+
numProcesso: string;
|
|
20
|
+
nomeProduto: string;
|
|
21
|
+
empresa: string;
|
|
22
|
+
idBulaPacienteProtegido?: string | undefined;
|
|
23
|
+
idBulaProfissionalProtegido?: string | undefined;
|
|
24
|
+
dataAtualizacao?: string | undefined;
|
|
25
|
+
}[];
|
|
26
|
+
};
|
|
27
|
+
_meta: {
|
|
28
|
+
data_da_base: string;
|
|
29
|
+
fonte: string;
|
|
30
|
+
defasagem_dias: number;
|
|
31
|
+
modo: "cache_local" | "online";
|
|
32
|
+
status?: "stale" | "ok" | undefined;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export declare function toBulaResult(output: BulaOutput): {
|
|
37
|
+
content: {
|
|
38
|
+
type: "text";
|
|
39
|
+
text: string;
|
|
40
|
+
}[];
|
|
41
|
+
structuredContent: {
|
|
42
|
+
data: {
|
|
43
|
+
numProcesso: string;
|
|
44
|
+
nomeProduto: string;
|
|
45
|
+
empresa: string;
|
|
46
|
+
tarja?: "LIVRE" | "VERMELHA" | "PRETA" | undefined;
|
|
47
|
+
classesTerapeuticas?: string[] | undefined;
|
|
48
|
+
principioAtivo?: string | undefined;
|
|
49
|
+
bulaPaciente?: {
|
|
50
|
+
id: string;
|
|
51
|
+
urlPdf?: string | undefined;
|
|
52
|
+
} | undefined;
|
|
53
|
+
bulaProfissional?: {
|
|
54
|
+
id: string;
|
|
55
|
+
urlPdf?: string | undefined;
|
|
56
|
+
} | undefined;
|
|
57
|
+
};
|
|
58
|
+
_meta: {
|
|
59
|
+
data_da_base: string;
|
|
60
|
+
fonte: string;
|
|
61
|
+
defasagem_dias: number;
|
|
62
|
+
modo: "cache_local" | "online";
|
|
63
|
+
status?: "stale" | "ok" | undefined;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
export declare function toApresentacoesResult(output: ApresentacoesOutput): {
|
|
68
|
+
content: {
|
|
69
|
+
type: "text";
|
|
70
|
+
text: string;
|
|
71
|
+
}[];
|
|
72
|
+
structuredContent: {
|
|
73
|
+
data: {
|
|
74
|
+
numProcesso: string;
|
|
75
|
+
nomeProduto: string;
|
|
76
|
+
empresa: string;
|
|
77
|
+
apresentacoes: {
|
|
78
|
+
descricao: string;
|
|
79
|
+
}[];
|
|
80
|
+
};
|
|
81
|
+
_meta: {
|
|
82
|
+
data_da_base: string;
|
|
83
|
+
fonte: string;
|
|
84
|
+
defasagem_dias: number;
|
|
85
|
+
modo: "cache_local" | "online";
|
|
86
|
+
status?: "stale" | "ok" | undefined;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
export declare function toErrorResult(err: unknown): {
|
|
91
|
+
content: {
|
|
92
|
+
type: "text";
|
|
93
|
+
text: string;
|
|
94
|
+
}[];
|
|
95
|
+
isError: true;
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/tools/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAEnG,eAAO,MAAM,gBAAgB;;;;;CAKnB,CAAC;AAEX,eAAO,MAAM,UAAU,QAEqE,CAAC;AAE7F,wBAAgB,YAAY,CAAC,MAAM,EAAE,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;EAK1D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAK9C;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,mBAAmB;;;;;;;;;;;;;;;;;;;;;;EAKhE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO;;;;;;EAMzC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const TOOL_ANNOTATIONS = {
|
|
2
|
+
readOnlyHint: true,
|
|
3
|
+
idempotentHint: true,
|
|
4
|
+
destructiveHint: false,
|
|
5
|
+
openWorldHint: true,
|
|
6
|
+
};
|
|
7
|
+
export const DISCLAIMER = "\n\n⚠️ Fonte de consulta farmacêutica oficial (ANVISA). " +
|
|
8
|
+
"Não substitui avaliação, diagnóstico ou prescrição por profissional de saúde habilitado.";
|
|
9
|
+
export function toListResult(output) {
|
|
10
|
+
return {
|
|
11
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) + DISCLAIMER }],
|
|
12
|
+
structuredContent: output,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function toBulaResult(output) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) + DISCLAIMER }],
|
|
18
|
+
structuredContent: output,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function toApresentacoesResult(output) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) + DISCLAIMER }],
|
|
24
|
+
structuredContent: output,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function toErrorResult(err) {
|
|
28
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: "text", text: `Erro ao consultar ANVISA: ${message}` }],
|
|
31
|
+
isError: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/tools/shared.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,YAAY,EAAE,IAAI;IAClB,cAAc,EAAE,IAAI;IACpB,eAAe,EAAE,KAAK;IACtB,aAAa,EAAE,IAAI;CACX,CAAC;AAEX,MAAM,CAAC,MAAM,UAAU,GACrB,0DAA0D;IAC1D,0FAA0F,CAAC;AAE7F,MAAM,UAAU,YAAY,CAAC,MAA8B;IACzD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,UAAU,EAAE,CAAC;QACxF,iBAAiB,EAAE,MAAM;KAC1B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAkB;IAC7C,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,UAAU,EAAE,CAAC;QACxF,iBAAiB,EAAE,MAAM;KAC1B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAA2B;IAC/D,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,UAAU,EAAE,CAAC;QACxF,iBAAiB,EAAE,MAAM;KAC1B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,6BAA6B,OAAO,EAAE,EAAE,CAAC;QAClF,OAAO,EAAE,IAAa;KACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class SourceAuthError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class MaxRetriesExceededError extends Error {
|
|
5
|
+
constructor(url: string, retries: number, cause?: Error);
|
|
6
|
+
}
|
|
7
|
+
export declare function withBackoff<T>(fn: () => Promise<T>, maxRetries: number, baseDelayMs: number, url: string): Promise<T>;
|
|
8
|
+
//# sourceMappingURL=backoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../../src/utils/backoff.ts"],"names":[],"mappings":"AAAA,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;CAKxD;AAMD,wBAAsB,WAAW,CAAC,CAAC,EACjC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,CAAC,CAAC,CAuBZ"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export class SourceAuthError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "SourceAuthError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class MaxRetriesExceededError extends Error {
|
|
8
|
+
constructor(url, retries, cause) {
|
|
9
|
+
super(`Max retries (${retries}) exceeded for ${url}: ${cause?.message}`);
|
|
10
|
+
this.name = "MaxRetriesExceededError";
|
|
11
|
+
this.cause = cause;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function jitter(baseMs) {
|
|
15
|
+
return Math.floor(Math.random() * baseMs);
|
|
16
|
+
}
|
|
17
|
+
export async function withBackoff(fn, maxRetries, baseDelayMs, url) {
|
|
18
|
+
let lastError;
|
|
19
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
20
|
+
try {
|
|
21
|
+
return await fn();
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
25
|
+
if (error instanceof SourceAuthError) {
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
lastError = error;
|
|
29
|
+
if (attempt < maxRetries) {
|
|
30
|
+
const delay = baseDelayMs * 2 ** (attempt - 1) + jitter(baseDelayMs);
|
|
31
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
throw new MaxRetriesExceededError(url, maxRetries, lastError);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=backoff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backoff.js","sourceRoot":"","sources":["../../src/utils/backoff.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,GAAW,EAAE,OAAe,EAAE,KAAa;QACrD,KAAK,CAAC,gBAAgB,OAAO,kBAAkB,GAAG,KAAK,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED,SAAS,MAAM,CAAC,MAAc;IAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,EAAoB,EACpB,UAAkB,EAClB,WAAmB,EACnB,GAAW;IAEX,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAElE,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;gBACrC,MAAM,KAAK,CAAC;YACd,CAAC;YAED,SAAS,GAAG,KAAK,CAAC;YAElB,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACzB,MAAM,KAAK,GAAG,WAAW,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;gBACrE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,uBAAuB,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface mínima que um cliente HTTP deve expor para `AnvisaApiSource`.
|
|
3
|
+
* Permite testar com mocks sem instanciar Playwright real.
|
|
4
|
+
*/
|
|
5
|
+
export interface HttpGet {
|
|
6
|
+
get(path: string, params?: Record<string, string>): Promise<unknown>;
|
|
7
|
+
close?(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* HttpClient que usa Playwright (Chromium real) por trás.
|
|
11
|
+
*
|
|
12
|
+
* A API ANVISA está atrás de Cloudflare em modo "detectar UA com 'Headless'
|
|
13
|
+
* + sinais óbvios de webdriver". Fetch nativo do Node ou tls-client-node com
|
|
14
|
+
* fingerprints conhecidos são bloqueados. Browser real (Chromium) com UA limpo
|
|
15
|
+
* + initScript de masking passa direto. Confirmado experimentalmente em 2026-05-13.
|
|
16
|
+
*
|
|
17
|
+
* Estratégia:
|
|
18
|
+
* - Browser singleton lazy-initialized na primeira chamada
|
|
19
|
+
* - Page única reutilizada entre requests (cookies persistem na sessão)
|
|
20
|
+
* - Cada request HTTP é executada como `fetch()` DENTRO do contexto JS da página
|
|
21
|
+
* (herda cookies, UA, JS state do browser)
|
|
22
|
+
* - `close()` deve ser chamado no shutdown (SIGINT/SIGTERM)
|
|
23
|
+
*
|
|
24
|
+
* Trade-offs:
|
|
25
|
+
* - Primeira chamada: ~2-3s (boot Chromium + navegação warm-up)
|
|
26
|
+
* - Chamadas subsequentes: ~100-300ms (apenas o page.evaluate)
|
|
27
|
+
* - Memória: +150-250MB (Chromium em background)
|
|
28
|
+
*/
|
|
29
|
+
export declare class HttpClient implements HttpGet {
|
|
30
|
+
private readonly baseUrl;
|
|
31
|
+
private readonly maxRetries;
|
|
32
|
+
private readonly baseDelayMs;
|
|
33
|
+
private readonly timeoutMs;
|
|
34
|
+
private browser;
|
|
35
|
+
private context;
|
|
36
|
+
private page;
|
|
37
|
+
private startPromise;
|
|
38
|
+
constructor(baseUrl: string, maxRetries: number, baseDelayMs: number, timeoutMs: number);
|
|
39
|
+
get(path: string, params?: Record<string, string>): Promise<unknown>;
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
private executeRequest;
|
|
42
|
+
private ensurePage;
|
|
43
|
+
private start;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/utils/http.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrE,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAeD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,UAAW,YAAW,OAAO;IAOtC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAT5B,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,YAAY,CAA4B;gBAG7B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM;IAG9B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBpE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YASd,cAAc;YAiFd,UAAU;YAcV,KAAK;CAoCpB"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { chromium } from "playwright";
|
|
2
|
+
import { SourceAuthError, withBackoff } from "./backoff.js";
|
|
3
|
+
/**
|
|
4
|
+
* User-Agent de Chrome estável (sem string "HeadlessChrome").
|
|
5
|
+
* Cloudflare bloqueia qualquer UA contendo "Headless" — override é mandatório.
|
|
6
|
+
*/
|
|
7
|
+
const CHROME_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36";
|
|
8
|
+
/**
|
|
9
|
+
* URL de warm-up — navegar nessa página antes da primeira API call.
|
|
10
|
+
* Estabelece contexto JS + cookies passivos de analytics (Dynatrace, etc.).
|
|
11
|
+
*/
|
|
12
|
+
const WARMUP_URL = "https://consultas.anvisa.gov.br/#/bulario/";
|
|
13
|
+
/**
|
|
14
|
+
* HttpClient que usa Playwright (Chromium real) por trás.
|
|
15
|
+
*
|
|
16
|
+
* A API ANVISA está atrás de Cloudflare em modo "detectar UA com 'Headless'
|
|
17
|
+
* + sinais óbvios de webdriver". Fetch nativo do Node ou tls-client-node com
|
|
18
|
+
* fingerprints conhecidos são bloqueados. Browser real (Chromium) com UA limpo
|
|
19
|
+
* + initScript de masking passa direto. Confirmado experimentalmente em 2026-05-13.
|
|
20
|
+
*
|
|
21
|
+
* Estratégia:
|
|
22
|
+
* - Browser singleton lazy-initialized na primeira chamada
|
|
23
|
+
* - Page única reutilizada entre requests (cookies persistem na sessão)
|
|
24
|
+
* - Cada request HTTP é executada como `fetch()` DENTRO do contexto JS da página
|
|
25
|
+
* (herda cookies, UA, JS state do browser)
|
|
26
|
+
* - `close()` deve ser chamado no shutdown (SIGINT/SIGTERM)
|
|
27
|
+
*
|
|
28
|
+
* Trade-offs:
|
|
29
|
+
* - Primeira chamada: ~2-3s (boot Chromium + navegação warm-up)
|
|
30
|
+
* - Chamadas subsequentes: ~100-300ms (apenas o page.evaluate)
|
|
31
|
+
* - Memória: +150-250MB (Chromium em background)
|
|
32
|
+
*/
|
|
33
|
+
export class HttpClient {
|
|
34
|
+
baseUrl;
|
|
35
|
+
maxRetries;
|
|
36
|
+
baseDelayMs;
|
|
37
|
+
timeoutMs;
|
|
38
|
+
browser;
|
|
39
|
+
context;
|
|
40
|
+
page;
|
|
41
|
+
startPromise;
|
|
42
|
+
constructor(baseUrl, maxRetries, baseDelayMs, timeoutMs) {
|
|
43
|
+
this.baseUrl = baseUrl;
|
|
44
|
+
this.maxRetries = maxRetries;
|
|
45
|
+
this.baseDelayMs = baseDelayMs;
|
|
46
|
+
this.timeoutMs = timeoutMs;
|
|
47
|
+
}
|
|
48
|
+
async get(path, params) {
|
|
49
|
+
const url = new URL(path, this.baseUrl);
|
|
50
|
+
if (params) {
|
|
51
|
+
for (const [key, value] of Object.entries(params)) {
|
|
52
|
+
url.searchParams.set(key, value);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const urlString = url.toString();
|
|
56
|
+
return withBackoff(() => this.executeRequest(urlString), this.maxRetries, this.baseDelayMs, urlString);
|
|
57
|
+
}
|
|
58
|
+
async close() {
|
|
59
|
+
if (this.browser) {
|
|
60
|
+
await this.browser.close().catch(() => undefined);
|
|
61
|
+
this.browser = undefined;
|
|
62
|
+
this.context = undefined;
|
|
63
|
+
this.page = undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async executeRequest(urlString) {
|
|
67
|
+
const page = await this.ensurePage();
|
|
68
|
+
const result = await page.evaluate(async ({ url, timeoutMs }) => {
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
71
|
+
try {
|
|
72
|
+
const r = await fetch(url, {
|
|
73
|
+
method: "GET",
|
|
74
|
+
headers: {
|
|
75
|
+
Accept: "application/json, text/plain, */*",
|
|
76
|
+
Authorization: "Guest",
|
|
77
|
+
},
|
|
78
|
+
credentials: "include",
|
|
79
|
+
signal: controller.signal,
|
|
80
|
+
});
|
|
81
|
+
const text = await r.text();
|
|
82
|
+
return { status: r.status, statusText: r.statusText, body: text };
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
return {
|
|
86
|
+
status: 0,
|
|
87
|
+
statusText: "fetch failed",
|
|
88
|
+
body: "",
|
|
89
|
+
error: err instanceof Error ? err.message : String(err),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
clearTimeout(timer);
|
|
94
|
+
}
|
|
95
|
+
}, { url: urlString, timeoutMs: this.timeoutMs });
|
|
96
|
+
if (result.error || result.status === 0) {
|
|
97
|
+
throw new Error(`Network error: ${result.error ?? "unknown"}`);
|
|
98
|
+
}
|
|
99
|
+
if (result.status === 401) {
|
|
100
|
+
throw new SourceAuthError(`ANVISA API requires authentication (HTTP 401). The Authorization header may have changed.`);
|
|
101
|
+
}
|
|
102
|
+
if (result.status === 403) {
|
|
103
|
+
const looksLikeCloudflareBlock = result.body.includes("Sorry, you have been blocked") ||
|
|
104
|
+
result.body.includes("cf-error-details");
|
|
105
|
+
if (looksLikeCloudflareBlock) {
|
|
106
|
+
throw new Error(`Cloudflare blocked request (HTTP 403). Browser fingerprint may have been flagged.`);
|
|
107
|
+
}
|
|
108
|
+
throw new SourceAuthError(`ANVISA API returned HTTP 403 with non-Cloudflare body.`);
|
|
109
|
+
}
|
|
110
|
+
if (result.status === 429 || (result.status >= 500 && result.status < 600)) {
|
|
111
|
+
const isBusinessException = result.body.includes("BusinessException");
|
|
112
|
+
if (!isBusinessException) {
|
|
113
|
+
throw new Error(`HTTP ${result.status}: ${result.statusText}`);
|
|
114
|
+
}
|
|
115
|
+
// BusinessException: API rejeitou parâmetro de negócio — não retriar
|
|
116
|
+
throw new Error(`ANVISA BusinessException (HTTP ${result.status}): ${result.body.substring(0, 300)}`);
|
|
117
|
+
}
|
|
118
|
+
if (result.status !== 200 && result.status !== 201) {
|
|
119
|
+
throw new Error(`HTTP ${result.status}: ${result.statusText}. Body: ${result.body.substring(0, 200)}`);
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(result.body);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
throw new Error(`Failed to parse JSON response. Body preview: ${result.body.substring(0, 200)}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async ensurePage() {
|
|
129
|
+
if (this.page)
|
|
130
|
+
return this.page;
|
|
131
|
+
if (!this.startPromise) {
|
|
132
|
+
this.startPromise = this.start().finally(() => {
|
|
133
|
+
this.startPromise = undefined;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
await this.startPromise;
|
|
137
|
+
if (!this.page) {
|
|
138
|
+
throw new Error("Playwright failed to initialize");
|
|
139
|
+
}
|
|
140
|
+
return this.page;
|
|
141
|
+
}
|
|
142
|
+
async start() {
|
|
143
|
+
this.browser = await chromium.launch({
|
|
144
|
+
headless: true,
|
|
145
|
+
args: [
|
|
146
|
+
"--disable-blink-features=AutomationControlled",
|
|
147
|
+
"--disable-features=IsolateOrigins,site-per-process",
|
|
148
|
+
],
|
|
149
|
+
});
|
|
150
|
+
this.context = await this.browser.newContext({
|
|
151
|
+
userAgent: CHROME_UA,
|
|
152
|
+
locale: "pt-BR",
|
|
153
|
+
timezoneId: "America/Sao_Paulo",
|
|
154
|
+
viewport: { width: 1920, height: 1080 },
|
|
155
|
+
deviceScaleFactor: 1,
|
|
156
|
+
extraHTTPHeaders: {
|
|
157
|
+
"Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
await this.context.addInitScript(() => {
|
|
161
|
+
Object.defineProperty(navigator, "webdriver", { get: () => undefined });
|
|
162
|
+
Object.defineProperty(navigator, "plugins", { get: () => [1, 2, 3, 4, 5] });
|
|
163
|
+
Object.defineProperty(navigator, "languages", {
|
|
164
|
+
get: () => ["pt-BR", "pt", "en"],
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
this.page = await this.context.newPage();
|
|
168
|
+
await this.page.goto(WARMUP_URL, {
|
|
169
|
+
waitUntil: "domcontentloaded",
|
|
170
|
+
timeout: 30_000,
|
|
171
|
+
});
|
|
172
|
+
await this.page.waitForTimeout(2_000);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/utils/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../src/utils/meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,GAAG,QAAQ,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,GAAG,IAAI,CAWP"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function buildMeta(params) {
|
|
2
|
+
const threshold = params.degradedThresholdDays ?? 30;
|
|
3
|
+
const parsed = Date.parse(params.dataBase);
|
|
4
|
+
const defasagem_dias = Number.isNaN(parsed) ? 0 : Math.floor((Date.now() - parsed) / 86_400_000);
|
|
5
|
+
return {
|
|
6
|
+
data_da_base: params.dataBase,
|
|
7
|
+
fonte: params.fonte,
|
|
8
|
+
defasagem_dias,
|
|
9
|
+
modo: params.modo,
|
|
10
|
+
status: defasagem_dias > threshold ? "stale" : "ok",
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.js","sourceRoot":"","sources":["../../src/utils/meta.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,MAKzB;IACC,MAAM,SAAS,GAAG,MAAM,CAAC,qBAAqB,IAAI,EAAE,CAAC;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC;IACjG,OAAO;QACL,YAAY,EAAE,MAAM,CAAC,QAAQ;QAC7B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,cAAc;QACd,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;KACpD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-http.d.ts","sourceRoot":"","sources":["../../src/utils/playwright-http.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-http.js","sourceRoot":"","sources":["../../src/utils/playwright-http.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,2EAA2E;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,70 @@
|
|
|
1
|
-
|
|
1
|
+
{
|
|
2
2
|
"name": "@mcpassure/mcp-anvisa-bulario",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"mcpName": "io.github.mcpassure/mcp-anvisa-bulario",
|
|
5
|
+
"description": "MCP server para consulta ao Bulário Eletrônico da ANVISA",
|
|
5
6
|
"license": "MIT",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
7
|
+
"author": "MCPAssure Brasil",
|
|
8
|
+
"homepage": "https://github.com/mcpassure/mcp-anvisa-bulario",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/mcpassure/mcp-anvisa-bulario.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"anvisa",
|
|
16
|
+
"bulario",
|
|
17
|
+
"medicamentos",
|
|
18
|
+
"farmacovigilancia",
|
|
19
|
+
"saude",
|
|
20
|
+
"healthtech",
|
|
21
|
+
"brasil",
|
|
22
|
+
"model-context-protocol",
|
|
23
|
+
"mcpassure"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"bin": {
|
|
27
|
+
"mcp-anvisa-bulario": "dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"main": "dist/index.js",
|
|
30
|
+
"files": [
|
|
31
|
+
"dist/**",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.build.json",
|
|
37
|
+
"start": "node dist/index.js",
|
|
38
|
+
"dev": "tsx src/index.ts",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"test:integration": "tsx integration-tests/bin/run-all.ts",
|
|
42
|
+
"test:integration:offline": "tsx integration-tests/bin/run-all.ts --offline",
|
|
43
|
+
"lint": "biome check src tests evals integration-tests test-utils",
|
|
44
|
+
"lint:fix": "biome check --write src tests evals integration-tests test-utils",
|
|
45
|
+
"format": "biome format --write src tests evals integration-tests test-utils",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"evals": "tsx evals/runner.ts",
|
|
48
|
+
"canary": "tsx src/scripts/canary.ts",
|
|
49
|
+
"demo": "tsx scripts/demo.ts",
|
|
50
|
+
"postinstall": "playwright install chromium --with-deps || true",
|
|
51
|
+
"prepublishOnly": "npm run lint && npm run typecheck && npm run test && npm run build"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
55
|
+
"better-sqlite3": "^12.10.0",
|
|
56
|
+
"playwright": "^1.55.0",
|
|
57
|
+
"zod": "^4.4.3"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@biomejs/biome": "^2.4.15",
|
|
61
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
62
|
+
"@types/node": "^22.10.0",
|
|
63
|
+
"tsx": "^4.21.0",
|
|
64
|
+
"typescript": "^5.9.3",
|
|
65
|
+
"vitest": "^4.1.6"
|
|
66
|
+
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=22.0.0"
|
|
69
|
+
}
|
|
70
|
+
}
|