@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.
Files changed (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +485 -0
  3. package/dist/clients/base-client.d.ts +31 -0
  4. package/dist/clients/base-client.js +102 -0
  5. package/dist/clients/base-client.js.map +1 -0
  6. package/dist/clients/fiql-builder.d.ts +118 -0
  7. package/dist/clients/fiql-builder.js +257 -0
  8. package/dist/clients/fiql-builder.js.map +1 -0
  9. package/dist/clients/freematica-client.d.ts +659 -0
  10. package/dist/clients/freematica-client.js +1327 -0
  11. package/dist/clients/freematica-client.js.map +1 -0
  12. package/dist/clients/hardened-base-client.d.ts +143 -0
  13. package/dist/clients/hardened-base-client.js +366 -0
  14. package/dist/clients/hardened-base-client.js.map +1 -0
  15. package/dist/config.d.ts +104 -0
  16. package/dist/config.js +105 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +55 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/logger.d.ts +71 -0
  22. package/dist/logger.js +114 -0
  23. package/dist/logger.js.map +1 -0
  24. package/dist/schemas/albaranes.d.ts +124 -0
  25. package/dist/schemas/albaranes.js +250 -0
  26. package/dist/schemas/albaranes.js.map +1 -0
  27. package/dist/schemas/cartera.d.ts +70 -0
  28. package/dist/schemas/cartera.js +104 -0
  29. package/dist/schemas/cartera.js.map +1 -0
  30. package/dist/schemas/contratos.d.ts +7 -0
  31. package/dist/schemas/contratos.js +2 -0
  32. package/dist/schemas/contratos.js.map +1 -0
  33. package/dist/schemas/facturas-electronicas.d.ts +60 -0
  34. package/dist/schemas/facturas-electronicas.js +79 -0
  35. package/dist/schemas/facturas-electronicas.js.map +1 -0
  36. package/dist/schemas/facturas-ventas.d.ts +118 -0
  37. package/dist/schemas/facturas-ventas.js +153 -0
  38. package/dist/schemas/facturas-ventas.js.map +1 -0
  39. package/dist/schemas/filters.d.ts +92 -0
  40. package/dist/schemas/filters.js +76 -0
  41. package/dist/schemas/filters.js.map +1 -0
  42. package/dist/schemas/master-data.d.ts +26 -0
  43. package/dist/schemas/master-data.js +86 -0
  44. package/dist/schemas/master-data.js.map +1 -0
  45. package/dist/schemas/pagination.d.ts +14 -0
  46. package/dist/schemas/pagination.js +27 -0
  47. package/dist/schemas/pagination.js.map +1 -0
  48. package/dist/schemas/pedidos-compras.d.ts +92 -0
  49. package/dist/schemas/pedidos-compras.js +189 -0
  50. package/dist/schemas/pedidos-compras.js.map +1 -0
  51. package/dist/server-instructions.d.ts +1 -0
  52. package/dist/server-instructions.js +83 -0
  53. package/dist/server-instructions.js.map +1 -0
  54. package/dist/server.d.ts +6 -0
  55. package/dist/server.js +41 -0
  56. package/dist/server.js.map +1 -0
  57. package/dist/tools/albaranes.d.ts +16 -0
  58. package/dist/tools/albaranes.js +202 -0
  59. package/dist/tools/albaranes.js.map +1 -0
  60. package/dist/tools/calendarios.d.ts +13 -0
  61. package/dist/tools/calendarios.js +82 -0
  62. package/dist/tools/calendarios.js.map +1 -0
  63. package/dist/tools/cartera.d.ts +13 -0
  64. package/dist/tools/cartera.js +78 -0
  65. package/dist/tools/cartera.js.map +1 -0
  66. package/dist/tools/clientes.d.ts +3 -0
  67. package/dist/tools/clientes.js +48 -0
  68. package/dist/tools/clientes.js.map +1 -0
  69. package/dist/tools/contabilidad.d.ts +14 -0
  70. package/dist/tools/contabilidad.js +259 -0
  71. package/dist/tools/contabilidad.js.map +1 -0
  72. package/dist/tools/contactos-clientes.d.ts +3 -0
  73. package/dist/tools/contactos-clientes.js +25 -0
  74. package/dist/tools/contactos-clientes.js.map +1 -0
  75. package/dist/tools/contratos.d.ts +10 -0
  76. package/dist/tools/contratos.js +35 -0
  77. package/dist/tools/contratos.js.map +1 -0
  78. package/dist/tools/facturas-compras.d.ts +13 -0
  79. package/dist/tools/facturas-compras.js +131 -0
  80. package/dist/tools/facturas-compras.js.map +1 -0
  81. package/dist/tools/facturas-electronicas.d.ts +17 -0
  82. package/dist/tools/facturas-electronicas.js +268 -0
  83. package/dist/tools/facturas-electronicas.js.map +1 -0
  84. package/dist/tools/facturas-ventas.d.ts +16 -0
  85. package/dist/tools/facturas-ventas.js +181 -0
  86. package/dist/tools/facturas-ventas.js.map +1 -0
  87. package/dist/tools/helpers.d.ts +17 -0
  88. package/dist/tools/helpers.js +36 -0
  89. package/dist/tools/helpers.js.map +1 -0
  90. package/dist/tools/localizaciones.d.ts +17 -0
  91. package/dist/tools/localizaciones.js +192 -0
  92. package/dist/tools/localizaciones.js.map +1 -0
  93. package/dist/tools/master-data.d.ts +10 -0
  94. package/dist/tools/master-data.js +70 -0
  95. package/dist/tools/master-data.js.map +1 -0
  96. package/dist/tools/oportunidades-negocio.d.ts +3 -0
  97. package/dist/tools/oportunidades-negocio.js +68 -0
  98. package/dist/tools/oportunidades-negocio.js.map +1 -0
  99. package/dist/tools/pedidos-compras.d.ts +14 -0
  100. package/dist/tools/pedidos-compras.js +105 -0
  101. package/dist/tools/pedidos-compras.js.map +1 -0
  102. package/dist/tools/personal.d.ts +13 -0
  103. package/dist/tools/personal.js +154 -0
  104. package/dist/tools/personal.js.map +1 -0
  105. package/dist/tools/prl.d.ts +47 -0
  106. package/dist/tools/prl.js +242 -0
  107. package/dist/tools/prl.js.map +1 -0
  108. package/dist/tools/proveedores.d.ts +14 -0
  109. package/dist/tools/proveedores.js +122 -0
  110. package/dist/tools/proveedores.js.map +1 -0
  111. package/dist/transports/http.d.ts +12 -0
  112. package/dist/transports/http.js +110 -0
  113. package/dist/transports/http.js.map +1 -0
  114. package/dist/transports/stdio.d.ts +12 -0
  115. package/dist/transports/stdio.js +15 -0
  116. package/dist/transports/stdio.js.map +1 -0
  117. package/dist/types/api-envelope.d.ts +27 -0
  118. package/dist/types/api-envelope.js +2 -0
  119. package/dist/types/api-envelope.js.map +1 -0
  120. package/dist/types/clientes.d.ts +9 -0
  121. package/dist/types/clientes.js +2 -0
  122. package/dist/types/clientes.js.map +1 -0
  123. package/dist/types/contactos-clientes.d.ts +7 -0
  124. package/dist/types/contactos-clientes.js +2 -0
  125. package/dist/types/contactos-clientes.js.map +1 -0
  126. package/dist/types/contratos.d.ts +8 -0
  127. package/dist/types/contratos.js +2 -0
  128. package/dist/types/contratos.js.map +1 -0
  129. package/dist/types/master-data.d.ts +9 -0
  130. package/dist/types/master-data.js +2 -0
  131. package/dist/types/master-data.js.map +1 -0
  132. package/dist/types/oportunidades-negocio.d.ts +8 -0
  133. package/dist/types/oportunidades-negocio.js +2 -0
  134. package/dist/types/oportunidades-negocio.js.map +1 -0
  135. package/dist/utils/size-guardrail.d.ts +22 -0
  136. package/dist/utils/size-guardrail.js +31 -0
  137. package/dist/utils/size-guardrail.js.map +1 -0
  138. 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":"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"}