nortex-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nortex-mcpDecime cuánto vendí hoy y cuántas facturas hice"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MuseNortex — MCP connector for Nortex (open source)
This repository contains the Model Context Protocol (MCP) server built for MuseNortex, a submission to the Meta Global AI Developer Hackathon (Startup track). It connects Nortex — a business-management SaaS for small shops in Nicaragua (invoicing, inventory, payroll) — to AI assistants through 10 read-only tools with short, voice-ready answers in Spanish (Nicaraguan voseo): today's sales, sales over a period, low stock, product prices, open inventory counts, and product-usage help grounded in the official manual.
The server is strictly read-only. Each business connects its own AI with its own revocable AI-access token generated inside Nortex, and can only ever see its own business's data.
The Nortex platform itself is proprietary and is not part of this repository; this connector talks to it over HTTP.
License: MIT — see LICENSE.
nortex-mcp — servidor MCP read-only para Nortex (F2 + F4 + F5 + F6 + F7 + F7.1)
Servidor MCP (stdio + Streamable HTTP) con 10 tools de solo lectura (9 de datos + resolver_duda, dudas de uso) que devuelven
respuestas cortas en voseo nicaragüense, listas para voz. Es el transporte que
usará la voz de MuseNortex (T6): las tools del hackatón son capabilities de este
mismo MCP, no un servicio aparte.
Ticket: T4-04 (track T4 mcp-readonly) · Fases: F2 — catálogo v1 completo
rate limit + auditoría · F4 — transporte Streamable HTTP · F5 —
informe_ventas, el Reporte integral a pedido (v0.3.0) · F6 —resolver_duda, NortexGPT: dudas de uso con fuentes oficiales (v0.4.0) · F7 — auth por cliente: cada negocio conecta SU IA con SU cuenta Nortex (v0.5.0) · F7.1 — el modo cliente acepta el «acceso para IA»nxt_que el dueño genera en Nortex (v0.5.1).
Límites (leer antes de usar)
Mocks, no staging. El backend es sintético (
src/mock.ts), pero desde el 2026-10-14 las FORMAS (rutas, parámetros, shapes de respuesta) están verificadas contra el código del monolitonortex.20(ver mapa abajo). La paridad contra staging vivo sigue siendo F3 (requiere JWT de staging — bloqueado, paso de cuenta de Noel).Sin OAuth. Desde F7.1 el cliente se autentica con el «acceso para IA»
nxt_que genera en Nortex (ver «Para clientes: conectá tu IA»; requiere el monolito con la ramastark/mcp-access-tokendesplegada). El JWT de sesión de F7 sigue aceptado. OAuth / conector publicable sigue pendiente (requiere OK de Noel).Solo lectura. Ninguna tool escribe.
detalle_ventaes fail closed: responde un string fijo porque su endpoint devuelve HTML, no JSON (ver abajo).precio_productoes base NIO. La extensión a USD vive en T6-10 (precio_dolares, MuseNortex) — esta tool no la duplica ni la anticipa.Proceso separado. Este proyecto NO toca el monolito
nortex.20: lo llama por HTTP con un service token. Nada de este repo vive dentro del monolito."Hoy" = día comercial America/Managua (UTC-6, sin DST), no UTC.
Related MCP server: Jobber MCP Server
Requisitos
Node 20+.
Cómo correrlo
npm install
npm run dev # stdio, modo mock (default)Modo contra backend real (F3, hoy bloqueado por falta de JWT de staging):
cp .env.example .env # completar NORTEX_API_URL y NORTEX_SERVICE_TOKEN
NORTEX_API_MODE=http npm run devEl token solo se lee de NORTEX_SERVICE_TOKEN en el entorno. Nunca va en el
repo, nunca se loguea (ver test/auth.test.ts y el grep de secretos abajo).
Variables opcionales:
Variable | Default | Qué hace |
|
| Etiqueta del tenant/sesión para rate limit y auditoría (nunca sale del proceso) |
|
| Máx. tool calls por minuto por tenant (ops/stdio) o por tenant+usuario (modo cliente) |
Tools
Tool | Input | Respuesta (ejemplo) |
| — | "Hoy vendiste C$12,450.00 en 38 facturas. Tu mejor producto fue Queso frito." |
|
| "Queso frito: C$120.00 la libra, te quedan 15 en Principal y 4 en Sucursal." |
|
| "Del Queso frito te quedan 19 libras en total: 15 en Principal, 4 en Sucursal." |
|
| "En los últimos 7 días vendiste C$82,749.00 en 261 facturas. El mejor día fue el viernes con C$14,014.00." |
|
| "Tenés 2 productos bajos: Queso fresco (8, mínimo 12) y Leche (6, mínimo 12)." |
|
| "Tenés 2 conteos abiertos: Principal (120 de 200 contados) y Sucursal (30 de 50 contados)." |
|
| "El Queso frito vale C$120.00 la libra." |
|
| "Ese detalle no lo tengo en texto." (fail closed, ver abajo) |
|
| "Sí. Creás el producto UNA vez… tocás el botón «+1 CAJA (12)»…" + |
|
| "En los últimos 30 días vendiste C$82,749 en 261 facturas, con utilidad bruta de C$24,317. Tu producto más vendido fue el Queso frito." + bloque de detalle (ver abajo) |
Reglas de voz: voseo, corto, sin IDs internos, sin JSON crudo. Vacío es normal
("No hubo ventas hoy todavía.", "No encontré nada como 'queso azul'.",
"No tenés productos bajos de stock.", "No tenés conteos abiertos.").
401/403 → "No pude acceder a tu negocio, revisá tu sesión." (fail closed, cero datos).
ventas_hoy/ventas_periodo ordenan products[]/daily[] del reporte
(el backend ya agrega); el servidor nunca recalcula dinero desde transacciones.
fmtMoney vive en un solo lugar (src/fmtMoney.ts) — F2 lo reutiliza, no lo duplica.
informe_ventas — el Reporte integral a pedido (F5)
Es la victoria rápida #2 del Plan Nivel Odoo (ítem 7): lo que la clienta pide por WhatsApp — «¿hay algún reporte de ventas? ¿un consolidado? ¿lo que más se vende?» — sin navegar la pantalla.
Composición (sin duplicar lógica de dinero): una sola llamada a
GET /api/reports/sales?startDate&endDate, el mismo endpoint que alimenta la pantalla de Reportes y el Excel de 5 hojas. La respuesta ya traesummary(brutas, devoluciones, netas, IVA, utilidad bruta, ticket promedio),paymentMethods[]yproducts[](cantidad neta, venta neta y utilidad por producto), calculados por el backend. La tool solo ordena y formatea: jamás suma ni resta dinero. No hizo falta ningún endpoint nuevo ennortex.20.Rankings ordenados de verdad: la tabla «Productos vendidos» de la UI sale en orden alfabético; acá «Lo que más se vendió» ordena por cantidad neta y «Lo que más utilidad dejó» por utilidad bruta, de mayor a menor.
Salida en dos bloques: resumen hablado de ≤ ~25 palabras (contrato de voz T6 §4.1, dinero con
fmtVoiceMoney, sin centavos) + bloque «Detalle del informe» en texto para pantalla/WhatsApp con el consolidado completo.Rango máximo 31 días en rangos personalizados: paridad con el documento/Excel del backend (
SALES_EXPORT_MAX_DAYS = 31). Los períodos rápidos sonhoy,ayer,semana(últimos 7 días) ymes(últimos 30).Fail closed: el tenant sale solo de la sesión (la tool nunca envía
tenantId); 401/403 → el mensaje de siempre, cero datos. Período sin ventas → "No hubo ventas en ese período.".
resolver_duda — NortexGPT: dudas de uso (F6)
Responde «¿cómo se hace X en Nortex?» solo con fuentes oficiales, sin APIs pagas ni embeddings externos:
knowledge/manual.json— el Manual de Usuario PDF segmentado por capítulo y subsección, con figuras y página. Se regenera connode scripts/build-manual-kb.mjs [ruta.pdf](requierepdftotext); el JSON va versionado, así que el servidor no necesita el PDF en runtime. Un test compara su sha256 contra el PDF oficial cuando el PDF está en la máquina.knowledge/casos.json— casos reales verificados contra el código denortex.20(empaque caja/unidad, código de barras único, regalías por ajuste, lotes vigentes/FEFO, anulación en negocio unipersonal). Editable: cada caso cita los archivos donde se verificó y la fecha. Nunca agregar un caso sin verificarlo.
Recuperación: BM25 propio (src/knowledge.ts) con normalización de tildes,
stopwords y stemming liviano en español. La respuesta es extractiva (texto
literal del caso o del segmento del manual), con Fuente: (caso, o capítulo ›
sección (Figura N), página), Ver también: y Confianza: alta|media. Si la
fuente elegida no cubre al menos el 60 % de los términos de la pregunta →
"Sin fundamento en el manual: …", sin pasos. No recibe la API ni toca datos
del tenant; si la base no carga, fail closed con mensaje en voseo.
detalle_venta — exclusión documentada (fail closed)
Evidencia: CONTRATOS-F0-BORRADOR.md §E5c (hallazgo T4-02): la ruta candidata
GET /api/reports/sales/document (backend/routes/salesReports.ts:249) responde
text/html (renderSalesReportHtml), no JSON. El JSON de
GET /api/reports/sales/transactions (E5b) es la fuente canónica, pero expone
IDs internos y paginación que el diseño v1 no resuelve todavía.
Decisión: no se parsea HTML en el MCP. El handler devuelve el string fijo
"Ese detalle no lo tengo en texto." sin tocar la red (ni siquiera valida el
argumento; los tests lo prueban contra un backend caído y en modo unauthorized).
Cuando el backend exponga el detalle como JSON (F3+), la tool se re-diseña con
su propio ticket.
Rate limit
30 tool calls por minuto por clave (ventana deslizante de 60 s). Clave: el tenant en stdio/ops;
tenant:usuario(claims del JWT) en modo cliente — cada persona su cubeta, y reconectar no la resetea;opaque:<sha256 del token>con acceso para IAnxt_(F7.1) — cada acceso su cubeta, aunque sean del mismo dueño. configurable conNORTEX_RATE_LIMIT_PER_MIN. El default es holgado para voz (una consulta ≈ 1–3 calls) y frena bucles accidentales del modelo antes de golpear el backend.Al excederlo, la tool responde en voseo (string congelado en tests):
"Estás yendo muy rápido. Esperá un momento y probá de nuevo."Se aplica antes de llamar al backend, en las 9 tools.
Auditoría
Cada tool call escribe una línea JSON por stderr (stdout es solo protocolo MCP):
{"event":"tool_call","tool":"ventas_hoy","tenant":"default","mode":"ops","ts":"2026-10-09T03:44:28.277Z","latencyMs":4,"status":"ok"}
{"event":"tool_call","tool":"ventas_hoy","tenant":"t-pulperia","userId":"u-ana","role":"OWNER","mode":"client","authKind":"jwt","ts":"…","latencyMs":14,"status":"ok"}
{"event":"tool_call","tool":"ventas_hoy","tenant":null,"mode":"client","authKind":"opaque","subject":"opaque:3f9c…(sha256 hex)","ts":"…","latencyMs":12,"status":"ok"}Campos: tool, tenant (null en sesiones opacas: el MCP no lo conoce), userId
y role (solo modo cliente con JWT), mode (ops | client), authKind
(jwt | opaque, solo modo cliente), subject (solo opaco: SHA-256 hex del
token, el mismo hash que guarda el monolito), ts (ISO), latencyMs, status
(ok | error | rate_limited). Nunca se registra el token ops, el JWT del
cliente (ni su firma, ni el email del payload: solo ids) ni el token opaco (ni
fragmentos: solo su hash), los argumentos del call (pueden llevar datos del
negocio) ni respuestas del backend.
Tests
npm test # vitest: 161 tests (F7: 21 de auth por cliente y aislamiento entre tenants) (happy/empty/error por tool, strings exactos en
# voseo, validación zod, aislamiento cross-tenant, rate limit,
# fail closed de detalle_venta, fmtMoney, envelope HTTP real)Sin secretos en el repo (verificado):
grep -rniE "token|secret|bearer|password|api[_-]?key" src/ test/ .env.example
# -> solo plomería: lectura de process.env.NORTEX_SERVICE_TOKEN,
# construcción del header Authorization, y fixtures falsos en tests.Cero writes (verificado): el único fetch del repo es el cliente GET de
src/nortexApi.ts; no hay POST/PUT/PATCH/DELETE ni SQL de escritura.
Typecheck (lo corre el coordinador en serie con los del monolito — no correr en paralelo):
npm run typecheckGate F2: inspector
Verificado con el MCP Inspector en modo CLI (2026-10-08, @modelcontextprotocol/inspector):
tools/list→ las 8 tools listadas,inputSchemaJSON Schema válido para cada una.tools/call→ las 5 nuevas responden con los strings exactos en voseo:ventas_periodo {"dias":7}→ "En los últimos 7 días vendiste C$82,749.00 en 261 facturas. El mejor día fue el viernes con C$14,014.00."inventario_bajo {}→ "Tenés 2 productos bajos: Queso fresco (8, mínimo 12) y Leche (6, mínimo 12)."estado_conteo {}→ "Tenés 2 conteos abiertos: Principal (120 de 200 contados) y Sucursal (30 de 50 contados)."precio_producto {"producto":"queso frito"}→ "El Queso frito vale C$120.00 la libra."detalle_venta {"factura":"A-001234"}→ "Ese detalle no lo tengo en texto."
(Nota: el CLI del inspector exige el target como primer posicional y las opciones
después de --; los args de tools/call van como pares --tool-arg k=v:
inspector --cli node dist/index.js -- --method tools/call --transport stdio --tool-name precio_producto --tool-arg 'producto=queso frito'.)
Mapa de endpoints reales (verificado contra el código del monolito, 2026-10-14)
Tool | Endpoint(s) real(es) que consume | Fuente en nortex.20 |
|
|
|
|
| idem |
|
|
|
|
| idem |
|
|
|
|
|
|
|
|
|
| — (fail closed, no consume red) |
|
|
|
|
Dos correcciones que salieron de esta verificación (aplicadas el 2026-10-14):
Envelope
{success, data}: el stock por bodega envuelve la respuesta;createHttpNortexApiahora la desenvuelve (unwrapEnvelope, con tests). Sin esto,buscar_producto/stock_productohabrían fallado contra el backend real en F3.Avance de conteo: la lista de conteos NO trae
countedItems/totalItems(eso era el borrador F0); trae solo_count.items.estado_conteoahora calcula el avance desde el detalle (itemsconcounted != null).
Gate 2026-10-14 (protocolo, además de los tests unitarios)
npm test→ 73/73 (6 archivos; +4 nuevos detest/nortexApi.test.ts).npm run typecheck→ exit 0.npm run build→ OK.Smoke de protocolo MCP (
scripts/protocol-smoke.mjs, cliente real del SDK sobre stdio contradist/index.js): handshake OK (nortex-mcp v0.2.0),tools/list= 8 tools con schemas, 9 llamadas (las 8 tools +estado_conteofiltrado) respondidas en voseo, auditoría por stderr (9 líneasstatus:"ok"). Transcripción completa enscripts/protocol-smoke-output.txt.
Gate F5 2026-10-15 (informe_ventas, v0.3.0)
npm test→ 105/105 (8 archivos; +20:test/informe.test.tscon 17 casos — happy con string exacto, rankings con órdenes distintos por cantidad vs. utilidad, rango de 31 días pasa / 32 rechazado sin tocar la red, fail closed sin fuga de datos, aislamiento (la tool solo envíastartDate/endDate) — y 3 defmtVoiceMoney).npm run typecheck→ exit 0.npm run build→ OK.Smokes de protocolo re-corridos: stdio (
scripts/protocol-smoke.mjs, ahora con 4 llamadas ainforme_ventas: mes, semana, rango válido y rango31 días rechazado) y HTTP (
scripts/protocol-smoke-http.mjs:tools/list= 9 tools, llamada ainforme_ventasOK, 401 sin token). Transcripciones enscripts/protocol-smoke-output.txtyscripts/protocol-smoke-http-output.txt.Cómo se prueba en staging (cuando F3 se desbloquee con el JWT): con
NORTEX_API_MODE=httpapuntando a staging,informe_ventas {periodo:"mes"}debe coincidir cifra por cifra con la pantalla de Reportes del mismo tenant y período (brutas/netas/IVA/utilidad/ticket), y el ranking «Lo que más se vendió» debe ser el mismo conjunto de la tabla «Productos vendidos» pero ordenado por cantidad neta descendente. Esa comparación ES el contract test de F3 para esta tool; hoy queda bloqueada por el mismo paso de cuenta que el resto de F3.
Estructura
src/
index.ts # entrada: elige transporte (stdio default, MCP_TRANSPORT=http)
server.ts # createNortexMcpServer(deps): las 10 tools + guarded (rate limit + auditoría), compartido por ambos transportes
http.ts # F4: Streamable HTTP stateless en /mcp + auth bearer interina
tools.ts # handlers (inyectan NortexApi): validación zod estricta + voseo
knowledge.ts # F6: corpus (manual + casos), tokenizador ES, BM25, decisión fail closed
resolverDuda.ts # F6: handler de resolver_duda (solo conocimiento)
nortexApi.ts # interfaz inyectable + createHttpNortexApi + NortexAuthError
mock.ts # mock F1+F2 basado en el borrador F0 (datos sintéticos)
types.ts # formas mínimas del contrato (E1, E4, E5a, stock-counts)
fmtMoney.ts # ÚNICO formateo de dinero/cantidades ("C$12,450.00")
time.ts # todayManagua(): día comercial America/Managua (+ helpers de período)
rateLimit.ts # RateLimiter por tenant/sesión (default 30/min)
audit.ts # auditLog: línea JSON por stderr (tool, tenant, ts, latencia, status)
test/
tools.test.ts # F1: happy/empty/error + strings exactos + validación zod
toolsF2.test.ts # F2: happy/empty/error por tool nueva + cross-tenant + fail closed
rateLimit.test.ts # límite, ventana de 60s, aislamiento por tenant, env
http.test.ts # F4: handshake/listTools/llamada por HTTP, 401, config fail closed
auth.test.ts # cross-tenant fail closed + token solo vía env
fmtMoney.test.ts # formateo centralizado (+ fmtVoiceMoney)
resolverDuda.test.ts # F6: banco de pruebas (5 preguntas reales) + sin fundamento + integridad de la base
informe.test.ts # F5: informe_ventas happy/vacío/error, rankings ordenados, rango ≤31 días, aislamiento
knowledge/
manual.json # F6: Manual PDF segmentado (generado por scripts/build-manual-kb.mjs)
casos.json # F6: casos verificados contra el código (editable)Conexión con MuseNortex (regla de oro)
Este servidor es el transporte de las tools de voz de T6 (MUSE-01): las 8 tools
nuevas del hackatón se montan como capabilities de este mismo MCP, no como un
servicio aparte. Heredan toda la governance: tenant fijado por la sesión
(NORTEX_TENANT_ID, nunca seteable por un tool call), fail closed en 401/403,
agregados server-side (el modelo narra, nunca calcula dinero), voseo, rate limit
y auditoría. F2 completa el set de lectura que el demo del hackatón Meta necesita.
precio_dolares (T6-10) extiende precio_producto a USD — vive en T6, no acá.
F4 — transporte Streamable HTTP
Además de stdio, el servidor habla Streamable HTTP (spec MCP) con las MISMAS 9 tools y la
misma governance (createNortexMcpServer).
npm run build
NORTEX_MCP_HTTP_TOKEN=<secreto> npm run start:http # o: MCP_TRANSPORT=http node dist/index.jsVariable | Default | Uso |
|
|
|
|
| puerto |
|
| loopback; no se abre a 0.0.0.0 salvo que lo pidas |
| — | token bearer de operador (modo ops). Obligatorio salvo que esté activo el modo cliente |
|
| tenant del modo ops; ningún header/parámetro lo cambia |
| apagado |
|
| — | en modo cliente: URL del monolito que valida el JWT y sirve los datos |
Sin NORTEX_MCP_HTTP_TOKEN ni modo cliente, el proceso no arranca (fail closed).
Modo ops: endpoint
POST /mcp, stateless (sessionIdGenerator: undefined): tools read-only e idempotentes, sin estado por sesión. El SDK exige un servidor+transport por request en este modo; rate limiter y api se comparten, así que el límite por tenant sigue siendo global. GET/DELETE → 405.Auth interina:
Authorization: Bearer <token>, comparación en tiempo constante (timingSafeEqualsobre SHA-256). Ausente o incorrecto → 401 sin cuerpo de protocolo.Smoke:
node scripts/protocol-smoke-http.mjs→scripts/protocol-smoke-http-output.txt.
Qué NO es: no es OAuth ni conector publicable (F5, requiere OK de Noel); no es paridad contra staging (F3, bloqueada por falta de JWT). Modo ops: un solo token compartido; sin TLS propio: para exponerlo fuera de loopback hace falta un proxy TLS delante.
F7 — modo cliente: cada negocio con SU cuenta
Dos modos de auth en el mismo endpoint /mcp, que pueden coexistir.
Precedencia por request: (1) si el bearer es NORTEX_MCP_HTTP_TOKEN → ops
(F4, stateless, tenant y api del entorno, sin cambios); (2) si no, y
NORTEX_MCP_CLIENT_AUTH está activo → cliente; (3) si no → 401.
NORTEX_MCP_CLIENT_AUTH=1 NORTEX_API_URL=https://<api de nortex> \
[NORTEX_MCP_HTTP_TOKEN=<secreto ops>] MCP_TRANSPORT=http node dist/index.jsMecánica del modo cliente (src/http.ts):
El cliente manda
Authorization: Bearer <JWT de Nortex>— el mismo JWT de sesión con el que la app autentica (HS256,{userId, tenantId, role}, 30 días). El MCP no tiene el secreto JWT ni verifica la firma: eso lo hace el monolito.Sin
Mcp-Session-Idsolo se aceptainitialize. Antes de tocar la red se decodifican los claims y se rechaza (401) si faltanuserId/tenantId/role, si traekind(tokens de repartidor o de tracking de pedido: nunca son sesión de usuario) o siexpya pasó.Una sonda con ese JWT:
GET /api/products?page=1&pageSize=1(la lectura más liviana que ya usa el MCP; exenta del paywall en el monolito). El monolito verifica firma + usuario activo contra BD. 401/403 → 401 sin cuerpo; cualquier otro fallo (caído, 5xx, red) → 503. En ambos casos no se crea sesión.Se abre una sesión MCP con estado (
Mcp-Session-Id) contenantId,userId,rolefijados y unNortexApipor sesión construido con el JWT del cliente. Cada tool call vuelve a pasar por el monolito, que re-valida el JWT y acota al tenant del token (si el usuario se deshabilita a mitad de sesión, la tool responde «revisá tu sesión», sin datos).La sesión está atada al SHA-256 del JWT que la abrió: el mismo
Mcp-Session-Idcon otro bearer → 401. Sesión inactiva 30 min o JWT vencido → se cierra.DELETE /mcpcon la sesión la cierra. Tope de 1000 sesiones vivas (503 al llenarse).Ninguna tool acepta tenant (test que recorre los
inputSchemade las 10); untenantIdinyectado en los argumentos se descarta.
F7.1 — acceso para IA (nxt_)
El monolito emite (Nortex → Configuración → «Acceso para IA», solo OWNER/ADMIN)
un token opaco nxt_ + 32 bytes base64url: solo lectura (403 en todo no-GET),
revocable, y en su BD vive solo el SHA-256. Si el bearer empieza con nxt_:
No se decodifica nada (no es JWT, no hay claims ni
exp). La validación es la MISMA sondaGET /api/products?page=1&pageSize=1: 200 → sesión; 401/403 → 401 sin cuerpo; 5xx/red → 503. Nunca hay sesión sin sonda OK.La sesión queda atada al SHA-256 del token (comparación en tiempo constante, igual que JWT): otro bearer — otro acceso del mismo dueño, su JWT, otro negocio — con ese
Mcp-Session-Id→ 401.tenantId/userId/rolequedannullen la sesión: el monolito resuelve el negocio por el token en cada llamada (y revalida que el emisor siga activo y no revocado). Ninguna tool recibe tenant del MCP (invariante de F7 intacto).Rate limit y auditoría por
opaque:<sha256 hex>; la auditoría marcaauthKind: "opaque". El token en claro no aparece en ningún log.Vencimiento: el de la sesión MCP (30 min sin uso /
DELETE); el MCP no inventa unexp. Si el dueño revoca el acceso, la próxima tool call ya responde «revisá tu sesión», sin datos.
El camino JWT y el modo ops no cambiaron (salvo el campo authKind: "jwt"
agregado a la auditoría de las sesiones JWT).
Para clientes: conectá tu IA
Entrá a Nortex con tu cuenta de dueño o administrador y andá a Configuración → «Acceso para IA».
Creá un acceso y ponele un nombre que te ayude a reconocerlo (por ejemplo, «Mi Claude» o «ChatGPT de la tienda»).
Nortex te muestra un bloque de conexión («copia y pega esto a tu IA»). Tocá «Copiar bloque» y copialo completo, tal como está: ya trae todo lo que tu IA necesita para conectarse.
Pegáselo a tu IA y seguí lo que te diga para terminar de conectarse.
Listo: preguntale cosas como «¿cuánto vendí hoy?», «¿qué productos me están quedando bajos?» o «¿cómo vendo por caja y por unidad?».
Bueno saber:
Solo puede ver, nunca cambiar nada. Tu IA consulta tus ventas, tu inventario y resuelve tus dudas de Nortex, pero no puede vender, anular ni modificar nada.
Ve solo tu negocio.
Guardalo bien. Nortex muestra el bloque una sola vez. Si lo perdés, no pasa nada: borralo y creá otro.
Lo apagás cuando querés. Desde la misma pantalla podés revocar un acceso y esa IA deja de ver tu negocio al instante. Si tu cuenta se desactiva, el acceso también deja de funcionar.
Si tu IA te dice que no pudo entrar a tu negocio, revisá que el acceso siga activo en «Acceso para IA», o creá uno nuevo y volvé a pegar el bloque.
Nota de operación (no es para clientes): el bloque trae la dirección del servidor MCP (
VITE_MCP_URL, defaulthttps://mcp.somosnortex.com/mcp) y el accesonxt_. Para que funcione, este MCP tiene que estar desplegado ahí conNORTEX_MCP_CLIENT_AUTH=1detrás de un proxy TLS (hoy escucha en loopback). El JWT de sesión de la app sigue aceptado (F7) para pruebas internas.
Smoke F7 + F7.1 (ops, JWT y acceso opaco + monolito falso en proceso): node scripts/protocol-smoke-http.mjs.
Siguiente
F3: paridad contra staging con JWT real (incluye el contract test de
informe_ventas contra la pantalla de Reportes y re-diseñar detalle_venta
si el backend expone JSON). OAuth / conector publicable (la otra mitad de la
«F5» original de fases; requiere OK de Noel) sigue pendiente: esta entrega
solo agregó la tool, no tocó la auth interina de F4.
Available Tools
10 toolsbuscar_productoB
Busca productos por nombre. Devuelve precio y stock por bodega, en voseo.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Texto a buscar (mínimo 2 letras) | |
| limit | No | Cuántos mostrar; default 10, máximo 50 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral burden and does add some return context: price and stock by warehouse. However, it does not describe search semantics (partial vs exact matching, ordering), error behavior, or pagination beyond what the schema already states. 'En voseo' is an unusual note that is not clearly explained.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is only two short sentences and is front-loaded with the core action. The trailing 'en voseo' is odd and slightly confusing, keeping it from a perfect score.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple with two fully documented parameters and no output schema, so the description's note about returning price and stock by warehouse is useful. Still, it omits the critical sibling differentiation and search behavior details that an agent would need to choose it over precio_producto or stock_producto.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both q and limit are already documented. The description's 'por nombre' aligns with q but adds no syntax, matching rules, or defaults beyond the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Busca productos por nombre' (search products by name). It is clear what the tool does, but it does not distinguish itself from siblings like precio_producto or stock_producto, which also relate to prices and stock per warehouse.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this search tool versus precio_producto or stock_producto. The description does not mention exclusions, prerequisites, or the fact that it is a name-based search returning multi-warehouse data.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
detalle_ventaA
Detalle de una factura por su referencia (ej. A-15): total, líneas con cantidades y precios, cliente y cómo pagó. Busca en los últimos 31 días.
| Name | Required | Description | Default |
|---|---|---|---|
| factura | Yes | Referencia de la factura, ej. A-15 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It does disclose two valuable traits beyond the schema: the returned payload composition and a 31-day lookback window that limits results. It does not say what happens when the reference is outside that window, does not exist, or whether the call is read-only.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with the return contents front-loaded and the 31-day constraint placed last as a caveat. The only waste is repeating the 'A-15' example already present in the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read tool with no output schema, the description appropriately enumerates the return shape and flags the 31-day data window. The remaining gap is edge-case behavior (invoice not found or older than 31 days), which an agent would need to know before calling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single parameter is already documented with an example ('Referencia de la factura, ej. A-15'). The description restates the same example without adding format constraints, case sensitivity, or accepted reference patterns, so it adds essentially nothing beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource (una factura) and lookup key (su referencia), and enumerates what is returned: total, líneas con cantidades y precios, cliente y cómo pagó. This clearly separates it from aggregate siblings like ventas_periodo or informe_ventas, though it never names an alternative explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: the agent can infer this is for looking up one known invoice by reference, and the 'últimos 31 días' note implicitly bounds when it applies. There is no explicit when-to-use vs. sibling guidance (e.g. use ventas_periodo for ranges) and no statement of prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
estado_conteoB
Conteos de inventario abiertos (qué bodega, cuánto llevan contado), en voseo.
| Name | Required | Description | Default |
|---|---|---|---|
| bodega | No | Nombre de la bodega; default: todas |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations at all, the description must carry the behavioral burden. It does disclose two useful traits beyond the schema: results are filtered to open counts and each row carries warehouse plus counted-progress, and 'en voseo' signals an output-language/register quirk. It says nothing about read-only safety (only implied), volume, pagination, or permissions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One short sentence plus a parenthetical, front-loaded with the resource and then the return shape. Nothing is padded, though the trailing 'en voseo' is cryptic and could be folded more cleanly into the sentence.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-optional-parameter read tool with no output schema, the essentials are covered: what entity, what filter state, and roughly what comes back. It stops short of saying whether items/lines are included or how many counts typically return, which an agent would want before calling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single 'bodega' parameter is already documented with its default ('todas'). The description's 'qué bodega' merely echoes that the parameter narrows results, adding no format, casing, or partial-match detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the resource precisely: open inventory counts ('Conteos de inventario abiertos') and even previews the returned fields (which warehouse, how far the count has progressed). That is enough to distinguish it from siblings like stock_producto or inventario_bajo, though the verb is nominal rather than an explicit 'list/consult'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The word 'abiertos' implicitly scopes results to non-closed counts, which is a small usage signal. But there is no statement of when to prefer this over stock_producto or inventario_bajo, no prerequisites, and no exclusions, so the agent must infer routing from the name alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
informe_ventasA
Informe integral de ventas a pedido: consolidado del período (ventas brutas/netas, IVA, utilidad bruta, ticket promedio), cómo pagaron y los productos que más se venden y más utilidad dejan, ordenados. Resumen en voseo listo para voz + detalle en texto.
| Name | Required | Description | Default |
|---|---|---|---|
| top | No | Cuántos productos por ranking; default 3, máximo 5 | |
| desde | No | Fecha inicial YYYY-MM-DD (rango personalizado, con `hasta`; máximo 31 días) | |
| hasta | No | Fecha final YYYY-MM-DD (rango personalizado, con `desde`; máximo 31 días) | |
| periodo | No | Período rápido; default: mes (últimos 30 días). semana = últimos 7 días |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose the report's composition plus the output shape: a voseo summary suitable for voice plus a text detail section. It does not state that the operation is strictly read-only or describe any cost/latency or data-freshness behavior, which is the remaining gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with what the report contains and ending with the output format, with no filler. The long parenthetical enumeration is dense but every item is a distinct output field, so it earns its space.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-optional-parameter read tool with no output schema, the description adequately conveys both the scope and the return content, including the dual voice/text presentation. It falls short only on explicit routing versus the sibling sales tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the parameters (top, desde, hasta, periodo) are already fully documented in the schema, including the 31-day range limit and the 3/5 default-max for top. The description adds no extra syntax or interaction rules beyond implying ranking via 'los productos que más se venden', so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific resource (an integral sales report) and enumerates its contents — gross/net sales, IVA, gross profit, average ticket, payment breakdown, top-selling and most-profitable products — so an agent knows exactly what it produces. It does not, however, name the siblings ventas_hoy or ventas_periodo to explain how it differs from them, which keeps it below a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'a pedido' implies this tool is for ad-hoc/custom-range requests, which loosely distinguishes it from the fixed-period siblings, and the mention of a voice-ready summary hints at its intended channel. But there is no explicit when-to-use or when-not-to-use guidance, and no sibling is named as the alternative for today's or a standard period's sales.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
inventario_bajoB
Productos con stock bajo (punto de reorden o umbral dado), en voseo.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Cuántos mostrar; default 10, máximo 50 | |
| umbral | No | Avisar si quedan N o menos; default: punto de reorden del producto |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are supplied, so the description carries the full behavioral burden and mostly doesn't. It does not state that this is a read-only query, how results are ordered or truncated when more than the limit qualify, or what each returned product includes. The trailing 'en voseo' is a cryptic response-format note that adds no actionable behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single short sentence with the key qualifier front-loaded. The trailing phrase 'en voseo' is unexplained noise in an otherwise tight definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read tool with full schema coverage and no output schema, the definition covers the core filter adequately. It stops short of describing return contents or ordering, which an agent would need to interpret results without annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both limit and umbral are already documented, including the reorder-point default. The description restates that same threshold/reorder-point semantics without adding formatting, interaction between limit and umbral, or defaults beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific resource and filter: products whose stock is at or below a reorder point or a supplied threshold. An agent can distinguish it from stock_producto (single-product lookup) or buscar_producto, though the description never names those siblings explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The condition for use is implied by 'stock bajo', but there is no explicit when-to-use or when-not-to-use guidance, no mention of how it relates to stock_producto or buscar_producto, and no stated prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
precio_productoB
Precio base (NIO) de un producto, en voseo. El precio en USD vive en T6 (precio_dolares).
| Name | Required | Description | Default |
|---|---|---|---|
| producto | Yes | Nombre del producto (mínimo 2 letras) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are supplied, so the description carries the full behavioral burden and does very little with it. It does not say whether the lookup is read-only, what happens when the product is not found, whether multiple matches return multiple prices, or what the response shape looks like.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the currency-bearing fact that matters most. The aside about voseo is a register note of marginal relevance to invocation, but it costs little.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter read tool with no output schema and full schema coverage, the description covers the essentials of what is returned and in which currency. It stops short of describing the return format or failure behavior, which an agent would still need to guess.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is a single required parameter and the schema already documents it at 100% coverage (product name, minimum 2 letters). The description adds no matching rules, case sensitivity, or ambiguity handling beyond what the schema states, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a concrete resource and unit: the base price in NIO of a product, which is clearly distinguishable from a USD price lookup. It does not name any sibling tool, so the agent must still infer whether buscar_producto or precio_producto is the right call.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent can infer it should call this when it needs a base price in córdobas. The note that the USD price lives in T6 (precio_dolares) hints at where the alternative lives, but it points at a table rather than at a sibling tool, leaving the routing decision partially unresolved.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolver_dudaA
Responde dudas de USO de Nortex (cómo se hace algo en la app: vender por caja, códigos de barras, regalías, lotes, anulaciones, etc.) con fuentes oficiales: el Manual de Usuario y casos verificados contra el código. Devuelve respuesta breve en voseo + fuente + confianza; si ninguna fuente respalda, dice explícitamente 'Sin fundamento en el manual'. Solo conocimiento: no lee ni toca datos del negocio.
| Name | Required | Description | Default |
|---|---|---|---|
| pregunta | Yes | La duda de la persona, en sus palabras (5 a 500 letras) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and mostly succeeds: it discloses the response shape (brief answer in voseo + source + confidence) and, importantly, the failure mode — it explicitly returns 'Sin fundamento en el manual' when nothing backs the answer. It also declares it does not read or touch business data. Not covered: latency, and what 'confianza' looks like in practice.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then output contract, then the negative boundary — a sensible ordering. The text is dense and slightly run-on (long parenthetical plus two clauses joined by semicolons), but no sentence is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter, no-annotations, no-output-schema knowledge tool, the definition covers what it does, what it returns, its sourcing, and its out-of-scope boundary. Minor gaps remain around confidence semantics and how off-topic questions are handled beyond the 'sin fundamento' fallback.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds value by scoping what counts as a valid question ('cómo se hace algo en la app', with the enumerated topics), which tells the agent how to phrase and constrain the free-text input rather than just passing raw chat text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: it answers USAGE questions about the Nortex app, and it enumerates concrete topic examples (vender por caja, códigos de barras, regalías, lotes, anulaciones). The closing line 'Solo conocimiento: no lee ni toca datos del negocio' cleanly separates it from every sibling tool, which are all business-data readers.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage context is explicit: it is for 'cómo se hace algo en la app' questions, and the no-data-access clause functions as a clear when-not for anything involving live business records. It stops short of naming the alternative siblings (ventas_hoy, stock_producto, etc.), so the agent must infer the routing itself.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stock_productoA
Cuánto stock queda de un producto, total y por bodega, en voseo.
| Name | Required | Description | Default |
|---|---|---|---|
| producto | Yes | Nombre del producto (mínimo 2 letras) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden; it does disclose the shape of the result (total plus a per-warehouse breakdown), which is genuinely useful. However, it says nothing about how the product name is matched (the schema's 'mínimo 2 letras' hints at substring search), what happens if several products match, or what is returned when nothing is found. The trailing 'en voseo' is a response-style note rather than tool behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One short sentence, front-loaded with the core question the tool answers. The only questionable element is the 'en voseo' clause, which is stylistic guidance rather than selection-relevant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read-only lookup with no output schema and no annotations, the description covers the essential question and the result granularity. A little more on match behavior and the empty-result case would make it fully self-sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and there is a single parameter already documented as 'Nombre del producto (mínimo 2 letras)'. The description only restates 'de un producto' and adds no detail about partial matching vs exact name, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a concrete resource (stock of a product) and states the granularity of the answer (total and per warehouse), which lets an agent tell it apart from siblings like inventario_bajo (low-stock alerts) or precio_producto (pricing). It stops short of explicitly naming a sibling it replaces, so it is clear but not maximally differentiating.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent can infer this is the tool for 'how much stock do I have of X'. There is no statement of when to prefer it over inventario_bajo or buscar_producto, and no prerequisites or exclusions are given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ventas_hoyA
Cuánto vendió el negocio hoy (día comercial America/Managua), en voseo listo para voz.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations supplied, the description carries the full burden, and it does add real behavioral detail: 'today' is defined as the America/Managua business day, which removes the main ambiguity for an agent. It also signals that the output is phrased in voseo and ready for voice. It stops short of describing what the response contains, such as a single total versus a breakdown, or how an empty day is handled.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single short sentence that front-loads the core question and then adds the two qualifying details. No filler, no restatement of the tool name beyond what is needed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless, likely read-only lookup with no output schema, the description covers what is returned conceptually and the time boundary that defines it. The only remaining gap is the exact shape of the result, which is a minor omission at this complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. The timezone note actually adds context that a parameterless tool might otherwise leave unclear.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'cuánto vendió el negocio hoy', which tells an agent this returns today's sales total. The time scope is made explicit, which implicitly distinguishes it from ventas_periodo, but that sibling is never named.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied by the word 'hoy'. The description never states when to prefer this over the sibling ventas_periodo, nor does it set exclusions, such as other ranges or per-user filtering.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ventas_periodoB
Ventas de los últimos N días (1-30, default 7) con el mejor día, en voseo.
| Name | Required | Description | Default |
|---|---|---|---|
| dias | No | Período en días: 1 a 30, default 7 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It implies a read-only sales query but does not explicitly state safety, permissions, rate limits, or what exactly is returned beyond 'el mejor día'; the phrase 'en voseo' is ambiguous.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with little wasted wording. The only clarity issue is the unexplained phrase 'en voseo', which may confuse readers despite the overall brevity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This is a simple one-parameter tool with a fully documented schema, so it does not need extensive parameter explanation. Still, with no output schema, the description could better specify the return shape, such as what 'mejor día' means and how sales values are presented.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is one optional parameter and schema description coverage is 100%, so the schema already defines the range and default. The description repeats that information but adds no extra meaning beyond what the schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific resource and scope: sales for the last N days plus the best day. It is distinguishable from ventas_hoy by its multi-day window, but it does not explicitly differentiate from siblings such as informe_ventas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the period scope: it is for sales over a 1-30 day range rather than just today. However, it gives no explicit when-to-use guidance, exclusions, or named alternatives such as ventas_hoy or informe_ventas.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.5.3- First observed
buscar_producto - First observed
detalle_venta - First observed
estado_conteo - First observed
informe_ventas - First observed
inventario_bajo - First observed
precio_producto - First observed
resolver_duda - First observed
stock_producto - First observed
ventas_hoy - First observed
ventas_periodo
TDQS
Scored across 10 tools
Tools are mostly distinct by resource and intent, with clear descriptions separating sales queries (today vs period vs report) and product lookups. Minor overlap: buscar_producto already returns price and stock, making stock_producto and precio_producto somewhat redundant, but each has a specific use case.
All names are snake_case in Spanish, but the pattern mixes verb-first (buscar_producto, resolver_duda) and noun-first (ventas_hoy, stock_producto) conventions. Still readable, but not a consistent verb_noun pattern.
10 tools is well within the ideal range for a business analytics assistant. Each tool covers a distinct query type (sales, product, stock, inventory, help) without excessive granularity.
Covers core sales and inventory queries, but notable gaps remain: no way to query sales for a specific product over time, no customer-level sales history, no data on voided/returned sales, and historical sales limited to 30 days. Agents may hit dead ends for common follow-up questions.
Maintenance
Related MCP Connectors
Run your Tecopos business from your AI: sales, inventory, cash, customers, purchases and reports.
Query your books, company data, business health, and ship invoices from your AI assistant.
Create invoices from your AI assistant in 6 languages and 10 currencies, ready for WhatsApp.
Spanish Veri*factu invoicing: create invoices, expenses and VAT returns from your AI assistant.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage business operations including invoicing, WooCommerce syncing, expense tracking, POS, inventory, and team management through natural language.28 npm2MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants like Claude, ChatGPT, and Copilot to query live Jobber data—clients, jobs, quotes, invoices, revenue, and schedule—using natural language, with read-only access and careful API budget management.1MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to query a retail and food-service point-of-sale database through predefined business tools for sales summaries, top products, margins, stagnant inventory, cash reconciliation, and optional stock adjustments, returning formatted markdown answers.MIT
- AlicenseBqualityAmaintenanceEnables AI assistants to query and manage self-hosted accounting data—invoices, balances, and books—through natural language, with read-only tools by default and optional scoped write operations.3019 PyPI1AGPL 3.0