@cgalaviz/easysell-mcp 0.4.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.md +256 -0
- package/dist/client.d.ts +53 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +372 -0
- package/dist/client.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +49 -0
- package/dist/main.js.map +1 -0
- package/dist/registry/audit.d.ts +3 -0
- package/dist/registry/audit.d.ts.map +1 -0
- package/dist/registry/audit.js +23 -0
- package/dist/registry/audit.js.map +1 -0
- package/dist/registry/binary-downloads.d.ts +3 -0
- package/dist/registry/binary-downloads.d.ts.map +1 -0
- package/dist/registry/binary-downloads.js +80 -0
- package/dist/registry/binary-downloads.js.map +1 -0
- package/dist/registry/canned-responses.d.ts +11 -0
- package/dist/registry/canned-responses.d.ts.map +1 -0
- package/dist/registry/canned-responses.js +54 -0
- package/dist/registry/canned-responses.js.map +1 -0
- package/dist/registry/catalog-images.d.ts +3 -0
- package/dist/registry/catalog-images.d.ts.map +1 -0
- package/dist/registry/catalog-images.js +48 -0
- package/dist/registry/catalog-images.js.map +1 -0
- package/dist/registry/catalog.d.ts +3 -0
- package/dist/registry/catalog.d.ts.map +1 -0
- package/dist/registry/catalog.js +156 -0
- package/dist/registry/catalog.js.map +1 -0
- package/dist/registry/conversations.d.ts +9 -0
- package/dist/registry/conversations.d.ts.map +1 -0
- package/dist/registry/conversations.js +246 -0
- package/dist/registry/conversations.js.map +1 -0
- package/dist/registry/customers.d.ts +3 -0
- package/dist/registry/customers.d.ts.map +1 -0
- package/dist/registry/customers.js +242 -0
- package/dist/registry/customers.js.map +1 -0
- package/dist/registry/iam.d.ts +3 -0
- package/dist/registry/iam.d.ts.map +1 -0
- package/dist/registry/iam.js +116 -0
- package/dist/registry/iam.js.map +1 -0
- package/dist/registry/index.d.ts +3 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +37 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/integrations.d.ts +3 -0
- package/dist/registry/integrations.d.ts.map +1 -0
- package/dist/registry/integrations.js +65 -0
- package/dist/registry/integrations.js.map +1 -0
- package/dist/registry/notifications.d.ts +3 -0
- package/dist/registry/notifications.d.ts.map +1 -0
- package/dist/registry/notifications.js +44 -0
- package/dist/registry/notifications.js.map +1 -0
- package/dist/registry/orders.d.ts +3 -0
- package/dist/registry/orders.d.ts.map +1 -0
- package/dist/registry/orders.js +134 -0
- package/dist/registry/orders.js.map +1 -0
- package/dist/registry/payments.d.ts +3 -0
- package/dist/registry/payments.d.ts.map +1 -0
- package/dist/registry/payments.js +39 -0
- package/dist/registry/payments.js.map +1 -0
- package/dist/registry/pricing.d.ts +3 -0
- package/dist/registry/pricing.d.ts.map +1 -0
- package/dist/registry/pricing.js +20 -0
- package/dist/registry/pricing.js.map +1 -0
- package/dist/registry/quotes.d.ts +19 -0
- package/dist/registry/quotes.d.ts.map +1 -0
- package/dist/registry/quotes.js +81 -0
- package/dist/registry/quotes.js.map +1 -0
- package/dist/registry/reports.d.ts +3 -0
- package/dist/registry/reports.d.ts.map +1 -0
- package/dist/registry/reports.js +33 -0
- package/dist/registry/reports.js.map +1 -0
- package/dist/registry/shipping.d.ts +3 -0
- package/dist/registry/shipping.d.ts.map +1 -0
- package/dist/registry/shipping.js +85 -0
- package/dist/registry/shipping.js.map +1 -0
- package/dist/registry/super-admin.d.ts +11 -0
- package/dist/registry/super-admin.d.ts.map +1 -0
- package/dist/registry/super-admin.js +222 -0
- package/dist/registry/super-admin.js.map +1 -0
- package/dist/registry/tenants.d.ts +13 -0
- package/dist/registry/tenants.d.ts.map +1 -0
- package/dist/registry/tenants.js +78 -0
- package/dist/registry/tenants.js.map +1 -0
- package/dist/registry/types.d.ts +67 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +10 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/registry/uploads.d.ts +3 -0
- package/dist/registry/uploads.d.ts.map +1 -0
- package/dist/registry/uploads.js +86 -0
- package/dist/registry/uploads.js.map +1 -0
- package/dist/registry/usage.d.ts +9 -0
- package/dist/registry/usage.d.ts.map +1 -0
- package/dist/registry/usage.js +33 -0
- package/dist/registry/usage.js.map +1 -0
- package/dist/tools.d.ts +8 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +150 -0
- package/dist/tools.js.map +1 -0
- package/package.json +41 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cliente HTTP hacia commerce-api. Autenticación por API key
|
|
3
|
+
* (`Authorization: Bearer npk_...`), la misma que expone el panel en
|
|
4
|
+
* Empresa → API keys — nunca sesión de usuario. `COMMERCE_API_KEY` es la
|
|
5
|
+
* única credencial que este proceso necesita; determina qué tenant y qué
|
|
6
|
+
* scopes puede usar (ver apps/commerce-api/src/auth/principal.guard.ts).
|
|
7
|
+
*
|
|
8
|
+
* `COMMERCE_TENANT_ID` es opcional y solo tiene efecto con una key GLOBAL
|
|
9
|
+
* (sin tenant propio, T-IAM-09b): manda `X-Tenant-Id` en cada request para
|
|
10
|
+
* decirle a commerce-api sobre qué empresa operar. Con una key de tenant
|
|
11
|
+
* normal no hace falta — el tenant ya viene fijo en la key — y el header,
|
|
12
|
+
* si se manda de todos modos, se ignora (PrincipalGuard solo lo lee cuando
|
|
13
|
+
* la key resolvió como global).
|
|
14
|
+
*/
|
|
15
|
+
import { SUPER_ADMIN_OVERVIEW_PATH } from "./registry/super-admin.js";
|
|
16
|
+
const DEFAULT_BASE_URL = "https://api-easysell.tupla.dev/api/v1";
|
|
17
|
+
/**
|
|
18
|
+
* Tope duro por request: sin esto, una conexión colgada a commerce-api deja
|
|
19
|
+
* la tool esperando indefinidamente y con ella el cliente MCP entero (stdio
|
|
20
|
+
* es un solo canal secuencial). 30s es generoso para cualquier endpoint real
|
|
21
|
+
* de este API; configurable por si un despliegue propio lo necesita más largo.
|
|
22
|
+
*/
|
|
23
|
+
function timeoutMs() {
|
|
24
|
+
const raw = process.env.COMMERCE_TIMEOUT_MS;
|
|
25
|
+
const parsed = raw ? Number(raw) : NaN;
|
|
26
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 30_000;
|
|
27
|
+
}
|
|
28
|
+
function baseUrl() {
|
|
29
|
+
const raw = process.env.COMMERCE_API_BASE_URL ?? DEFAULT_BASE_URL;
|
|
30
|
+
// CRÍTICO: validar scheme. Sin esto, un `.mcp.json` o config compartida
|
|
31
|
+
// puede apuntar el MCP a `http://evil.com` y exfiltrar la API key en cada
|
|
32
|
+
// request vía el header `Authorization: Bearer npk_...`. Permitimos
|
|
33
|
+
// `http://` SOLO para hosts locales (loopback) — útil para dev contra
|
|
34
|
+
// un commerce-api local sin TLS — y exigimos `https://` para todo lo
|
|
35
|
+
// demás, sin excepciones.
|
|
36
|
+
let parsed;
|
|
37
|
+
try {
|
|
38
|
+
parsed = new URL(raw.replace(/\/+$/, ""));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new Error(`COMMERCE_API_BASE_URL inválida: ${JSON.stringify(raw)}. Debe ser una URL absoluta (ej. https://api.example.com/api/v1).`);
|
|
42
|
+
}
|
|
43
|
+
const isLoopback = parsed.hostname === "localhost" ||
|
|
44
|
+
parsed.hostname === "127.0.0.1" ||
|
|
45
|
+
parsed.hostname === "[::1]" ||
|
|
46
|
+
parsed.hostname === "::1";
|
|
47
|
+
if (parsed.protocol === "http:" && !isLoopback) {
|
|
48
|
+
throw new Error(`COMMERCE_API_BASE_URL insegura: ${parsed.protocol}//${parsed.host} no es loopback. ` +
|
|
49
|
+
"Solo se permite http:// hacia localhost/127.0.0.1/[::1] (dev). En cualquier otro caso usa https://.");
|
|
50
|
+
}
|
|
51
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
52
|
+
throw new Error(`COMMERCE_API_BASE_URL insegura: scheme ${JSON.stringify(parsed.protocol)} no soportado. Usa https:// (o http://localhost en dev).`);
|
|
53
|
+
}
|
|
54
|
+
return parsed.toString().replace(/\/+$/, "");
|
|
55
|
+
}
|
|
56
|
+
function apiKey() {
|
|
57
|
+
const key = process.env.COMMERCE_API_KEY;
|
|
58
|
+
if (!key) {
|
|
59
|
+
throw new Error("COMMERCE_API_KEY no configurada. Crea una API key en el panel (Empresa → API keys) " +
|
|
60
|
+
"y pásala como variable de entorno al arrancar este servidor MCP.");
|
|
61
|
+
}
|
|
62
|
+
return key;
|
|
63
|
+
}
|
|
64
|
+
function authHeaders() {
|
|
65
|
+
const headers = { authorization: `Bearer ${apiKey()}` };
|
|
66
|
+
const tenantId = process.env.COMMERCE_TENANT_ID;
|
|
67
|
+
if (tenantId)
|
|
68
|
+
headers["x-tenant-id"] = tenantId;
|
|
69
|
+
return headers;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Lee el body de una Response con tope de tamaño. Intenta parsear JSON; si
|
|
73
|
+
* falla, devuelve el texto crudo (cubre text/html de proxies, mensajes 502
|
|
74
|
+
* vacíos, etc.). Devuelve `null` si el body está vacío. Si excede
|
|
75
|
+
* `RESPONSE_MAX_BYTES`, devuelve `{ error: "RESPONSE_TOO_LARGE" }` sin
|
|
76
|
+
* retener el contenido (cliente MCP recibe error accionable, no se Buffea
|
|
77
|
+
* un payload de GBs en RAM).
|
|
78
|
+
*/
|
|
79
|
+
const RESPONSE_MAX_BYTES = 16 * 1024 * 1024; // 16 MiB
|
|
80
|
+
async function readBody(res) {
|
|
81
|
+
// Early check vía Content-Length (cuando el backend lo manda). Si declara
|
|
82
|
+
// más del tope, ni siquiera intentamos leer.
|
|
83
|
+
const cl = res.headers.get("content-length");
|
|
84
|
+
if (cl !== null) {
|
|
85
|
+
const declared = Number(cl);
|
|
86
|
+
if (Number.isFinite(declared) && declared > RESPONSE_MAX_BYTES) {
|
|
87
|
+
return {
|
|
88
|
+
error: "RESPONSE_TOO_LARGE",
|
|
89
|
+
message: `El backend anunció ${declared} bytes; el tope local es ${RESPONSE_MAX_BYTES}.`,
|
|
90
|
+
maxBytes: RESPONSE_MAX_BYTES,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Lectura streaming con cap real (cubre chunked sin Content-Length).
|
|
95
|
+
const reader = res.body?.getReader();
|
|
96
|
+
if (!reader)
|
|
97
|
+
return null;
|
|
98
|
+
const chunks = [];
|
|
99
|
+
let total = 0;
|
|
100
|
+
while (true) {
|
|
101
|
+
const { done, value } = await reader.read();
|
|
102
|
+
if (done)
|
|
103
|
+
break;
|
|
104
|
+
total += value.byteLength;
|
|
105
|
+
if (total > RESPONSE_MAX_BYTES) {
|
|
106
|
+
await reader.cancel().catch(() => { });
|
|
107
|
+
return {
|
|
108
|
+
error: "RESPONSE_TOO_LARGE",
|
|
109
|
+
message: `La respuesta excedió ${RESPONSE_MAX_BYTES} bytes durante la lectura.`,
|
|
110
|
+
maxBytes: RESPONSE_MAX_BYTES,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
chunks.push(value);
|
|
114
|
+
}
|
|
115
|
+
if (chunks.length === 0)
|
|
116
|
+
return null;
|
|
117
|
+
// Concatenación robusta (no depende de Buffer.concat con Uint8Array[]):
|
|
118
|
+
// un solo Uint8Array del tamaño exacto + TextDecoder.
|
|
119
|
+
const merged = new Uint8Array(total);
|
|
120
|
+
let pos = 0;
|
|
121
|
+
for (const c of chunks) {
|
|
122
|
+
merged.set(c, pos);
|
|
123
|
+
pos += c.byteLength;
|
|
124
|
+
}
|
|
125
|
+
const text = new TextDecoder().decode(merged);
|
|
126
|
+
if (!text)
|
|
127
|
+
return null;
|
|
128
|
+
try {
|
|
129
|
+
return JSON.parse(text);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return text;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Ejecuta la petición. Nunca lanza por un error HTTP del backend (4xx/5xx):
|
|
137
|
+
* ese error es información útil para el agente (falta un scope, un recurso
|
|
138
|
+
* no existe, una validación falló), así que se devuelve como dato normal
|
|
139
|
+
* con `ok:false`, no como excepción — el handler de la tool lo traduce a
|
|
140
|
+
* `isError` de MCP sin perder el detalle del body. Tampoco lanza por
|
|
141
|
+
* configuración inválida (URL malformada, scheme no permitido) — eso sería
|
|
142
|
+
* una falla de arranque, no de tool, y debe llegar al cliente como un
|
|
143
|
+
* mensaje accionable, no como un stack trace.
|
|
144
|
+
*/
|
|
145
|
+
export async function apiRequest(method, path, opts = {}) {
|
|
146
|
+
let url;
|
|
147
|
+
try {
|
|
148
|
+
url = new URL(baseUrl() + path);
|
|
149
|
+
for (const [k, v] of Object.entries(opts.query ?? {})) {
|
|
150
|
+
if (v !== undefined)
|
|
151
|
+
url.searchParams.set(k, v);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
return { ok: false, status: 0, data: { error: "CONFIG_ERROR", message: err.message } };
|
|
156
|
+
}
|
|
157
|
+
const hasBody = opts.body !== undefined;
|
|
158
|
+
let res;
|
|
159
|
+
try {
|
|
160
|
+
res = await fetch(url, {
|
|
161
|
+
method,
|
|
162
|
+
headers: {
|
|
163
|
+
...authHeaders(),
|
|
164
|
+
...(hasBody ? { "content-type": "application/json" } : {}),
|
|
165
|
+
},
|
|
166
|
+
body: hasBody ? JSON.stringify(opts.body) : undefined,
|
|
167
|
+
signal: AbortSignal.timeout(timeoutMs()),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
return { ok: false, status: 0, data: { error: "NETWORK_ERROR", message: err.message, url: url.toString() } };
|
|
172
|
+
}
|
|
173
|
+
return { ok: res.ok, status: res.status, data: await readBody(res) };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Tope para descargas binarias. PDFs de cotización reales son < 5 MB;
|
|
177
|
+
* CSVs de notificaciones con cientos de miles de filas pueden ser más
|
|
178
|
+
* grandes pero rara vez > 32 MB. 32 MB decoded = ~43 MB de base64 — un
|
|
179
|
+
* cliente MCP que guarda el base64 en su transcript se queda sin disco
|
|
180
|
+
* rápido si pasamos de ahí. El backend controla el origen del archivo;
|
|
181
|
+
* este cap es defensa en profundidad contra respuestas corruptas/gigantes.
|
|
182
|
+
*/
|
|
183
|
+
const DOWNLOAD_MAX_BYTES = 32 * 1024 * 1024;
|
|
184
|
+
/** Descarga binaria (PDF de cotización, CSV de notificaciones): la respuesta no es JSON, se devuelve en base64 con tope. */
|
|
185
|
+
export async function apiDownload(path) {
|
|
186
|
+
let url;
|
|
187
|
+
try {
|
|
188
|
+
url = new URL(baseUrl() + path);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
return { ok: false, status: 0, error: { error: "CONFIG_ERROR", message: err.message } };
|
|
192
|
+
}
|
|
193
|
+
let res;
|
|
194
|
+
try {
|
|
195
|
+
res = await fetch(url, { method: "GET", headers: authHeaders(), signal: AbortSignal.timeout(timeoutMs()) });
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
return { ok: false, status: 0, error: { message: err.message, url: url.toString() } };
|
|
199
|
+
}
|
|
200
|
+
if (!res.ok) {
|
|
201
|
+
// Errores del backend: pasar por readBody para tener JSON parseado (o
|
|
202
|
+
// texto crudo) sin riesgo de OOM por respuestas de error gigantes.
|
|
203
|
+
const data = await readBody(res);
|
|
204
|
+
return { ok: false, status: res.status, error: data ?? null };
|
|
205
|
+
}
|
|
206
|
+
// Early check vía Content-Length: si declara > tope, no leemos.
|
|
207
|
+
const cl = res.headers.get("content-length");
|
|
208
|
+
if (cl !== null) {
|
|
209
|
+
const declared = Number(cl);
|
|
210
|
+
if (Number.isFinite(declared) && declared > DOWNLOAD_MAX_BYTES) {
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
status: res.status,
|
|
214
|
+
error: {
|
|
215
|
+
error: "DOWNLOAD_TOO_LARGE",
|
|
216
|
+
message: `El backend anunció ${declared} bytes; el tope local es ${DOWNLOAD_MAX_BYTES}.`,
|
|
217
|
+
maxBytes: DOWNLOAD_MAX_BYTES,
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Streaming con cap (cubre chunked sin Content-Length).
|
|
223
|
+
const reader = res.body?.getReader();
|
|
224
|
+
if (!reader) {
|
|
225
|
+
return { ok: false, status: res.status, error: { message: "response body vacío" } };
|
|
226
|
+
}
|
|
227
|
+
const chunks = [];
|
|
228
|
+
let total = 0;
|
|
229
|
+
while (true) {
|
|
230
|
+
const { done, value } = await reader.read();
|
|
231
|
+
if (done)
|
|
232
|
+
break;
|
|
233
|
+
total += value.byteLength;
|
|
234
|
+
if (total > DOWNLOAD_MAX_BYTES) {
|
|
235
|
+
await reader.cancel().catch(() => { });
|
|
236
|
+
return {
|
|
237
|
+
ok: false,
|
|
238
|
+
status: res.status,
|
|
239
|
+
error: {
|
|
240
|
+
error: "DOWNLOAD_TOO_LARGE",
|
|
241
|
+
message: `El binario excedió ${DOWNLOAD_MAX_BYTES} bytes durante la lectura.`,
|
|
242
|
+
maxBytes: DOWNLOAD_MAX_BYTES,
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
chunks.push(value);
|
|
247
|
+
}
|
|
248
|
+
if (chunks.length === 0) {
|
|
249
|
+
return {
|
|
250
|
+
ok: true,
|
|
251
|
+
status: res.status,
|
|
252
|
+
base64: "",
|
|
253
|
+
mimeType: res.headers.get("content-type") ?? "application/octet-stream",
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const merged = new Uint8Array(total);
|
|
257
|
+
let pos = 0;
|
|
258
|
+
for (const c of chunks) {
|
|
259
|
+
merged.set(c, pos);
|
|
260
|
+
pos += c.byteLength;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
ok: true,
|
|
264
|
+
status: res.status,
|
|
265
|
+
base64: Buffer.from(merged).toString("base64"),
|
|
266
|
+
mimeType: res.headers.get("content-type") ?? "application/octet-stream",
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Tope local ANTES de salir a la red. El backend (`ProductImageUploadInterceptor`)
|
|
271
|
+
* ya tope a 5 MB, pero este es un segundo piso de defensa: rechaza payloads
|
|
272
|
+
* obviamente sobredimensionados (5 MB decoded + overhead de multipart) sin
|
|
273
|
+
* pagar el costo de decodificar el base64 gigante, armar FormData y mandar la
|
|
274
|
+
* request. 8 MiB decoded = ~10.7 MiB de base64 = aprox el peor caso real
|
|
275
|
+
* (imagen PNG 6000x6000 + cabecera + FormData boundary).
|
|
276
|
+
*/
|
|
277
|
+
const UPLOAD_MAX_BYTES = 8 * 1024 * 1024;
|
|
278
|
+
/**
|
|
279
|
+
* Caracteres máximos del string base64 ANTES del decode. Base64 son 4
|
|
280
|
+
* chars por cada 3 bytes; redondeamos hacia arriba con un margen para
|
|
281
|
+
* padding/espacios. Sin este check, un payload malicioso de 1 GB de base64
|
|
282
|
+
* se decodifica a ~750 MB en RAM (`Buffer.from(b64, 'base64')` no es
|
|
283
|
+
* lazy) y mata el proceso antes de poder validar el tamaño post-decode.
|
|
284
|
+
*/
|
|
285
|
+
const UPLOAD_MAX_B64_CHARS = Math.ceil((UPLOAD_MAX_BYTES * 4) / 3) + 64;
|
|
286
|
+
/**
|
|
287
|
+
* Subida multipart (fotos de catálogo): las dos únicas rutas del registro
|
|
288
|
+
* que no son JSON puro, así que no encajan en `apiRequest` / el DSL
|
|
289
|
+
* declarativo de `registry/types.ts`. El agente manda la imagen en base64;
|
|
290
|
+
* aquí se valida el tamaño ANTES del decode y se arma el
|
|
291
|
+
* `multipart/form-data` que espera `ProductImageUploadInterceptor` (campo
|
|
292
|
+
* `file`).
|
|
293
|
+
*/
|
|
294
|
+
export async function apiUpload(path, file) {
|
|
295
|
+
let url;
|
|
296
|
+
try {
|
|
297
|
+
url = new URL(baseUrl() + path);
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
return { ok: false, status: 0, data: { error: "CONFIG_ERROR", message: err.message } };
|
|
301
|
+
}
|
|
302
|
+
// Primer piso: cap por longitud del string base64 antes del decode.
|
|
303
|
+
// Más barato que decodificar y validar después, y evita memory-bomb.
|
|
304
|
+
if (file.base64.length > UPLOAD_MAX_B64_CHARS) {
|
|
305
|
+
return {
|
|
306
|
+
ok: false,
|
|
307
|
+
status: 0,
|
|
308
|
+
data: {
|
|
309
|
+
error: "PAYLOAD_TOO_LARGE",
|
|
310
|
+
message: `El base64 mide ${file.base64.length} caracteres; el tope es ~${UPLOAD_MAX_B64_CHARS} (~${UPLOAD_MAX_BYTES} bytes decoded).`,
|
|
311
|
+
maxBytes: UPLOAD_MAX_BYTES,
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
const bytes = Buffer.from(file.base64, "base64");
|
|
316
|
+
// Segundo piso (defensa en profundidad): por si el base64 traía padding
|
|
317
|
+
// excesivo que infló la longitud sin representar tantos bytes.
|
|
318
|
+
if (bytes.byteLength > UPLOAD_MAX_BYTES) {
|
|
319
|
+
return {
|
|
320
|
+
ok: false,
|
|
321
|
+
status: 0,
|
|
322
|
+
data: {
|
|
323
|
+
error: "PAYLOAD_TOO_LARGE",
|
|
324
|
+
message: `El archivo decodificado pesa ${bytes.byteLength} bytes; el tope es ${UPLOAD_MAX_BYTES}.`,
|
|
325
|
+
maxBytes: UPLOAD_MAX_BYTES,
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
const form = new FormData();
|
|
330
|
+
form.append("file", new Blob([bytes], { type: file.mimeType }), file.filename);
|
|
331
|
+
let res;
|
|
332
|
+
try {
|
|
333
|
+
res = await fetch(url, {
|
|
334
|
+
method: "POST",
|
|
335
|
+
headers: authHeaders(),
|
|
336
|
+
body: form,
|
|
337
|
+
signal: AbortSignal.timeout(timeoutMs()),
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
return { ok: false, status: 0, data: { error: "NETWORK_ERROR", message: err.message, url: url.toString() } };
|
|
342
|
+
}
|
|
343
|
+
return { ok: res.ok, status: res.status, data: await readBody(res) };
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Detecta, al arrancar, si la key configurada es GLOBAL (T-IAM-09b) —
|
|
347
|
+
* probando un endpoint que solo `SuperAdminGuard` deja pasar. Nunca lanza:
|
|
348
|
+
* "unknown" si no se pudo determinar (red caída, key inválida — eso ya lo
|
|
349
|
+
* va a reportar la primera tool que se use). Solo informativo, para el aviso
|
|
350
|
+
* de arranque en `main.ts`; ninguna tool depende de este resultado.
|
|
351
|
+
*
|
|
352
|
+
* El path del probe viene del registry (`SUPER_ADMIN_OVERVIEW_PATH`) para
|
|
353
|
+
* que un rename de la ruta no rompa silenciosamente el aviso de "key global".
|
|
354
|
+
*/
|
|
355
|
+
export async function probeKeyKind() {
|
|
356
|
+
try {
|
|
357
|
+
const res = await fetch(new URL(baseUrl() + SUPER_ADMIN_OVERVIEW_PATH), {
|
|
358
|
+
method: "GET",
|
|
359
|
+
headers: authHeaders(),
|
|
360
|
+
signal: AbortSignal.timeout(timeoutMs()),
|
|
361
|
+
});
|
|
362
|
+
if (res.status === 200)
|
|
363
|
+
return "global";
|
|
364
|
+
if (res.status === 403 || res.status === 401)
|
|
365
|
+
return "tenant";
|
|
366
|
+
return "unknown";
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
return "unknown";
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { registerRoutes } from "./tools.js";
|
|
5
|
+
import { registerCatalogImageTools } from "./registry/catalog-images.js";
|
|
6
|
+
import { registerBinaryDownloadTools } from "./registry/binary-downloads.js";
|
|
7
|
+
import { registerTenantUploads } from "./registry/uploads.js";
|
|
8
|
+
import { allRoutes } from "./registry/index.js";
|
|
9
|
+
import { probeKeyKind } from "./client.js";
|
|
10
|
+
// Falla rápido y claro en vez de que la primera tool llamada reviente con un
|
|
11
|
+
// error genérico de fetch — quien arranca esto por primera vez ve el motivo
|
|
12
|
+
// exacto antes de intentar conectarse desde un cliente MCP.
|
|
13
|
+
if (!process.env.COMMERCE_API_KEY) {
|
|
14
|
+
console.error("easysell MCP no arrancó: falta COMMERCE_API_KEY. Crea una API key en el panel " +
|
|
15
|
+
"(Empresa → API keys, o Plataforma → API keys globales si eres super-admin) y pásala " +
|
|
16
|
+
"como variable de entorno.");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const server = new McpServer({ name: "easysell", version: "0.1.0" });
|
|
20
|
+
registerRoutes(server, allRoutes);
|
|
21
|
+
registerCatalogImageTools(server);
|
|
22
|
+
registerBinaryDownloadTools(server);
|
|
23
|
+
registerTenantUploads(server);
|
|
24
|
+
async function main() {
|
|
25
|
+
const transport = new StdioServerTransport();
|
|
26
|
+
await server.connect(transport);
|
|
27
|
+
console.error(`easysell MCP: ${allRoutes.length + 4} tools registradas, esperando por stdio.`);
|
|
28
|
+
// Best-effort, no bloquea el arranque: solo avisa por stderr si la key
|
|
29
|
+
// resultó ser GLOBAL (T-IAM-09b), para que quede claro que TODAS las tools
|
|
30
|
+
// de negocio ahora pueden operar sobre CUALQUIER empresa (con X-Tenant-Id)
|
|
31
|
+
// y que las ~18 tools `super_admin_*` ya están activas. Con una key normal
|
|
32
|
+
// de tenant, o si la key es inválida, o si no hay red, no dice nada.
|
|
33
|
+
probeKeyKind()
|
|
34
|
+
.then((kind) => {
|
|
35
|
+
if (kind === "global") {
|
|
36
|
+
console.error("⚠️ easysell MCP: esta API key es GLOBAL — opera sobre CUALQUIER empresa " +
|
|
37
|
+
"(usa COMMERCE_TENANT_ID para acotarla a una) e incluye las tools super_admin_*. " +
|
|
38
|
+
"Si no era la intención, revoca esta key y usa una de tenant (Empresa → API keys).");
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
.catch(() => {
|
|
42
|
+
// El probe ya no lanza (ver client.ts); este catch es solo defensivo.
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
main().catch((err) => {
|
|
46
|
+
console.error("easysell MCP no pudo arrancar:", err);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,6EAA6E;AAC7E,4EAA4E;AAC5E,4DAA4D;AAC5D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAClC,OAAO,CAAC,KAAK,CACX,gFAAgF;QAC9E,sFAAsF;QACtF,2BAA2B,CAC9B,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AAErE,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAClC,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAClC,2BAA2B,CAAC,MAAM,CAAC,CAAC;AACpC,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAE9B,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,iBAAiB,SAAS,CAAC,MAAM,GAAG,CAAC,0CAA0C,CAAC,CAAC;IAE/F,uEAAuE;IACvE,2EAA2E;IAC3E,2EAA2E;IAC3E,2EAA2E;IAC3E,qEAAqE;IACrE,YAAY,EAAE;SACX,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACb,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CACX,2EAA2E;gBACzE,kFAAkF;gBAClF,mFAAmF,CACtF,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;SACD,KAAK,CAAC,GAAG,EAAE;QACV,sEAAsE;IACxE,CAAC,CAAC,CAAC;AACP,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;IACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/registry/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,WAAW,EAAE,QAAQ,EAqBjC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const auditRoutes = [
|
|
2
|
+
{
|
|
3
|
+
name: "audit_list_events",
|
|
4
|
+
method: "GET",
|
|
5
|
+
path: "/audit/events",
|
|
6
|
+
description: "Lista eventos de auditoría del tenant (cambios sensibles), más recientes primero.",
|
|
7
|
+
scopes: ["audit.read"],
|
|
8
|
+
query: {
|
|
9
|
+
action: { type: "string", description: "p. ej. \"delivery_zone.created\"." },
|
|
10
|
+
targetType: { type: "string", description: "p. ej. \"DeliveryZone\"." },
|
|
11
|
+
limit: { type: "number", description: "Default 50, tope 200." },
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "audit_get_event",
|
|
16
|
+
method: "GET",
|
|
17
|
+
path: "/audit/events/:id",
|
|
18
|
+
description: "Detalle de un evento de auditoría por id.",
|
|
19
|
+
scopes: ["audit.read"],
|
|
20
|
+
pathParams: { id: { type: "string" } },
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/registry/audit.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,WAAW,GAAe;IACrC;QACE,IAAI,EAAE,mBAAmB;QACzB,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE,mFAAmF;QAChG,MAAM,EAAE,CAAC,YAAY,CAAC;QACtB,KAAK,EAAE;YACL,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mCAAmC,EAAE;YAC5E,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0BAA0B,EAAE;YACvE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE;SAChE;KACF;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE,2CAA2C;QACxD,MAAM,EAAE,CAAC,YAAY,CAAC;QACtB,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KACvC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-downloads.d.ts","sourceRoot":"","sources":["../../src/registry/binary-downloads.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA0CzE,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+CnE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Endpoints que devuelven un binario (no JSON): PDF de cotización, CSV de
|
|
3
|
+
* notificaciones, CSV de clientes. Se registran a mano igual que las
|
|
4
|
+
* subidas de imagen (`catalog-images.ts` / `uploads.ts`): no encajan en
|
|
5
|
+
* el DSL declarativo pensado para JSON.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { apiDownload } from "../client.js";
|
|
9
|
+
/**
|
|
10
|
+
* Helper compartido: dado el resultado de apiDownload, devuelve el
|
|
11
|
+
* CallToolResult con text + resource. Centraliza el shape para que las
|
|
12
|
+
* tres tools se comporten igual y un cambio futuro (p. ej. logging de
|
|
13
|
+
* tamaño, mime type extra) sea en un solo lugar.
|
|
14
|
+
*
|
|
15
|
+
* Tipado con los tipos literales del SDK (`type: "text"` / `"resource"`)
|
|
16
|
+
* para satisfacer `CallToolResult` sin necesidad de castear.
|
|
17
|
+
*/
|
|
18
|
+
function downloadToResult(result) {
|
|
19
|
+
if (!result.ok) {
|
|
20
|
+
return {
|
|
21
|
+
isError: true,
|
|
22
|
+
content: [{ type: "text", text: JSON.stringify({ status: result.status, error: result.error }, null, 2) }],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: JSON.stringify({
|
|
30
|
+
status: result.status,
|
|
31
|
+
mimeType: result.mimeType,
|
|
32
|
+
base64Length: result.base64.length,
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: "resource",
|
|
37
|
+
resource: {
|
|
38
|
+
uri: `data:${result.mimeType};base64,${result.base64}`,
|
|
39
|
+
mimeType: result.mimeType,
|
|
40
|
+
blob: result.base64,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function registerBinaryDownloadTools(server) {
|
|
47
|
+
server.registerTool("quotes_download_pdf", {
|
|
48
|
+
description: "Descarga el PDF de una cotización. Devuelve el archivo en base64 (mimeType application/pdf) " +
|
|
49
|
+
"para que quien llama lo guarde o lo reenvíe. Requiere scope: quotes.read.",
|
|
50
|
+
inputSchema: { id: z.string().describe("id de la cotización") },
|
|
51
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
52
|
+
}, async ({ id }) => downloadToResult(await apiDownload(`/quotes/${encodeURIComponent(id)}/pdf`)));
|
|
53
|
+
server.registerTool("notifications_export_csv", {
|
|
54
|
+
description: "Descarga el CSV de notificaciones del tenant. Devuelve el archivo en base64 (mimeType text/csv). " +
|
|
55
|
+
"Requiere scope: notifications.read.",
|
|
56
|
+
inputSchema: {},
|
|
57
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
58
|
+
}, async () => downloadToResult(await apiDownload("/notifications/export.csv")));
|
|
59
|
+
server.registerTool("customers_export_csv", {
|
|
60
|
+
description: "Descarga el CSV de clientes del tenant con los mismos filtros que `customers_list` (sin paginación). " +
|
|
61
|
+
"Devuelve el archivo en base64 (mimeType text/csv). Requiere scope: customers.read.",
|
|
62
|
+
inputSchema: {
|
|
63
|
+
q: z.string().optional().describe("Búsqueda por nombre/teléfono/correo."),
|
|
64
|
+
tag: z.string().optional().describe("Etiqueta exacta."),
|
|
65
|
+
hasPendingPayment: z.enum(["true", "false"]).optional(),
|
|
66
|
+
},
|
|
67
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
68
|
+
}, async (args) => {
|
|
69
|
+
const params = new URLSearchParams();
|
|
70
|
+
if (args.q)
|
|
71
|
+
params.set("q", args.q);
|
|
72
|
+
if (args.tag)
|
|
73
|
+
params.set("tag", args.tag);
|
|
74
|
+
if (args.hasPendingPayment)
|
|
75
|
+
params.set("hasPendingPayment", args.hasPendingPayment);
|
|
76
|
+
const qs = params.toString();
|
|
77
|
+
return downloadToResult(await apiDownload(`/customers/export.csv${qs ? `?${qs}` : ""}`));
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=binary-downloads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-downloads.js","sourceRoot":"","sources":["../../src/registry/binary-downloads.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RouteDef } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Respuestas predeterminadas (`/tenants/me/canned-responses`): atajos de
|
|
4
|
+
* texto que los agentes humanos pueden insertar con un shortcut. Es la
|
|
5
|
+
* configuración operativa principal de cualquier equipo de WhatsApp.
|
|
6
|
+
*
|
|
7
|
+
* El backend declara `chat.read` para listar y `chat.write` para el resto
|
|
8
|
+
* (mismo criterio que conversaciones: leer no requiere scope de escritura).
|
|
9
|
+
*/
|
|
10
|
+
export declare const cannedResponseRoutes: RouteDef[];
|
|
11
|
+
//# sourceMappingURL=canned-responses.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canned-responses.d.ts","sourceRoot":"","sources":["../../src/registry/canned-responses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,EA6C1C,CAAC"}
|