{"record":{"id":"ad0b625ada9085a7","repo":"vercel/ai","slug":"claude-code-mcp-server-name-harness-tools-is-res","errorCode":null,"errorMessage":"Claude Code MCP server name \"harness-tools\" is reserved for HarnessAgent tools.","messagePattern":"Claude Code MCP server name \"harness-tools\" is reserved for HarnessAgent tools\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness-claude-code/src/claude-code-harness.ts","lineNumber":827,"sourceCode":"   * relying on the SDK's `continue` flag — \"most recent thread in this\n   * workdir\" — which silently picks the wrong one once a second thread\n   * exists there. Hosts that captured the id themselves (it is surfaced as\n   * `harnessMetadata['claude-code'].sessionId` on `finish` parts) may also\n   * set it explicitly.\n   */\n  claudeSessionId: z.string().optional(),\n});\n\ntype ClaudeCodeBridgeCoords = z.infer<typeof claudeCodeBridgeCoordsSchema>;\n\nexport function createClaudeCode(\n  settings: ClaudeCodeHarnessSettings = {},\n): HarnessV1<typeof CLAUDE_CODE_BUILTIN_TOOLS> {\n  if (\n    settings.mcpServers != null &&\n    Object.prototype.hasOwnProperty.call(settings.mcpServers, 'harness-tools')\n  ) {\n    throw new Error(\n      'Claude Code MCP server name \"harness-tools\" is reserved for HarnessAgent tools.',\n    );\n  }\n  const thinking = settings.thinking ?? {\n    type: 'adaptive',\n    display: 'summarized',\n  };\n\n  return {\n    specificationVersion: 'harness-v1',\n    harnessId: 'claude-code',\n    builtinTools: CLAUDE_CODE_BUILTIN_TOOLS,\n    supportsBuiltinToolApprovals: true,\n    supportsBuiltinToolFiltering: true,\n    lifecycleStateSchema: claudeCodeResumeStateSchema,\n    getBootstrap: getClaudeCodeBootstrap,\n    doStart: async startOpts => {\n      const sandboxSession = startOpts.sandboxSession;","sourceCodeStart":809,"sourceCodeEnd":845,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-claude-code/src/claude-code-harness.ts#L809-L845","documentation":"`createClaudeCode` reserves the MCP server key `harness-tools` for the tools the HarnessAgent itself injects into the Claude Code session. If `settings.mcpServers` contains a property named `harness-tools`, the harness throws immediately to prevent a collision between user-supplied and built-in tools.","triggerScenarios":"Calling `createClaudeCode({ mcpServers: { 'harness-tools': {...} } })` — i.e. any settings object whose `mcpServers` record has its own `harness-tools` property (detected via `Object.prototype.hasOwnProperty`, so even `hasOwnProperty`-true inherited-style keys and explicit `undefined` values trigger it).","commonSituations":"Reusing a shared MCP server config object across harnesses where another setup already used the reserved name; copy-pasting example config that registered `harness-tools` manually; merging user MCP servers with defaults that include the reserved key.","solutions":["Rename your MCP server entry to any other key, e.g. `my-tools`, in `settings.mcpServers`.","Delete the `harness-tools` key if it was added by a shared/merged config; harness built-in tools are provided automatically and need no manual registration.","Sanitize merged config objects: `const { 'harness-tools': _, ...mcpServers } = merged` before passing to `createClaudeCode`.","If you actually intended to expose harness tools, rely on the built-in `CLAUDE_CODE_BUILTIN_TOOLS` instead of registering an MCP server."],"exampleFix":"// before\ncreateClaudeCode({ mcpServers: { 'harness-tools': { command: './my-tools' } } });\n// after\ncreateClaudeCode({ mcpServers: { 'my-tools': { command: './my-tools' } } });","handlingStrategy":"validation","validationCode":"function assertNoReservedMcpServers(settings) {\n  if (settings.mcpServers != null &&\n      Object.prototype.hasOwnProperty.call(settings.mcpServers, 'harness-tools')) {\n    throw new TypeError('mcpServers must not contain the reserved key \"harness-tools\"');\n  }\n}\nassertNoReservedMcpServers(settings);","typeGuard":"function hasReservedMcpServer(settings) {\n  return settings.mcpServers != null &&\n    Object.prototype.hasOwnProperty.call(settings.mcpServers, 'harness-tools');\n}","tryCatchPattern":"try {\n  const harness = createClaudeCode(settings);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('\"harness-tools\" is reserved')) {\n    const { 'harness-tools': _, ...mcpServers } = settings.mcpServers ?? {};\n    return createClaudeCode({ ...settings, mcpServers });\n  }\n  throw err;\n}","preventionTips":["Never name a user MCP server 'harness-tools'; treat it as reserved","Sanitize merged/shared MCP config objects by stripping the reserved key","Rely on CLAUDE_CODE_BUILTIN_TOOLS instead of re-registering harness tools"],"tags":["configuration","naming-collision","mcp","harness-claude-code"],"backgroundTag":"reserved-name-collision","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}