payloadcms/payload · error · APIError
Error initializing MCP handler: ${String(error)}
Error message
Error initializing MCP handler: ${String(error)} What it means
Thrown by the MCP plugin's server builder when anything inside the tool/resource registration loop throws — a malformed Zod input schema that fails `toStandardSchema`, a duplicate `mcpName`, or a rejection from the underlying `server.registerTool`/`server.registerResource` call. The whole build is wrapped in one try/catch that re-throws the original cause as a 500 APIError, so the root error is preserved in the message suffix.
Source
Thrown at packages/plugin-mcp/src/mcp/buildMcpServer.ts:243
input: toolInput,
req,
serverContext: ctx,
})
return finalizeToolResponse({
input: toolInput,
overrideResponse: tool.overrideResponse,
response,
toolName: item.mcpName,
})
},
)
logger.info(`✅ Tool: ${item.mcpName} Registered.`)
break
}
}
}
} catch (error) {
throw new APIError(`Error initializing MCP handler: ${String(error)}`, 500)
}
return server
}
const withSlugInput = ({
name,
input,
}: {
input?: ToolInputSchema
name: 'collectionSlug' | 'globalSlug'
}): ToolInputSchema => {
const description = name === 'collectionSlug' ? 'The collection slug' : 'The global slug'
const slugSchema = z.string().describe(description)
if (!input) {
return z.object({ [name]: slugSchema })
}View on GitHub (pinned to 00c58b35c0)
Solutions
- Read the `String(error)` suffix — it carries the original cause verbatim and pinpoints the failing item
- Validate each tool's `input` Zod schema in isolation before registering it with the plugin
- Ensure every tool/resource `mcpName` is unique across builtin + custom items
- Pin `@modelcontextprotocol/sdk` to the exact version the installed plugin-mcp release expects
Example fix
// before: schema with an unsupported construct
const input = z.object({ id: z.any() })
// after: use a concrete primitive
const input = z.object({ id: z.string() }) Defensive patterns
Strategy: try-catch
Validate before calling
// Validate each tool's Zod schema parses a sample input before registering
import type { ZodTypeAny } from 'zod'
function assertSchemaOk(name: string, schema: ZodTypeAny | undefined, sample: unknown) {
if (!schema) return
const r = schema.safeParse(sample)
if (!r.success) throw new Error(`tool ${name} schema rejects sample: ${r.error.message}`)
} Type guard
import { APIError } from 'payload'
function isMcpInitError(e: unknown): e is APIError {
return e instanceof APIError && typeof (e as any).statusCode === 'number'
&& /^Error initializing MCP handler:/.test((e as APIError).message)
} Try / catch
try {
const server = await buildMcpServer(config)
} catch (e) {
if (e instanceof APIError && /^Error initializing MCP handler:/.test(e.message)) {
// e.message suffix is the original cause — log it and surface to operator
const cause = e.message.replace('Error initializing MCP handler: ', '')
logger.error('MCP build failed: ' + cause)
}
throw e
} Prevention
- Author tool input schemas with concrete Zod primitives; avoid z.any()/z.unknown() at the top level
- Namespace every custom tool's mcpName to avoid collisions with builtin tools
- Pin @modelcontextprotocol/sdk to the version the plugin release was built against
- Add a startup self-test that registers all tools against a throwaway MCP server before the real one boots
When it happens
Trigger: Registering an MCP tool whose `input` Zod schema uses a construct `toStandardSchema` cannot convert; registering two tools/resources with the same `mcpName`; an incompatible `@modelcontextprotocol/sdk` version where `registerTool`'s signature changed; a builtin resource whose URI template is invalid.
Common situations: Custom MCP tool authored with an unsupported Zod type (e.g. `z.any()`, `z.transform`); name collision between builtin collections tool and a user-supplied tool; major SDK upgrade of `@modelcontextprotocol/sdk` without bumping the plugin; passing a non-Zod schema object as `input`.
Related errors
- Error: the payload config is required to initialize payload.
- Collection "${collectionSlug}" does not support file uploads
- Uploading files from URLs is disabled for collection "${coll
- File exceeds the ${maxFileSize} byte upload limit.
- Operator handler "${handler.name}" cannot define both "build
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/6c7054131cd3fcb1.
Report an issue: GitHub.