vercel/ai · error

Claude Code MCP server name "harness-tools" is reserved for

Error message

Claude Code MCP server name "harness-tools" is reserved for HarnessAgent tools.

What it means

`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.

Source

Thrown at packages/harness-claude-code/src/claude-code-harness.ts:827

   * relying on the SDK's `continue` flag — "most recent thread in this
   * workdir" — which silently picks the wrong one once a second thread
   * exists there. Hosts that captured the id themselves (it is surfaced as
   * `harnessMetadata['claude-code'].sessionId` on `finish` parts) may also
   * set it explicitly.
   */
  claudeSessionId: z.string().optional(),
});

type ClaudeCodeBridgeCoords = z.infer<typeof claudeCodeBridgeCoordsSchema>;

export function createClaudeCode(
  settings: ClaudeCodeHarnessSettings = {},
): HarnessV1<typeof CLAUDE_CODE_BUILTIN_TOOLS> {
  if (
    settings.mcpServers != null &&
    Object.prototype.hasOwnProperty.call(settings.mcpServers, 'harness-tools')
  ) {
    throw new Error(
      'Claude Code MCP server name "harness-tools" is reserved for HarnessAgent tools.',
    );
  }
  const thinking = settings.thinking ?? {
    type: 'adaptive',
    display: 'summarized',
  };

  return {
    specificationVersion: 'harness-v1',
    harnessId: 'claude-code',
    builtinTools: CLAUDE_CODE_BUILTIN_TOOLS,
    supportsBuiltinToolApprovals: true,
    supportsBuiltinToolFiltering: true,
    lifecycleStateSchema: claudeCodeResumeStateSchema,
    getBootstrap: getClaudeCodeBootstrap,
    doStart: async startOpts => {
      const sandboxSession = startOpts.sandboxSession;

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Rename your MCP server entry to any other key, e.g. `my-tools`, in `settings.mcpServers`.
  2. 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.
  3. Sanitize merged config objects: `const { 'harness-tools': _, ...mcpServers } = merged` before passing to `createClaudeCode`.
  4. If you actually intended to expose harness tools, rely on the built-in `CLAUDE_CODE_BUILTIN_TOOLS` instead of registering an MCP server.

Example fix

// before
createClaudeCode({ mcpServers: { 'harness-tools': { command: './my-tools' } } });
// after
createClaudeCode({ mcpServers: { 'my-tools': { command: './my-tools' } } });
Defensive patterns

Strategy: validation

Validate before calling

function assertNoReservedMcpServers(settings) {
  if (settings.mcpServers != null &&
      Object.prototype.hasOwnProperty.call(settings.mcpServers, 'harness-tools')) {
    throw new TypeError('mcpServers must not contain the reserved key "harness-tools"');
  }
}
assertNoReservedMcpServers(settings);

Type guard

function hasReservedMcpServer(settings) {
  return settings.mcpServers != null &&
    Object.prototype.hasOwnProperty.call(settings.mcpServers, 'harness-tools');
}

Try / catch

try {
  const harness = createClaudeCode(settings);
} catch (err) {
  if (err instanceof Error && err.message.includes('"harness-tools" is reserved')) {
    const { 'harness-tools': _, ...mcpServers } = settings.mcpServers ?? {};
    return createClaudeCode({ ...settings, mcpServers });
  }
  throw err;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/ad0b625ada9085a7. Report an issue: GitHub.