@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.
- package/README.md +111 -43
- package/dist/event-bus.d.ts +14 -0
- package/dist/event-bus.js +128 -0
- package/dist/event-bus.js.map +1 -0
- package/dist/http.d.ts +31 -0
- package/dist/http.js +280 -0
- package/dist/http.js.map +1 -0
- package/dist/index.js +47 -4
- package/dist/index.js.map +1 -1
- package/dist/register.d.ts +18 -4
- package/dist/register.js +82 -17
- package/dist/register.js.map +1 -1
- package/dist/resources/events.d.ts +12 -0
- package/dist/resources/events.js +192 -0
- package/dist/resources/events.js.map +1 -0
- package/dist/schemas.d.ts +4405 -0
- package/dist/schemas.js +332 -0
- package/dist/schemas.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +13 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/contacts.d.ts +1 -1
- package/dist/tools/contacts.js +8 -6
- package/dist/tools/contacts.js.map +1 -1
- package/dist/tools/conversations.js +17 -3
- package/dist/tools/conversations.js.map +1 -1
- package/dist/tools/customers.d.ts +1 -1
- package/dist/tools/customers.js +11 -9
- package/dist/tools/customers.js.map +1 -1
- package/dist/tools/media.d.ts +94 -0
- package/dist/tools/media.js +90 -2
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/messages.js +5 -4
- package/dist/tools/messages.js.map +1 -1
- package/dist/tools/misc.js +3 -2
- package/dist/tools/misc.js.map +1 -1
- package/dist/tools/phone-numbers.js +4 -3
- package/dist/tools/phone-numbers.js.map +1 -1
- package/dist/tools/templates.js +4 -3
- package/dist/tools/templates.js.map +1 -1
- package/dist/tools/webhooks.d.ts +1 -1
- package/dist/tools/webhooks.js +10 -8
- package/dist/tools/webhooks.js.map +1 -1
- 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
|
package/dist/http.js.map
ADDED
|
@@ -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
|
|
3
|
+
* Bootstrap do servidor MCP do BotoZap.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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":";AACA;;;;;;GAMG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEzD,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,iBAAiB,EAAE,CAAC;QAC5D,MAAM,gBAAgB,EAAE,CAAC;QACzB,OAAO;IACT,CAAC;IAED,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,KAAK,UAAU,gBAAgB;IAC7B,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,IAAI,EAAE,CAAC;IAC5E,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,gFAAgF,CACjF,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI,WAAW,CAAC;IACjE,MAAM,WAAW,GAAG,MAAM,0BAA0B,CAAC,gBAAgB,CAAC,CAAC;IACvE,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,yBAAyB,CAAC;YACvC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,eAAe;YAC/D,WAAW;YACX,IAAI;YACJ,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC;QAC1B,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,OAAO,CAAC,KAAK,CACX,4DAA4D,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAC9E,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,GAAuB;IACxC,MAAM,KAAK,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,MAAM,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,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"}
|
package/dist/register.d.ts
CHANGED
|
@@ -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.
|
|
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
|
|
19
|
-
export
|
|
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.
|
|
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
|
-
|
|
16
|
-
|
|
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
|
|
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
|
|
91
|
+
content,
|
|
92
|
+
structuredContent: data,
|
|
21
93
|
};
|
|
22
94
|
}
|
|
23
95
|
catch (err) {
|
|
24
|
-
|
|
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:
|
|
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
|
package/dist/register.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA
|
|
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;
|