import { z } from 'zod'; import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import type { SoapClient } from '../clients/soap-client.js'; import { formatQueryResponse } from '../utils/response.js'; import { formatToolError, classifyError, getErrorHint } from '../utils/errors.js'; export function registerSoapQueryTool( server: McpServer, soapClient: SoapClient, ): void { server.registerTool( 'sage_soap_query', { description: 'Query Sage X3 records via SOAP. Returns a list of records matching criteria. Use for bulk data retrieval via SOAP pools.', inputSchema: { publicName: z .string() .describe('SOAP publication name, e.g. SIH, SOH, WSBPC, WITM'), listSize: z .number() .min(1) .max(200) .optional() .describe('Maximum number of records to return (1-200, default 20)'), inputXml: z .string() .optional() .describe('Optional XML filter criteria for the query'), }, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, }, async ({ publicName, listSize, inputXml }) => { try { const result = await soapClient.query({ publicName, listSize, inputXml }); if (result.status === 1) { const records = Array.isArray(result.data) ? result.data : result.data ? [result.data] : []; return formatQueryResponse(records, { returned: records.length, hasMore: false, }); } const messages = result.messages.map((m) => m.message).join('; '); return formatToolError( new Error(messages || 'SOAP query failed'), 'Check the publicName. Use sage_describe_entity to discover available fields.', ); } catch (error) { const classification = classifyError(error); return formatToolError(error, getErrorHint(classification)); } }, ); }