diff --git a/.gitignore b/.gitignore index af057b1..160ab38 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,9 @@ facturascripts/ .claude/ /mysql/ +# Módulos locales del usuario (nunca se suben al repositorio) +src/modules-local/ + # OpenAPI files - keep the current one, exclude the old one /facturascripts.openapi.json # Keep the current OpenAPI definition file diff --git a/src/index.ts b/src/index.ts index 70b60a5..89f216f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -110,6 +110,8 @@ import { tarifasToolDefinition, tarifasToolImplementation } from './modules/conf import { totalModelesToolDefinition, totalModelesToolImplementation } from './modules/system/totalmodeles/index.js'; import { variantesToolDefinition, variantesToolImplementation } from './modules/core-business/variantes/index.js'; import { workEventesToolDefinition, workEventesToolImplementation } from './modules/system/workeventes/index.js'; +import { loadLocalModules } from './local-loader.js'; +import type { LoadedLocalModule } from './types/local-module.js'; const server = new Server( { @@ -203,6 +205,8 @@ const subcuentasResource = new SubcuentasResource(fsClient); const tarifasResource = new TarifasResource(fsClient); const totalModelesResource = new TotalModelesResource(fsClient); const workEventesResource = new WorkEventesResource(fsClient); +// Módulos locales — se cargan en runServer() antes de conectar +let localModules: LoadedLocalModule[] = []; server.setRequestHandler(ListToolsRequestSchema, async () => { return { @@ -2095,6 +2099,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { }, }, }, + // Módulos locales (cargados dinámicamente desde src/modules-local/) + ...localModules.map(m => m.toolDefinition), ], }; }); @@ -2570,6 +2576,13 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => { description: 'Lista de eventos y trabajos del sistema para monitoreo y seguimiento de procesos', mimeType: 'application/json', }, + // Módulos locales (cargados dinámicamente desde src/modules-local/) + ...localModules.map(m => ({ + uri: `facturascripts://${m.resourceName}`, + name: `FacturaScripts Local — ${m.resourceName}`, + description: m.resourceDescription, + mimeType: 'application/json', + })), ], }; }); @@ -2889,6 +2902,13 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => { return await workEventesResource.getResource(uri); } + // Módulos locales + for (const localModule of localModules) { + if (localModule.instance.matchesUri(uri)) { + return await localModule.instance.getResource(uri); + } + } + throw new Error(`Resource not found: ${uri}`); }); @@ -3836,8 +3856,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { }; } - default: + + default: { + // Módulos locales + const localMod = localModules.find(m => m.toolDefinition.name === name); + if (localMod) { + const uri = buildUri(localMod.resourceName); + const result = await localMod.instance.getResource(uri); + return { + content: [ + { + type: 'text', + text: (result as any).contents?.[0]?.text || 'No data', + }, + ], + }; + } throw new Error(`Unknown tool: ${name}`); + } } } catch (error) { return { @@ -3853,6 +3889,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { }); async function runServer() { + localModules = await loadLocalModules(fsClient); const transport = new StdioServerTransport(); await server.connect(transport); console.error('MCP FacturaScripts server running on stdio'); diff --git a/src/local-loader.ts b/src/local-loader.ts new file mode 100644 index 0000000..76b968e --- /dev/null +++ b/src/local-loader.ts @@ -0,0 +1,81 @@ +import { readdirSync, existsSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath, pathToFileURL } from 'url'; +import { FacturaScriptsClient } from './fs/client.js'; +import type { LocalModuleConfig, LoadedLocalModule } from './types/local-module.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +/** + * Carga dinámicamente todos los módulos locales de la carpeta modules-local/. + * + * Cada subcarpeta de modules-local/ debe tener un index.js (compilado desde index.ts) + * que exporte `moduleConfig` siguiendo la interfaz LocalModuleConfig. + * + * La carpeta src/modules-local/ está en .gitignore, por lo que los módulos + * locales nunca se subirán al repositorio. + */ +export async function loadLocalModules(fsClient: FacturaScriptsClient): Promise { + const localModulesDir = join(__dirname, 'modules-local'); + + if (!existsSync(localModulesDir)) { + return []; + } + + const loaded: LoadedLocalModule[] = []; + + let entries: import('fs').Dirent[]; + try { + entries = readdirSync(localModulesDir, { withFileTypes: true, encoding: 'utf8' }); + } catch { + console.error('[local-loader] No se pudo leer el directorio modules-local/'); + return []; + } + + const dirs = entries.filter(e => e.isDirectory()); + + for (const dir of dirs) { + const indexPath = join(localModulesDir, dir.name as string, 'index.js'); + + if (!existsSync(indexPath)) { + console.error(`[local-loader] Módulo "${dir.name}" no tiene index.js compilado — ejecuta npm run build`); + continue; + } + + try { + const moduleUrl = pathToFileURL(indexPath).href; + const mod = await import(moduleUrl); + const config: LocalModuleConfig = mod.moduleConfig; + + if (!config) { + console.error(`[local-loader] Módulo "${dir.name}" no exporta "moduleConfig"`); + continue; + } + + if (!config.resourceName || !config.Resource || !config.toolDefinition) { + console.error(`[local-loader] Módulo "${dir.name}": moduleConfig incompleto (faltan resourceName, Resource o toolDefinition)`); + continue; + } + + const instance = new config.Resource(fsClient); + + loaded.push({ + instance, + toolDefinition: config.toolDefinition, + resourceName: config.resourceName, + resourceDescription: config.resourceDescription ?? '', + }); + + console.error(`[local-loader] ✓ Módulo local cargado: ${dir.name} (tool: ${config.toolDefinition.name})`); + } catch (err) { + console.error(`[local-loader] Error al cargar módulo "${dir.name}":`, err); + } + } + + if (loaded.length > 0) { + console.error(`[local-loader] ${loaded.length} módulo(s) local(es) cargado(s)`); + } + + return loaded; +} diff --git a/src/types/local-module.ts b/src/types/local-module.ts new file mode 100644 index 0000000..e00b1dc --- /dev/null +++ b/src/types/local-module.ts @@ -0,0 +1,39 @@ +import { FacturaScriptsClient } from '../fs/client.js'; + +/** + * Interfaz que debe exportar cada módulo local como `moduleConfig`. + * + * Ejemplo de uso en src/modules-local/mi-modulo/index.ts: + * + * import { MiResource } from './resource.js'; + * import { toolDefinition } from './tool.js'; + * import type { LocalModuleConfig } from '../../types/local-module.js'; + * + * export const moduleConfig: LocalModuleConfig = { + * resourceName: 'mi-recurso', + * resourceDescription: 'Descripción de mi recurso local', + * Resource: MiResource, + * toolDefinition, + * }; + */ +export interface LocalModuleConfig { + /** Nombre del recurso, usado en la URI: facturascripts://{resourceName} */ + resourceName: string; + /** Descripción visible en el listado de recursos MCP */ + resourceDescription: string; + /** Clase del recurso. Debe implementar getResource(uri) y matchesUri(uri) */ + Resource: new (client: FacturaScriptsClient) => any; + /** Definición de la herramienta MCP (name, description, inputSchema) */ + toolDefinition: { + name: string; + description: string; + inputSchema: object; + }; +} + +export interface LoadedLocalModule { + instance: any; + toolDefinition: LocalModuleConfig['toolDefinition']; + resourceName: string; + resourceDescription: string; +} diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo deleted file mode 100644 index 6d2f70b..0000000 --- a/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"root":["./src/env.ts","./src/index.ts","./src/fs/client.ts","./src/modules/index.ts","./src/modules/accounting/index.ts","./src/modules/accounting/asientos/index.ts","./src/modules/accounting/asientos/resource.ts","./src/modules/accounting/asientos/tool.ts","./src/modules/accounting/conceptopartidas/index.ts","./src/modules/accounting/conceptopartidas/resource.ts","./src/modules/accounting/conceptopartidas/tool.ts","./src/modules/accounting/cuentas/index.ts","./src/modules/accounting/cuentas/resource.ts","./src/modules/accounting/cuentas/tool.ts","./src/modules/accounting/diarios/index.ts","./src/modules/accounting/diarios/resource.ts","./src/modules/accounting/diarios/tool.ts","./src/modules/accounting/ejercicios/index.ts","./src/modules/accounting/ejercicios/resource.ts","./src/modules/accounting/ejercicios/tool.ts","./src/modules/accounting/partidas/index.ts","./src/modules/accounting/partidas/resource.ts","./src/modules/accounting/partidas/tool.ts","./src/modules/accounting/subcuentas/index.ts","./src/modules/accounting/subcuentas/resource.ts","./src/modules/accounting/subcuentas/tool.ts","./src/modules/communication/index.ts","./src/modules/communication/contactos/index.ts","./src/modules/communication/contactos/resource.ts","./src/modules/communication/contactos/tool.ts","./src/modules/communication/emailnotifications/index.ts","./src/modules/communication/emailnotifications/resource.ts","./src/modules/communication/emailnotifications/tool.ts","./src/modules/communication/emailsentes/index.ts","./src/modules/communication/emailsentes/resource.ts","./src/modules/communication/emailsentes/tool.ts","./src/modules/configuration/index.ts","./src/modules/configuration/agenciatransportes/index.ts","./src/modules/configuration/agenciatransportes/resource.ts","./src/modules/configuration/agenciatransportes/tool.ts","./src/modules/configuration/agentes/index.ts","./src/modules/configuration/agentes/resource.ts","./src/modules/configuration/agentes/tool.ts","./src/modules/configuration/almacenes/index.ts","./src/modules/configuration/almacenes/resource.ts","./src/modules/configuration/almacenes/tool.ts","./src/modules/configuration/atributos/index.ts","./src/modules/configuration/atributos/resource.ts","./src/modules/configuration/atributos/tool.ts","./src/modules/configuration/atributovalores/index.ts","./src/modules/configuration/atributovalores/resource.ts","./src/modules/configuration/atributovalores/tool.ts","./src/modules/configuration/estadodocumentos/index.ts","./src/modules/configuration/estadodocumentos/resource.ts","./src/modules/configuration/estadodocumentos/tool.ts","./src/modules/configuration/fabricantes/index.ts","./src/modules/configuration/fabricantes/resource.ts","./src/modules/configuration/fabricantes/tool.ts","./src/modules/configuration/familias/index.ts","./src/modules/configuration/familias/resource.ts","./src/modules/configuration/familias/tool.ts","./src/modules/configuration/formapagos/index.ts","./src/modules/configuration/formapagos/resource.ts","./src/modules/configuration/formapagos/tool.ts","./src/modules/configuration/formatodocumentos/index.ts","./src/modules/configuration/formatodocumentos/resource.ts","./src/modules/configuration/formatodocumentos/tool.ts","./src/modules/configuration/grupoclientes/index.ts","./src/modules/configuration/grupoclientes/resource.ts","./src/modules/configuration/grupoclientes/tool.ts","./src/modules/configuration/identificadorfiscales/index.ts","./src/modules/configuration/identificadorfiscales/resource.ts","./src/modules/configuration/identificadorfiscales/tool.ts","./src/modules/configuration/impuestos/index.ts","./src/modules/configuration/impuestos/resource.ts","./src/modules/configuration/impuestos/tool.ts","./src/modules/configuration/impuestozonas/index.ts","./src/modules/configuration/impuestozonas/resource.ts","./src/modules/configuration/impuestozonas/tool.ts","./src/modules/configuration/regularizacionimpuestos/index.ts","./src/modules/configuration/regularizacionimpuestos/resource.ts","./src/modules/configuration/regularizacionimpuestos/tool.ts","./src/modules/configuration/retenciones/index.ts","./src/modules/configuration/retenciones/resource.ts","./src/modules/configuration/retenciones/tool.ts","./src/modules/configuration/secuenciadocumentos/index.ts","./src/modules/configuration/secuenciadocumentos/resource.ts","./src/modules/configuration/secuenciadocumentos/tool.ts","./src/modules/configuration/series/index.ts","./src/modules/configuration/series/resource.ts","./src/modules/configuration/series/tool.ts","./src/modules/configuration/tarifas/index.ts","./src/modules/configuration/tarifas/resource.ts","./src/modules/configuration/tarifas/tool.ts","./src/modules/core-business/index.ts","./src/modules/core-business/clientes/index.ts","./src/modules/core-business/clientes/resource.ts","./src/modules/core-business/clientes/tool.ts","./src/modules/core-business/productoimagenes/index.ts","./src/modules/core-business/productoimagenes/resource.ts","./src/modules/core-business/productoimagenes/tool.ts","./src/modules/core-business/productos/index.ts","./src/modules/core-business/productos/resource.ts","./src/modules/core-business/productos/tool.ts","./src/modules/core-business/proveedores/index.ts","./src/modules/core-business/proveedores/resource.ts","./src/modules/core-business/proveedores/tool.ts","./src/modules/core-business/stocks/index.ts","./src/modules/core-business/stocks/resource.ts","./src/modules/core-business/stocks/tool.ts","./src/modules/core-business/variantes/index.ts","./src/modules/core-business/variantes/resource.ts","./src/modules/core-business/variantes/tool.ts","./src/modules/finance/index.ts","./src/modules/finance/cuentabancoclientes/index.ts","./src/modules/finance/cuentabancoclientes/resource.ts","./src/modules/finance/cuentabancoclientes/tool.ts","./src/modules/finance/cuentabancoproveedores/index.ts","./src/modules/finance/cuentabancoproveedores/resource.ts","./src/modules/finance/cuentabancoproveedores/tool.ts","./src/modules/finance/cuentabancos/index.ts","./src/modules/finance/cuentabancos/resource.ts","./src/modules/finance/cuentabancos/tool.ts","./src/modules/finance/cuentaespeciales/index.ts","./src/modules/finance/cuentaespeciales/resource.ts","./src/modules/finance/cuentaespeciales/tool.ts","./src/modules/finance/divisas/index.ts","./src/modules/finance/divisas/resource.ts","./src/modules/finance/divisas/tool.ts","./src/modules/finance/pagoclientes/index.ts","./src/modules/finance/pagoclientes/resource.ts","./src/modules/finance/pagoclientes/tool.ts","./src/modules/finance/pagoproveedores/index.ts","./src/modules/finance/pagoproveedores/resource.ts","./src/modules/finance/pagoproveedores/tool.ts","./src/modules/finance/reciboclientes/index.ts","./src/modules/finance/reciboclientes/resource.ts","./src/modules/finance/reciboclientes/tool.ts","./src/modules/finance/reciboproveedores/index.ts","./src/modules/finance/reciboproveedores/resource.ts","./src/modules/finance/reciboproveedores/tool.ts","./src/modules/geographic/index.ts","./src/modules/geographic/ciudades/index.ts","./src/modules/geographic/ciudades/resource.ts","./src/modules/geographic/ciudades/tool.ts","./src/modules/geographic/codigopostales/index.ts","./src/modules/geographic/codigopostales/resource.ts","./src/modules/geographic/codigopostales/tool.ts","./src/modules/geographic/empresas/index.ts","./src/modules/geographic/empresas/resource.ts","./src/modules/geographic/empresas/tool.ts","./src/modules/geographic/pais/index.ts","./src/modules/geographic/pais/resource.ts","./src/modules/geographic/pais/tool.ts","./src/modules/geographic/provincias/index.ts","./src/modules/geographic/provincias/resource.ts","./src/modules/geographic/provincias/tool.ts","./src/modules/geographic/puntointeresciudades/index.ts","./src/modules/geographic/puntointeresciudades/resource.ts","./src/modules/geographic/puntointeresciudades/tool.ts","./src/modules/purchasing/index.ts","./src/modules/purchasing/albaranproveedores/index.ts","./src/modules/purchasing/albaranproveedores/resource.ts","./src/modules/purchasing/albaranproveedores/tool.ts","./src/modules/purchasing/facturaproveedores/index.ts","./src/modules/purchasing/facturaproveedores/resource.ts","./src/modules/purchasing/facturaproveedores/tool.ts","./src/modules/purchasing/pedidoproveedores/index.ts","./src/modules/purchasing/pedidoproveedores/resource.ts","./src/modules/purchasing/pedidoproveedores/tool.ts","./src/modules/purchasing/presupuestoproveedores/index.ts","./src/modules/purchasing/presupuestoproveedores/resource.ts","./src/modules/purchasing/presupuestoproveedores/tool.ts","./src/modules/purchasing/productoproveedores/index.ts","./src/modules/purchasing/productoproveedores/resource.ts","./src/modules/purchasing/productoproveedores/tool.ts","./src/modules/sales-orders/index.ts","./src/modules/sales-orders/albaranclientes/index.ts","./src/modules/sales-orders/albaranclientes/resource.ts","./src/modules/sales-orders/albaranclientes/tool.ts","./src/modules/sales-orders/facturaclientes/index.ts","./src/modules/sales-orders/facturaclientes/resource.ts","./src/modules/sales-orders/facturaclientes/tool-tiempo-beneficios-bulk.ts","./src/modules/sales-orders/facturaclientes/tool-tiempo-beneficios.ts","./src/modules/sales-orders/facturaclientes/tool.ts","./src/modules/sales-orders/line-items/index.ts","./src/modules/sales-orders/line-items/lineaalbaranclientes/index.ts","./src/modules/sales-orders/line-items/lineaalbaranclientes/resource.ts","./src/modules/sales-orders/line-items/lineaalbaranclientes/tool.ts","./src/modules/sales-orders/line-items/lineaalbaranproveedores/index.ts","./src/modules/sales-orders/line-items/lineaalbaranproveedores/resource.ts","./src/modules/sales-orders/line-items/lineaalbaranproveedores/tool.ts","./src/modules/sales-orders/line-items/lineafacturaclientes/index.ts","./src/modules/sales-orders/line-items/lineafacturaclientes/resource.ts","./src/modules/sales-orders/line-items/lineafacturaclientes/tool.ts","./src/modules/sales-orders/line-items/lineafacturaproveedores/index.ts","./src/modules/sales-orders/line-items/lineafacturaproveedores/resource.ts","./src/modules/sales-orders/line-items/lineafacturaproveedores/tool.ts","./src/modules/sales-orders/line-items/lineapedidoclientes/index.ts","./src/modules/sales-orders/line-items/lineapedidoclientes/resource.ts","./src/modules/sales-orders/line-items/lineapedidoclientes/tool.ts","./src/modules/sales-orders/line-items/lineapedidoproveedores/index.ts","./src/modules/sales-orders/line-items/lineapedidoproveedores/resource.ts","./src/modules/sales-orders/line-items/lineapedidoproveedores/tool.ts","./src/modules/sales-orders/line-items/lineapresupuestoclientes/index.ts","./src/modules/sales-orders/line-items/lineapresupuestoclientes/resource.ts","./src/modules/sales-orders/line-items/lineapresupuestoclientes/tool.ts","./src/modules/sales-orders/line-items/lineapresupuestoproveedores/index.ts","./src/modules/sales-orders/line-items/lineapresupuestoproveedores/resource.ts","./src/modules/sales-orders/line-items/lineapresupuestoproveedores/tool.ts","./src/modules/sales-orders/pedidoclientes/index.ts","./src/modules/sales-orders/pedidoclientes/resource.ts","./src/modules/sales-orders/pedidoclientes/tool.ts","./src/modules/sales-orders/presupuestoclientes/index.ts","./src/modules/sales-orders/presupuestoclientes/resource.ts","./src/modules/sales-orders/presupuestoclientes/tool.ts","./src/modules/system/index.ts","./src/modules/system/apiaccess/index.ts","./src/modules/system/apiaccess/resource.ts","./src/modules/system/apiaccess/tool.ts","./src/modules/system/apikeyes/index.ts","./src/modules/system/apikeyes/resource.ts","./src/modules/system/apikeyes/tool.ts","./src/modules/system/attachedfilerelations/index.ts","./src/modules/system/attachedfilerelations/resource.ts","./src/modules/system/attachedfilerelations/tool.ts","./src/modules/system/attachedfiles/index.ts","./src/modules/system/attachedfiles/resource.ts","./src/modules/system/attachedfiles/tool.ts","./src/modules/system/cronjobes/index.ts","./src/modules/system/cronjobes/resource.ts","./src/modules/system/cronjobes/tool.ts","./src/modules/system/doctransformations/index.ts","./src/modules/system/doctransformations/resource.ts","./src/modules/system/doctransformations/tool.ts","./src/modules/system/logmessages/index.ts","./src/modules/system/logmessages/resource.ts","./src/modules/system/logmessages/tool.ts","./src/modules/system/pagefilteres/index.ts","./src/modules/system/pagefilteres/resource.ts","./src/modules/system/pagefilteres/tool.ts","./src/modules/system/pages/index.ts","./src/modules/system/pages/resource.ts","./src/modules/system/pages/tool.ts","./src/modules/system/totalmodeles/index.ts","./src/modules/system/totalmodeles/resource.ts","./src/modules/system/totalmodeles/tool.ts","./src/modules/system/workeventes/index.ts","./src/modules/system/workeventes/resource.ts","./src/modules/system/workeventes/tool.ts","./src/types/facturascripts.ts","./src/utils/filterparser.ts"],"version":"5.9.2"} \ No newline at end of file