@nubiia/mcp-freematica 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +485 -0
- package/dist/clients/base-client.d.ts +31 -0
- package/dist/clients/base-client.js +102 -0
- package/dist/clients/base-client.js.map +1 -0
- package/dist/clients/fiql-builder.d.ts +118 -0
- package/dist/clients/fiql-builder.js +257 -0
- package/dist/clients/fiql-builder.js.map +1 -0
- package/dist/clients/freematica-client.d.ts +659 -0
- package/dist/clients/freematica-client.js +1327 -0
- package/dist/clients/freematica-client.js.map +1 -0
- package/dist/clients/hardened-base-client.d.ts +143 -0
- package/dist/clients/hardened-base-client.js +366 -0
- package/dist/clients/hardened-base-client.js.map +1 -0
- package/dist/config.d.ts +104 -0
- package/dist/config.js +105 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +71 -0
- package/dist/logger.js +114 -0
- package/dist/logger.js.map +1 -0
- package/dist/schemas/albaranes.d.ts +124 -0
- package/dist/schemas/albaranes.js +250 -0
- package/dist/schemas/albaranes.js.map +1 -0
- package/dist/schemas/cartera.d.ts +70 -0
- package/dist/schemas/cartera.js +104 -0
- package/dist/schemas/cartera.js.map +1 -0
- package/dist/schemas/contratos.d.ts +7 -0
- package/dist/schemas/contratos.js +2 -0
- package/dist/schemas/contratos.js.map +1 -0
- package/dist/schemas/facturas-electronicas.d.ts +60 -0
- package/dist/schemas/facturas-electronicas.js +79 -0
- package/dist/schemas/facturas-electronicas.js.map +1 -0
- package/dist/schemas/facturas-ventas.d.ts +118 -0
- package/dist/schemas/facturas-ventas.js +153 -0
- package/dist/schemas/facturas-ventas.js.map +1 -0
- package/dist/schemas/filters.d.ts +92 -0
- package/dist/schemas/filters.js +76 -0
- package/dist/schemas/filters.js.map +1 -0
- package/dist/schemas/master-data.d.ts +26 -0
- package/dist/schemas/master-data.js +86 -0
- package/dist/schemas/master-data.js.map +1 -0
- package/dist/schemas/pagination.d.ts +14 -0
- package/dist/schemas/pagination.js +27 -0
- package/dist/schemas/pagination.js.map +1 -0
- package/dist/schemas/pedidos-compras.d.ts +92 -0
- package/dist/schemas/pedidos-compras.js +189 -0
- package/dist/schemas/pedidos-compras.js.map +1 -0
- package/dist/server-instructions.d.ts +1 -0
- package/dist/server-instructions.js +83 -0
- package/dist/server-instructions.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +41 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/albaranes.d.ts +16 -0
- package/dist/tools/albaranes.js +202 -0
- package/dist/tools/albaranes.js.map +1 -0
- package/dist/tools/calendarios.d.ts +13 -0
- package/dist/tools/calendarios.js +82 -0
- package/dist/tools/calendarios.js.map +1 -0
- package/dist/tools/cartera.d.ts +13 -0
- package/dist/tools/cartera.js +78 -0
- package/dist/tools/cartera.js.map +1 -0
- package/dist/tools/clientes.d.ts +3 -0
- package/dist/tools/clientes.js +48 -0
- package/dist/tools/clientes.js.map +1 -0
- package/dist/tools/contabilidad.d.ts +14 -0
- package/dist/tools/contabilidad.js +259 -0
- package/dist/tools/contabilidad.js.map +1 -0
- package/dist/tools/contactos-clientes.d.ts +3 -0
- package/dist/tools/contactos-clientes.js +25 -0
- package/dist/tools/contactos-clientes.js.map +1 -0
- package/dist/tools/contratos.d.ts +10 -0
- package/dist/tools/contratos.js +35 -0
- package/dist/tools/contratos.js.map +1 -0
- package/dist/tools/facturas-compras.d.ts +13 -0
- package/dist/tools/facturas-compras.js +131 -0
- package/dist/tools/facturas-compras.js.map +1 -0
- package/dist/tools/facturas-electronicas.d.ts +17 -0
- package/dist/tools/facturas-electronicas.js +268 -0
- package/dist/tools/facturas-electronicas.js.map +1 -0
- package/dist/tools/facturas-ventas.d.ts +16 -0
- package/dist/tools/facturas-ventas.js +181 -0
- package/dist/tools/facturas-ventas.js.map +1 -0
- package/dist/tools/helpers.d.ts +17 -0
- package/dist/tools/helpers.js +36 -0
- package/dist/tools/helpers.js.map +1 -0
- package/dist/tools/localizaciones.d.ts +17 -0
- package/dist/tools/localizaciones.js +192 -0
- package/dist/tools/localizaciones.js.map +1 -0
- package/dist/tools/master-data.d.ts +10 -0
- package/dist/tools/master-data.js +70 -0
- package/dist/tools/master-data.js.map +1 -0
- package/dist/tools/oportunidades-negocio.d.ts +3 -0
- package/dist/tools/oportunidades-negocio.js +68 -0
- package/dist/tools/oportunidades-negocio.js.map +1 -0
- package/dist/tools/pedidos-compras.d.ts +14 -0
- package/dist/tools/pedidos-compras.js +105 -0
- package/dist/tools/pedidos-compras.js.map +1 -0
- package/dist/tools/personal.d.ts +13 -0
- package/dist/tools/personal.js +154 -0
- package/dist/tools/personal.js.map +1 -0
- package/dist/tools/prl.d.ts +47 -0
- package/dist/tools/prl.js +242 -0
- package/dist/tools/prl.js.map +1 -0
- package/dist/tools/proveedores.d.ts +14 -0
- package/dist/tools/proveedores.js +122 -0
- package/dist/tools/proveedores.js.map +1 -0
- package/dist/transports/http.d.ts +12 -0
- package/dist/transports/http.js +110 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/stdio.d.ts +12 -0
- package/dist/transports/stdio.js +15 -0
- package/dist/transports/stdio.js.map +1 -0
- package/dist/types/api-envelope.d.ts +27 -0
- package/dist/types/api-envelope.js +2 -0
- package/dist/types/api-envelope.js.map +1 -0
- package/dist/types/clientes.d.ts +9 -0
- package/dist/types/clientes.js +2 -0
- package/dist/types/clientes.js.map +1 -0
- package/dist/types/contactos-clientes.d.ts +7 -0
- package/dist/types/contactos-clientes.js +2 -0
- package/dist/types/contactos-clientes.js.map +1 -0
- package/dist/types/contratos.d.ts +8 -0
- package/dist/types/contratos.js +2 -0
- package/dist/types/contratos.js.map +1 -0
- package/dist/types/master-data.d.ts +9 -0
- package/dist/types/master-data.js +2 -0
- package/dist/types/master-data.js.map +1 -0
- package/dist/types/oportunidades-negocio.d.ts +8 -0
- package/dist/types/oportunidades-negocio.js +2 -0
- package/dist/types/oportunidades-negocio.js.map +1 -0
- package/dist/utils/size-guardrail.d.ts +22 -0
- package/dist/utils/size-guardrail.js +31 -0
- package/dist/utils/size-guardrail.js.map +1 -0
- package/package.json +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Freemática
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# mcp-freematica
|
|
2
|
+
|
|
3
|
+
MCP server que expone operaciones del API REST de Freemática para ser consumidas por Claude a través de Nubiia.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
- TypeScript + Node.js ≥20
|
|
8
|
+
- `@modelcontextprotocol/sdk` (stdio + Streamable HTTP transports)
|
|
9
|
+
- Express + axios + zod
|
|
10
|
+
- pino (logging estructurado)
|
|
11
|
+
- Vitest + nock para tests
|
|
12
|
+
|
|
13
|
+
## Tools expuestas
|
|
14
|
+
|
|
15
|
+
| Tool | Endpoint Freemática | Descripción |
|
|
16
|
+
| ---------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `freematica_list_materiales_asignados_servicios` | `GET /pvss/v2/contratos-servicios-material` | Lista de material asignado a servicios |
|
|
18
|
+
| `freematica_get_master_data` | (24 endpoints según `catalog`) | Devuelve un catálogo de datos maestros (tipos, geográficos, organizativos, inventario, financiero) |
|
|
19
|
+
| `freematica_list_clientes` | `GET /pgrl/v2/clientes` | Lista paginada de clientes |
|
|
20
|
+
| `freematica_get_cliente` | `GET /pgrl/v2/clientes/{idReg}` | Detalle de un cliente |
|
|
21
|
+
| `freematica_list_contactos_clientes` | `GET /pgrl/v2/contactos-clientes` | Lista paginada de contactos |
|
|
22
|
+
| `freematica_list_oportunidades_negocio` | `GET /pcrm/v2/oportunidades-negocio` | Lista paginada de oportunidades |
|
|
23
|
+
| `freematica_get_oportunidad_negocio` | `GET /pcrm/v2/oportunidades-negocio/{idReg}` | Detalle de oportunidad |
|
|
24
|
+
| `freematica_get_oportunidad_negocio_datos_ampliados` | `GET /pcrm/v2/oportunidades-negocio/{idReg}/datos-ampliados` | Datos ampliados (puede 404) |
|
|
25
|
+
| `freematica_get_ficha_prev_cliente` | `GET /pprl/v2/ficha-prev-cliente` | Ficha PRL de un cliente |
|
|
26
|
+
| `freematica_list_vigilancia_salud` | `GET /pprl/v1/vigilancia-salud` | Lista paginada de registros de Vigilancia de la Salud |
|
|
27
|
+
| `freematica_get_vigilancia_salud` | `GET /pprl/v1/vigilancia-salud/{idreg}` | Detalle de un registro de Vigilancia de la Salud |
|
|
28
|
+
| `freematica_list_personal` | `GET /pers/v2/personal` | Lista paginada de personas (RRHH) |
|
|
29
|
+
| `freematica_get_persona` | `GET /pers/v2/personal/{idreg}` | Detalle de una persona |
|
|
30
|
+
| `freematica_list_calendarios` | `GET /pgrl/v1/calendarios` | Lista paginada de calendarios laborales |
|
|
31
|
+
| `freematica_list_calendario_periodos` | `GET /pgrl/v1/calendarios/{idreg}/periodos` | Periodos de un calendario laboral |
|
|
32
|
+
| `freematica_list_cartera_clientes` | `GET /pcar/v1/cartera-clientes` | Lista paginada de cartera de clientes (cobros, impagados) |
|
|
33
|
+
| `freematica_get_cartera_cliente` | `GET /pcar/v1/cartera-clientes/{idReg}` | Detalle de un documento de cartera |
|
|
34
|
+
| `freematica_list_facturas_cabecera` | `GET /pven/v1/facturas-cabecera` | Lista paginada de facturas de ventas (cabecera) |
|
|
35
|
+
| `freematica_get_factura_cabecera` | `GET /pven/v1/facturas-cabecera/{idReg}` | Detalle de una factura de venta |
|
|
36
|
+
| `freematica_list_factura_lineas` | `GET /pven/v1/facturas-cabecera/{idReg}/lineas` | Líneas de detalle de una factura de venta |
|
|
37
|
+
| `freematica_list_factura_iva` | `GET /pven/v1/facturas-cabecera/{idReg}/iva` | Líneas de IVA de una factura de venta |
|
|
38
|
+
| `freematica_list_factura_vencimientos` | `GET /pven/v1/facturas-cabecera/{idReg}/vencimientos` | Vencimientos de cobro de una factura de venta |
|
|
39
|
+
| `freematica_list_facturas_compras` | `GET /pcom/v1/facturas-cabecera` | Lista paginada de facturas de compras |
|
|
40
|
+
| `freematica_get_factura_compra` | `GET /pcom/v1/facturas-cabecera/{idReg}` | Detalle de una factura de compra |
|
|
41
|
+
| `freematica_list_proveedores` | `GET /pgrl/v2/proveedores` | Lista paginada de proveedores |
|
|
42
|
+
| `freematica_get_proveedor` | `GET /pgrl/v2/proveedores/{idReg}` | Detalle de un proveedor |
|
|
43
|
+
| `freematica_list_localizaciones_cobro_clientes` | `GET /pgrl/v2/localizaciones-cobro-clientes` | Localizaciones de cobro de clientes (SEPA, domiciliaciones) |
|
|
44
|
+
| `freematica_list_localizaciones_pago_proveedores` | `GET /pgrl/v2/localizaciones-pago-proveedores` | Localizaciones de pago de proveedores |
|
|
45
|
+
| `freematica_list_localizaciones_servicio_clientes` | `GET /pgrl/v2/localizaciones-servicio-clientes` | Localizaciones de servicio de clientes |
|
|
46
|
+
| `freematica_list_cuentas_contables` | `GET /pcon/v2/cuentas` | Plan de cuentas contables (COD_CTA, COD_PLAN, etc.) |
|
|
47
|
+
| `freematica_list_cuentas_analiticas` | `GET /pcon/v2/cuentas-analiticas` | Catálogo de cuentas analíticas |
|
|
48
|
+
| `freematica_export_asientos` | `GET /pcon/v2/asientos` | Exporta asientos contables de un período (CSV/JSON) |
|
|
49
|
+
| `freematica_list_pedidos_compra` | `GET /pcmp/v2/pedidos` | Lista paginada de pedidos de compra (filtros nativos + FIQL + estado enum) |
|
|
50
|
+
| `freematica_get_pedido_compra` | `GET /pcmp/v2/pedidos/{idReg}` | Detalle de un pedido de compra (cabecera + proveedor + líneas) |
|
|
51
|
+
| `freematica_list_albaranes_ventas` | `GET /pven/v2/albaranes-ventas` | Lista paginada de albaranes de ventas (filtros todos nativos, empresa requerido) |
|
|
52
|
+
| `freematica_get_albaran_venta` | `GET /pven/v2/albaranes-ventas/{idReg}` | Detalle de un albarán de venta |
|
|
53
|
+
| `freematica_list_albaranes_factura` | `GET /pven/v2/albaranes-facturas` | Lista paginada de vinculaciones albarán↔factura (idReg nativo + FIQL) |
|
|
54
|
+
| `freematica_get_albaran_factura` | `GET /pven/v2/albaranes-facturas/{idReg}` | Detalle de una vinculación albarán-factura |
|
|
55
|
+
| `freematica_list_resultados_facturacion` | `GET /pvss/v1/facturacion-resultados` | Resultados del proceso batch de facturación automática de vigilancia (FIQL) |
|
|
56
|
+
|
|
57
|
+
## Filtros tipados (FIQL interno)
|
|
58
|
+
|
|
59
|
+
El MCP expone **parámetros tipados con zod** al consumidor (Claude/LLM). Internamente, el cliente los traduce a sintaxis FIQL antes de llamar al API de Freemática. El consumidor nunca ve ni escribe FIQL.
|
|
60
|
+
|
|
61
|
+
### Ejemplo
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// El LLM llama:
|
|
65
|
+
freematica_list_cartera_clientes({
|
|
66
|
+
empresa: '0001',
|
|
67
|
+
fechaDocDesde: '2026-01-01',
|
|
68
|
+
estado: 'pendiente',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// El MCP construye internamente:
|
|
72
|
+
// rquery=CARCL_EMP==0001;CARCL_FECDOC=ge=2026-01-01;CARCL_SITCAR==1
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Ventajas del patrón
|
|
76
|
+
|
|
77
|
+
- El LLM solo necesita conocer los parámetros tipados con nombres semánticos en español.
|
|
78
|
+
- La complejidad de la sintaxis FIQL queda encapsulada en el adapter interno (`src/clients/fiql-builder.ts`).
|
|
79
|
+
- Los errores de validación son claros: "empresa debe ser string", no "FIQL parse error".
|
|
80
|
+
- Los filtros booleanos (`soloImpagados: true`) se traducen automáticamente al operador FIQL correcto.
|
|
81
|
+
|
|
82
|
+
### Más ejemplos
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Facturas pendientes de traspaso a contabilidad:
|
|
86
|
+
freematica_list_facturas_cabecera({
|
|
87
|
+
empresa: '0001',
|
|
88
|
+
traspasadoContabilidad: false,
|
|
89
|
+
});
|
|
90
|
+
// → rquery=FAC_CODEMP==0001;FAC_TRASP_CONTAB==0
|
|
91
|
+
|
|
92
|
+
// Proveedores activos que contienen "GARCIA" en el nombre:
|
|
93
|
+
freematica_list_proveedores({
|
|
94
|
+
nombre: 'GARCIA',
|
|
95
|
+
activo: true,
|
|
96
|
+
});
|
|
97
|
+
// → rquery=NOMBRE_PRO=lk=GARCIA;FECHA_BAJA==null
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Modos de transporte
|
|
101
|
+
|
|
102
|
+
El binario soporta dos transportes seleccionables. La selección sigue este orden de precedencia:
|
|
103
|
+
|
|
104
|
+
1. CLI flag: `--transport=<modo>`
|
|
105
|
+
2. Variable de entorno: `MCP_TRANSPORT=<modo>`
|
|
106
|
+
3. Default: `stdio`
|
|
107
|
+
|
|
108
|
+
| Modo | Cuándo usar | Comando |
|
|
109
|
+
| ------- | -------------------------------------------- | ----------------------------------------------------------------------- |
|
|
110
|
+
| `stdio` | Claude Desktop, Claude Code, ejecución local | `mcp-freematica` (default) o `mcp-freematica --transport=stdio` |
|
|
111
|
+
| `http` | Nubiia, deploy como servicio web | `mcp-freematica --transport=http` o `MCP_TRANSPORT=http mcp-freematica` |
|
|
112
|
+
|
|
113
|
+
### Configurar en Claude Desktop (stdio)
|
|
114
|
+
|
|
115
|
+
Edita `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"freematica": {
|
|
121
|
+
"command": "npx",
|
|
122
|
+
"args": ["-y", "@nubiia/mcp-freematica"],
|
|
123
|
+
"env": {
|
|
124
|
+
"FREEMATICA_AUTH_TOKEN": "...",
|
|
125
|
+
"FREEMATICA_AUTH_COMPANY": "...",
|
|
126
|
+
"FREEMATICA_AUTH_ORGANIZATION": "...",
|
|
127
|
+
"FREEMATICA_AUTH_APP": "...",
|
|
128
|
+
"FREEMATICA_AUTH_SESSION": "..."
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Reinicia Claude Desktop y las tools de `freematica_*` aparecerán disponibles.
|
|
136
|
+
|
|
137
|
+
### Configurar en Claude Code (stdio)
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
claude mcp add freematica npx -y @nubiia/mcp-freematica \
|
|
141
|
+
-e FREEMATICA_AUTH_TOKEN=... \
|
|
142
|
+
-e FREEMATICA_AUTH_COMPANY=... \
|
|
143
|
+
-e FREEMATICA_AUTH_ORGANIZATION=... \
|
|
144
|
+
-e FREEMATICA_AUTH_APP=... \
|
|
145
|
+
-e FREEMATICA_AUTH_SESSION=...
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Configurar en Nubiia (HTTP)
|
|
149
|
+
|
|
150
|
+
Setear `MCP_TRANSPORT=http` en las variables de entorno del proceso, junto a las 5 `FREEMATICA_AUTH_*`. Resto de variables (puerto, origins) opcionales — ver tabla en "Configuración avanzada".
|
|
151
|
+
|
|
152
|
+
## Datos maestros disponibles
|
|
153
|
+
|
|
154
|
+
La tool `freematica_get_master_data` acepta un parámetro `catalog` con uno de los 24 valores siguientes. Cada uno mapea a un endpoint específico de Freemática.
|
|
155
|
+
|
|
156
|
+
| `catalog` | Endpoint Freemática | Contenido |
|
|
157
|
+
| --------------------------- | ---------------------------------------- | ---------------------------------- |
|
|
158
|
+
| **Tipos / clasificaciones** | | |
|
|
159
|
+
| `tipos-contrato` | `GET /ppre/v2/tipos-contrato` | Tipos de contrato comercial |
|
|
160
|
+
| `tipo-instalacion` | `GET /ppre/v1/tipo-instalacion` | Tipos de instalación física |
|
|
161
|
+
| `clases-servicios` | `GET /pvss/v1/clases-servicios` | Clases de servicio operativas |
|
|
162
|
+
| `tipos-casos` | `GET /pcrm/v2/tipos-casos` | Tipos de caso CRM |
|
|
163
|
+
| `subtipos-casos` | `GET /pcrm/v2/subtipos-casos` | Subtipos de caso CRM |
|
|
164
|
+
| `tipos-oportunidad-negocio` | `GET /pcrm/v2/tipos-oportunidad-negocio` | Tipos de oportunidad comercial |
|
|
165
|
+
| `tipos-impuestos` | `GET /pgrl/v2/tipos-impuestos` | IVA, IRPF, retenciones |
|
|
166
|
+
| `naturalezas-abono` | `GET /pven/v1/naturalezas-abono` | Naturalezas de abono comercial |
|
|
167
|
+
| `incidencecode` | `GET /pvss/v2/incidencecode` | Códigos de incidencia en servicios |
|
|
168
|
+
| `claves-facturacion` | `GET /pvss/v2/claves-facturacion` | Claves de facturación de servicios |
|
|
169
|
+
| **Geográficos** | | |
|
|
170
|
+
| `paises` | `GET /pgrl/v1/paises` | Países |
|
|
171
|
+
| `nacionalidades` | `GET /pgrl/v1/nacionalidades` | Nacionalidades |
|
|
172
|
+
| `provincias` | `GET /pgrl/v1/provincias` | Provincias |
|
|
173
|
+
| `poblaciones` | `GET /pgrl/v2/poblaciones` | Municipios |
|
|
174
|
+
| **Organizativos** | | |
|
|
175
|
+
| `empresas` | `GET /pgrl/v1/empresas` | Empresas |
|
|
176
|
+
| `delegaciones` | `GET /pgrl/v1/delegaciones/agrupcod` | Delegaciones (listado global) |
|
|
177
|
+
| `lineas-negocio` | `GET /pgrl/v2/lineas-negocio` | Líneas de negocio |
|
|
178
|
+
| `cargos-clientes` | `GET /pgrl/v2/cargos-clientes` | Cargos de contactos |
|
|
179
|
+
| `calendarios` | `GET /pgrl/v1/calendarios` | Calendarios laborales |
|
|
180
|
+
| `series` | `GET /pgrl/v2/series` | Series de numeración de documentos |
|
|
181
|
+
| **Inventario** | | |
|
|
182
|
+
| `familias` | `GET /part/v1/familias` | Familias de artículos |
|
|
183
|
+
| `subfamilias` | `GET /part/v1/subfamilias` | Subfamilias |
|
|
184
|
+
| `lineas` | `GET /part/v1/lineas` | Líneas de artículos |
|
|
185
|
+
| **Financiero** | | |
|
|
186
|
+
| `bancos` | `GET /pgrl/v2/bancos` | Entidades bancarias |
|
|
187
|
+
|
|
188
|
+
Respuesta: `{ catalog, items, count, total }`. Patrón típico de uso: llamar primero al catálogo correspondiente cuando otra tool devuelva IDs crípticos para resolverlos a nombres humanos.
|
|
189
|
+
|
|
190
|
+
## Paginación
|
|
191
|
+
|
|
192
|
+
Todas las tools `freematica_list_*` aceptan dos parámetros opcionales:
|
|
193
|
+
|
|
194
|
+
- **`page`** (int, ≥1, default 1): página a recuperar, **1-indexed**.
|
|
195
|
+
- **`items`** (int, 1..50, default 20): items por página.
|
|
196
|
+
|
|
197
|
+
La respuesta incluye siempre `total` (total de elementos en el dataset) para que el LLM pueda iterar páginas.
|
|
198
|
+
|
|
199
|
+
> El parámetro está bloqueado en `page >= 1` porque el API real de Freemática trata `page=0` como "devuelve TODO el dataset" (varios MB en endpoints grandes como `clientes`). Verificado empíricamente.
|
|
200
|
+
|
|
201
|
+
## IDs opacos en endpoints de detalle
|
|
202
|
+
|
|
203
|
+
Las tools `freematica_get_*` requieren un `id` que **NO** es el código natural (`COD_CLI`, `ID_OPORTUNIDAD`) sino el campo **`idReg`** que aparece en los items del listado correspondiente. Es un string opaco base64 como `MV9fMTAwMA==`.
|
|
204
|
+
|
|
205
|
+
Si pasas un código natural, el API responde `not_found`.
|
|
206
|
+
|
|
207
|
+
Patrón típico de uso desde el LLM:
|
|
208
|
+
|
|
209
|
+
1. `freematica_list_clientes(page=1, items=20)` → encontrar el cliente que interesa.
|
|
210
|
+
2. Tomar el campo `idReg` de ese item.
|
|
211
|
+
3. `freematica_get_cliente(id="<idReg>")` → detalle completo.
|
|
212
|
+
|
|
213
|
+
## Configuración
|
|
214
|
+
|
|
215
|
+
El servidor lee toda su configuración de **variables de entorno** al arrancar. Si falta alguna obligatoria, el proceso muere con un mensaje claro listando qué falta — no acepta requests hasta que la configuración sea válida.
|
|
216
|
+
|
|
217
|
+
### Variables de entorno obligatorias
|
|
218
|
+
|
|
219
|
+
| Variable | Descripción |
|
|
220
|
+
| ------------------------------ | ------------------------------------------------------------------------- |
|
|
221
|
+
| `FREEMATICA_AUTH_TOKEN` | Header `x-auth-token` para autenticar con la API de Freemática. |
|
|
222
|
+
| `FREEMATICA_AUTH_COMPANY` | Header `x-auth-company`. Identifica la empresa dentro del API. |
|
|
223
|
+
| `FREEMATICA_AUTH_ORGANIZATION` | Header `x-auth-organization`. Identifica la organización. |
|
|
224
|
+
| `FREEMATICA_AUTH_APP` | Header `x-auth-app`. Identifica la aplicación que consume (p.ej. `pvss`). |
|
|
225
|
+
| `FREEMATICA_AUTH_SESSION` | Header `x-auth-session`. Identifica la sesión activa. |
|
|
226
|
+
|
|
227
|
+
### Configuración avanzada
|
|
228
|
+
|
|
229
|
+
Variables de entorno opcionales para tuning de comportamiento en producción:
|
|
230
|
+
|
|
231
|
+
| Variable | Default | Descripción |
|
|
232
|
+
| --------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
233
|
+
| `FREEMATICA_BASE_URL` | `https://api-p01.clientservicepanel.com/restsat/api` | Base URL del API de Freemática. Cambiar solo si Freemática cambia el host o se usa un entorno alternativo. |
|
|
234
|
+
| `FREEMATICA_TIMEOUT_MS` | `30000` | Timeout por petición en milisegundos. Si el upstream no responde en este tiempo, el request se cancela con `network_error`. |
|
|
235
|
+
| `FREEMATICA_MAX_RETRIES` | `3` | Número máximo de reintentos para errores 5xx y errores de red. Los errores 4xx nunca se reintentan. |
|
|
236
|
+
| `FREEMATICA_CIRCUIT_BREAKER_THRESHOLD` | `5` | Número de operaciones lógicas fallidas consecutivas (post-retry-exhaustion) necesarias para abrir el circuit breaker. |
|
|
237
|
+
| `FREEMATICA_CIRCUIT_BREAKER_TIMEOUT_MS` | `30000` | Tiempo en ms que el circuit breaker permanece abierto antes de pasar a half-open. |
|
|
238
|
+
| `FREEMATICA_MAX_RESPONSE_SIZE_MB` | `10` | Tamaño máximo de respuesta aceptada del upstream en MB. Respuestas mayores se truncan con aviso en el campo `truncated`. |
|
|
239
|
+
| `MCP_TRANSPORT` | `stdio` | Transporte: `stdio` (para Claude Desktop/Code) o `http` (para Nubiia). Equivale al flag `--transport=`. |
|
|
240
|
+
| `MCP_PORT` | `3000` | Puerto TCP donde el servidor MCP expone `/mcp` y `/health`. Ignorado en modo stdio. |
|
|
241
|
+
| `MCP_ALLOWED_ORIGINS` | `*` | CORS: lista de orígenes permitidos separados por coma, o `*` para todos. En producción restringir al dominio de Nubiia. Ignorado en modo stdio. |
|
|
242
|
+
| `LOG_LEVEL` | `info` | Nivel de log para pino: `fatal`, `error`, `warn`, `info`, `debug`, `trace`. En producción usar `info`. En desarrollo usar `debug`. |
|
|
243
|
+
|
|
244
|
+
### De dónde salen las credenciales `x-auth-*`
|
|
245
|
+
|
|
246
|
+
Las 5 credenciales `x-auth-*` son **preexistentes** — el Postman collection de Freemática no expone un endpoint de login, así que estas credenciales se obtienen fuera de banda (típicamente por el departamento de IT de Freemática o tu integrador).
|
|
247
|
+
|
|
248
|
+
- Pide los 5 valores al responsable técnico de Freemática (o recupéralos de tu gestor de secretos si ya están provisionados).
|
|
249
|
+
- **NO comitees credenciales reales**. El repositorio tiene `.env.*` en `.gitignore` (excepto `.env.example`).
|
|
250
|
+
- Si las credenciales caducan, el MCP devolverá `invalid_token` en todas las llamadas. Renovarlas implica reiniciar el proceso (no hay refresh automático).
|
|
251
|
+
|
|
252
|
+
### Configuración en Nubiia (producción)
|
|
253
|
+
|
|
254
|
+
Nubiia despliega el servidor MCP y se encarga de inyectar las variables de entorno. En el panel de Nubiia:
|
|
255
|
+
|
|
256
|
+
1. Crea un MCP server apuntando a la URL del contenedor desplegado (por ejemplo `https://freematica-mcp.example.com/mcp`).
|
|
257
|
+
2. Añade las variables de entorno listadas arriba en la sección de secretos.
|
|
258
|
+
3. Recomendado: setear `MCP_ALLOWED_ORIGINS` al dominio exacto desde el que Claude/Nubiia conecta, no `*`.
|
|
259
|
+
4. Verifica con el endpoint `GET /health` que el servidor está vivo antes de habilitar el MCP en Claude.
|
|
260
|
+
|
|
261
|
+
### Configuración local (desarrollo)
|
|
262
|
+
|
|
263
|
+
Para desarrollo local crea un `.env` (o `.env.local`) en la raíz:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
cp .env.example .env
|
|
267
|
+
# Edita .env y rellena los 5 FREEMATICA_AUTH_*
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Luego arranca con:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
set -a; source .env; set +a
|
|
274
|
+
npm run dev
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
O todo en una línea:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
node --env-file=.env --import tsx src/index.ts
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Verificación de la configuración
|
|
284
|
+
|
|
285
|
+
1. **Sin env vars** — el proceso debe morir inmediatamente con un mensaje listando las 5 variables que faltan:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
node dist/index.js
|
|
289
|
+
# [freematica-mcp] FATAL: Invalid configuration:
|
|
290
|
+
# - FREEMATICA_AUTH_TOKEN: Required
|
|
291
|
+
# - FREEMATICA_AUTH_COMPANY: Required
|
|
292
|
+
# ...
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
2. **Con env vars válidas (modo HTTP)** — arranca con `MCP_TRANSPORT=http` y el proceso queda escuchando; `/health` devuelve `{ "status": "ok", "version": "0.5.0", "sessions": 0 }`:
|
|
296
|
+
```bash
|
|
297
|
+
MCP_TRANSPORT=http node dist/index.js &
|
|
298
|
+
curl http://localhost:3000/health
|
|
299
|
+
```
|
|
300
|
+
En modo stdio (default) no hay endpoints HTTP — el server escucha JSON-RPC por stdin/stdout.
|
|
301
|
+
|
|
302
|
+
## Troubleshooting
|
|
303
|
+
|
|
304
|
+
### `invalid_token` en todas las llamadas
|
|
305
|
+
|
|
306
|
+
Las credenciales `x-auth-*` han caducado o son incorrectas. Pasos:
|
|
307
|
+
|
|
308
|
+
1. Verificar que las 5 variables `FREEMATICA_AUTH_*` están seteadas correctamente.
|
|
309
|
+
2. Contactar al responsable técnico de Freemática para renovar el token/sesión.
|
|
310
|
+
3. Reiniciar el proceso del MCP tras actualizar las variables (el cliente no refresca credenciales en caliente).
|
|
311
|
+
|
|
312
|
+
### `network_error` — upstream Freemática caído
|
|
313
|
+
|
|
314
|
+
El API de Freemática no está respondiendo. Puede ser:
|
|
315
|
+
|
|
316
|
+
- Mantenimiento programado del upstream.
|
|
317
|
+
- Circuit breaker activo tras varios fallos consecutivos (ver logs: `Circuit breaker opened`).
|
|
318
|
+
- Problema de red entre el servidor y `api-p01.clientservicepanel.com`.
|
|
319
|
+
|
|
320
|
+
Pasos:
|
|
321
|
+
|
|
322
|
+
1. Verificar en los logs si hay `circuitBreaker: "open"` — si es así, esperar `FREEMATICA_CIRCUIT_BREAKER_TIMEOUT_MS` ms (default 30s) para que pase a half-open.
|
|
323
|
+
2. Verificar conectividad al upstream: `curl https://api-p01.clientservicepanel.com/`.
|
|
324
|
+
3. Aumentar `FREEMATICA_TIMEOUT_MS` si el upstream es lento (default 30s puede ser insuficiente en algunas redes).
|
|
325
|
+
|
|
326
|
+
### `rate_limit_exceeded` — throttling de Freemática
|
|
327
|
+
|
|
328
|
+
Freemática devuelve 429. El MCP reintenta automáticamente honrando el header `Retry-After` si está presente. Si el problema persiste:
|
|
329
|
+
|
|
330
|
+
1. Reducir la frecuencia de llamadas desde Claude.
|
|
331
|
+
2. Aumentar `FREEMATICA_MAX_RETRIES` (default 3) si los reintentos no son suficientes.
|
|
332
|
+
3. Contactar a Freemática para revisar los límites de rate asignados a la cuenta.
|
|
333
|
+
|
|
334
|
+
### `Response truncated` en `freematica_export_asientos`
|
|
335
|
+
|
|
336
|
+
El endpoint de exportación de asientos devuelve demasiados registros para un rango de fechas amplio. Pasos:
|
|
337
|
+
|
|
338
|
+
1. Usar un rango de fechas más corto: en lugar de un trimestre, exportar mes a mes.
|
|
339
|
+
2. Añadir filtros adicionales: filtrar por empresa, delegación o cuenta específica.
|
|
340
|
+
3. Si el volumen esperado es grande por diseño, aumentar `FREEMATICA_MAX_RESPONSE_SIZE_MB` (default 10 MB).
|
|
341
|
+
|
|
342
|
+
### El pre-commit hook falla al hacer `git commit`
|
|
343
|
+
|
|
344
|
+
Lint o formato detectaron errores en los ficheros staged. Pasos:
|
|
345
|
+
|
|
346
|
+
1. Revisar el output del hook: indicará qué fichero y qué regla falló.
|
|
347
|
+
2. Ejecutar `npm run lint:fix` y `npm run format` para corregir automáticamente.
|
|
348
|
+
3. Hacer `git add` de los ficheros corregidos antes de volver a intentar el commit.
|
|
349
|
+
|
|
350
|
+
## Desarrollo
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
npm install
|
|
354
|
+
npm run dev # arranca con tsx (hot reload)
|
|
355
|
+
npm test # ejecuta vitest una vez
|
|
356
|
+
npm run test:watch # watcher
|
|
357
|
+
npm run test:coverage # coverage report
|
|
358
|
+
npm run lint
|
|
359
|
+
npm run lint:fix
|
|
360
|
+
npm run format
|
|
361
|
+
npm run typecheck
|
|
362
|
+
npm run build # compila a dist/
|
|
363
|
+
npm start # ejecuta dist/index.js
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Testing
|
|
367
|
+
|
|
368
|
+
### Tests unitarios e integración
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
# Ejecutar todos los tests
|
|
372
|
+
npm test
|
|
373
|
+
|
|
374
|
+
# Ejecutar con coverage report
|
|
375
|
+
npm test -- --coverage
|
|
376
|
+
|
|
377
|
+
# Ejecutar un archivo concreto
|
|
378
|
+
npm test -- tests/fiql-builder.property.test.ts
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Los tests están en `tests/` organizados por tipo:
|
|
382
|
+
|
|
383
|
+
- `tests/clients/` — tests de los clientes HTTP (con nock)
|
|
384
|
+
- `tests/schemas/` — tests de los schemas Zod
|
|
385
|
+
- `tests/tools/` — tests de los handlers MCP (unit + integration)
|
|
386
|
+
- `tests/fiql-builder.*.test.ts` — tests unitarios y property-based del FIQL builder
|
|
387
|
+
- `tests/pagination-edge-cases.test.ts` — edge cases del adapter de paginación
|
|
388
|
+
- `tests/coverage-gaps.test.ts` — cobertura específica de ramas no cubiertas
|
|
389
|
+
|
|
390
|
+
**Coverage thresholds** (configurados en `vitest.config.ts`):
|
|
391
|
+
|
|
392
|
+
- Global statements: ≥85%
|
|
393
|
+
- src/clients/: ≥90%
|
|
394
|
+
- src/schemas/: ≥90%
|
|
395
|
+
- src/tools/: ≥85%
|
|
396
|
+
|
|
397
|
+
### Testing manual con MCP Inspector
|
|
398
|
+
|
|
399
|
+
Para probar el servidor MCP de forma interactiva, usa [MCP Inspector](https://github.com/modelcontextprotocol/inspector).
|
|
400
|
+
|
|
401
|
+
Ver instrucciones detalladas en [docs/testing-e2e.md](docs/testing-e2e.md).
|
|
402
|
+
|
|
403
|
+
## Endpoints HTTP
|
|
404
|
+
|
|
405
|
+
| Método | Path | Descripción |
|
|
406
|
+
| ------ | --------- | ---------------------------------------------------- |
|
|
407
|
+
| POST | `/mcp` | JSON-RPC del MCP (envía `initialize` la primera vez) |
|
|
408
|
+
| GET | `/mcp` | Stream SSE (requiere `Mcp-Session-Id`) |
|
|
409
|
+
| DELETE | `/mcp` | Terminar sesión |
|
|
410
|
+
| GET | `/health` | Healthcheck |
|
|
411
|
+
|
|
412
|
+
## Cómo añadir una nueva operación
|
|
413
|
+
|
|
414
|
+
1. **Tipar la respuesta** en `src/types/<grupo>.ts`.
|
|
415
|
+
2. **Añadir método** al `FreematicaClient` (`src/clients/freematica-client.ts`):
|
|
416
|
+
```ts
|
|
417
|
+
getContratosServicios(): Promise<VoContratosServicios[]> {
|
|
418
|
+
return this.get<VoContratosServicios[]>('/pvss/v1/contratos-servicios');
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
3. **Añadir `server.tool(...)`** en `src/tools/<grupo>.ts` (o crear el archivo si el grupo es nuevo).
|
|
422
|
+
4. **Si el grupo es nuevo:** registrar `register<Grupo>Tools(server, client)` en `src/server.ts`.
|
|
423
|
+
5. **Tests:** un test del método del client (con nock) + un test del handler de la tool.
|
|
424
|
+
|
|
425
|
+
Patrón inspirado en `mcp-nevent` y `mcp-holded`.
|
|
426
|
+
|
|
427
|
+
## Despliegue
|
|
428
|
+
|
|
429
|
+
Pensado para ejecutarse dentro de **Nubiia**. La plataforma se encarga del deploy y de inyectar las variables de entorno. Tienes 3 opciones:
|
|
430
|
+
|
|
431
|
+
### Opción 1 — Instalar como paquete npm (recomendada)
|
|
432
|
+
|
|
433
|
+
El paquete se publica como `@nubiia/mcp-freematica` en el registry público de npm cuando se crea un tag `v*` en `main`.
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
# Instalar y ejecutar (no requiere autenticación)
|
|
437
|
+
npm install -g @nubiia/mcp-freematica
|
|
438
|
+
mcp-freematica # binario instalado, lee env vars y arranca el server
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Opción 2 — Docker
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
docker build -t mcp-freematica .
|
|
445
|
+
docker run --env-file .env -p 3000:3000 mcp-freematica
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Opción 3 — Clone + build
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
git clone https://github.com/nubiia-dev/mcp-freematica.git
|
|
452
|
+
cd mcp-freematica
|
|
453
|
+
npm ci && npm run build
|
|
454
|
+
node dist/index.js
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## Publicar una nueva versión
|
|
458
|
+
|
|
459
|
+
1. Bump de versión en `package.json` (semver).
|
|
460
|
+
2. Commit + merge a `main`.
|
|
461
|
+
3. Crear y empujar un tag con prefijo `v`:
|
|
462
|
+
```bash
|
|
463
|
+
git tag v0.5.0
|
|
464
|
+
git push origin v0.5.0
|
|
465
|
+
```
|
|
466
|
+
4. El workflow `.github/workflows/publish.yml` se dispara, valida (lint + typecheck + test + build) y publica en npm.
|
|
467
|
+
|
|
468
|
+
## Especificaciones y diseño
|
|
469
|
+
|
|
470
|
+
- v0.1.0 spec: `docs/superpowers/specs/2026-05-18-freematica-mcp-design.md` (bootstrap inicial)
|
|
471
|
+
- v0.1.0 plan: `docs/superpowers/plans/2026-05-18-freematica-mcp.md`
|
|
472
|
+
- v0.2.0 spec: `docs/superpowers/specs/2026-05-19-stdio-transport-design.md` (stdio support)
|
|
473
|
+
- v0.2.0 plan: `docs/superpowers/plans/2026-05-19-stdio-transport.md`
|
|
474
|
+
- v0.3.0 spec: `docs/superpowers/specs/2026-05-19-master-data-tool-design.md` (master data tool)
|
|
475
|
+
- v0.3.0 plan: `docs/superpowers/plans/2026-05-19-master-data-tool.md`
|
|
476
|
+
- v0.4.0 spec: `docs/superpowers/specs/2026-05-20-commercial-tools-design.md` (commercial tools + envelope unwrap fix)
|
|
477
|
+
- v0.4.0 plan: `docs/superpowers/plans/2026-05-20-commercial-tools.md`
|
|
478
|
+
- v0.4.1: hotfix de catálogos `delegaciones` (endpoint cambiado a /agrupcod) y `tipos-marcajes` (eliminado — endpoint roto). Ver CHANGELOG.
|
|
479
|
+
- ADRs de arquitectura: `docs/adr/` (ver índice en ese directorio)
|
|
480
|
+
- API: `apidocs/Freematica API - Complete Collection.postman_collection.json`
|
|
481
|
+
- CHANGELOG: `CHANGELOG.md`
|
|
482
|
+
|
|
483
|
+
## Licencia
|
|
484
|
+
|
|
485
|
+
MIT — ver `LICENSE`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type AxiosInstance } from 'axios';
|
|
2
|
+
import type { FreematicaEnvelope } from '../types/api-envelope.js';
|
|
3
|
+
export type FreematicaErrorCode = 'invalid_token' | 'forbidden' | 'not_found' | 'rate_limit_exceeded' | 'server_error' | 'network_error' | 'unexpected_error';
|
|
4
|
+
export declare class FreematicaError extends Error {
|
|
5
|
+
readonly code: FreematicaErrorCode;
|
|
6
|
+
readonly retryAfter?: number | undefined;
|
|
7
|
+
constructor(code: FreematicaErrorCode, message: string, retryAfter?: number | undefined);
|
|
8
|
+
}
|
|
9
|
+
export interface AuthHeaders {
|
|
10
|
+
'x-auth-token': string;
|
|
11
|
+
'x-auth-company': string;
|
|
12
|
+
'x-auth-organization': string;
|
|
13
|
+
'x-auth-app': string;
|
|
14
|
+
'x-auth-session': string;
|
|
15
|
+
}
|
|
16
|
+
export interface BaseClientConfig {
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
authHeaders: AuthHeaders;
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare class BaseClient {
|
|
22
|
+
protected readonly http: AxiosInstance;
|
|
23
|
+
constructor(config: BaseClientConfig);
|
|
24
|
+
protected get<T>(path: string): Promise<T>;
|
|
25
|
+
protected post<T>(path: string, body?: unknown): Promise<T>;
|
|
26
|
+
protected put<T>(path: string, body?: unknown): Promise<T>;
|
|
27
|
+
protected delete<T>(path: string): Promise<T>;
|
|
28
|
+
protected request<T>(method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body?: unknown): Promise<T>;
|
|
29
|
+
protected mapEnvelopeError(env: FreematicaEnvelope<unknown> | undefined): FreematicaError;
|
|
30
|
+
protected mapAxiosError(err: unknown): FreematicaError;
|
|
31
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import axios, { AxiosError } from 'axios';
|
|
2
|
+
export class FreematicaError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
retryAfter;
|
|
5
|
+
constructor(code, message, retryAfter) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.retryAfter = retryAfter;
|
|
9
|
+
this.name = 'FreematicaError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class BaseClient {
|
|
13
|
+
http;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.http = axios.create({
|
|
16
|
+
baseURL: config.baseUrl,
|
|
17
|
+
timeout: config.timeoutMs ?? 30_000,
|
|
18
|
+
headers: {
|
|
19
|
+
...config.authHeaders,
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async get(path) {
|
|
25
|
+
return this.request('GET', path);
|
|
26
|
+
}
|
|
27
|
+
async post(path, body) {
|
|
28
|
+
return this.request('POST', path, body);
|
|
29
|
+
}
|
|
30
|
+
async put(path, body) {
|
|
31
|
+
return this.request('PUT', path, body);
|
|
32
|
+
}
|
|
33
|
+
async delete(path) {
|
|
34
|
+
return this.request('DELETE', path);
|
|
35
|
+
}
|
|
36
|
+
async request(method, path, body) {
|
|
37
|
+
try {
|
|
38
|
+
const res = await this.http.request({
|
|
39
|
+
method,
|
|
40
|
+
url: path,
|
|
41
|
+
data: body,
|
|
42
|
+
});
|
|
43
|
+
const envelope = res.data;
|
|
44
|
+
if (envelope?.errorCode !== '200') {
|
|
45
|
+
throw this.mapEnvelopeError(envelope);
|
|
46
|
+
}
|
|
47
|
+
return envelope.data;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (err instanceof FreematicaError)
|
|
51
|
+
throw err;
|
|
52
|
+
throw this.mapAxiosError(err);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
mapEnvelopeError(env) {
|
|
56
|
+
const code = env?.errorCode ?? 'unknown';
|
|
57
|
+
const msg = env?.errorMessage || `API error (envelope errorCode=${code})`;
|
|
58
|
+
if (code === '401')
|
|
59
|
+
return new FreematicaError('invalid_token', msg);
|
|
60
|
+
if (code === '403')
|
|
61
|
+
return new FreematicaError('forbidden', msg);
|
|
62
|
+
if (code === '404')
|
|
63
|
+
return new FreematicaError('not_found', msg);
|
|
64
|
+
if (code === '429')
|
|
65
|
+
return new FreematicaError('rate_limit_exceeded', msg);
|
|
66
|
+
if (code.startsWith('5'))
|
|
67
|
+
return new FreematicaError('server_error', msg);
|
|
68
|
+
return new FreematicaError('unexpected_error', msg);
|
|
69
|
+
}
|
|
70
|
+
mapAxiosError(err) {
|
|
71
|
+
if (err instanceof AxiosError) {
|
|
72
|
+
if (err.response) {
|
|
73
|
+
const status = err.response.status;
|
|
74
|
+
if (status === 401)
|
|
75
|
+
return new FreematicaError('invalid_token', 'Authentication failed (401)');
|
|
76
|
+
if (status === 403)
|
|
77
|
+
return new FreematicaError('forbidden', 'Insufficient permissions (403)');
|
|
78
|
+
if (status === 404)
|
|
79
|
+
return new FreematicaError('not_found', 'Resource not found (404)');
|
|
80
|
+
if (status === 429) {
|
|
81
|
+
const retryAfterHeader = err.response.headers['retry-after'];
|
|
82
|
+
const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : undefined;
|
|
83
|
+
return new FreematicaError('rate_limit_exceeded', `Rate limit exceeded${retryAfter ? ` (retry after ${retryAfter}s)` : ''}`, retryAfter);
|
|
84
|
+
}
|
|
85
|
+
if (status >= 500) {
|
|
86
|
+
return new FreematicaError('server_error', `Upstream server error (${status})`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (err.code === 'ECONNREFUSED' || err.code === 'ETIMEDOUT' || err.code === 'ENOTFOUND') {
|
|
90
|
+
return new FreematicaError('network_error', `Network error: ${err.message}`);
|
|
91
|
+
}
|
|
92
|
+
// ECONNABORTED: Axios timeout (axios timeout option fired)
|
|
93
|
+
// ERR_CANCELED: Axios cancellation via AbortController signal
|
|
94
|
+
if (err.code === 'ECONNABORTED' || err.code === 'ERR_CANCELED') {
|
|
95
|
+
return new FreematicaError('network_error', `Request timed out: ${err.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
99
|
+
return new FreematicaError('unexpected_error', `Unexpected error: ${msg}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=base-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-client.js","sourceRoot":"","sources":["../../src/clients/base-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAsB,UAAU,EAAE,MAAM,OAAO,CAAC;AAY9D,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAEtB;IAEA;IAHlB,YACkB,IAAyB,EACzC,OAAe,EACC,UAAmB;QAEnC,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAqB;QAEzB,eAAU,GAAV,UAAU,CAAS;QAGnC,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAgBD,MAAM,OAAO,UAAU;IACF,IAAI,CAAgB;IAEvC,YAAY,MAAwB;QAClC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC;YACvB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM,CAAC,SAAS,IAAI,MAAM;YACnC,OAAO,EAAE;gBACP,GAAG,MAAM,CAAC,WAAW;gBACrB,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;IACL,CAAC;IAES,KAAK,CAAC,GAAG,CAAI,IAAY;QACjC,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAES,KAAK,CAAC,IAAI,CAAI,IAAY,EAAE,IAAc;QAClD,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAES,KAAK,CAAC,GAAG,CAAI,IAAY,EAAE,IAAc;QACjD,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAES,KAAK,CAAC,MAAM,CAAI,IAAY;QACpC,OAAO,IAAI,CAAC,OAAO,CAAI,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAES,KAAK,CAAC,OAAO,CACrB,MAAyC,EACzC,IAAY,EACZ,IAAc;QAEd,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAwB;gBACzD,MAAM;gBACN,GAAG,EAAE,IAAI;gBACT,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC;YAC1B,IAAI,QAAQ,EAAE,SAAS,KAAK,KAAK,EAAE,CAAC;gBAClC,MAAM,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YACxC,CAAC;YACD,OAAO,QAAQ,CAAC,IAAI,CAAC;QACvB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,eAAe;gBAAE,MAAM,GAAG,CAAC;YAC9C,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAES,gBAAgB,CAAC,GAA4C;QACrE,MAAM,IAAI,GAAG,GAAG,EAAE,SAAS,IAAI,SAAS,CAAC;QACzC,MAAM,GAAG,GAAG,GAAG,EAAE,YAAY,IAAI,iCAAiC,IAAI,GAAG,CAAC;QAC1E,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,IAAI,eAAe,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;QACrE,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,IAAI,eAAe,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QACjE,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,IAAI,eAAe,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QACjE,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,IAAI,eAAe,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;QAC3E,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,eAAe,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QAC1E,OAAO,IAAI,eAAe,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACtD,CAAC;IAES,aAAa,CAAC,GAAY;QAClC,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;YAC9B,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;gBACjB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACnC,IAAI,MAAM,KAAK,GAAG;oBAAE,OAAO,IAAI,eAAe,CAAC,eAAe,EAAE,6BAA6B,CAAC,CAAC;gBAC/F,IAAI,MAAM,KAAK,GAAG;oBAAE,OAAO,IAAI,eAAe,CAAC,WAAW,EAAE,gCAAgC,CAAC,CAAC;gBAC9F,IAAI,MAAM,KAAK,GAAG;oBAAE,OAAO,IAAI,eAAe,CAAC,WAAW,EAAE,0BAA0B,CAAC,CAAC;gBACxF,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;oBACnB,MAAM,gBAAgB,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;oBAC7D,MAAM,UAAU,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;oBAC3E,OAAO,IAAI,eAAe,CACxB,qBAAqB,EACrB,sBAAsB,UAAU,CAAC,CAAC,CAAC,iBAAiB,UAAU,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EACzE,UAAU,CACX,CAAC;gBACJ,CAAC;gBACD,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;oBAClB,OAAO,IAAI,eAAe,CAAC,cAAc,EAAE,0BAA0B,MAAM,GAAG,CAAC,CAAC;gBAClF,CAAC;YACH,CAAC;YACD,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACxF,OAAO,IAAI,eAAe,CAAC,eAAe,EAAE,kBAAkB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/E,CAAC;YACD,2DAA2D;YAC3D,8DAA8D;YAC9D,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBAC/D,OAAO,IAAI,eAAe,CAAC,eAAe,EAAE,sBAAsB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,IAAI,eAAe,CAAC,kBAAkB,EAAE,qBAAqB,GAAG,EAAE,CAAC,CAAC;IAC7E,CAAC;CACF"}
|