{"record":{"id":"d5ce1ba2648daf77","repo":"thedotmack/claude-mem","slug":"mcp-server-cjs-contains-external-zodrequirematch","errorCode":null,"errorMessage":"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.","messagePattern":"mcp-server\\.cjs contains external (.+?)\\. Claude Desktop can launch this bundle without plugin node_modules available, so Zod must be bundled into the MCP server\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"scripts/build-hooks.js","lineNumber":532,"sourceCode":"\n    stripHardcodedDirname(`${hooksDir}/${MCP_SERVER.name}.cjs`);\n\n    fs.chmodSync(`${hooksDir}/${MCP_SERVER.name}.cjs`, 0o755);\n    const mcpServerStats = fs.statSync(`${hooksDir}/${MCP_SERVER.name}.cjs`);\n    console.log(`✓ mcp-server built (${(mcpServerStats.size / 1024).toFixed(2)} KB)`);\n\n    const mcpBundleContent = fs.readFileSync(`${hooksDir}/${MCP_SERVER.name}.cjs`, 'utf-8');\n    const bunRequireRegex = /require\\(\\s*[\"']bun:[a-z][a-z0-9_-]*[\"']\\s*\\)/;\n    const bunRequireMatch = mcpBundleContent.match(bunRequireRegex);\n    if (bunRequireMatch) {\n      throw new Error(\n        `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.`\n      );\n    }\n    const zodRequireRegex = /require\\(\\s*[\"']zod(?:\\/[^\"']*)?[\"']\\s*\\)/;\n    const zodRequireMatch = mcpBundleContent.match(zodRequireRegex);\n    if (zodRequireMatch) {\n      throw new Error(\n        `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.`\n      );\n    }\n\n    const MCP_SERVER_MAX_BYTES = 600 * 1024;\n    if (mcpServerStats.size > MCP_SERVER_MAX_BYTES) {\n      console.warn(\n        `⚠️  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).`\n      );\n    }\n\n    console.log(`\\n🔧 Building context generator...`);\n    await build({\n      entryPoints: [CONTEXT_GENERATOR.source],\n      bundle: true,\n      platform: 'node',\n      target: 'node18',\n      format: 'cjs',","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/scripts/build-hooks.js#L514-L550","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before — esbuild config marks zod external\nesbuild.build({ entryPoints:['src/servers/mcp-server.ts'], external:['zod'], ... })\n\n// after — bundle zod in so the .cjs is self-contained\nesbuild.build({ entryPoints:['src/servers/mcp-server.ts'], /* no external:zod */ ... })","handlingStrategy":"validation","validationCode":"// Confirm Zod is inlined (no external require survives):\nconst out = fs.readFileSync('hooks/mcp-server.cjs','utf8');\nif (/require\\(\\s*[\"']zod(?:\\/[^\"']*)?[\"']\\s*\\)/.test(out)) {\n  throw new Error('zod must be bundled, not external, in mcp-server.cjs');\n}","typeGuard":null,"tryCatchPattern":"// Build-time only. Fix the esbuild config: remove 'zod' from the external list\n// for the MCP_SERVER bundle. Do not suppress.","preventionTips":["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."],"tags":["build","verification","bundle","zod","mcp-server","claude-desktop"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}