Skip to main content
Glama
IBM
by IBM
registration.ts7.48 kB
/** * @fileoverview Handles registration and error handling for the `execute_sql` tool. * This module acts as the "handler" layer, connecting the pure business logic to the * MCP server and ensuring all outcomes (success or failure) are handled gracefully. * @module src/mcp-server/tools/executeSql/registration * @see {@link src/mcp-server/tools/executeSql/logic.ts} for the core business logic and schemas. */ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { JsonRpcErrorCode } from "../../../types-global/errors.js"; import { ErrorHandler, requestContextService } from "../../../utils/index.js"; import { logOperationStart, logOperationSuccess, } from "../../../utils/internal/logging-helpers.js"; import { ResponseFormatter } from "../../../mcp-server/tools/utils/tool-utils.js"; import { measureToolExecution } from "../../../utils/internal/performance.js"; import { getRequestContext } from "../../../utils/internal/asyncContext.js"; import { ExecuteSqlInput, ExecuteSqlInputSchema, executeSqlLogic, ExecuteSqlResponse, ExecuteSqlResponseSchema, } from "./logic.js"; // The unique name for the tool, used for registration and identification. const TOOL_NAME = "execute_sql"; // A concise description for the LLM. More detailed guidance should be in the // parameter descriptions within the Zod schema in `logic.ts`. const TOOL_DESCRIPTION = `Execute SQL statements against the IBM i database. Only SELECT and read-only operations are allowed for security.`; const responseFormatter: ResponseFormatter<ExecuteSqlResponse> = (result) => ({ structuredContent: result, content: [ { type: "text", text: `SQL Query executed successfully. Returned ${result.rowCount} rows.\n\n${JSON.stringify(result.data, null, 2)}`, }, ], }); /** * Configuration interface for the execute SQL tool * This allows the tool to be enabled/disabled and configured via YAML */ export interface ExecuteSqlToolConfig { /** Whether the tool is enabled */ enabled: boolean; /** Tool description override */ description?: string; /** Security configuration */ security?: { /** Whether to enforce read-only mode (default: true) */ readOnly?: boolean; /** Maximum query length (default: 10000) */ maxQueryLength?: number; /** Additional forbidden keywords */ forbiddenKeywords?: string[]; }; } /** * Default configuration for the execute SQL tool */ const DEFAULT_CONFIG: ExecuteSqlToolConfig = { enabled: true, security: { readOnly: true, maxQueryLength: 10000, forbiddenKeywords: [], }, }; /** * Internal configuration storage */ let toolConfig: ExecuteSqlToolConfig = DEFAULT_CONFIG; /** * Set the configuration for the execute SQL tool * This is called by the YAML configuration system * @param config - Tool configuration */ export function setExecuteSqlConfig( config: Partial<ExecuteSqlToolConfig>, ): void { toolConfig = { ...DEFAULT_CONFIG, ...config, security: { ...DEFAULT_CONFIG.security, ...config.security, }, }; const context = requestContextService.createRequestContext({ operation: "ConfigUpdate", toolName: TOOL_NAME, }); logOperationSuccess(context, "Execute SQL tool configuration updated", { enabled: toolConfig.enabled, readOnly: toolConfig.security?.readOnly, maxQueryLength: toolConfig.security?.maxQueryLength, }); } /** * Get the current configuration for the execute SQL tool * @returns Current tool configuration */ export function getExecuteSqlConfig(): ExecuteSqlToolConfig { return toolConfig; } /** * Check if the execute SQL tool is enabled * @returns True if the tool is enabled */ export function isExecuteSqlEnabled(): boolean { return toolConfig.enabled; } /** * Registers the 'execute_sql' tool and its handler with the provided MCP server instance. * This function uses ErrorHandler.tryCatch to ensure that any failure during the * registration process itself is caught and logged, preventing server startup failures. * * @param server - The MCP server instance to register the tool with. */ export const registerExecuteSqlTool = async ( server: McpServer, ): Promise<void> => { const registrationContext = requestContextService.createRequestContext({ operation: "RegisterTool", toolName: TOOL_NAME, }); logOperationStart( registrationContext, `Checking if tool should be registered: '${TOOL_NAME}'`, { enabled: toolConfig.enabled, }, ); // Only register if the tool is enabled if (!toolConfig.enabled) { logOperationSuccess( registrationContext, `Tool '${TOOL_NAME}' is disabled, skipping registration`, ); return; } logOperationStart(registrationContext, `Registering tool: '${TOOL_NAME}'`); await ErrorHandler.tryCatch( async () => { const description = toolConfig.description || TOOL_DESCRIPTION; server.registerTool( TOOL_NAME, { title: "Execute SQL", description, inputSchema: ExecuteSqlInputSchema.shape, outputSchema: ExecuteSqlResponseSchema.shape, annotations: { readOnlyHint: toolConfig.security?.readOnly, // Default to true for safety destructiveHint: !(toolConfig.security?.readOnly ?? true), // Destructive if not read-only openWorldHint: !(toolConfig.security?.readOnly ?? true), // Open world if not read-only }, _meta: { requiresAuth: true, // Requires database authentication }, }, async (params: unknown, mcpContext: Record<string, unknown>) => { try { const result = await measureToolExecution( TOOL_NAME, () => executeSqlLogic(params as ExecuteSqlInput), params, ); return responseFormatter(result); } catch (error) { const handlerContext = requestContextService.createRequestContext({ parentContext: mcpContext, operation: `tool:${TOOL_NAME}`, }); const handledError = ErrorHandler.handleError(error, { operation: `tool:${TOOL_NAME}`, context: getRequestContext() ?? handlerContext, input: params, }); return { isError: true, content: [ { type: "text", text: `SQL Error: ${(handledError as Error).message}`, }, ], structuredContent: { // eslint-disable-next-line @typescript-eslint/no-explicit-any code: (handledError as any).code, message: (handledError as Error).message, // eslint-disable-next-line @typescript-eslint/no-explicit-any details: (handledError as any).details, }, }; } }, ); logOperationSuccess( registrationContext, `Tool '${TOOL_NAME}' registered successfully.`, { enabled: toolConfig.enabled, readOnly: toolConfig.security?.readOnly, }, ); }, { operation: `RegisteringTool_${TOOL_NAME}`, context: registrationContext, errorCode: JsonRpcErrorCode.InitializationFailed, critical: true, // A failure to register a tool is a critical startup error. }, ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/IBM/ibmi-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server