import type { Tool } from '@modelcontextprotocol/server'; import type { MCPServer } from '../server/MCPServer.js'; import { readOnlyAnnotations } from '../utils/ToolPolicy.js'; import { isMcpToolResult } from '../utils/McpToolResult.js'; type DomainOperation = { publicName: string; internalName: string; }; const HELM_OPERATIONS: DomainOperation[] = [ { publicName: 'list', internalName: 'helm_list' }, { publicName: 'get', internalName: 'helm_get' }, { publicName: 'debug', internalName: 'helm_debug' }, ]; const ARGO_OPERATIONS: DomainOperation[] = [ { publicName: 'list', internalName: 'argo_list' }, { publicName: 'get', internalName: 'argo_get' }, { publicName: 'logs', internalName: 'argo_logs' }, { publicName: 'cron_list', internalName: 'argo_cron_list' }, ]; const ARGOCD_OPERATIONS: DomainOperation[] = [ { publicName: 'list', internalName: 'argocd_app' }, { publicName: 'get', internalName: 'argocd_app' }, { publicName: 'resources', internalName: 'argocd_app' }, { publicName: 'logs', internalName: 'argocd_app' }, { publicName: 'history', internalName: 'argocd_app' }, { publicName: 'status', internalName: 'argocd_app' }, ]; export function createHelmTool(): Tool { return createDomainTool( 'helm', 'Inspect Helm Releases', 'List, inspect, or debug Helm releases using Kubernetes-backed release data.', HELM_OPERATIONS, ); } export function createArgoTool(options: { workflows: boolean; cronWorkflows: boolean }): Tool { const operations = ARGO_OPERATIONS.filter((operation) => operation.publicName === 'cron_list' ? options.cronWorkflows : options.workflows, ); return createDomainTool( 'argo', 'Inspect Argo Workflows', 'List, inspect, and read logs for detected Argo Workflows resources.', operations, ); } export function createArgoCDTool(): Tool { return createDomainTool( 'argocd', 'Inspect Argo CD Applications', 'Inspect detected Argo CD applications, resources, history, status, and logs.', ARGOCD_OPERATIONS, ); } export async function executeDomainTool( internalServer: MCPServer, operations: DomainOperation[], params: unknown, ): Promise { const input = params && typeof params === 'object' ? (params as Record) : {}; const operationName = String(input.operation ?? ''); const operation = operations.find((candidate) => candidate.publicName === operationName); if (!operation) throw new Error(`Unsupported operation: ${operationName}`); const args = input.arguments && typeof input.arguments === 'object' ? input.arguments : {}; const forwarded = operation.internalName === 'argocd_app' ? { ...(args as object), operation: operationName } : args; const result = await internalServer.executeTool(operation.internalName, forwarded); return { operation: operationName, result: unwrapMcpResult(result) }; } export const directDomainOperations = { helm: HELM_OPERATIONS, argo: ARGO_OPERATIONS, argocd: ARGOCD_OPERATIONS, } as const; function createDomainTool( name: string, title: string, description: string, operations: DomainOperation[], ): Tool { return { name, title, description, inputSchema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { operation: { type: 'string', enum: operations.map((operation) => operation.publicName), description: 'Domain operation to perform.', }, arguments: { type: 'object', description: 'Arguments for the selected operation. Use run_code for typed granular APIs.', }, }, required: ['operation'], additionalProperties: false, oneOf: operations.map((operation) => ({ properties: { operation: { const: operation.publicName } }, required: ['operation'], })), }, outputSchema: { type: 'object', properties: { operation: { type: 'string' }, result: {}, }, required: ['operation', 'result'], }, annotations: readOnlyAnnotations(title), }; } function unwrapMcpResult(result: unknown): unknown { if (!isMcpToolResult(result)) return result; if (result.isError) { const message = result.content .filter((block: any) => block?.type === 'text' && typeof block.text === 'string') .map((block: any) => block.text) .join('\n'); throw new Error(message || 'Internal domain operation failed'); } if (result.structuredContent !== undefined) return result.structuredContent; const text = result.content .filter((block: any) => block?.type === 'text' && typeof block.text === 'string') .map((block: any) => block.text) .join('\n'); try { return text ? JSON.parse(text) : null; } catch { return text; } }