feat: MCP Server para control de impresoras
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 2m8s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 2m8s
- Endpoint HTTP JSON-RPC en /api/mcp - 6 tools: list_templates, list_printers, print_template, print_raw, create_template, update_template - Guia de formato para impresora TM-U220 - Protegido por Authentik forward auth
This commit is contained in:
206
.claude/epson-tmu220-printing-guide.md
Normal file
206
.claude/epson-tmu220-printing-guide.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# Guia de Impresion para Epson TM-U220
|
||||
|
||||
Esta guia documenta las mejores practicas para generar impresiones legibles en impresoras Epson TM-U220 usando el sistema printerCentral.
|
||||
|
||||
## Especificaciones de la Impresora
|
||||
|
||||
| Caracteristica | Valor |
|
||||
|----------------|-------|
|
||||
| Columnas (Font A) | 40-42 caracteres |
|
||||
| Columnas (Font B) | 33-35 caracteres |
|
||||
| Columnas con doble ancho | ~20 caracteres |
|
||||
| Caracteres por pulgada | 17.8/16 cpi |
|
||||
| Ancho de papel | 57.5mm, 69.5mm, o 76mm |
|
||||
|
||||
## Reglas Fundamentales
|
||||
|
||||
### 1. Limite de Caracteres por Linea
|
||||
|
||||
```
|
||||
Texto normal: max 40 caracteres
|
||||
Texto doble ancho: max 20 caracteres
|
||||
Texto doble alto: max 40 caracteres
|
||||
Texto doble ambos: max 20 caracteres
|
||||
```
|
||||
|
||||
### 2. Caracteres Soportados
|
||||
|
||||
**USAR** - Caracteres ASCII basicos:
|
||||
```
|
||||
= - _ * + | / \ ( ) [ ] < >
|
||||
A-Z a-z 0-9
|
||||
```
|
||||
|
||||
**EVITAR** - Caracteres Unicode extendidos:
|
||||
```
|
||||
Box drawing: ╔ ═ ╗ ║ ╚ ╝ ┌ ┐ └ ┘
|
||||
Simbolos especiales: → ← ↑ ↓ ★ ● ○
|
||||
Emojis: cualquiera
|
||||
```
|
||||
|
||||
### 3. Espaciado con Feed
|
||||
|
||||
Siempre usar `feed` para crear espacio visual. La impresora NO interpreta lineas vacias automaticamente.
|
||||
|
||||
```json
|
||||
{ "op": "feed", "lines": 1 } // Espacio simple
|
||||
{ "op": "feed", "lines": 2 } // Espacio entre secciones
|
||||
{ "op": "feed", "lines": 4 } // Espacio antes del corte
|
||||
```
|
||||
|
||||
## Estructura Recomendada de un Ticket
|
||||
|
||||
### Header (Centrado, Destacado)
|
||||
|
||||
```json
|
||||
{ "op": "align", "align": "center" },
|
||||
{ "op": "style", "bold": true, "width": 2, "height": 2 },
|
||||
{ "op": "text", "value": "TITULO" },
|
||||
{ "op": "style", "bold": false, "width": 1, "height": 1 },
|
||||
{ "op": "feed", "lines": 2 }
|
||||
```
|
||||
|
||||
### Separadores
|
||||
|
||||
Siempre con feed antes y despues para que queden en su propia linea:
|
||||
|
||||
```json
|
||||
{ "op": "text", "value": "================================" },
|
||||
{ "op": "feed", "lines": 1 }
|
||||
```
|
||||
|
||||
Para 40 columnas usar 32 caracteres `=` (deja margen visual).
|
||||
|
||||
### Titulos de Seccion
|
||||
|
||||
Usar bold + underline, seguido de feed:
|
||||
|
||||
```json
|
||||
{ "op": "style", "bold": true, "underline": true },
|
||||
{ "op": "text", "value": "NOMBRE SECCION" },
|
||||
{ "op": "style", "bold": false, "underline": false },
|
||||
{ "op": "feed", "lines": 1 }
|
||||
```
|
||||
|
||||
### Items de Lista
|
||||
|
||||
Cada item con su propio feed para legibilidad:
|
||||
|
||||
```json
|
||||
{ "op": "text", "value": "[ ] Item uno" },
|
||||
{ "op": "feed", "lines": 1 },
|
||||
{ "op": "text", "value": "[ ] Item dos" },
|
||||
{ "op": "feed", "lines": 1 }
|
||||
```
|
||||
|
||||
### Footer y Corte
|
||||
|
||||
```json
|
||||
{ "op": "align", "align": "center" },
|
||||
{ "op": "text", "value": "Texto de pie" },
|
||||
{ "op": "feed", "lines": 4 },
|
||||
{ "op": "cut" }
|
||||
```
|
||||
|
||||
## Ejemplo Completo: Lista de Compras
|
||||
|
||||
```json
|
||||
{
|
||||
"operations": [
|
||||
{ "op": "align", "align": "center" },
|
||||
{ "op": "style", "bold": true, "width": 2, "height": 2 },
|
||||
{ "op": "text", "value": "MI TITULO" },
|
||||
{ "op": "style", "bold": false, "width": 1, "height": 1 },
|
||||
{ "op": "feed", "lines": 2 },
|
||||
|
||||
{ "op": "text", "value": "================================" },
|
||||
{ "op": "feed", "lines": 1 },
|
||||
{ "op": "style", "bold": true },
|
||||
{ "op": "text", "value": "SUBTITULO" },
|
||||
{ "op": "feed", "lines": 1 },
|
||||
{ "op": "style", "bold": false },
|
||||
{ "op": "text", "value": "================================" },
|
||||
{ "op": "feed", "lines": 2 },
|
||||
|
||||
{ "op": "align", "align": "left" },
|
||||
{ "op": "style", "bold": true, "underline": true },
|
||||
{ "op": "text", "value": "SECCION 1" },
|
||||
{ "op": "style", "bold": false, "underline": false },
|
||||
{ "op": "feed", "lines": 1 },
|
||||
{ "op": "text", "value": "[ ] Item A" },
|
||||
{ "op": "feed", "lines": 1 },
|
||||
{ "op": "text", "value": "[ ] Item B" },
|
||||
{ "op": "feed", "lines": 2 },
|
||||
|
||||
{ "op": "align", "align": "center" },
|
||||
{ "op": "text", "value": "================================" },
|
||||
{ "op": "feed", "lines": 1 },
|
||||
{ "op": "text", "value": "Pie de pagina" },
|
||||
{ "op": "feed", "lines": 4 },
|
||||
{ "op": "cut" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Operaciones Disponibles
|
||||
|
||||
| Operacion | Parametros | Ejemplo |
|
||||
|-----------|------------|---------|
|
||||
| `text` | `value` | `{ "op": "text", "value": "Hola" }` |
|
||||
| `feed` | `lines` | `{ "op": "feed", "lines": 2 }` |
|
||||
| `cut` | - | `{ "op": "cut" }` |
|
||||
| `align` | `align`: left, center, right | `{ "op": "align", "align": "center" }` |
|
||||
| `style` | `bold`, `underline`, `width`, `height` | `{ "op": "style", "bold": true }` |
|
||||
|
||||
## Endpoints de Impresion
|
||||
|
||||
### Imprimir Template Guardado
|
||||
|
||||
```
|
||||
POST /api/print/template
|
||||
{
|
||||
"templateId": "template_xxx",
|
||||
"variables": { "nombre": "Juan" },
|
||||
"printerId": "printer_xxx", // opcional
|
||||
"dryRun": false // true para preview
|
||||
}
|
||||
```
|
||||
|
||||
### Imprimir Operaciones Directas
|
||||
|
||||
```
|
||||
POST /api/print/raw
|
||||
{
|
||||
"operations": [...],
|
||||
"variables": { "var1": "valor1" },
|
||||
"printerId": "printer_xxx", // opcional
|
||||
"dryRun": false
|
||||
}
|
||||
```
|
||||
|
||||
## Variables en Templates
|
||||
|
||||
Sintaxis: `{{nombre}}` o `{{nombre:label:default}}`
|
||||
|
||||
```json
|
||||
{ "op": "text", "value": "Cliente: {{cliente}}" },
|
||||
{ "op": "text", "value": "Fecha: {{fecha:Fecha:2025-01-01}}" }
|
||||
```
|
||||
|
||||
## Errores Comunes
|
||||
|
||||
1. **Texto cortado o ilegible**: Excediste 40 caracteres por linea
|
||||
2. **Todo pegado**: Falta usar `feed` entre elementos
|
||||
3. **Caracteres raros**: Usaste Unicode no soportado
|
||||
4. **Separadores mezclados**: Falta `feed` antes/despues de lineas `===`
|
||||
5. **Header muy largo**: Con doble ancho solo caben ~20 chars
|
||||
|
||||
## Checklist Pre-Impresion
|
||||
|
||||
- [ ] Ninguna linea excede 40 caracteres (20 si doble ancho)
|
||||
- [ ] Solo caracteres ASCII basicos
|
||||
- [ ] `feed` entre cada item para legibilidad
|
||||
- [ ] `feed` antes y despues de separadores `===`
|
||||
- [ ] `feed: 4` antes del `cut` final
|
||||
- [ ] Header centrado con estilo destacado
|
||||
- [ ] Secciones con titulos bold+underline
|
||||
487
package-lock.json
generated
487
package-lock.json
generated
@@ -7,6 +7,7 @@
|
||||
"name": "printerCentral",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.22.0",
|
||||
"@nuxt/ui": "^4.1.0",
|
||||
"@vite-pwa/nuxt": "^1.0.7",
|
||||
"axios": "^1.13.2",
|
||||
@@ -3027,6 +3028,47 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.22.0.tgz",
|
||||
"integrity": "sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.17.1",
|
||||
"ajv-formats": "^3.0.1",
|
||||
"content-type": "^1.0.5",
|
||||
"cors": "^2.8.5",
|
||||
"cross-spawn": "^7.0.5",
|
||||
"eventsource": "^3.0.2",
|
||||
"eventsource-parser": "^3.0.0",
|
||||
"express": "^5.0.1",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"pkce-challenge": "^5.0.0",
|
||||
"raw-body": "^3.0.0",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.24.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cfworker/json-schema": "^4.1.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cfworker/json-schema": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk/node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz",
|
||||
@@ -6311,6 +6353,19 @@
|
||||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "^3.0.0",
|
||||
"negotiator": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
@@ -6375,6 +6430,23 @@
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
|
||||
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/alien-signals": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.0.tgz",
|
||||
@@ -6837,6 +6909,46 @@
|
||||
"integrity": "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz",
|
||||
"integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "^3.1.2",
|
||||
"content-type": "^1.0.5",
|
||||
"debug": "^4.4.3",
|
||||
"http-errors": "^2.0.0",
|
||||
"iconv-lite": "^0.7.0",
|
||||
"on-finished": "^2.4.1",
|
||||
"qs": "^6.14.0",
|
||||
"raw-body": "^3.0.1",
|
||||
"type-is": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/iconv-lite": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
|
||||
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
@@ -6960,6 +7072,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/c12": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/c12/-/c12-3.3.2.tgz",
|
||||
@@ -7364,18 +7485,58 @@
|
||||
"node": "^14.18.0 || >=16.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
|
||||
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-es": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.0.tgz",
|
||||
"integrity": "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
||||
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-anything": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
|
||||
@@ -7418,6 +7579,19 @@
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/crc-32": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
||||
@@ -8481,6 +8655,18 @@
|
||||
"bare-events": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
|
||||
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eventsource-parser": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource-parser": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz",
|
||||
@@ -8518,6 +8704,63 @@
|
||||
"resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
|
||||
"integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
||||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.0",
|
||||
"content-disposition": "^1.0.0",
|
||||
"content-type": "^1.0.5",
|
||||
"cookie": "^0.7.1",
|
||||
"cookie-signature": "^1.2.1",
|
||||
"debug": "^4.4.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"finalhandler": "^2.1.0",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"merge-descriptors": "^2.0.0",
|
||||
"mime-types": "^3.0.0",
|
||||
"on-finished": "^2.4.1",
|
||||
"once": "^1.4.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"qs": "^6.14.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"router": "^2.2.0",
|
||||
"send": "^1.1.0",
|
||||
"serve-static": "^2.2.0",
|
||||
"statuses": "^2.0.1",
|
||||
"type-is": "^2.0.1",
|
||||
"vary": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express-rate-limit": {
|
||||
"version": "7.5.1",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
|
||||
"integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/express-rate-limit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": ">= 4.11"
|
||||
}
|
||||
},
|
||||
"node_modules/exsolve": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz",
|
||||
@@ -8665,6 +8908,23 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"parseurl": "^1.3.3",
|
||||
"statuses": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
@@ -8786,6 +9046,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
|
||||
@@ -9310,28 +9579,23 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
"depd": "~2.0.0",
|
||||
"inherits": "~2.0.4",
|
||||
"setprototypeof": "~1.2.0",
|
||||
"statuses": "~2.0.2",
|
||||
"toidentifier": "~1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors/node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/http-shutdown": {
|
||||
@@ -9517,6 +9781,15 @@
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/iron-webcrypto": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz",
|
||||
@@ -9842,6 +10115,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-promise": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
||||
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-reference": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
|
||||
@@ -10761,6 +11040,27 @@
|
||||
"integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
||||
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
@@ -11001,6 +11301,15 @@
|
||||
"integrity": "sha512-9ca1h0Xjvo9bEkE4UOxgAzLV0jHKe6LMaxo37ND2DAhhAtd0j8pR1Wxz+/goMrZO8AEZTWCmyaOsFI/W5AdpCQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/nitropack": {
|
||||
"version": "2.12.9",
|
||||
"resolved": "https://registry.npmjs.org/nitropack/-/nitropack-2.12.9.tgz",
|
||||
@@ -11349,6 +11658,15 @@
|
||||
"node": "^14.16.0 || >=16.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
@@ -11760,6 +12078,16 @@
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz",
|
||||
@@ -11836,6 +12164,15 @@
|
||||
"node": ">=12.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pkce-challenge": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz",
|
||||
"integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-types": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
|
||||
@@ -12387,6 +12724,19 @@
|
||||
"integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
@@ -12402,6 +12752,21 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/quansync": {
|
||||
"version": "0.2.11",
|
||||
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
|
||||
@@ -12462,6 +12827,37 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
|
||||
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "~3.1.2",
|
||||
"http-errors": "~2.0.1",
|
||||
"iconv-lite": "~0.7.0",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
|
||||
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/rc9": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz",
|
||||
@@ -12860,6 +13256,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"depd": "^2.0.0",
|
||||
"is-promise": "^4.0.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"path-to-regexp": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
|
||||
@@ -14073,6 +14485,20 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"content-type": "^1.0.5",
|
||||
"media-typer": "^1.1.0",
|
||||
"mime-types": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/type-level-regexp": {
|
||||
"version": "0.1.17",
|
||||
"resolved": "https://registry.npmjs.org/type-level-regexp/-/type-level-regexp-0.1.17.tgz",
|
||||
@@ -14406,6 +14832,15 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin": {
|
||||
"version": "2.3.10",
|
||||
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.10.tgz",
|
||||
@@ -14805,6 +15240,15 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/vaul-vue": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/vaul-vue/-/vaul-vue-0.4.1.tgz",
|
||||
@@ -16208,6 +16652,15 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zod-to-json-schema": {
|
||||
"version": "3.25.0",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz",
|
||||
"integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25 || ^4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.22.0",
|
||||
"@nuxt/ui": "^4.1.0",
|
||||
"@vite-pwa/nuxt": "^1.0.7",
|
||||
"axios": "^1.13.2",
|
||||
|
||||
115
server/api/mcp/index.post.ts
Normal file
115
server/api/mcp/index.post.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
// MCP Server Endpoint - JSON-RPC 2.0 over HTTP
|
||||
// Protocolo MCP para agentes de IA
|
||||
|
||||
import { MCP_TOOLS, handleToolCall } from '../../utils/mcp'
|
||||
|
||||
interface JsonRpcRequest {
|
||||
jsonrpc: '2.0'
|
||||
id: string | number
|
||||
method: string
|
||||
params?: any
|
||||
}
|
||||
|
||||
interface JsonRpcResponse {
|
||||
jsonrpc: '2.0'
|
||||
id: string | number | null
|
||||
result?: any
|
||||
error?: {
|
||||
code: number
|
||||
message: string
|
||||
data?: any
|
||||
}
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const body = await readBody(event) as JsonRpcRequest
|
||||
|
||||
// Validar JSON-RPC
|
||||
if (body.jsonrpc !== '2.0') {
|
||||
return createJsonRpcError(body.id, -32600, 'Invalid Request: jsonrpc must be "2.0"')
|
||||
}
|
||||
|
||||
if (!body.method) {
|
||||
return createJsonRpcError(body.id, -32600, 'Invalid Request: method is required')
|
||||
}
|
||||
|
||||
// Manejar métodos MCP
|
||||
switch (body.method) {
|
||||
case 'initialize': {
|
||||
// Handshake inicial del protocolo MCP
|
||||
return createJsonRpcResponse(body.id, {
|
||||
protocolVersion: '2024-11-05',
|
||||
capabilities: {
|
||||
tools: {}
|
||||
},
|
||||
serverInfo: {
|
||||
name: 'printercentral-mcp',
|
||||
version: '1.0.0'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
case 'initialized': {
|
||||
// Notificación de que el cliente está listo
|
||||
return createJsonRpcResponse(body.id, {})
|
||||
}
|
||||
|
||||
case 'tools/list': {
|
||||
// Listar todas las tools disponibles
|
||||
return createJsonRpcResponse(body.id, {
|
||||
tools: MCP_TOOLS
|
||||
})
|
||||
}
|
||||
|
||||
case 'tools/call': {
|
||||
// Ejecutar una tool
|
||||
const { name, arguments: args } = body.params || {}
|
||||
|
||||
if (!name) {
|
||||
return createJsonRpcError(body.id, -32602, 'Invalid params: tool name is required')
|
||||
}
|
||||
|
||||
// Verificar que la tool existe
|
||||
const tool = MCP_TOOLS.find(t => t.name === name)
|
||||
if (!tool) {
|
||||
return createJsonRpcError(body.id, -32602, `Tool not found: ${name}`)
|
||||
}
|
||||
|
||||
// Ejecutar la tool
|
||||
const result = await handleToolCall(name, args || {})
|
||||
return createJsonRpcResponse(body.id, result)
|
||||
}
|
||||
|
||||
case 'ping': {
|
||||
return createJsonRpcResponse(body.id, {})
|
||||
}
|
||||
|
||||
default:
|
||||
return createJsonRpcError(body.id, -32601, `Method not found: ${body.method}`)
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('MCP Error:', err)
|
||||
return createJsonRpcError(null, -32603, `Internal error: ${err.message}`)
|
||||
}
|
||||
})
|
||||
|
||||
function createJsonRpcResponse(id: string | number | null, result: any): JsonRpcResponse {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
id: id ?? null,
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
function createJsonRpcError(id: string | number | null, code: number, message: string, data?: any): JsonRpcResponse {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
id: id ?? null,
|
||||
error: {
|
||||
code,
|
||||
message,
|
||||
...(data && { data })
|
||||
}
|
||||
}
|
||||
}
|
||||
414
server/utils/mcp.ts
Normal file
414
server/utils/mcp.ts
Normal file
@@ -0,0 +1,414 @@
|
||||
// MCP Server para PrinterCentral
|
||||
// Permite a agentes de IA gestionar impresoras y templates
|
||||
|
||||
import { getAllTemplates, getTemplateById, createTemplate, updateTemplate, resolveVariables } from './templates'
|
||||
import type { Operation } from './templates'
|
||||
import { getAllPrinters, getSelectedPrinter, getPrinterById } from './printers'
|
||||
import { buildFromOperations } from './eposBuilder'
|
||||
import { buildSoapEnvelope, sendToPrinter, parsePrinterResponse } from './printer'
|
||||
|
||||
// Definición de las tools MCP
|
||||
export const MCP_TOOLS = [
|
||||
{
|
||||
name: 'printercentral_list_templates',
|
||||
description: 'Lista todos los templates de impresión disponibles. Retorna id, nombre, descripción (con instrucciones de uso) y variables requeridas para cada template.',
|
||||
inputSchema: {
|
||||
type: 'object' as const,
|
||||
properties: {},
|
||||
required: [] as string[]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'printercentral_list_printers',
|
||||
description: 'Lista todas las impresoras configuradas. Retorna id, nombre, host e indica cuál es la impresora por defecto.',
|
||||
inputSchema: {
|
||||
type: 'object' as const,
|
||||
properties: {},
|
||||
required: [] as string[]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'printercentral_print_template',
|
||||
description: 'Imprime un template guardado con variables resueltas. Usa list_templates primero para ver los templates disponibles y sus variables.',
|
||||
inputSchema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
templateId: {
|
||||
type: 'string',
|
||||
description: 'ID del template a imprimir'
|
||||
},
|
||||
variables: {
|
||||
type: 'object',
|
||||
description: 'Objeto con las variables a resolver. Las claves son los nombres de las variables definidas en el template.',
|
||||
additionalProperties: { type: 'string' }
|
||||
},
|
||||
printerId: {
|
||||
type: 'string',
|
||||
description: 'ID de la impresora a usar. Si no se especifica, usa la impresora por defecto.'
|
||||
}
|
||||
},
|
||||
required: ['templateId']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'printercentral_print_raw',
|
||||
description: `Imprime operaciones ePOS directamente. Útil para impresiones personalizadas sin crear un template.
|
||||
|
||||
FORMATO DE OPERACIONES (para impresora TM-U220, max 40 caracteres por línea):
|
||||
- { op: "text", value: "texto" } - Imprime texto
|
||||
- { op: "feed", lines: N } - Avanza N líneas (usar entre elementos para legibilidad)
|
||||
- { op: "cut" } - Corta el papel
|
||||
- { op: "align", align: "left|center|right" } - Alinea el texto
|
||||
- { op: "style", bold: true/false, underline: true/false, width: 1-2, height: 1-2 } - Estilo de texto
|
||||
|
||||
EJEMPLO:
|
||||
[
|
||||
{ "op": "align", "align": "center" },
|
||||
{ "op": "style", "bold": true, "width": 2, "height": 2 },
|
||||
{ "op": "text", "value": "TITULO" },
|
||||
{ "op": "style", "bold": false, "width": 1, "height": 1 },
|
||||
{ "op": "feed", "lines": 2 },
|
||||
{ "op": "text", "value": "Contenido aquí" },
|
||||
{ "op": "feed", "lines": 4 },
|
||||
{ "op": "cut" }
|
||||
]
|
||||
|
||||
REGLAS IMPORTANTES:
|
||||
- Max 40 caracteres por línea (20 con width:2)
|
||||
- Usar solo caracteres ASCII (evitar unicode especial)
|
||||
- Siempre terminar con feed y cut`,
|
||||
inputSchema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
operations: {
|
||||
type: 'array',
|
||||
description: 'Array de operaciones ePOS a ejecutar',
|
||||
items: { type: 'object' }
|
||||
},
|
||||
variables: {
|
||||
type: 'object',
|
||||
description: 'Variables a resolver en el texto. Usa sintaxis {{nombreVariable}} en los valores de text.',
|
||||
additionalProperties: { type: 'string' }
|
||||
},
|
||||
printerId: {
|
||||
type: 'string',
|
||||
description: 'ID de la impresora a usar. Si no se especifica, usa la impresora por defecto.'
|
||||
}
|
||||
},
|
||||
required: ['operations']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'printercentral_create_template',
|
||||
description: 'Crea un nuevo template de impresión. El template queda guardado para uso futuro.',
|
||||
inputSchema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Nombre del template'
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: 'Descripción del template. IMPORTANTE: Incluir instrucciones de uso y explicación de cada variable para que otros agentes sepan cómo usarlo.'
|
||||
},
|
||||
operations: {
|
||||
type: 'array',
|
||||
description: 'Array de operaciones ePOS. Usa {{variable}} para definir variables.',
|
||||
items: { type: 'object' }
|
||||
}
|
||||
},
|
||||
required: ['name', 'operations']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'printercentral_update_template',
|
||||
description: 'Actualiza un template existente. Solo se actualizan los campos proporcionados.',
|
||||
inputSchema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
templateId: {
|
||||
type: 'string',
|
||||
description: 'ID del template a actualizar'
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Nuevo nombre del template'
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: 'Nueva descripción del template'
|
||||
},
|
||||
operations: {
|
||||
type: 'array',
|
||||
description: 'Nuevas operaciones del template',
|
||||
items: { type: 'object' }
|
||||
}
|
||||
},
|
||||
required: ['templateId']
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// Handlers para cada tool
|
||||
export async function handleToolCall(toolName: string, args: Record<string, any>): Promise<{ content: Array<{ type: string; text: string }> }> {
|
||||
switch (toolName) {
|
||||
case 'printercentral_list_templates': {
|
||||
const templates = await getAllTemplates()
|
||||
const result = templates.map(t => ({
|
||||
id: t.id,
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
variables: t.variables,
|
||||
createdAt: t.createdAt,
|
||||
updatedAt: t.updatedAt
|
||||
}))
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
case 'printercentral_list_printers': {
|
||||
const printers = await getAllPrinters()
|
||||
const selected = await getSelectedPrinter()
|
||||
const result = printers.map(p => ({
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
host: p.host,
|
||||
deviceId: p.deviceId,
|
||||
isDefault: p.isDefault,
|
||||
isSelected: selected?.id === p.id
|
||||
}))
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify(result, null, 2)
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
case 'printercentral_print_template': {
|
||||
const { templateId, variables = {}, printerId } = args
|
||||
|
||||
// Obtener template
|
||||
const template = await getTemplateById(templateId)
|
||||
if (!template) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: `Template no encontrado: ${templateId}` })
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
// Resolver variables
|
||||
const resolvedOps = resolveVariables(template.operations, variables)
|
||||
|
||||
// Construir XML
|
||||
const inner = buildFromOperations(resolvedOps)
|
||||
const soap = buildSoapEnvelope(inner)
|
||||
|
||||
// Obtener impresora
|
||||
const printer = printerId
|
||||
? await getPrinterById(printerId)
|
||||
: await getSelectedPrinter()
|
||||
|
||||
if (!printer) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: 'No hay impresora configurada' })
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
// Enviar a impresora
|
||||
try {
|
||||
const result = await sendToPrinter(soap, printer.host, printer.deviceId, printer.timeout)
|
||||
const { success, code } = parsePrinterResponse(result.data)
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
ok: success,
|
||||
templateName: template.name,
|
||||
printerUsed: { id: printer.id, name: printer.name },
|
||||
code
|
||||
})
|
||||
}]
|
||||
}
|
||||
} catch (err: any) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: err.message })
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'printercentral_print_raw': {
|
||||
const { operations, variables = {}, printerId } = args
|
||||
|
||||
if (!operations || !Array.isArray(operations) || operations.length === 0) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: 'operations es requerido y debe ser un array no vacío' })
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
// Resolver variables
|
||||
const resolvedOps = resolveVariables(operations as Operation[], variables)
|
||||
|
||||
// Construir XML
|
||||
const inner = buildFromOperations(resolvedOps)
|
||||
const soap = buildSoapEnvelope(inner)
|
||||
|
||||
// Obtener impresora
|
||||
const printer = printerId
|
||||
? await getPrinterById(printerId)
|
||||
: await getSelectedPrinter()
|
||||
|
||||
if (!printer) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: 'No hay impresora configurada' })
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
// Enviar a impresora
|
||||
try {
|
||||
const result = await sendToPrinter(soap, printer.host, printer.deviceId, printer.timeout)
|
||||
const { success, code } = parsePrinterResponse(result.data)
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
ok: success,
|
||||
printerUsed: { id: printer.id, name: printer.name },
|
||||
code
|
||||
})
|
||||
}]
|
||||
}
|
||||
} catch (err: any) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: err.message })
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'printercentral_create_template': {
|
||||
const { name, description, operations } = args
|
||||
|
||||
if (!name || !operations) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: 'name y operations son requeridos' })
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const template = await createTemplate({
|
||||
name,
|
||||
description: description || '',
|
||||
operations
|
||||
})
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
ok: true,
|
||||
template: {
|
||||
id: template.id,
|
||||
name: template.name,
|
||||
variables: template.variables
|
||||
}
|
||||
})
|
||||
}]
|
||||
}
|
||||
} catch (err: any) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: err.message })
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'printercentral_update_template': {
|
||||
const { templateId, name, description, operations } = args
|
||||
|
||||
if (!templateId) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: 'templateId es requerido' })
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
const updateData: any = {}
|
||||
if (name !== undefined) updateData.name = name
|
||||
if (description !== undefined) updateData.description = description
|
||||
if (operations !== undefined) updateData.operations = operations
|
||||
|
||||
try {
|
||||
const template = await updateTemplate(templateId, updateData)
|
||||
|
||||
if (!template) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: `Template no encontrado: ${templateId}` })
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
ok: true,
|
||||
template: {
|
||||
id: template.id,
|
||||
name: template.name,
|
||||
variables: template.variables
|
||||
}
|
||||
})
|
||||
}]
|
||||
}
|
||||
} catch (err: any) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: err.message })
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ ok: false, error: `Tool desconocida: ${toolName}` })
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user