thedotmack/claude-mem · critical · Error
mcp-server.cjs contains external ${zodRequireMatch[0]}. Clau
Error message
mcp-server.cjs contains external ${zodRequireMatch[0]}. Claude Desktop can launch this bundle without plugin node_modules available, so Zod must be bundled into the MCP server. What it means
Companion guard to [7], scanning the built mcp-server.cjs for require('zod') or require('zod/...'). Claude Desktop launches this bundle without the plugin's node_modules on the module resolution path, so any external Zod require would fail at MCP startup. Zod (used for tool/schema validation) must be bundled into the CJS file by esbuild, not left as an external dependency.
Source
Thrown at scripts/build-hooks.js:532
stripHardcodedDirname(`${hooksDir}/${MCP_SERVER.name}.cjs`);
fs.chmodSync(`${hooksDir}/${MCP_SERVER.name}.cjs`, 0o755);
const mcpServerStats = fs.statSync(`${hooksDir}/${MCP_SERVER.name}.cjs`);
console.log(`✓ mcp-server built (${(mcpServerStats.size / 1024).toFixed(2)} KB)`);
const mcpBundleContent = fs.readFileSync(`${hooksDir}/${MCP_SERVER.name}.cjs`, 'utf-8');
const bunRequireRegex = /require\(\s*["']bun:[a-z][a-z0-9_-]*["']\s*\)/;
const bunRequireMatch = mcpBundleContent.match(bunRequireRegex);
if (bunRequireMatch) {
throw new Error(
`mcp-server.cjs contains a Bun-only ${bunRequireMatch[0]} call. This means a transitive import in src/servers/mcp-server.ts pulled in code from worker-service.ts (or another module that touches DatabaseManager/ChromaSync). The MCP server runs under Node and cannot load bun:* modules. Audit recent imports in src/servers/mcp-server.ts and src/services/worker-spawner.ts — the spawner module is intentionally lightweight and MUST NOT import anything that touches SQLite or other Bun-only modules. See PR #1645 for context.`
);
}
const zodRequireRegex = /require\(\s*["']zod(?:\/[^"']*)?["']\s*\)/;
const zodRequireMatch = mcpBundleContent.match(zodRequireRegex);
if (zodRequireMatch) {
throw new Error(
`mcp-server.cjs contains external ${zodRequireMatch[0]}. Claude Desktop can launch this bundle without plugin node_modules available, so Zod must be bundled into the MCP server.`
);
}
const MCP_SERVER_MAX_BYTES = 600 * 1024;
if (mcpServerStats.size > MCP_SERVER_MAX_BYTES) {
console.warn(
`⚠️ mcp-server.cjs is ${(mcpServerStats.size / 1024).toFixed(2)} KB (advisory budget ${(MCP_SERVER_MAX_BYTES / 1024).toFixed(0)} KB). If this jumped unexpectedly, a transitive import may have pulled worker-service.ts or another heavy module into the MCP bundle (see #1645).`
);
}
console.log(`\n🔧 Building context generator...`);
await build({
entryPoints: [CONTEXT_GENERATOR.source],
bundle: true,
platform: 'node',
target: 'node18',
format: 'cjs',View on GitHub (pinned to d768ba3643)
Solutions
- In the esbuild config for MCP_SERVER, ensure 'zod' is NOT in the external list (or set bundle:true with no matching external entry) so Zod is inlined.
- After changing config, re-run node scripts/build-hooks.js and confirm the scan finds no require('zod...') in the built .cjs.
- If bundle size is a concern, prefer tree-shaking or a lighter schema lib over marking zod external.
- Verify the MCP server still starts under a host with no node_modules: the bundle must be self-contained.
Example fix
// before — esbuild config marks zod external
esbuild.build({ entryPoints:['src/servers/mcp-server.ts'], external:['zod'], ... })
// after — bundle zod in so the .cjs is self-contained
esbuild.build({ entryPoints:['src/servers/mcp-server.ts'], /* no external:zod */ ... }) Defensive patterns
Strategy: validation
Validate before calling
// Confirm Zod is inlined (no external require survives):
const out = fs.readFileSync('hooks/mcp-server.cjs','utf8');
if (/require\(\s*["']zod(?:\/[^"']*)?["']\s*\)/.test(out)) {
throw new Error('zod must be bundled, not external, in mcp-server.cjs');
} Try / catch
// Build-time only. Fix the esbuild config: remove 'zod' from the external list // for the MCP_SERVER bundle. Do not suppress.
Prevention
- Keep the MCP server esbuild config's external array explicit and minimal; don't wildcard externals.
- After any bundler config change, run the build and confirm the bundle is self-contained.
- Smoke-test the .cjs in a clean dir with no node_modules to prove it loads under Claude Desktop.
When it happens
Trigger: esbuild is configured with `external: ['zod']` (or a wildcard external that matches zod) when bundling the MCP server, so the require call survives into the output. Adding a new external dependency declaration that accidentally covers zod.
Common situations: A bundler config change marks zod external to shrink the bundle. A dependency update that re-exports zod. Marking all of node_modules external for convenience.
Related errors
- mcp-server.cjs contains a Bun-only ${bunRequireMatch[0]} cal
- Hand-edited shell string detected in ${filePath} (mcp-search
- Hand-edited shell string detected in ${filePath} (${dottedPa
- Hand-edited Windows shell string detected in ${filePath} (${
- plugin/scripts/bun-runner.js is missing fixBrokenScriptPath
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/d5ce1ba2648daf77.
Report an issue: GitHub.