farseer-cli 1.0.0
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 +15 -0
- package/README.md +741 -0
- package/dist/commands/app.d.ts +2 -0
- package/dist/commands/app.js +349 -0
- package/dist/commands/app.js.map +7 -0
- package/dist/commands/apps.d.ts +2 -0
- package/dist/commands/apps.js +111 -0
- package/dist/commands/apps.js.map +7 -0
- package/dist/commands/checkout.d.ts +2 -0
- package/dist/commands/checkout.js +166 -0
- package/dist/commands/checkout.js.map +7 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +139 -0
- package/dist/commands/config.js.map +7 -0
- package/dist/commands/diff.d.ts +2 -0
- package/dist/commands/diff.js +183 -0
- package/dist/commands/diff.js.map +7 -0
- package/dist/commands/files.js +99 -0
- package/dist/commands/files.js.map +7 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +79 -0
- package/dist/commands/install.js.map +7 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +92 -0
- package/dist/commands/list.js.map +7 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +134 -0
- package/dist/commands/login.js.map +7 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.js +59 -0
- package/dist/commands/logout.js.map +7 -0
- package/dist/commands/mcp-server.d.ts +8 -0
- package/dist/commands/mcp-server.js +41 -0
- package/dist/commands/mcp-server.js.map +7 -0
- package/dist/commands/model.d.ts +2 -0
- package/dist/commands/model.js +189 -0
- package/dist/commands/model.js.map +7 -0
- package/dist/commands/pull.d.ts +2 -0
- package/dist/commands/pull.js +287 -0
- package/dist/commands/pull.js.map +7 -0
- package/dist/commands/push.d.ts +2 -0
- package/dist/commands/push.js +251 -0
- package/dist/commands/push.js.map +7 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +246 -0
- package/dist/commands/run.js.map +7 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +137 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +145 -0
- package/dist/commands/status.js.map +7 -0
- package/dist/commands/unsetup.d.ts +2 -0
- package/dist/commands/unsetup.js +122 -0
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.js +63 -0
- package/dist/commands/whoami.js.map +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +7 -0
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.js +35 -0
- package/dist/mcp/index.js.map +7 -0
- package/dist/mcp/prompts/workflows.d.ts +7 -0
- package/dist/mcp/prompts/workflows.js +374 -0
- package/dist/mcp/prompts/workflows.js.map +7 -0
- package/dist/mcp/resources/documentation.d.ts +8 -0
- package/dist/mcp/resources/documentation.js +167 -0
- package/dist/mcp/resources/documentation.js.map +7 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +49 -0
- package/dist/mcp/server.js.map +7 -0
- package/dist/mcp/tools/appTools.d.ts +7 -0
- package/dist/mcp/tools/appTools.js +377 -0
- package/dist/mcp/tools/appTools.js.map +7 -0
- package/dist/mcp/tools/authTools.d.ts +7 -0
- package/dist/mcp/tools/authTools.js +158 -0
- package/dist/mcp/tools/authTools.js.map +7 -0
- package/dist/mcp/tools/modelTools.d.ts +7 -0
- package/dist/mcp/tools/modelTools.js +331 -0
- package/dist/mcp/tools/modelTools.js.map +7 -0
- package/dist/mcp/tools/runTools.d.ts +7 -0
- package/dist/mcp/tools/runTools.js +231 -0
- package/dist/mcp/tools/runTools.js.map +7 -0
- package/dist/mcp/tools/syncTools.d.ts +7 -0
- package/dist/mcp/tools/syncTools.js +382 -0
- package/dist/mcp/tools/syncTools.js.map +7 -0
- package/dist/mcp/utils/helpers.d.ts +69 -0
- package/dist/mcp/utils/helpers.js +113 -0
- package/dist/mcp/utils/helpers.js.map +7 -0
- package/dist/services/appSyncService.d.ts +75 -0
- package/dist/services/appSyncService.js +370 -0
- package/dist/services/appSyncService.js.map +7 -0
- package/dist/services/configService.d.ts +39 -0
- package/dist/services/configService.js +196 -0
- package/dist/services/configService.js.map +7 -0
- package/dist/services/farseerApi.d.ts +166 -0
- package/dist/services/farseerApi.js +378 -0
- package/dist/services/farseerApi.js.map +7 -0
- package/dist/services/farseerFactory.d.ts +88 -0
- package/dist/services/farseerFactory.js +179 -0
- package/dist/services/farseerFactory.js.map +7 -0
- package/dist/services/farseerService.d.ts +96 -0
- package/dist/services/farseerService.js +614 -0
- package/dist/services/farseerService.js.map +7 -0
- package/dist/services/gitService.d.ts +31 -0
- package/dist/services/gitService.js +134 -0
- package/dist/services/gitService.js.map +7 -0
- package/dist/services/syncService.d.ts +44 -0
- package/dist/services/syncService.js +320 -0
- package/dist/services/syncService.js.map +7 -0
- package/dist/utils/constants.d.ts +7 -0
- package/dist/utils/constants.js +46 -0
- package/dist/utils/constants.js.map +7 -0
- package/dist/utils/helpers.d.ts +69 -0
- package/dist/utils/helpers.js +413 -0
- package/dist/utils/helpers.js.map +7 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.js +76 -0
- package/dist/utils/logger.js.map +7 -0
- package/package.json +62 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mcp/tools/appTools.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * App Management Tools\n *\n * Tools for managing Farseer apps (Remote Jobs).\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { getFarseerClientWithFallback } from '../../services/farseerFactory';\nimport { getCurrentCheckout } from '../../services/configService';\nimport {\n successResponse,\n errorResponse,\n authRequiredResponse,\n tenantRequiredResponse,\n} from '../utils/helpers';\n\n// Define schemas outside of tool registration to avoid deep type instantiation\nconst listAppsSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n details: z.boolean().optional().describe('Include full details for each app'),\n};\n\nconst getAppSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n appName: z.string().describe('Name of the app (can include spaces)'),\n};\n\nconst createAppSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n appName: z.string().describe('Name for the new app'),\n};\n\nconst argumentSchema = z.object({\n name: z.string(),\n defaultValue: z.string(),\n});\n\nconst configureAppSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n appName: z.string().describe('Name of the app to configure'),\n entrypoint: z.string().optional().describe('Main script filename (e.g., \"index.ts\")'),\n scripts: z.array(z.string()).optional().describe('List of script filenames to include'),\n description: z.string().optional().describe('App description'),\n arguments: z.array(argumentSchema).optional().describe('App arguments with default values'),\n newName: z.string().optional().describe('Rename the app'),\n};\n\nconst deleteAppSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n appName: z.string().describe('Name of the app to delete'),\n};\n\nexport function registerAppTools(server: McpServer): void {\n // farseer_list_apps - List all apps\n server.tool(\n 'farseer_list_apps',\n `List all apps (Remote Jobs) configured on a Farseer tenant.\n\nApps are executable script configurations that include:\n- Entry point script\n- List of included scripts\n- Expected arguments with default values\n\nUse details=true to include full information for each app.\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n listAppsSchema,\n async (params: { tenant?: string; details?: boolean }) => {\n const { tenant: tenantArg, details } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n const appList = await clientResult.client.listApps();\n\n if (!details) {\n return successResponse({\n tenant,\n totalApps: appList.length,\n apps: appList.map((a) => ({\n name: a.name,\n id: a.id,\n reference: a.reference,\n })),\n });\n }\n\n // Get full details for each app\n const appsWithDetails = [];\n for (const item of appList) {\n const app = await clientResult.client.getApp(item.reference);\n if (app) {\n const mainScript = app.scriptFiles.find((s) => s.id === app.mainScriptFileId);\n appsWithDetails.push({\n name: app.name,\n id: app.id,\n description: app.description || '',\n entrypoint: mainScript?.name || null,\n scripts: app.scriptFiles.map((s) => s.name),\n arguments: app.expectedArguments.map((a) => ({\n name: a.name,\n defaultValue: a.defaultValue,\n })),\n });\n }\n }\n\n return successResponse({\n tenant,\n totalApps: appsWithDetails.length,\n apps: appsWithDetails,\n });\n } catch (error) {\n return errorResponse(\n `List apps failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n\n // farseer_get_app - Get app details\n server.tool(\n 'farseer_get_app',\n `Get detailed information about a specific Farseer app.\n\nReturns:\n- App name and description\n- Entry point script (main script that runs)\n- List of all included scripts\n- Expected arguments with their default values\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n getAppSchema,\n async (params: { tenant?: string; appName: string }) => {\n const { tenant: tenantArg, appName } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n const app = await clientResult.client.getAppByName(appName);\n\n if (!app) {\n return errorResponse(`App \"${appName}\" not found`, 'NOT_FOUND');\n }\n\n const mainScript = app.scriptFiles.find((s) => s.id === app.mainScriptFileId);\n\n return successResponse({\n tenant,\n app: {\n id: app.id,\n name: app.name,\n description: app.description || '',\n entrypoint: mainScript?.name || null,\n scripts: app.scriptFiles.map((s) => ({\n id: s.id,\n name: s.name,\n isEntrypoint: s.id === app.mainScriptFileId,\n })),\n arguments: app.expectedArguments.map((a) => ({\n name: a.name,\n type: a.type,\n defaultValue: a.defaultValue,\n })),\n status: app.status,\n lastHeartbeat: app.lastHeartbeat,\n },\n });\n } catch (error) {\n return errorResponse(\n `Get app failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n\n // farseer_create_app - Create new app\n server.tool(\n 'farseer_create_app',\n `Create a new app (Remote Job) on a Farseer tenant.\n\nThis creates an empty app shell. After creation, use farseer_configure_app to:\n- Set the entry point script\n- Add scripts to the app\n- Configure arguments\n\nScripts must exist on the remote (push them first if they're new).\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n createAppSchema,\n async (params: { tenant?: string; appName: string }) => {\n const { tenant: tenantArg, appName } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n // Check if app already exists\n const existing = await clientResult.client.getAppByName(appName);\n if (existing) {\n return errorResponse(`App \"${appName}\" already exists`, 'VALIDATION_ERROR');\n }\n\n const app = await clientResult.client.createApp(appName);\n\n return successResponse({\n tenant,\n app: {\n id: app.id,\n name: app.name,\n },\n message: `App \"${appName}\" created. Use farseer_configure_app to set entrypoint, scripts, and arguments.`,\n });\n } catch (error) {\n return errorResponse(\n `Create app failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n\n // farseer_configure_app - Configure app settings\n server.tool(\n 'farseer_configure_app',\n `Configure an existing Farseer app.\n\nYou can set:\n- entrypoint: Main script to execute (e.g., \"index.ts\")\n- scripts: List of scripts to include in the app\n- description: App description\n- arguments: List of expected arguments with default values\n- newName: Rename the app\n\nImportant:\n- Scripts must exist on the remote (push them first)\n- If you set scripts but not entrypoint, the first script becomes entrypoint\n- Arguments are passed to the script via command line\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n configureAppSchema,\n async (params: {\n tenant?: string;\n appName: string;\n entrypoint?: string;\n scripts?: string[];\n description?: string;\n arguments?: Array<{ name: string; defaultValue: string }>;\n newName?: string;\n }) => {\n const { tenant: tenantArg, appName, entrypoint, scripts, description, arguments: args, newName } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n const app = await clientResult.client.getAppByName(appName);\n if (!app) {\n return errorResponse(`App \"${appName}\" not found`, 'NOT_FOUND');\n }\n\n // Get available scripts for ID lookup\n const availableScripts = await clientResult.client.getScriptFiles();\n const scriptIdMap = new Map(availableScripts.map((s) => [s.name.toLowerCase(), s.id]));\n\n // Build update payload\n const update: {\n name?: string;\n description?: string;\n expectedArguments?: Array<{ name: string; type: 'variable'; defaultValue: string }>;\n mainScriptFileId?: string | null;\n scriptFileIds?: string[];\n } = {};\n\n if (newName) {\n update.name = newName;\n }\n\n if (description !== undefined) {\n update.description = description;\n }\n\n if (args) {\n update.expectedArguments = args.map((a) => ({\n name: a.name,\n type: 'variable' as const,\n defaultValue: a.defaultValue,\n }));\n }\n\n if (scripts && scripts.length > 0) {\n const scriptIds: string[] = [];\n const notFound: string[] = [];\n\n for (const scriptName of scripts) {\n const id = scriptIdMap.get(scriptName.toLowerCase());\n if (id) {\n scriptIds.push(id);\n } else {\n notFound.push(scriptName);\n }\n }\n\n if (notFound.length > 0) {\n return errorResponse(\n `Scripts not found on remote: ${notFound.join(', ')}. Push them first.`,\n 'VALIDATION_ERROR'\n );\n }\n\n update.scriptFileIds = scriptIds;\n\n // Set entrypoint to first script if not explicitly specified\n if (!entrypoint && scriptIds.length > 0) {\n update.mainScriptFileId = scriptIds[0];\n }\n }\n\n if (entrypoint) {\n const entrypointId = scriptIdMap.get(entrypoint.toLowerCase());\n if (!entrypointId) {\n return errorResponse(\n `Entrypoint script \"${entrypoint}\" not found on remote. Push it first.`,\n 'VALIDATION_ERROR'\n );\n }\n update.mainScriptFileId = entrypointId;\n }\n\n // Apply update\n const reference = app.id.toString();\n await clientResult.client.updateApp(reference, update);\n\n return successResponse({\n tenant,\n appName: newName || appName,\n updated: Object.keys(update),\n message: `App \"${appName}\" configured successfully`,\n });\n } catch (error) {\n return errorResponse(\n `Configure app failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n\n // farseer_delete_app - Delete app\n server.tool(\n 'farseer_delete_app',\n `Delete an app from a Farseer tenant.\n\nWARNING: This is irreversible - the app will be permanently removed from the remote instance.\n\nThis does NOT delete the script files, only the app configuration.\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n deleteAppSchema,\n async (params: { tenant?: string; appName: string }) => {\n const { tenant: tenantArg, appName } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n const app = await clientResult.client.getAppByName(appName);\n if (!app) {\n return errorResponse(`App \"${appName}\" not found`, 'NOT_FOUND');\n }\n\n await clientResult.client.deleteApp(app.id.toString());\n\n return successResponse({\n tenant,\n deletedApp: appName,\n message: `App \"${appName}\" has been deleted`,\n });\n } catch (error) {\n return errorResponse(\n `Delete app failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,iBAAkB;AAClB,4BAA6C;AAC7C,2BAAmC;AACnC,qBAKO;AAGP,MAAM,iBAAiB;AAAA,EACnB,QAAQ,aAAE,OAAO,EAAE,SAAS,EAAE,SAAS,aAAa;AAAA,EACpD,SAAS,aAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,mCAAmC;AAChF;AAEA,MAAM,eAAe;AAAA,EACjB,QAAQ,aAAE,OAAO,EAAE,SAAS,EAAE,SAAS,aAAa;AAAA,EACpD,SAAS,aAAE,OAAO,EAAE,SAAS,sCAAsC;AACvE;AAEA,MAAM,kBAAkB;AAAA,EACpB,QAAQ,aAAE,OAAO,EAAE,SAAS,EAAE,SAAS,aAAa;AAAA,EACpD,SAAS,aAAE,OAAO,EAAE,SAAS,sBAAsB;AACvD;AAEA,MAAM,iBAAiB,aAAE,OAAO;AAAA,EAC5B,MAAM,aAAE,OAAO;AAAA,EACf,cAAc,aAAE,OAAO;AAC3B,CAAC;AAED,MAAM,qBAAqB;AAAA,EACvB,QAAQ,aAAE,OAAO,EAAE,SAAS,EAAE,SAAS,aAAa;AAAA,EACpD,SAAS,aAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,EAC3D,YAAY,aAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,EACpF,SAAS,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,qCAAqC;AAAA,EACtF,aAAa,aAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iBAAiB;AAAA,EAC7D,WAAW,aAAE,MAAM,cAAc,EAAE,SAAS,EAAE,SAAS,mCAAmC;AAAA,EAC1F,SAAS,aAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gBAAgB;AAC5D;AAEA,MAAM,kBAAkB;AAAA,EACpB,QAAQ,aAAE,OAAO,EAAE,SAAS,EAAE,SAAS,aAAa;AAAA,EACpD,SAAS,aAAE,OAAO,EAAE,SAAS,2BAA2B;AAC5D;AAEO,SAAS,iBAAiB,QAAyB;AAEtD,SAAO;AAAA,IACH;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA;AAAA,IACA,OAAO,WAAmD;AACtD,YAAM,EAAE,QAAQ,WAAW,QAAQ,IAAI;AACvC,YAAM,eAAW,yCAAmB;AACpC,YAAM,SAAS,aAAa,UAAU;AACtC,YAAM,eAAe,aAAa,UAAU;AAE5C,UAAI,CAAC,UAAU,CAAC,cAAc;AAC1B,mBAAO,uCAAuB;AAAA,MAClC;AAEA,YAAM,eAAe,UAAM,oDAA6B,cAAc,MAAM;AAC5E,UAAI,CAAC,cAAc;AACf,mBAAO,qCAAqB,MAAM;AAAA,MACtC;AAEA,UAAI;AACA,cAAM,UAAU,MAAM,aAAa,OAAO,SAAS;AAEnD,YAAI,CAAC,SAAS;AACV,qBAAO,gCAAgB;AAAA,YACnB;AAAA,YACA,WAAW,QAAQ;AAAA,YACnB,MAAM,QAAQ,IAAI,CAAC,OAAO;AAAA,cACtB,MAAM,EAAE;AAAA,cACR,IAAI,EAAE;AAAA,cACN,WAAW,EAAE;AAAA,YACjB,EAAE;AAAA,UACN,CAAC;AAAA,QACL;AAGA,cAAM,kBAAkB,CAAC;AACzB,mBAAW,QAAQ,SAAS;AACxB,gBAAM,MAAM,MAAM,aAAa,OAAO,OAAO,KAAK,SAAS;AAC3D,cAAI,KAAK;AACL,kBAAM,aAAa,IAAI,YAAY,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,gBAAgB;AAC5E,4BAAgB,KAAK;AAAA,cACjB,MAAM,IAAI;AAAA,cACV,IAAI,IAAI;AAAA,cACR,aAAa,IAAI,eAAe;AAAA,cAChC,YAAY,YAAY,QAAQ;AAAA,cAChC,SAAS,IAAI,YAAY,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,cAC1C,WAAW,IAAI,kBAAkB,IAAI,CAAC,OAAO;AAAA,gBACzC,MAAM,EAAE;AAAA,gBACR,cAAc,EAAE;AAAA,cACpB,EAAE;AAAA,YACN,CAAC;AAAA,UACL;AAAA,QACJ;AAEA,mBAAO,gCAAgB;AAAA,UACnB;AAAA,UACA,WAAW,gBAAgB;AAAA,UAC3B,MAAM;AAAA,QACV,CAAC;AAAA,MACL,SAAS,OAAO;AACZ,mBAAO;AAAA,UACH,qBAAqB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,UAC7E;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAGA,SAAO;AAAA,IACH;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA;AAAA,IACA,OAAO,WAAiD;AACpD,YAAM,EAAE,QAAQ,WAAW,QAAQ,IAAI;AACvC,YAAM,eAAW,yCAAmB;AACpC,YAAM,SAAS,aAAa,UAAU;AACtC,YAAM,eAAe,aAAa,UAAU;AAE5C,UAAI,CAAC,UAAU,CAAC,cAAc;AAC1B,mBAAO,uCAAuB;AAAA,MAClC;AAEA,YAAM,eAAe,UAAM,oDAA6B,cAAc,MAAM;AAC5E,UAAI,CAAC,cAAc;AACf,mBAAO,qCAAqB,MAAM;AAAA,MACtC;AAEA,UAAI;AACA,cAAM,MAAM,MAAM,aAAa,OAAO,aAAa,OAAO;AAE1D,YAAI,CAAC,KAAK;AACN,qBAAO,8BAAc,QAAQ,OAAO,eAAe,WAAW;AAAA,QAClE;AAEA,cAAM,aAAa,IAAI,YAAY,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,gBAAgB;AAE5E,mBAAO,gCAAgB;AAAA,UACnB;AAAA,UACA,KAAK;AAAA,YACD,IAAI,IAAI;AAAA,YACR,MAAM,IAAI;AAAA,YACV,aAAa,IAAI,eAAe;AAAA,YAChC,YAAY,YAAY,QAAQ;AAAA,YAChC,SAAS,IAAI,YAAY,IAAI,CAAC,OAAO;AAAA,cACjC,IAAI,EAAE;AAAA,cACN,MAAM,EAAE;AAAA,cACR,cAAc,EAAE,OAAO,IAAI;AAAA,YAC/B,EAAE;AAAA,YACF,WAAW,IAAI,kBAAkB,IAAI,CAAC,OAAO;AAAA,cACzC,MAAM,EAAE;AAAA,cACR,MAAM,EAAE;AAAA,cACR,cAAc,EAAE;AAAA,YACpB,EAAE;AAAA,YACF,QAAQ,IAAI;AAAA,YACZ,eAAe,IAAI;AAAA,UACvB;AAAA,QACJ,CAAC;AAAA,MACL,SAAS,OAAO;AACZ,mBAAO;AAAA,UACH,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,UAC3E;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAGA,SAAO;AAAA,IACH;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA;AAAA,IACA,OAAO,WAAiD;AACpD,YAAM,EAAE,QAAQ,WAAW,QAAQ,IAAI;AACvC,YAAM,eAAW,yCAAmB;AACpC,YAAM,SAAS,aAAa,UAAU;AACtC,YAAM,eAAe,aAAa,UAAU;AAE5C,UAAI,CAAC,UAAU,CAAC,cAAc;AAC1B,mBAAO,uCAAuB;AAAA,MAClC;AAEA,YAAM,eAAe,UAAM,oDAA6B,cAAc,MAAM;AAC5E,UAAI,CAAC,cAAc;AACf,mBAAO,qCAAqB,MAAM;AAAA,MACtC;AAEA,UAAI;AAEA,cAAM,WAAW,MAAM,aAAa,OAAO,aAAa,OAAO;AAC/D,YAAI,UAAU;AACV,qBAAO,8BAAc,QAAQ,OAAO,oBAAoB,kBAAkB;AAAA,QAC9E;AAEA,cAAM,MAAM,MAAM,aAAa,OAAO,UAAU,OAAO;AAEvD,mBAAO,gCAAgB;AAAA,UACnB;AAAA,UACA,KAAK;AAAA,YACD,IAAI,IAAI;AAAA,YACR,MAAM,IAAI;AAAA,UACd;AAAA,UACA,SAAS,QAAQ,OAAO;AAAA,QAC5B,CAAC;AAAA,MACL,SAAS,OAAO;AACZ,mBAAO;AAAA,UACH,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,UAC9E;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAGA,SAAO;AAAA,IACH;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAeA;AAAA,IACA,OAAO,WAQD;AACF,YAAM,EAAE,QAAQ,WAAW,SAAS,YAAY,SAAS,aAAa,WAAW,MAAM,QAAQ,IAAI;AACnG,YAAM,eAAW,yCAAmB;AACpC,YAAM,SAAS,aAAa,UAAU;AACtC,YAAM,eAAe,aAAa,UAAU;AAE5C,UAAI,CAAC,UAAU,CAAC,cAAc;AAC1B,mBAAO,uCAAuB;AAAA,MAClC;AAEA,YAAM,eAAe,UAAM,oDAA6B,cAAc,MAAM;AAC5E,UAAI,CAAC,cAAc;AACf,mBAAO,qCAAqB,MAAM;AAAA,MACtC;AAEA,UAAI;AACA,cAAM,MAAM,MAAM,aAAa,OAAO,aAAa,OAAO;AAC1D,YAAI,CAAC,KAAK;AACN,qBAAO,8BAAc,QAAQ,OAAO,eAAe,WAAW;AAAA,QAClE;AAGA,cAAM,mBAAmB,MAAM,aAAa,OAAO,eAAe;AAClE,cAAM,cAAc,IAAI,IAAI,iBAAiB,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,YAAY,GAAG,EAAE,EAAE,CAAC,CAAC;AAGrF,cAAM,SAMF,CAAC;AAEL,YAAI,SAAS;AACT,iBAAO,OAAO;AAAA,QAClB;AAEA,YAAI,gBAAgB,QAAW;AAC3B,iBAAO,cAAc;AAAA,QACzB;AAEA,YAAI,MAAM;AACN,iBAAO,oBAAoB,KAAK,IAAI,CAAC,OAAO;AAAA,YACxC,MAAM,EAAE;AAAA,YACR,MAAM;AAAA,YACN,cAAc,EAAE;AAAA,UACpB,EAAE;AAAA,QACN;AAEA,YAAI,WAAW,QAAQ,SAAS,GAAG;AAC/B,gBAAM,YAAsB,CAAC;AAC7B,gBAAM,WAAqB,CAAC;AAE5B,qBAAW,cAAc,SAAS;AAC9B,kBAAM,KAAK,YAAY,IAAI,WAAW,YAAY,CAAC;AACnD,gBAAI,IAAI;AACJ,wBAAU,KAAK,EAAE;AAAA,YACrB,OAAO;AACH,uBAAS,KAAK,UAAU;AAAA,YAC5B;AAAA,UACJ;AAEA,cAAI,SAAS,SAAS,GAAG;AACrB,uBAAO;AAAA,cACH,gCAAgC,SAAS,KAAK,IAAI,CAAC;AAAA,cACnD;AAAA,YACJ;AAAA,UACJ;AAEA,iBAAO,gBAAgB;AAGvB,cAAI,CAAC,cAAc,UAAU,SAAS,GAAG;AACrC,mBAAO,mBAAmB,UAAU,CAAC;AAAA,UACzC;AAAA,QACJ;AAEA,YAAI,YAAY;AACZ,gBAAM,eAAe,YAAY,IAAI,WAAW,YAAY,CAAC;AAC7D,cAAI,CAAC,cAAc;AACf,uBAAO;AAAA,cACH,sBAAsB,UAAU;AAAA,cAChC;AAAA,YACJ;AAAA,UACJ;AACA,iBAAO,mBAAmB;AAAA,QAC9B;AAGA,cAAM,YAAY,IAAI,GAAG,SAAS;AAClC,cAAM,aAAa,OAAO,UAAU,WAAW,MAAM;AAErD,mBAAO,gCAAgB;AAAA,UACnB;AAAA,UACA,SAAS,WAAW;AAAA,UACpB,SAAS,OAAO,KAAK,MAAM;AAAA,UAC3B,SAAS,QAAQ,OAAO;AAAA,QAC5B,CAAC;AAAA,MACL,SAAS,OAAO;AACZ,mBAAO;AAAA,UACH,yBAAyB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,UACjF;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAGA,SAAO;AAAA,IACH;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA;AAAA,IACA,OAAO,WAAiD;AACpD,YAAM,EAAE,QAAQ,WAAW,QAAQ,IAAI;AACvC,YAAM,eAAW,yCAAmB;AACpC,YAAM,SAAS,aAAa,UAAU;AACtC,YAAM,eAAe,aAAa,UAAU;AAE5C,UAAI,CAAC,UAAU,CAAC,cAAc;AAC1B,mBAAO,uCAAuB;AAAA,MAClC;AAEA,YAAM,eAAe,UAAM,oDAA6B,cAAc,MAAM;AAC5E,UAAI,CAAC,cAAc;AACf,mBAAO,qCAAqB,MAAM;AAAA,MACtC;AAEA,UAAI;AACA,cAAM,MAAM,MAAM,aAAa,OAAO,aAAa,OAAO;AAC1D,YAAI,CAAC,KAAK;AACN,qBAAO,8BAAc,QAAQ,OAAO,eAAe,WAAW;AAAA,QAClE;AAEA,cAAM,aAAa,OAAO,UAAU,IAAI,GAAG,SAAS,CAAC;AAErD,mBAAO,gCAAgB;AAAA,UACnB;AAAA,UACA,YAAY;AAAA,UACZ,SAAS,QAAQ,OAAO;AAAA,QAC5B,CAAC;AAAA,MACL,SAAS,OAAO;AACZ,mBAAO;AAAA,UACH,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,UAC9E;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var authTools_exports = {};
|
|
19
|
+
__export(authTools_exports, {
|
|
20
|
+
registerAuthTools: () => registerAuthTools
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(authTools_exports);
|
|
23
|
+
var import_zod = require("zod");
|
|
24
|
+
var import_configService = require("../../services/configService");
|
|
25
|
+
var import_farseerFactory = require("../../services/farseerFactory");
|
|
26
|
+
var import_syncService = require("../../services/syncService");
|
|
27
|
+
var import_appSyncService = require("../../services/appSyncService");
|
|
28
|
+
var import_helpers = require("../utils/helpers");
|
|
29
|
+
const checkoutSchema = {
|
|
30
|
+
organisation: import_zod.z.string().optional().describe("Farseer organisation (subdomain). If omitted with tenant, assumes org=tenant."),
|
|
31
|
+
tenant: import_zod.z.string().optional().describe("Tenant ID. If omitted, returns current checkout status."),
|
|
32
|
+
skipPull: import_zod.z.boolean().optional().describe("Skip automatic pull after checkout (default: false)")
|
|
33
|
+
};
|
|
34
|
+
function registerAuthTools(server) {
|
|
35
|
+
server.tool(
|
|
36
|
+
"farseer_whoami",
|
|
37
|
+
`Check Farseer authentication status.
|
|
38
|
+
|
|
39
|
+
Returns information about:
|
|
40
|
+
- Whether a JWT token is valid (from browser login)
|
|
41
|
+
- JWT token expiration time
|
|
42
|
+
- Currently checked-out tenant and organisation
|
|
43
|
+
- List of tenants with API keys configured
|
|
44
|
+
|
|
45
|
+
Use this before any operation to verify you have valid credentials.
|
|
46
|
+
If not authenticated, run "farseer login" in terminal.`,
|
|
47
|
+
{},
|
|
48
|
+
async () => {
|
|
49
|
+
const status = (0, import_helpers.getAuthStatus)();
|
|
50
|
+
return (0, import_helpers.successResponse)({
|
|
51
|
+
jwtValid: status.jwtValid,
|
|
52
|
+
jwtExpiresAt: status.jwtExpiresAt,
|
|
53
|
+
currentCheckout: status.currentCheckout,
|
|
54
|
+
configuredTenants: status.configuredTenants,
|
|
55
|
+
message: status.jwtValid ? "Authenticated via browser login (JWT)" : status.configuredTenants.length > 0 ? "Not authenticated via browser, but API keys are configured" : 'Not authenticated. Run "farseer login" to authenticate.'
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
server.tool(
|
|
60
|
+
"farseer_checkout",
|
|
61
|
+
`Set a default tenant for all subsequent Farseer operations, or get the current checkout status.
|
|
62
|
+
|
|
63
|
+
When setting a tenant:
|
|
64
|
+
- Verifies authentication works for that tenant
|
|
65
|
+
- Optionally pulls files and apps from remote
|
|
66
|
+
- Sets the tenant as default for all future commands
|
|
67
|
+
|
|
68
|
+
Organisation and tenant terminology:
|
|
69
|
+
- Organisation: subdomain of Farseer instance (e.g., "fs-personal" for fs-personal.farseer.io)
|
|
70
|
+
- Tenant: workspace ID sent as X-TENANT-ID header
|
|
71
|
+
|
|
72
|
+
Often org and tenant are the same (e.g., "tthotels"), but they can differ
|
|
73
|
+
(e.g., org="fs-personal", tenant="john-doe").
|
|
74
|
+
|
|
75
|
+
If you only provide tenant without organisation, it assumes org=tenant.`,
|
|
76
|
+
checkoutSchema,
|
|
77
|
+
async (params) => {
|
|
78
|
+
const { organisation, tenant, skipPull } = params;
|
|
79
|
+
if (!tenant) {
|
|
80
|
+
const current = (0, import_configService.getCurrentCheckout)();
|
|
81
|
+
if (current) {
|
|
82
|
+
return (0, import_helpers.successResponse)({
|
|
83
|
+
currentCheckout: current,
|
|
84
|
+
message: `Currently checked out: ${current.tenant} (org: ${current.organisation})`
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
return (0, import_helpers.successResponse)({
|
|
88
|
+
currentCheckout: null,
|
|
89
|
+
message: "No tenant currently checked out. Use tenant parameter to checkout."
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const org = organisation || tenant;
|
|
94
|
+
const clientResult = await (0, import_farseerFactory.getFarseerClientWithFallback)(org, tenant);
|
|
95
|
+
if (!clientResult) {
|
|
96
|
+
return (0, import_helpers.authRequiredResponse)(tenant);
|
|
97
|
+
}
|
|
98
|
+
if (org === tenant) {
|
|
99
|
+
(0, import_configService.setCurrentTenant)(tenant);
|
|
100
|
+
} else {
|
|
101
|
+
(0, import_configService.setCurrentCheckout)(org, tenant);
|
|
102
|
+
}
|
|
103
|
+
let pullResult = null;
|
|
104
|
+
let appPullResult = null;
|
|
105
|
+
if (!skipPull) {
|
|
106
|
+
try {
|
|
107
|
+
const syncService = new import_syncService.SyncService(org, clientResult.client);
|
|
108
|
+
pullResult = await syncService.pull();
|
|
109
|
+
if (clientResult.authType === "jwt") {
|
|
110
|
+
const appSyncService = new import_appSyncService.AppSyncService(org, clientResult.client);
|
|
111
|
+
appPullResult = await appSyncService.pull();
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return (0, import_helpers.successResponse)({
|
|
115
|
+
currentCheckout: { organisation: org, tenant },
|
|
116
|
+
message: `Checked out ${tenant}, but pull failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
117
|
+
pullError: true
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return (0, import_helpers.successResponse)({
|
|
122
|
+
currentCheckout: { organisation: org, tenant },
|
|
123
|
+
message: `Successfully checked out ${tenant}${skipPull ? "" : " and pulled latest files"}`,
|
|
124
|
+
files: pullResult ? {
|
|
125
|
+
downloaded: pullResult.downloaded,
|
|
126
|
+
deleted: pullResult.deleted,
|
|
127
|
+
unchanged: pullResult.unchanged.length
|
|
128
|
+
} : void 0,
|
|
129
|
+
apps: appPullResult ? {
|
|
130
|
+
created: appPullResult.created,
|
|
131
|
+
updated: appPullResult.updated,
|
|
132
|
+
deleted: appPullResult.deleted,
|
|
133
|
+
unchanged: appPullResult.unchanged.length
|
|
134
|
+
} : void 0
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
server.tool(
|
|
139
|
+
"farseer_checkout_clear",
|
|
140
|
+
`Clear the current tenant checkout.
|
|
141
|
+
|
|
142
|
+
After clearing, you'll need to specify tenant explicitly for all commands.`,
|
|
143
|
+
{},
|
|
144
|
+
async () => {
|
|
145
|
+
const current = (0, import_configService.getCurrentCheckout)();
|
|
146
|
+
(0, import_configService.clearCurrentTenant)();
|
|
147
|
+
return (0, import_helpers.successResponse)({
|
|
148
|
+
previousCheckout: current,
|
|
149
|
+
message: current ? `Cleared checkout for ${current.tenant}` : "No tenant was checked out"
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
155
|
+
0 && (module.exports = {
|
|
156
|
+
registerAuthTools
|
|
157
|
+
});
|
|
158
|
+
//# sourceMappingURL=authTools.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mcp/tools/authTools.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Authentication and Configuration Tools\n *\n * Tools for checking auth status and managing tenant checkout.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n getCurrentCheckout,\n setCurrentCheckout,\n setCurrentTenant,\n clearCurrentTenant,\n} from '../../services/configService';\nimport { getFarseerClientWithFallback } from '../../services/farseerFactory';\nimport { SyncService } from '../../services/syncService';\nimport { AppSyncService } from '../../services/appSyncService';\nimport {\n getAuthStatus,\n successResponse,\n errorResponse,\n authRequiredResponse,\n} from '../utils/helpers';\n\n// Define schemas outside of tool registration to avoid deep type instantiation\nconst checkoutSchema = {\n organisation: z\n .string()\n .optional()\n .describe('Farseer organisation (subdomain). If omitted with tenant, assumes org=tenant.'),\n tenant: z.string().optional().describe('Tenant ID. If omitted, returns current checkout status.'),\n skipPull: z.boolean().optional().describe('Skip automatic pull after checkout (default: false)'),\n};\n\nexport function registerAuthTools(server: McpServer): void {\n // farseer_whoami - Check authentication status\n server.tool(\n 'farseer_whoami',\n `Check Farseer authentication status.\n\nReturns information about:\n- Whether a JWT token is valid (from browser login)\n- JWT token expiration time\n- Currently checked-out tenant and organisation\n- List of tenants with API keys configured\n\nUse this before any operation to verify you have valid credentials.\nIf not authenticated, run \"farseer login\" in terminal.`,\n {},\n async () => {\n const status = getAuthStatus();\n return successResponse({\n jwtValid: status.jwtValid,\n jwtExpiresAt: status.jwtExpiresAt,\n currentCheckout: status.currentCheckout,\n configuredTenants: status.configuredTenants,\n message: status.jwtValid\n ? 'Authenticated via browser login (JWT)'\n : status.configuredTenants.length > 0\n ? 'Not authenticated via browser, but API keys are configured'\n : 'Not authenticated. Run \"farseer login\" to authenticate.',\n });\n }\n );\n\n // farseer_checkout - Set or get default tenant\n server.tool(\n 'farseer_checkout',\n `Set a default tenant for all subsequent Farseer operations, or get the current checkout status.\n\nWhen setting a tenant:\n- Verifies authentication works for that tenant\n- Optionally pulls files and apps from remote\n- Sets the tenant as default for all future commands\n\nOrganisation and tenant terminology:\n- Organisation: subdomain of Farseer instance (e.g., \"fs-personal\" for fs-personal.farseer.io)\n- Tenant: workspace ID sent as X-TENANT-ID header\n\nOften org and tenant are the same (e.g., \"tthotels\"), but they can differ\n(e.g., org=\"fs-personal\", tenant=\"john-doe\").\n\nIf you only provide tenant without organisation, it assumes org=tenant.`,\n checkoutSchema,\n async (params: { organisation?: string; tenant?: string; skipPull?: boolean }) => {\n const { organisation, tenant, skipPull } = params;\n\n // If no tenant provided, return current checkout status\n if (!tenant) {\n const current = getCurrentCheckout();\n if (current) {\n return successResponse({\n currentCheckout: current,\n message: `Currently checked out: ${current.tenant} (org: ${current.organisation})`,\n });\n } else {\n return successResponse({\n currentCheckout: null,\n message: 'No tenant currently checked out. Use tenant parameter to checkout.',\n });\n }\n }\n\n // Resolve organisation (default to tenant if not specified)\n const org = organisation || tenant;\n\n // Test connection before setting checkout\n const clientResult = await getFarseerClientWithFallback(org, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n // Set checkout\n if (org === tenant) {\n setCurrentTenant(tenant);\n } else {\n setCurrentCheckout(org, tenant);\n }\n\n let pullResult = null;\n let appPullResult = null;\n\n // Pull files and apps unless skipped\n if (!skipPull) {\n try {\n const syncService = new SyncService(org, clientResult.client);\n pullResult = await syncService.pull();\n\n // Pull apps if using JWT auth (API key doesn't support apps)\n if (clientResult.authType === 'jwt') {\n const appSyncService = new AppSyncService(org, clientResult.client);\n appPullResult = await appSyncService.pull();\n }\n } catch (error) {\n // Pull failed but checkout succeeded\n return successResponse({\n currentCheckout: { organisation: org, tenant },\n message: `Checked out ${tenant}, but pull failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n pullError: true,\n });\n }\n }\n\n return successResponse({\n currentCheckout: { organisation: org, tenant },\n message: `Successfully checked out ${tenant}${skipPull ? '' : ' and pulled latest files'}`,\n files: pullResult\n ? {\n downloaded: pullResult.downloaded,\n deleted: pullResult.deleted,\n unchanged: pullResult.unchanged.length,\n }\n : undefined,\n apps: appPullResult\n ? {\n created: appPullResult.created,\n updated: appPullResult.updated,\n deleted: appPullResult.deleted,\n unchanged: appPullResult.unchanged.length,\n }\n : undefined,\n });\n }\n );\n\n // farseer_checkout_clear - Clear current checkout\n server.tool(\n 'farseer_checkout_clear',\n `Clear the current tenant checkout.\n\nAfter clearing, you'll need to specify tenant explicitly for all commands.`,\n {},\n async () => {\n const current = getCurrentCheckout();\n clearCurrentTenant();\n\n return successResponse({\n previousCheckout: current,\n message: current\n ? `Cleared checkout for ${current.tenant}`\n : 'No tenant was checked out',\n });\n }\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var modelTools_exports = {};
|
|
19
|
+
__export(modelTools_exports, {
|
|
20
|
+
registerModelTools: () => registerModelTools
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(modelTools_exports);
|
|
23
|
+
var import_zod = require("zod");
|
|
24
|
+
var import_farseerFactory = require("../../services/farseerFactory");
|
|
25
|
+
var import_configService = require("../../services/configService");
|
|
26
|
+
var import_helpers = require("../utils/helpers");
|
|
27
|
+
const exportModelSchema = {
|
|
28
|
+
tenant: import_zod.z.string().optional().describe("Tenant name")
|
|
29
|
+
};
|
|
30
|
+
const listTablesSchema = {
|
|
31
|
+
tenant: import_zod.z.string().optional().describe("Tenant name")
|
|
32
|
+
};
|
|
33
|
+
const listVariablesSchema = {
|
|
34
|
+
tenant: import_zod.z.string().optional().describe("Tenant name")
|
|
35
|
+
};
|
|
36
|
+
const getTableSchema = {
|
|
37
|
+
tenant: import_zod.z.string().optional().describe("Tenant name"),
|
|
38
|
+
tableName: import_zod.z.string().describe("Name of the dimension table")
|
|
39
|
+
};
|
|
40
|
+
const getVariableSchema = {
|
|
41
|
+
tenant: import_zod.z.string().optional().describe("Tenant name"),
|
|
42
|
+
variableName: import_zod.z.string().describe("Name of the variable")
|
|
43
|
+
};
|
|
44
|
+
function registerModelTools(server) {
|
|
45
|
+
server.tool(
|
|
46
|
+
"farseer_export_model",
|
|
47
|
+
`Export the complete data model structure from a Farseer tenant.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
- All dimension tables with member counts and foreign key relationships
|
|
51
|
+
- All variables with their dimensions, formulas, and roll-up types
|
|
52
|
+
|
|
53
|
+
This is essential for understanding a tenant's data structure before writing import scripts.
|
|
54
|
+
|
|
55
|
+
The model export can be large for complex tenants. Use farseer_list_tables or
|
|
56
|
+
farseer_list_variables for a summary view.
|
|
57
|
+
|
|
58
|
+
If no tenant is specified, uses the currently checked-out tenant.`,
|
|
59
|
+
exportModelSchema,
|
|
60
|
+
async (params) => {
|
|
61
|
+
const { tenant: tenantArg } = params;
|
|
62
|
+
const checkout = (0, import_configService.getCurrentCheckout)();
|
|
63
|
+
const tenant = tenantArg || checkout?.tenant;
|
|
64
|
+
const organisation = tenantArg || checkout?.organisation;
|
|
65
|
+
if (!tenant || !organisation) {
|
|
66
|
+
return (0, import_helpers.tenantRequiredResponse)();
|
|
67
|
+
}
|
|
68
|
+
const clientResult = await (0, import_farseerFactory.getFarseerClientWithFallback)(organisation, tenant);
|
|
69
|
+
if (!clientResult) {
|
|
70
|
+
return (0, import_helpers.authRequiredResponse)(tenant);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const model = await clientResult.client.exportModel();
|
|
74
|
+
const tables = model.tables.map((t) => ({
|
|
75
|
+
name: t.name,
|
|
76
|
+
memberCount: t.rows?.length || 0,
|
|
77
|
+
columns: t.columns.map((c) => ({
|
|
78
|
+
type: c.type,
|
|
79
|
+
name: c.name,
|
|
80
|
+
foreignKeyTable: c.foreignKeyTableName
|
|
81
|
+
})),
|
|
82
|
+
foreignKeys: t.columns.filter((c) => c.type === "FOREIGN_KEY").map((c) => c.foreignKeyTableName),
|
|
83
|
+
folderPath: t.folderPath
|
|
84
|
+
}));
|
|
85
|
+
const variables = model.variables.map((v) => ({
|
|
86
|
+
name: v.name,
|
|
87
|
+
dimensions: v.tables || [],
|
|
88
|
+
formula: v.formula || void 0,
|
|
89
|
+
rollupType: v.rollupType,
|
|
90
|
+
numberFormat: v.numberFormat,
|
|
91
|
+
readonly: v.readonly,
|
|
92
|
+
calculateOnDemand: v.calculateOnDemand,
|
|
93
|
+
folderPath: v.folderPath
|
|
94
|
+
}));
|
|
95
|
+
return (0, import_helpers.successResponse)({
|
|
96
|
+
tenant,
|
|
97
|
+
summary: {
|
|
98
|
+
tableCount: tables.length,
|
|
99
|
+
variableCount: variables.length
|
|
100
|
+
},
|
|
101
|
+
tables,
|
|
102
|
+
variables
|
|
103
|
+
});
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return (0, import_helpers.errorResponse)(
|
|
106
|
+
`Export model failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
107
|
+
"NETWORK_ERROR"
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
server.tool(
|
|
113
|
+
"farseer_list_tables",
|
|
114
|
+
`List all dimension tables in a Farseer tenant model.
|
|
115
|
+
|
|
116
|
+
Dimension tables define the structure of data:
|
|
117
|
+
- Product, Region, Customer (business dimensions)
|
|
118
|
+
- Months, Years (time dimensions - built-in)
|
|
119
|
+
- Versions (scenario dimension - built-in)
|
|
120
|
+
|
|
121
|
+
Returns table names, member counts, and foreign key relationships.
|
|
122
|
+
|
|
123
|
+
If no tenant is specified, uses the currently checked-out tenant.`,
|
|
124
|
+
listTablesSchema,
|
|
125
|
+
async (params) => {
|
|
126
|
+
const { tenant: tenantArg } = params;
|
|
127
|
+
const checkout = (0, import_configService.getCurrentCheckout)();
|
|
128
|
+
const tenant = tenantArg || checkout?.tenant;
|
|
129
|
+
const organisation = tenantArg || checkout?.organisation;
|
|
130
|
+
if (!tenant || !organisation) {
|
|
131
|
+
return (0, import_helpers.tenantRequiredResponse)();
|
|
132
|
+
}
|
|
133
|
+
const clientResult = await (0, import_farseerFactory.getFarseerClientWithFallback)(organisation, tenant);
|
|
134
|
+
if (!clientResult) {
|
|
135
|
+
return (0, import_helpers.authRequiredResponse)(tenant);
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const model = await clientResult.client.exportModel();
|
|
139
|
+
const tables = model.tables.map((t) => ({
|
|
140
|
+
name: t.name,
|
|
141
|
+
memberCount: t.rows?.length || 0,
|
|
142
|
+
foreignKeys: t.columns.filter((c) => c.type === "FOREIGN_KEY").map((c) => c.foreignKeyTableName)
|
|
143
|
+
}));
|
|
144
|
+
tables.sort((a, b) => a.name.localeCompare(b.name));
|
|
145
|
+
return (0, import_helpers.successResponse)({
|
|
146
|
+
tenant,
|
|
147
|
+
totalTables: tables.length,
|
|
148
|
+
tables
|
|
149
|
+
});
|
|
150
|
+
} catch (error) {
|
|
151
|
+
return (0, import_helpers.errorResponse)(
|
|
152
|
+
`List tables failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
153
|
+
"NETWORK_ERROR"
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
server.tool(
|
|
159
|
+
"farseer_list_variables",
|
|
160
|
+
`List all variables defined in a Farseer tenant model.
|
|
161
|
+
|
|
162
|
+
Variables store data and calculations - they are the core of the data model.
|
|
163
|
+
|
|
164
|
+
Each variable has:
|
|
165
|
+
- Dimensions it spans (Product, Region, Month, etc.)
|
|
166
|
+
- Optional formula for calculated values
|
|
167
|
+
- Roll-up type (how values aggregate up hierarchies)
|
|
168
|
+
|
|
169
|
+
If no tenant is specified, uses the currently checked-out tenant.`,
|
|
170
|
+
listVariablesSchema,
|
|
171
|
+
async (params) => {
|
|
172
|
+
const { tenant: tenantArg } = params;
|
|
173
|
+
const checkout = (0, import_configService.getCurrentCheckout)();
|
|
174
|
+
const tenant = tenantArg || checkout?.tenant;
|
|
175
|
+
const organisation = tenantArg || checkout?.organisation;
|
|
176
|
+
if (!tenant || !organisation) {
|
|
177
|
+
return (0, import_helpers.tenantRequiredResponse)();
|
|
178
|
+
}
|
|
179
|
+
const clientResult = await (0, import_farseerFactory.getFarseerClientWithFallback)(organisation, tenant);
|
|
180
|
+
if (!clientResult) {
|
|
181
|
+
return (0, import_helpers.authRequiredResponse)(tenant);
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const model = await clientResult.client.exportModel();
|
|
185
|
+
const variables = model.variables.map((v) => ({
|
|
186
|
+
name: v.name,
|
|
187
|
+
dimensions: v.tables || [],
|
|
188
|
+
hasFormula: !!v.formula,
|
|
189
|
+
rollupType: v.rollupType,
|
|
190
|
+
readonly: v.readonly
|
|
191
|
+
}));
|
|
192
|
+
variables.sort((a, b) => a.name.localeCompare(b.name));
|
|
193
|
+
return (0, import_helpers.successResponse)({
|
|
194
|
+
tenant,
|
|
195
|
+
totalVariables: variables.length,
|
|
196
|
+
variables
|
|
197
|
+
});
|
|
198
|
+
} catch (error) {
|
|
199
|
+
return (0, import_helpers.errorResponse)(
|
|
200
|
+
`List variables failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
201
|
+
"NETWORK_ERROR"
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
server.tool(
|
|
207
|
+
"farseer_get_table",
|
|
208
|
+
`Get detailed information about a specific dimension table.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
- Table columns with their types
|
|
212
|
+
- Foreign key relationships to other tables
|
|
213
|
+
- Sample members (first 20)
|
|
214
|
+
- Total member count
|
|
215
|
+
|
|
216
|
+
This helps understand the structure needed for import scripts.
|
|
217
|
+
|
|
218
|
+
If no tenant is specified, uses the currently checked-out tenant.`,
|
|
219
|
+
getTableSchema,
|
|
220
|
+
async (params) => {
|
|
221
|
+
const { tenant: tenantArg, tableName } = params;
|
|
222
|
+
const checkout = (0, import_configService.getCurrentCheckout)();
|
|
223
|
+
const tenant = tenantArg || checkout?.tenant;
|
|
224
|
+
const organisation = tenantArg || checkout?.organisation;
|
|
225
|
+
if (!tenant || !organisation) {
|
|
226
|
+
return (0, import_helpers.tenantRequiredResponse)();
|
|
227
|
+
}
|
|
228
|
+
const clientResult = await (0, import_farseerFactory.getFarseerClientWithFallback)(organisation, tenant);
|
|
229
|
+
if (!clientResult) {
|
|
230
|
+
return (0, import_helpers.authRequiredResponse)(tenant);
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const model = await clientResult.client.exportModel();
|
|
234
|
+
const table = model.tables.find(
|
|
235
|
+
(t) => t.name.toLowerCase() === tableName.toLowerCase()
|
|
236
|
+
);
|
|
237
|
+
if (!table) {
|
|
238
|
+
return (0, import_helpers.errorResponse)(`Table "${tableName}" not found`, "NOT_FOUND");
|
|
239
|
+
}
|
|
240
|
+
const primaryKeyColumn = table.columns.find((c) => c.type === "PRIMARY_KEY");
|
|
241
|
+
let sampleMembers = [];
|
|
242
|
+
if (table.rows && table.rows.length > 0) {
|
|
243
|
+
const pkIndex = primaryKeyColumn ? table.columns.indexOf(primaryKeyColumn) : 0;
|
|
244
|
+
sampleMembers = table.rows.slice(0, 20).map((row) => String(row[pkIndex]));
|
|
245
|
+
}
|
|
246
|
+
return (0, import_helpers.successResponse)({
|
|
247
|
+
tenant,
|
|
248
|
+
table: {
|
|
249
|
+
name: table.name,
|
|
250
|
+
columns: table.columns.map((c) => ({
|
|
251
|
+
type: c.type,
|
|
252
|
+
name: c.name,
|
|
253
|
+
foreignKeyTable: c.foreignKeyTableName
|
|
254
|
+
})),
|
|
255
|
+
memberCount: table.rows?.length || 0,
|
|
256
|
+
sampleMembers,
|
|
257
|
+
folderPath: table.folderPath
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return (0, import_helpers.errorResponse)(
|
|
262
|
+
`Get table failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
263
|
+
"NETWORK_ERROR"
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
server.tool(
|
|
269
|
+
"farseer_get_variable",
|
|
270
|
+
`Get detailed information about a specific variable.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
- Dimensions the variable spans
|
|
274
|
+
- Formula (if calculated)
|
|
275
|
+
- Roll-up type (SUM, AVERAGE, etc.)
|
|
276
|
+
- Data type and format
|
|
277
|
+
- Whether it's readonly or calculated on demand
|
|
278
|
+
|
|
279
|
+
This is essential for understanding what dimensions and values are needed for imports.
|
|
280
|
+
|
|
281
|
+
If no tenant is specified, uses the currently checked-out tenant.`,
|
|
282
|
+
getVariableSchema,
|
|
283
|
+
async (params) => {
|
|
284
|
+
const { tenant: tenantArg, variableName } = params;
|
|
285
|
+
const checkout = (0, import_configService.getCurrentCheckout)();
|
|
286
|
+
const tenant = tenantArg || checkout?.tenant;
|
|
287
|
+
const organisation = tenantArg || checkout?.organisation;
|
|
288
|
+
if (!tenant || !organisation) {
|
|
289
|
+
return (0, import_helpers.tenantRequiredResponse)();
|
|
290
|
+
}
|
|
291
|
+
const clientResult = await (0, import_farseerFactory.getFarseerClientWithFallback)(organisation, tenant);
|
|
292
|
+
if (!clientResult) {
|
|
293
|
+
return (0, import_helpers.authRequiredResponse)(tenant);
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
const model = await clientResult.client.exportModel();
|
|
297
|
+
const variable = model.variables.find(
|
|
298
|
+
(v) => v.name.toLowerCase() === variableName.toLowerCase()
|
|
299
|
+
);
|
|
300
|
+
if (!variable) {
|
|
301
|
+
return (0, import_helpers.errorResponse)(`Variable "${variableName}" not found`, "NOT_FOUND");
|
|
302
|
+
}
|
|
303
|
+
return (0, import_helpers.successResponse)({
|
|
304
|
+
tenant,
|
|
305
|
+
variable: {
|
|
306
|
+
name: variable.name,
|
|
307
|
+
description: variable.description,
|
|
308
|
+
formula: variable.formula,
|
|
309
|
+
dimensions: variable.tables || [],
|
|
310
|
+
rollupType: variable.rollupType,
|
|
311
|
+
dataType: variable.dataType,
|
|
312
|
+
numberFormat: variable.numberFormat,
|
|
313
|
+
readonly: variable.readonly,
|
|
314
|
+
calculateOnDemand: variable.calculateOnDemand,
|
|
315
|
+
folderPath: variable.folderPath
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
} catch (error) {
|
|
319
|
+
return (0, import_helpers.errorResponse)(
|
|
320
|
+
`Get variable failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
321
|
+
"NETWORK_ERROR"
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
328
|
+
0 && (module.exports = {
|
|
329
|
+
registerModelTools
|
|
330
|
+
});
|
|
331
|
+
//# sourceMappingURL=modelTools.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mcp/tools/modelTools.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Model Inspection Tools\n *\n * Tools for exploring Farseer data models (dimensions, variables, formulas).\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { getFarseerClientWithFallback } from '../../services/farseerFactory';\nimport { getCurrentCheckout } from '../../services/configService';\nimport {\n successResponse,\n errorResponse,\n authRequiredResponse,\n tenantRequiredResponse,\n} from '../utils/helpers';\n\n// Define schemas outside of tool registration to avoid deep type instantiation\nconst exportModelSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n};\n\nconst listTablesSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n};\n\nconst listVariablesSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n};\n\nconst getTableSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n tableName: z.string().describe('Name of the dimension table'),\n};\n\nconst getVariableSchema = {\n tenant: z.string().optional().describe('Tenant name'),\n variableName: z.string().describe('Name of the variable'),\n};\n\nexport function registerModelTools(server: McpServer): void {\n // farseer_export_model - Export complete model\n server.tool(\n 'farseer_export_model',\n `Export the complete data model structure from a Farseer tenant.\n\nReturns:\n- All dimension tables with member counts and foreign key relationships\n- All variables with their dimensions, formulas, and roll-up types\n\nThis is essential for understanding a tenant's data structure before writing import scripts.\n\nThe model export can be large for complex tenants. Use farseer_list_tables or\nfarseer_list_variables for a summary view.\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n exportModelSchema,\n async (params: { tenant?: string }) => {\n const { tenant: tenantArg } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n const model = await clientResult.client.exportModel();\n\n // Process tables\n const tables = model.tables.map((t) => ({\n name: t.name,\n memberCount: t.rows?.length || 0,\n columns: t.columns.map((c) => ({\n type: c.type,\n name: c.name,\n foreignKeyTable: c.foreignKeyTableName,\n })),\n foreignKeys: t.columns\n .filter((c) => c.type === 'FOREIGN_KEY')\n .map((c) => c.foreignKeyTableName!),\n folderPath: t.folderPath,\n }));\n\n // Process variables\n const variables = model.variables.map((v) => ({\n name: v.name,\n dimensions: v.tables || [],\n formula: v.formula || undefined,\n rollupType: v.rollupType,\n numberFormat: v.numberFormat,\n readonly: v.readonly,\n calculateOnDemand: v.calculateOnDemand,\n folderPath: v.folderPath,\n }));\n\n return successResponse({\n tenant,\n summary: {\n tableCount: tables.length,\n variableCount: variables.length,\n },\n tables,\n variables,\n });\n } catch (error) {\n return errorResponse(\n `Export model failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n\n // farseer_list_tables - List dimension tables\n server.tool(\n 'farseer_list_tables',\n `List all dimension tables in a Farseer tenant model.\n\nDimension tables define the structure of data:\n- Product, Region, Customer (business dimensions)\n- Months, Years (time dimensions - built-in)\n- Versions (scenario dimension - built-in)\n\nReturns table names, member counts, and foreign key relationships.\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n listTablesSchema,\n async (params: { tenant?: string }) => {\n const { tenant: tenantArg } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n const model = await clientResult.client.exportModel();\n\n const tables = model.tables.map((t) => ({\n name: t.name,\n memberCount: t.rows?.length || 0,\n foreignKeys: t.columns\n .filter((c) => c.type === 'FOREIGN_KEY')\n .map((c) => c.foreignKeyTableName!),\n }));\n\n // Sort by name\n tables.sort((a, b) => a.name.localeCompare(b.name));\n\n return successResponse({\n tenant,\n totalTables: tables.length,\n tables,\n });\n } catch (error) {\n return errorResponse(\n `List tables failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n\n // farseer_list_variables - List variables\n server.tool(\n 'farseer_list_variables',\n `List all variables defined in a Farseer tenant model.\n\nVariables store data and calculations - they are the core of the data model.\n\nEach variable has:\n- Dimensions it spans (Product, Region, Month, etc.)\n- Optional formula for calculated values\n- Roll-up type (how values aggregate up hierarchies)\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n listVariablesSchema,\n async (params: { tenant?: string }) => {\n const { tenant: tenantArg } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n const model = await clientResult.client.exportModel();\n\n const variables = model.variables.map((v) => ({\n name: v.name,\n dimensions: v.tables || [],\n hasFormula: !!v.formula,\n rollupType: v.rollupType,\n readonly: v.readonly,\n }));\n\n // Sort by name\n variables.sort((a, b) => a.name.localeCompare(b.name));\n\n return successResponse({\n tenant,\n totalVariables: variables.length,\n variables,\n });\n } catch (error) {\n return errorResponse(\n `List variables failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n\n // farseer_get_table - Get dimension table details\n server.tool(\n 'farseer_get_table',\n `Get detailed information about a specific dimension table.\n\nReturns:\n- Table columns with their types\n- Foreign key relationships to other tables\n- Sample members (first 20)\n- Total member count\n\nThis helps understand the structure needed for import scripts.\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n getTableSchema,\n async (params: { tenant?: string; tableName: string }) => {\n const { tenant: tenantArg, tableName } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n const model = await clientResult.client.exportModel();\n\n const table = model.tables.find(\n (t) => t.name.toLowerCase() === tableName.toLowerCase()\n );\n\n if (!table) {\n return errorResponse(`Table \"${tableName}\" not found`, 'NOT_FOUND');\n }\n\n // Get sample members (first column is usually the primary key / name)\n const primaryKeyColumn = table.columns.find((c) => c.type === 'PRIMARY_KEY');\n let sampleMembers: string[] = [];\n\n if (table.rows && table.rows.length > 0) {\n const pkIndex = primaryKeyColumn\n ? table.columns.indexOf(primaryKeyColumn)\n : 0;\n sampleMembers = table.rows.slice(0, 20).map((row) => String(row[pkIndex]));\n }\n\n return successResponse({\n tenant,\n table: {\n name: table.name,\n columns: table.columns.map((c) => ({\n type: c.type,\n name: c.name,\n foreignKeyTable: c.foreignKeyTableName,\n })),\n memberCount: table.rows?.length || 0,\n sampleMembers,\n folderPath: table.folderPath,\n },\n });\n } catch (error) {\n return errorResponse(\n `Get table failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n\n // farseer_get_variable - Get variable details\n server.tool(\n 'farseer_get_variable',\n `Get detailed information about a specific variable.\n\nReturns:\n- Dimensions the variable spans\n- Formula (if calculated)\n- Roll-up type (SUM, AVERAGE, etc.)\n- Data type and format\n- Whether it's readonly or calculated on demand\n\nThis is essential for understanding what dimensions and values are needed for imports.\n\nIf no tenant is specified, uses the currently checked-out tenant.`,\n getVariableSchema,\n async (params: { tenant?: string; variableName: string }) => {\n const { tenant: tenantArg, variableName } = params;\n const checkout = getCurrentCheckout();\n const tenant = tenantArg || checkout?.tenant;\n const organisation = tenantArg || checkout?.organisation;\n\n if (!tenant || !organisation) {\n return tenantRequiredResponse();\n }\n\n const clientResult = await getFarseerClientWithFallback(organisation, tenant);\n if (!clientResult) {\n return authRequiredResponse(tenant);\n }\n\n try {\n const model = await clientResult.client.exportModel();\n\n const variable = model.variables.find(\n (v) => v.name.toLowerCase() === variableName.toLowerCase()\n );\n\n if (!variable) {\n return errorResponse(`Variable \"${variableName}\" not found`, 'NOT_FOUND');\n }\n\n return successResponse({\n tenant,\n variable: {\n name: variable.name,\n description: variable.description,\n formula: variable.formula,\n dimensions: variable.tables || [],\n rollupType: variable.rollupType,\n dataType: variable.dataType,\n numberFormat: variable.numberFormat,\n readonly: variable.readonly,\n calculateOnDemand: variable.calculateOnDemand,\n folderPath: variable.folderPath,\n },\n });\n } catch (error) {\n return errorResponse(\n `Get variable failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'NETWORK_ERROR'\n );\n }\n }\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|