@botozap/mcp 0.1.0 → 0.2.1

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 (44) hide show
  1. package/README.md +111 -43
  2. package/dist/event-bus.d.ts +14 -0
  3. package/dist/event-bus.js +128 -0
  4. package/dist/event-bus.js.map +1 -0
  5. package/dist/http.d.ts +31 -0
  6. package/dist/http.js +280 -0
  7. package/dist/http.js.map +1 -0
  8. package/dist/index.js +47 -4
  9. package/dist/index.js.map +1 -1
  10. package/dist/register.d.ts +18 -4
  11. package/dist/register.js +82 -17
  12. package/dist/register.js.map +1 -1
  13. package/dist/resources/events.d.ts +12 -0
  14. package/dist/resources/events.js +192 -0
  15. package/dist/resources/events.js.map +1 -0
  16. package/dist/schemas.d.ts +4405 -0
  17. package/dist/schemas.js +332 -0
  18. package/dist/schemas.js.map +1 -0
  19. package/dist/server.d.ts +7 -0
  20. package/dist/server.js +13 -2
  21. package/dist/server.js.map +1 -1
  22. package/dist/tools/contacts.d.ts +1 -1
  23. package/dist/tools/contacts.js +8 -6
  24. package/dist/tools/contacts.js.map +1 -1
  25. package/dist/tools/conversations.js +17 -3
  26. package/dist/tools/conversations.js.map +1 -1
  27. package/dist/tools/customers.d.ts +1 -1
  28. package/dist/tools/customers.js +11 -9
  29. package/dist/tools/customers.js.map +1 -1
  30. package/dist/tools/media.d.ts +94 -0
  31. package/dist/tools/media.js +90 -2
  32. package/dist/tools/media.js.map +1 -1
  33. package/dist/tools/messages.js +5 -4
  34. package/dist/tools/messages.js.map +1 -1
  35. package/dist/tools/misc.js +3 -2
  36. package/dist/tools/misc.js.map +1 -1
  37. package/dist/tools/phone-numbers.js +4 -3
  38. package/dist/tools/phone-numbers.js.map +1 -1
  39. package/dist/tools/templates.js +4 -3
  40. package/dist/tools/templates.js.map +1 -1
  41. package/dist/tools/webhooks.d.ts +1 -1
  42. package/dist/tools/webhooks.js +10 -8
  43. package/dist/tools/webhooks.js.map +1 -1
  44. package/package.json +4 -2
package/dist/http.js ADDED
@@ -0,0 +1,280 @@
1
+ import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
2
+ import { createServer, } from "node:http";
3
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
5
+ import { createClient } from "./client.js";
6
+ import { buildServer } from "./server.js";
7
+ const MAX_BODY_BYTES = 1_048_576;
8
+ /**
9
+ * Inicia um endpoint MCP remoto stateful. Cada sessão é criada a partir da
10
+ * chave Bearer do initialize e fica presa ao mesmo fingerprint nos requests
11
+ * seguintes; a autoridade da Conta continua sendo derivada pela API BotoZap.
12
+ */
13
+ export async function startStreamableHttpServer(options) {
14
+ const sessions = new Map();
15
+ const reservations = { byApiKey: new Map(), total: 0 };
16
+ const lifecycle = { closing: false };
17
+ const idleTimeoutMs = Math.max(1, options.sessionIdleTimeoutMs ?? 5 * 60_000);
18
+ const sweep = setInterval(() => {
19
+ const now = Date.now();
20
+ for (const [sessionId, session] of sessions) {
21
+ if (session.activeRequests === 0 &&
22
+ now - session.lastActivityAt >= idleTimeoutMs) {
23
+ void closeSession(sessions, sessionId, session);
24
+ }
25
+ }
26
+ }, Math.max(1, options.sessionSweepIntervalMs ?? 30_000));
27
+ sweep.unref?.();
28
+ const http = createServer((request, response) => {
29
+ void handleRequest(request, response, sessions, reservations, lifecycle, options).catch(() => {
30
+ if (!response.headersSent) {
31
+ jsonRpcError(response, 500, -32603, "Erro interno do servidor MCP.");
32
+ }
33
+ else if (!response.writableEnded) {
34
+ response.end();
35
+ }
36
+ });
37
+ });
38
+ const host = options.host ?? "127.0.0.1";
39
+ try {
40
+ await listen(http, options.port ?? 0, host);
41
+ }
42
+ catch (error) {
43
+ clearInterval(sweep);
44
+ throw error;
45
+ }
46
+ const address = http.address();
47
+ if (!address || typeof address === "string") {
48
+ clearInterval(sweep);
49
+ await closeHttp(http);
50
+ throw new Error("Servidor MCP remoto iniciou sem endereço TCP.");
51
+ }
52
+ let closePromise;
53
+ return {
54
+ url: new URL(`http://${displayHost(host)}:${address.port}/mcp`),
55
+ close() {
56
+ if (closePromise)
57
+ return closePromise;
58
+ lifecycle.closing = true;
59
+ clearInterval(sweep);
60
+ const httpClosing = closeHttp(http);
61
+ closePromise = (async () => {
62
+ await closeAllSessions(sessions);
63
+ await httpClosing;
64
+ // Um initialize já autenticado pode ter registrado a sessão enquanto
65
+ // o servidor HTTP drenava o request. A segunda passagem fecha essa janela.
66
+ await closeAllSessions(sessions);
67
+ })();
68
+ return closePromise;
69
+ },
70
+ };
71
+ }
72
+ async function handleRequest(request, response, sessions, reservations, lifecycle, options) {
73
+ const url = new URL(request.url ?? "/", "http://mcp.invalid");
74
+ if (url.pathname !== "/mcp") {
75
+ jsonRpcError(response, 404, -32001, "Endpoint MCP não encontrado.");
76
+ return;
77
+ }
78
+ if (lifecycle.closing) {
79
+ jsonRpcError(response, 503, -32000, "Servidor MCP em encerramento.");
80
+ return;
81
+ }
82
+ const apiKey = bearerToken(request);
83
+ if (!apiKey) {
84
+ response.setHeader("WWW-Authenticate", "Bearer");
85
+ jsonRpcError(response, 401, -32001, "Autenticação Bearer obrigatória.");
86
+ return;
87
+ }
88
+ const sessionId = singleHeader(request, "mcp-session-id");
89
+ if (sessionId) {
90
+ const session = sessions.get(sessionId);
91
+ if (!session || !sameFingerprint(session.apiKeyFingerprint, apiKey)) {
92
+ jsonRpcError(response, 404, -32001, "Sessão MCP inválida.");
93
+ return;
94
+ }
95
+ const body = request.method === "POST" ? await readJsonBody(request) : undefined;
96
+ await handleSessionRequest(session, request, response, body);
97
+ return;
98
+ }
99
+ if (request.method !== "POST") {
100
+ jsonRpcError(response, 400, -32000, "Inicialize uma sessão MCP primeiro.");
101
+ return;
102
+ }
103
+ const body = await readJsonBody(request);
104
+ if (!isInitializeRequest(body)) {
105
+ jsonRpcError(response, 400, -32000, "Requisição initialize obrigatória.");
106
+ return;
107
+ }
108
+ if (!(await authenticatesForEvents(apiKey, options))) {
109
+ response.setHeader("WWW-Authenticate", "Bearer");
110
+ jsonRpcError(response, 401, -32001, "Credencial inválida ou sem events:read.");
111
+ return;
112
+ }
113
+ if (lifecycle.closing) {
114
+ jsonRpcError(response, 503, -32000, "Servidor MCP em encerramento.");
115
+ return;
116
+ }
117
+ if (sessions.size + reservations.total >= (options.maxSessions ?? 1_000)) {
118
+ jsonRpcError(response, 429, -32000, "Limite de sessões MCP atingido.");
119
+ return;
120
+ }
121
+ const apiKeyFingerprint = fingerprint(apiKey);
122
+ const apiKeyReservationKey = apiKeyFingerprint.toString("base64url");
123
+ const sessionsForApiKey = [...sessions.values()].filter((candidate) => sameFingerprint(candidate.apiKeyFingerprint, apiKey)).length;
124
+ const reservedForApiKey = reservations.byApiKey.get(apiKeyReservationKey) ?? 0;
125
+ if (sessionsForApiKey + reservedForApiKey >=
126
+ (options.maxSessionsPerApiKey ?? 5)) {
127
+ jsonRpcError(response, 429, -32000, "Limite de sessões MCP atingido.");
128
+ return;
129
+ }
130
+ reservations.total += 1;
131
+ reservations.byApiKey.set(apiKeyReservationKey, reservedForApiKey + 1);
132
+ let reservationReleased = false;
133
+ const releaseReservation = () => {
134
+ if (reservationReleased)
135
+ return;
136
+ reservationReleased = true;
137
+ reservations.total -= 1;
138
+ const remaining = (reservations.byApiKey.get(apiKeyReservationKey) ?? 1) - 1;
139
+ if (remaining === 0)
140
+ reservations.byApiKey.delete(apiKeyReservationKey);
141
+ else
142
+ reservations.byApiKey.set(apiKeyReservationKey, remaining);
143
+ };
144
+ let session;
145
+ let initializedSessionId;
146
+ const transport = new StreamableHTTPServerTransport({
147
+ sessionIdGenerator: randomUUID,
148
+ onsessioninitialized: (newSessionId) => {
149
+ initializedSessionId = newSessionId;
150
+ if (session)
151
+ sessions.set(newSessionId, session);
152
+ releaseReservation();
153
+ },
154
+ onsessionclosed: async (closedSessionId) => {
155
+ const closed = sessions.get(closedSessionId);
156
+ if (closed)
157
+ await closeSession(sessions, closedSessionId, closed);
158
+ },
159
+ });
160
+ const server = buildServer({
161
+ apiKey,
162
+ baseUrl: options.baseUrl,
163
+ fetch: options.fetch,
164
+ eventPollIntervalMs: options.eventPollIntervalMs ?? 15_000,
165
+ eventSignal: options.eventSignal,
166
+ maxEventSubscriptions: options.maxEventSubscriptions,
167
+ });
168
+ session = {
169
+ activeRequests: 0,
170
+ apiKeyFingerprint,
171
+ lastActivityAt: Date.now(),
172
+ server,
173
+ transport,
174
+ };
175
+ try {
176
+ await server.connect(transport);
177
+ if (lifecycle.closing) {
178
+ await server.close().catch(() => { });
179
+ jsonRpcError(response, 503, -32000, "Servidor MCP em encerramento.");
180
+ return;
181
+ }
182
+ await handleSessionRequest(session, request, response, body);
183
+ }
184
+ catch (error) {
185
+ if (initializedSessionId) {
186
+ await closeSession(sessions, initializedSessionId, session);
187
+ }
188
+ else {
189
+ await server.close().catch(() => { });
190
+ }
191
+ throw error;
192
+ }
193
+ finally {
194
+ releaseReservation();
195
+ }
196
+ }
197
+ async function handleSessionRequest(session, request, response, body) {
198
+ session.activeRequests += 1;
199
+ session.lastActivityAt = Date.now();
200
+ try {
201
+ await session.transport.handleRequest(request, response, body);
202
+ }
203
+ finally {
204
+ session.activeRequests -= 1;
205
+ session.lastActivityAt = Date.now();
206
+ }
207
+ }
208
+ async function closeAllSessions(sessions) {
209
+ await Promise.allSettled([...sessions].map(([sessionId, session]) => closeSession(sessions, sessionId, session)));
210
+ }
211
+ function closeSession(sessions, sessionId, session) {
212
+ if (session.closing)
213
+ return session.closing;
214
+ if (sessions.get(sessionId) === session)
215
+ sessions.delete(sessionId);
216
+ session.closing = session.server.close().catch(() => { });
217
+ return session.closing;
218
+ }
219
+ async function authenticatesForEvents(apiKey, options) {
220
+ try {
221
+ await createClient({
222
+ apiKey,
223
+ baseUrl: options.baseUrl,
224
+ fetch: options.fetch,
225
+ }).events.list({ after: "0", limit: 1 });
226
+ return true;
227
+ }
228
+ catch {
229
+ return false;
230
+ }
231
+ }
232
+ function bearerToken(request) {
233
+ const header = singleHeader(request, "authorization");
234
+ const match = header?.match(/^Bearer ([^\s,]+)$/i);
235
+ return match?.[1] ?? null;
236
+ }
237
+ function singleHeader(request, name) {
238
+ const value = request.headers[name];
239
+ return typeof value === "string" ? value : null;
240
+ }
241
+ function fingerprint(apiKey) {
242
+ return createHash("sha256").update(apiKey).digest();
243
+ }
244
+ function sameFingerprint(expected, apiKey) {
245
+ return timingSafeEqual(expected, fingerprint(apiKey));
246
+ }
247
+ async function readJsonBody(request) {
248
+ const chunks = [];
249
+ let size = 0;
250
+ for await (const chunk of request) {
251
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
252
+ size += bytes.length;
253
+ if (size > MAX_BODY_BYTES)
254
+ throw new Error("Corpo MCP excede 1 MiB.");
255
+ chunks.push(bytes);
256
+ }
257
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
258
+ }
259
+ function jsonRpcError(response, status, code, message) {
260
+ response.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
261
+ response.end(JSON.stringify({ jsonrpc: "2.0", error: { code, message }, id: null }));
262
+ }
263
+ function listen(server, port, host) {
264
+ return new Promise((resolve, reject) => {
265
+ server.once("error", reject);
266
+ server.listen(port, host, () => {
267
+ server.off("error", reject);
268
+ resolve();
269
+ });
270
+ });
271
+ }
272
+ function closeHttp(server) {
273
+ return new Promise((resolve, reject) => {
274
+ server.close((error) => (error ? reject(error) : resolve()));
275
+ });
276
+ }
277
+ function displayHost(host) {
278
+ return host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
279
+ }
280
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,20 +1,63 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Bootstrap do servidor MCP do BotoZap (transporte stdio).
3
+ * Bootstrap do servidor MCP do BotoZap.
4
4
  *
5
- * Uso: defina `BOTOZAP_API_KEY` (e opcionalmente `BOTOZAP_API_URL`) e rode o
6
- * binário `botozap-mcp`. O servidor fala MCP por stdin/stdout — não imprima
7
- * nada em stdout fora do protocolo; logs vão para stderr.
5
+ * O padrão continua sendo stdio. `BOTOZAP_MCP_TRANSPORT=streamable-http`
6
+ * inicia o endpoint remoto autenticado por Bearer e o event bus PostgreSQL.
7
+ * Em stdio, nunca imprima fora do protocolo; todos os logs vão para stderr.
8
8
  */
9
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
+ import { connectPostgresEventSignal } from "./event-bus.js";
11
+ import { startStreamableHttpServer } from "./http.js";
12
+ import { DEFAULT_API_URL } from "./client.js";
10
13
  import { buildServer, configFromEnv } from "./server.js";
11
14
  async function main() {
15
+ if (process.env.BOTOZAP_MCP_TRANSPORT === "streamable-http") {
16
+ await startHttpFromEnv();
17
+ return;
18
+ }
12
19
  const config = configFromEnv();
13
20
  const server = buildServer(config);
14
21
  const transport = new StdioServerTransport();
15
22
  await server.connect(transport);
16
23
  console.error("[botozap-mcp] servidor MCP iniciado (stdio).");
17
24
  }
25
+ async function startHttpFromEnv() {
26
+ const connectionString = process.env.BOTOZAP_EVENT_BUS_DATABASE_URL?.trim();
27
+ if (!connectionString) {
28
+ throw new Error("BOTOZAP_EVENT_BUS_DATABASE_URL não definida para o transporte streamable-http.");
29
+ }
30
+ const port = parsePort(process.env.BOTOZAP_MCP_PORT);
31
+ const host = process.env.BOTOZAP_MCP_HOST?.trim() || "127.0.0.1";
32
+ const eventSignal = await connectPostgresEventSignal(connectionString);
33
+ let remote;
34
+ try {
35
+ remote = await startStreamableHttpServer({
36
+ baseUrl: process.env.BOTOZAP_API_URL?.trim() || DEFAULT_API_URL,
37
+ eventSignal,
38
+ host,
39
+ port,
40
+ });
41
+ }
42
+ catch (error) {
43
+ await eventSignal.close();
44
+ throw error;
45
+ }
46
+ const shutdown = async () => {
47
+ await remote.close();
48
+ await eventSignal.close();
49
+ };
50
+ process.once("SIGINT", () => void shutdown().finally(() => process.exit(0)));
51
+ process.once("SIGTERM", () => void shutdown().finally(() => process.exit(0)));
52
+ console.error(`[botozap-mcp] servidor MCP iniciado (streamable-http) em ${remote.url.href}`);
53
+ }
54
+ function parsePort(raw) {
55
+ const value = raw === undefined ? 3001 : Number(raw);
56
+ if (!Number.isInteger(value) || value < 0 || value > 65_535) {
57
+ throw new Error("BOTOZAP_MCP_PORT deve ser um inteiro entre 0 e 65535.");
58
+ }
59
+ return value;
60
+ }
18
61
  main().catch((err) => {
19
62
  const message = err instanceof Error ? err.message : String(err);
20
63
  console.error(`[botozap-mcp] erro fatal: ${message}`);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEzD,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;AAChE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,CAAC,KAAK,CAAC,6BAA6B,OAAO,EAAE,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -3,17 +3,31 @@
3
3
  * 1. validar args (zod, feito pelo SDK a partir do `inputSchema`),
4
4
  * 2. chamar a API via cliente do `@botozap/sdk`,
5
5
  * 3. devolver o JSON cru como conteúdo de texto (JSON pretty),
6
- * 4. converter `BotoZapError`/exceções em resultado `isError` com mensagem PT-BR.
6
+ * 4. nas tools migradas, validar a saída forte e devolvê-la também como
7
+ * `structuredContent`, com `outputSchema` compatível com MCP SDK 1.29,
8
+ * 5. converter `BotoZapError`/exceções em resultado `isError` com mensagem PT-BR.
7
9
  */
8
10
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
- import type { ZodRawShape } from "zod";
11
+ import type { AnyZodObject, ZodRawShape } from "zod";
10
12
  import { type Client } from "./client.js";
11
13
  /** Assinatura do handler de uma ferramenta: recebe o client + args validados. */
12
14
  export type ToolHandler<Args> = (client: Client, args: Args) => Promise<unknown>;
15
+ declare const STRUCTURED_RESULT: unique symbol;
16
+ type StructuredToolResult = {
17
+ [STRUCTURED_RESULT]: true;
18
+ textFallback: unknown;
19
+ structuredContent: Record<string, unknown>;
20
+ };
21
+ /** Resultado compatível de uma operação que concluiu sem corpo HTTP. */
22
+ export declare function emptyOperationResult(): StructuredToolResult;
23
+ export interface Register {
24
+ (name: string, description: string, inputSchema: ZodRawShape, handler: ToolHandler<Record<string, unknown>>): void;
25
+ (name: string, description: string, inputSchema: ZodRawShape, outputSchema: AnyZodObject, handler: ToolHandler<Record<string, unknown>>): void;
26
+ }
13
27
  /**
14
28
  * Fábrica que devolve um `register(...)` ligado a um server + client.
15
29
  * `inputSchema` é um *raw shape* zod (objeto de schemas), como o
16
30
  * `registerTool` do SDK espera.
17
31
  */
18
- export declare function createRegister(server: McpServer, client: Client): (name: string, description: string, inputSchema: ZodRawShape, handler: ToolHandler<Record<string, unknown>>) => void;
19
- export type Register = ReturnType<typeof createRegister>;
32
+ export declare function createRegister(server: McpServer, client: Client, apiKey?: string): Register;
33
+ export {};
package/dist/register.js CHANGED
@@ -3,40 +3,105 @@
3
3
  * 1. validar args (zod, feito pelo SDK a partir do `inputSchema`),
4
4
  * 2. chamar a API via cliente do `@botozap/sdk`,
5
5
  * 3. devolver o JSON cru como conteúdo de texto (JSON pretty),
6
- * 4. converter `BotoZapError`/exceções em resultado `isError` com mensagem PT-BR.
6
+ * 4. nas tools migradas, validar a saída forte e devolvê-la também como
7
+ * `structuredContent`, com `outputSchema` compatível com MCP SDK 1.29,
8
+ * 5. converter `BotoZapError`/exceções em resultado `isError` com mensagem PT-BR.
7
9
  */
8
10
  import { BotoZapError } from "./client.js";
11
+ import { compatibleOutputSchema, structuredError, } from "./schemas.js";
12
+ const STRUCTURED_RESULT = Symbol("structured-result");
13
+ /**
14
+ * Mantém o valor textual legado quando a API não tem corpo, mas permite que a
15
+ * tool publique um contrato estruturado explícito para clientes novos.
16
+ */
17
+ function structuredToolResult(textFallback, structuredContent) {
18
+ return { [STRUCTURED_RESULT]: true, textFallback, structuredContent };
19
+ }
20
+ /** Resultado compatível de uma operação que concluiu sem corpo HTTP. */
21
+ export function emptyOperationResult() {
22
+ return structuredToolResult(null, { success: true });
23
+ }
24
+ const API_KEY_PATTERN = /\bbz_(?:live|sandbox)_[A-Za-z0-9._-]+\b/g;
25
+ const BEARER_PATTERN = /\bBearer\s+\S+/gi;
26
+ function safeMessage(value, apiKey) {
27
+ let message = String(value).replace(BEARER_PATTERN, "Bearer [credencial removida]");
28
+ if (apiKey)
29
+ message = message.split(apiKey).join("[credencial removida]");
30
+ return message.replace(API_KEY_PATTERN, "[credencial removida]");
31
+ }
32
+ function errorResult(err, apiKey) {
33
+ if (err instanceof BotoZapError) {
34
+ const message = safeMessage(err.message, apiKey);
35
+ return {
36
+ text: `Erro [${err.code}]: ${message}`,
37
+ structured: structuredError(err.code, message, err.status),
38
+ };
39
+ }
40
+ const message = safeMessage(err instanceof Error ? err.message : err, apiKey);
41
+ return {
42
+ text: `Erro: ${message}`,
43
+ structured: structuredError("tool_error", message, 0),
44
+ };
45
+ }
46
+ function isObject(value) {
47
+ return value !== null && typeof value === "object" && !Array.isArray(value);
48
+ }
49
+ function isStructuredToolResult(value) {
50
+ return isObject(value) && Reflect.get(value, STRUCTURED_RESULT) === true;
51
+ }
9
52
  /**
10
53
  * Fábrica que devolve um `register(...)` ligado a um server + client.
11
54
  * `inputSchema` é um *raw shape* zod (objeto de schemas), como o
12
55
  * `registerTool` do SDK espera.
13
56
  */
14
- export function createRegister(server, client) {
15
- return function register(name, description, inputSchema, handler) {
16
- server.registerTool(name, { description, inputSchema }, async (args) => {
57
+ export function createRegister(server, client, apiKey) {
58
+ const register = function register(name, description, inputSchema, outputOrHandler, maybeHandler) {
59
+ const outputSchema = maybeHandler ? outputOrHandler : undefined;
60
+ const handler = maybeHandler ?? outputOrHandler;
61
+ server.registerTool(name, {
62
+ description,
63
+ inputSchema,
64
+ ...(outputSchema
65
+ ? { outputSchema: compatibleOutputSchema(outputSchema) }
66
+ : {}),
67
+ }, async (args) => {
17
68
  try {
18
- const data = await handler(client, (args ?? {}));
69
+ const handlerResult = await handler(client, (args ?? {}));
70
+ const data = isStructuredToolResult(handlerResult)
71
+ ? handlerResult.structuredContent
72
+ : handlerResult;
73
+ const textFallback = isStructuredToolResult(handlerResult)
74
+ ? handlerResult.textFallback
75
+ : handlerResult;
76
+ const content = [
77
+ { type: "text", text: JSON.stringify(textFallback, null, 2) },
78
+ ];
79
+ if (!outputSchema)
80
+ return { content };
81
+ if (!isObject(data)) {
82
+ throw new Error(`Resposta de ${name} viola o output schema: era esperado um objeto.`);
83
+ }
84
+ const parsed = await outputSchema.safeParseAsync(data);
85
+ if (!parsed.success) {
86
+ throw new Error(`Resposta de ${name} viola o output schema: ${parsed.error.issues
87
+ .map((issue) => issue.message)
88
+ .join(" ")}`);
89
+ }
19
90
  return {
20
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
91
+ content,
92
+ structuredContent: data,
21
93
  };
22
94
  }
23
95
  catch (err) {
24
- let message;
25
- if (err instanceof BotoZapError) {
26
- message = `Erro [${err.code}]: ${err.message}`;
27
- }
28
- else if (err instanceof Error) {
29
- message = `Erro: ${err.message}`;
30
- }
31
- else {
32
- message = `Erro: ${String(err)}`;
33
- }
96
+ const result = errorResult(err, apiKey);
34
97
  return {
35
- content: [{ type: "text", text: message }],
98
+ content: [{ type: "text", text: result.text }],
99
+ ...(outputSchema ? { structuredContent: result.structured } : {}),
36
100
  isError: true,
37
101
  };
38
102
  }
39
103
  });
40
104
  };
105
+ return register;
41
106
  }
42
107
  //# sourceMappingURL=register.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAQxD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAiB,EAAE,MAAc;IAC9D,OAAO,SAAS,QAAQ,CACtB,IAAY,EACZ,WAAmB,EACnB,WAAwB,EACxB,OAA6C;QAE7C,MAAM,CAAC,YAAY,CACjB,IAAI,EACJ,EAAE,WAAW,EAAE,WAAW,EAAE,EAC5B,KAAK,EAAE,IAAI,EAA2B,EAAE;YACtC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC,CAAC;gBAC5E,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;iBACjE,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,OAAe,CAAC;gBACpB,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;oBAChC,OAAO,GAAG,SAAS,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;gBACjD,CAAC;qBAAM,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;oBAChC,OAAO,GAAG,SAAS,GAAG,CAAC,OAAO,EAAE,CAAC;gBACnC,CAAC;qBAAM,CAAC;oBACN,OAAO,GAAG,SAAS,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACnC,CAAC;gBACD,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oBAC1C,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { Client } from "../client.js";
3
+ export declare const EVENTS_URI_TEMPLATE = "botozap://events{?after,limit}";
4
+ export interface EventSignalSource {
5
+ subscribe(listener: () => void): () => void;
6
+ }
7
+ export interface RegisterEventResourcesOptions {
8
+ maxSubscriptions?: number;
9
+ pollIntervalMs: number;
10
+ eventSignal?: EventSignalSource;
11
+ }
12
+ export declare function registerEventResources(server: McpServer, client: Client, options: RegisterEventResourcesOptions): () => void;