import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { ContentfulMcpTools, hasExoM1Entitlement } from '@contentful/mcp-tools'; import { env } from '../config/env.js'; import { getVersion } from '../getVersion.js'; /** * Registers all Contentful MCP tools with the server. * Each tool is registered with its title, description, input schema, annotations, and implementation. * * ExO tool collections require both the ENABLE_EXO_TOOLS env var and the exoM1 entitlement; fails closed. * * Special handling for space-to-space migration workflow tools: * - The param collection, export, and import tools are disabled by default * - The migration handler controls their enable/disable state */ export async function registerAllTools(server: McpServer): Promise { if (!env.success || !env.data) { throw new Error('Environment variables are not properly configured'); } // Parse protectedEnvironments: collapse empty arrays (e.g. ", ,") to undefined // so that undefined consistently means "feature disabled" const parsedProtectedEnvs = env.data.PROTECTED_ENVIRONMENTS ? env.data.PROTECTED_ENVIRONMENTS.split(',') .map((e) => e.trim()) .filter(Boolean) : undefined; const protectedEnvironments = parsedProtectedEnvs?.length ? parsedProtectedEnvs : undefined; const maxBulkSize = env.data.MAX_BULK_SIZE ? Number(env.data.MAX_BULK_SIZE) : undefined; const baseConfig = { accessToken: env.data.CONTENTFUL_MANAGEMENT_ACCESS_TOKEN, host: env.data.CONTENTFUL_HOST, spaceId: env.data.SPACE_ID, environmentId: env.data.ENVIRONMENT_ID, organizationId: env.data.ORGANIZATION_ID, appId: env.data.APP_ID, mcpVersion: getVersion(), mcpSource: 'local' as const, deliveryToken: env.data.CONTENTFUL_DELIVERY_TOKEN, hostDelivery: env.data.CONTENTFUL_DELIVERY_HOST, protectedEnvironments, maxBulkSize, }; // Skip the entitlement network call entirely when the env var is off. const registerExoTools = env.data.ENABLE_EXO_TOOLS && (await hasExoM1Entitlement(baseConfig)); // Initialize tools with configuration from environment variables const tools = new ContentfulMcpTools({ ...baseConfig, exoToolsRegistered: registerExoTools, }); // Classic (always-registered) tool collections const aiActionTools = tools.getAiActionTools(); const assetTools = tools.getAssetTools(); const contentPreviewTools = tools.getContentPreviewTools(); const contentTypeTools = tools.getContentTypeTools(); const contextTools = tools.getContextTools(); const editorInterfaceTools = tools.getEditorInterfaceTools(); const entryTools = tools.getEntryTools(); const environmentTools = tools.getEnvironmentTools(); const localeTools = tools.getLocaleTools(); const orgTools = tools.getOrgTools(); const spaceTools = tools.getSpaceTools(); const tagTools = tools.getTagTools(); const taxonomyTools = tools.getTaxonomyTools(); const usageTools = tools.getUsageTools(); // ExO collections appended only when both gates pass. const allToolCollections = [ aiActionTools, assetTools, contentPreviewTools, contentTypeTools, contextTools, editorInterfaceTools, entryTools, environmentTools, localeTools, orgTools, spaceTools, tagTools, taxonomyTools, usageTools, ...(registerExoTools ? [ tools.getComponentTools(), tools.getDataAssemblyTools(), tools.getDesignTokenTools(), tools.getExperienceTools(), tools.getExperienceTemplateTools(), tools.getExperienceFragmentTools(), ] : []), ]; // Register each tool from standard collections allToolCollections.forEach((toolCollection) => { Object.values(toolCollection).forEach((tool) => { server.registerTool( tool.title, { description: tool.description, inputSchema: tool.inputParams, annotations: tool.annotations, }, tool.tool, ); }); }); const jobTools = tools.getJobTools(); const workflowToolsToDisable = [ 'spaceToSpaceParamCollection', 'exportSpace', 'importSpace', ] as const; const registeredWorkflowTools = workflowToolsToDisable.map((toolKey) => { const toolConfig = jobTools[toolKey]; const registeredTool = server.registerTool( toolConfig.title, { description: toolConfig.description, inputSchema: toolConfig.inputParams, annotations: toolConfig.annotations, }, toolConfig.tool, ); // Disable these tools by default - they'll be enabled by the migration handler registeredTool.disable(); return registeredTool; }); // Register the migration handler with references to the workflow tools const handlerConfig = jobTools.spaceToSpaceMigrationHandler; server.registerTool( handlerConfig.title, { description: handlerConfig.description, inputSchema: handlerConfig.inputParams, annotations: handlerConfig.annotations, }, handlerConfig.tool(registeredWorkflowTools), ); }