Claude Code Evaluation Guide

August 1, 2026 · View on GitHub

This guide covers how to use EvalBench for evaluating Claude Code agent workflows using MCP Servers (HTTP and stdio). It includes configuration reference, evaluation dataset format, scoring metrics, and step-by-step instructions for running evaluations locally.

The Claude Code generator mirrors the Gemini CLI generator in this repo — same evalset format, same orchestrator, same scorers — so most of what you know about Gemini CLI evaluation carries over.


Table of Contents


Overview

EvalBench's Claude Code integration enables automated, multi-turn evaluation of agentic AI workflows powered by Anthropic's Claude Code CLI. The CLI acts as the orchestrator that connects to MCP server backends and executes scenarios defined in an evaluation dataset. A simulated user powered by an LLM drives multi-turn conversations following a conversation plan.

Key Capabilities

  • Multi-turn evaluation with LLM-powered simulated users
  • Two auth modes: Vertex AI (GCP ADC) or direct Anthropic API key
  • Two MCP transport modes: HTTP/SSE (with Google Cloud OAuth auto-injection) and stdio
  • Pinned CLI versions via npm exec (matches Gemini CLI's pattern)
  • 8 built-in scorers covering correctness, efficiency, and behavior quality
  • CSV and BigQuery reporting

What carries over from the Gemini CLI integration

AspectSame / Different
Evalset JSON formatSamescenarios[] with id, starting_prompt, conversation_plan, expected_trajectory, max_turns, env
dataset_formatNew: agent-format (or keep gemini-cli-format — both work)
orchestratorNew: agent (or keep geminicli — both work)
ScorersSame (trajectory_matcher, goal_completion, behavioral_metrics, etc.)
Simulated userSame (simulated_user_model_config)
ReportingSame (CSV / BigQuery)
MCP server configSame schema for HTTP servers (httpUrl, authProviderType: google_credentials, headers) — auto-translated to Claude Code's native format

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                          EvalBench Pipeline                        │
│                                                                     │
│  ┌──────────────┐    ┌──────────────────┐    ┌───────────────────┐  │
│  │  Run Config  │───▶│  AgentOrchestrator│───▶│  AgentEvaluator   │  │
│  │  (YAML)      │    │                  │    │                   │  │
│  └──────────────┘    └──────────────────┘    └────────┬──────────┘  │
│                                                       │             │
│  ┌──────────────┐              ┌──────────────────────┼──────────┐  │
│  │  Eval Dataset│              │     Per Scenario      │          │  │
│  │  (JSON)      │─────────────▶│                      ▼          │  │
│  └──────────────┘              │  ┌──────────────────────────┐   │  │
│                                │  │   ClaudeCodeGenerator     │   │  │
│  ┌──────────────┐              │  │  ┌──────────┐ ┌────────┐ │   │  │
│  │ Model Config │──────────────│─▶│  │MCP / API │ │Sim.    │ │   │  │
│  │ (YAML)       │              │  │  │ (Vertex) │ │User    │ │   │  │
│  └──────────────┘              │  │  └──────────┘ └────────┘ │   │  │
│                                │  └───────────┬──────────────┘   │  │
│                                │              │                  │  │
│                                │              ▼                  │  │
│                                │  ┌──────────────────────────┐   │  │
│                                │  │   Scorers (8 metrics)    │   │  │
│                                │  └──────────────────────────┘   │  │
│                                └─────────────────────────────────┘  │
│                                                                     │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │                  Reporting (CSV / BigQuery)                   │   │
│  └──────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘

Flow:

  1. The Run Config ties together the dataset, model config, scorers, and reporting.
  2. The AgentOrchestrator (orchestrator: agent) loads the AgentEvaluator.
  3. The evaluator instantiates ClaudeCodeGenerator based on generator: claude_code in the model config.
  4. For each scenario in the evalset, the evaluator runs a multi-turn loop:
    • Sends the starting prompt to Claude Code via npm exec --yes <version> -- -p <prompt>
    • A SimulatedUser (LLM) generates realistic follow-up responses
    • Tools and stats are accumulated across turns from the stream-json output
    • Conversation continues until max_turns is reached or the simulated user sends TERMINATE
  5. Results are scored and written to CSV and/or BigQuery.

Prerequisites

  1. Python 3.10+ and project dependencies installed
  2. Node.js and npm (for running Claude Code via npm exec)
  3. Claude Code CLI — either:
    • Globally installed: npm install -g @anthropic-ai/claude-code (then use claude_code_version: "claude"), or
    • Pinned version (recommended for reproducibility): claude_code_version: "@anthropic-ai/claude-code@2.1.85"npm exec will install it on first use
  4. Authentication — either:
    • Vertex AI: gcloud auth application-default login and Claude models enabled in your GCP project's Model Garden
    • Direct API: export ANTHROPIC_API_KEY=sk-ant-...
  5. Environment variables for the simulated user / scorer model:
    export EVAL_GCP_PROJECT_ID=your_project_id
    export EVAL_GCP_PROJECT_REGION=us-central1
    

Quick Start

1. Choose a run config

# Real MCP server (Cloud SQL Admin API):
export EVAL_CONFIG=datasets/claude-code-tools/example_run_config.yaml

# Fake MCP (offline testing):
export EVAL_CONFIG=datasets/claude-code-tools/example_run_fake_config.yaml

2. Run the evaluation

./evalbench/run.sh

Results land in results/<job_id>/ as CSV files.


Configuration Reference

1. Run Configuration

The top-level config that ties everything together.

KeyRequiredDescription
dataset_configYesPath to the evalset JSON file
dataset_formatYesagent-format (or gemini-cli-format — alias)
orchestratorYesagent (or geminicli — alias)
model_configYesPath to the Claude Code model config YAML
simulated_user_model_configYesPath to the model config for the simulated user LLM
scorersYesDictionary of scorer configurations
runners.agent_runnersOptionalConcurrency (default 10). Set to 1 for sequential runs.
reportingOptionalCSV and/or BigQuery output options

Example (example_run_config.yaml):

dataset_config: datasets/claude-code-tools/claude-code.evalset.json
dataset_format: agent-format

orchestrator: agent
model_config: datasets/model_configs/claude_code_model.yaml
simulated_user_model_config: datasets/model_configs/gemini_2.5_pro_model.yaml

# Run scenarios sequentially (default is 10 in parallel)
runners:
  agent_runners: 1

scorers:
  trajectory_matcher: {}
  goal_completion:
    model_config: datasets/model_configs/gemini_2.5_pro_model.yaml
  behavioral_metrics:
    model_config: datasets/model_configs/gemini_2.5_pro_model.yaml
  parameter_analysis:
    model_config: datasets/model_configs/gemini_2.5_pro_model.yaml
  turn_count: {}
  end_to_end_latency: {}
  tool_call_latency: {}
  token_consumption: {}

reporting:
  csv:
    output_directory: 'results'

2. Model Configuration

The model config defines the Claude Code CLI version, model, auth, environment, and MCP server setup.

Common Fields

KeyRequiredDescription
claude_code_versionYesEither "claude" (uses the globally installed binary) or an npm spec like "@anthropic-ai/claude-code@2.1.85" (uses npm exec --yes)
generatorYesMust be claude_code
modelYesModel ID (see Authentication for valid IDs)
use_vertexOptionaltrue to route through Vertex AI; false/omit for direct Anthropic API
vertex_project_idIf use_vertexGCP project for Vertex AI
vertex_regionIf use_vertexVertex region (e.g., us-east5)
envOptionalEnvironment variables passed to the CLI process
setup.mcp_serversOptionalMCP server configurations (see MCP Servers)
allowed_toolsOptionalList of tool names to allow (e.g., ["Bash", "mcp__cloud-sql"])

3. Evaluation Dataset (Evalset)

Uses the shared scenario schema. See the agentic dataset format for the full field reference, including the canonical tool name format used in expected_trajectory.

Minimal example:

{
  "scenarios": [
    {
      "id": "cloud-sql-list-instances-01",
      "starting_prompt": "list all Cloud SQL instances in project astana-evaluation",
      "conversation_plan": "Ask the agent to list instances. If nl2code exists, get its state and verify it is RUNNABLE.",
      "expected_trajectory": ["cloud-sql__list_instances", "cloud-sql__get_instance"],
      "env": { "GOOGLE_CLOUD_PROJECT": "astana-evaluation" },
      "kind": "tools",
      "max_turns": 3
    }
  ]
}

Authentication

Claude Code supports two auth modes, controlled by use_vertex in the model config.

Vertex AI

Recommended on GCP — uses Application Default Credentials, no API key needed.

use_vertex: true
vertex_project_id: "astana-evaluation"
vertex_region: "us-east5"
model: "claude-opus-4-6"   # Vertex model ID format

Vertex model IDs (no date suffix, or @YYYYMMDD):

  • claude-opus-4-6
  • claude-sonnet-4@20250514

Requirements:

  • Run gcloud auth application-default login (locally) or use a service account with Vertex AI User role (Cloud Build / GKE)
  • Claude models must be enabled in your GCP project via Model Garden
  • Sets these env vars under the hood: CLAUDE_CODE_USE_VERTEX=1, ANTHROPIC_VERTEX_PROJECT_ID, CLOUD_ML_REGION

Direct Anthropic API

use_vertex: false
model: "claude-opus-4-20250514"   # Direct API model ID format
env:
  ANTHROPIC_API_KEY: "sk-ant-..."   # OR export it in your shell

Direct API model IDs (with date suffix):

  • claude-opus-4-20250514
  • claude-sonnet-4-20250514

Tip: Don't commit API keys into model_config.yaml. Prefer export ANTHROPIC_API_KEY=... in your shell, or use Secret Manager when running on Cloud Build / GKE.


MCP Servers

EvalBench accepts the same MCP server config schema as Gemini CLI for HTTP servers. The Claude Code generator auto-translates Gemini-style fields into Claude Code's native format at runtime:

Gemini-style fieldClaude Code translation
httpUrlurl + auto-adds type: "http"
authProviderType: google_credentials→ injects Authorization: Bearer <ADC token> (from gcloud auth application-default print-access-token, falling back to gcloud auth print-access-token) and sets a headersHelper so Claude Code re-mints a fresh ADC token on every connection (avoids ~1h expiry). Google API MCP endpoints reject the plain user token on tool calls — see Troubleshooting.
oauth.scopes(dropped — Claude Code doesn't use Gemini's OAuth delegation)
headers→ passed through as-is
command / args (stdio)→ passed through as-is

HTTP MCP server (Cloud SQL Managed)

setup:
  mcp_servers:
    "cloud-sql":
      httpUrl: "https://sqladmin.googleapis.com/mcp"
      authProviderType: google_credentials
      oauth:
        scopes:
        - https://www.googleapis.com/auth/cloud-platform
      headers:
        X-Goog-User-Project: astana-evaluation

This generates the following mcp_servers.json for Claude Code:

{
  "mcpServers": {
    "cloud-sql": {
      "type": "http",
      "url": "https://sqladmin.googleapis.com/mcp",
      "headers": {
        "X-Goog-User-Project": "astana-evaluation",
      }
    }
  }
}

Stdio MCP server

setup:
  mcp_servers:
    "my-server":
      command: "python"
      args:
        - "path/to/server.py"
        - "--some-flag"

How it works under the hood

  1. ClaudeCodeGenerator._setup_mcp_servers writes the translated config to <fake_home>/.claude/mcp_servers.json
  2. The CLI is invoked with --mcp-config <path> so it loads only the configured servers (no host-machine pollution)
  3. Each scenario runs in a sandboxed HOME (.venv/fake_home_claude/ locally, /tmp_sessions/<session_id>/fake_home in gRPC mode)

Scorers

See the scorer reference for the full catalog and configuration options.

Quick reference:

ScorerTypeDescription
trajectory_matcherDeterministicJaccard or Levenshtein match between expected and actual tool trajectory. Native Claude Code tools (Read, Bash, Edit, ToolSearch, ...) are dropped from both sides by default — set filter_native_tools: false to score them too.
goal_completionLLMDid the agent accomplish the conversation plan?
behavioral_metricsLLMHallucination rate + clarification rate
parameter_analysisLLMQualitative feedback on tool parameters
turn_countDeterministicNumber of conversation turns
end_to_end_latencyDeterministicTotal latency (model + tool execution)
tool_call_latencyDeterministicSum of tool execution durations
token_consumptionDeterministicTotal input + output tokens

End-to-End Examples

Example 1: Vertex AI + Cloud SQL Managed MCP

Goal: Use Claude Opus on Vertex AI to manage Cloud SQL instances.

# datasets/model_configs/claude_code_model.yaml
claude_code_version: "@anthropic-ai/claude-code@2.1.85"
generator: claude_code
model: "claude-opus-4-6"

use_vertex: true
vertex_project_id: "astana-evaluation"
vertex_region: "us-east5"

env:
  GOOGLE_CLOUD_PROJECT: "astana-evaluation"

setup:
  mcp_servers:
    "cloud-sql":
      httpUrl: "https://sqladmin.googleapis.com/mcp"
      authProviderType: google_credentials
      oauth:
        scopes:
        - https://www.googleapis.com/auth/cloud-platform
      headers:
        X-Goog-User-Project: astana-evaluation

Run:

gcloud auth application-default login
export EVAL_GCP_PROJECT_ID=astana-evaluation
export EVAL_CONFIG=datasets/claude-code-tools/example_run_config.yaml
./evalbench/run.sh

Example 2: Direct Anthropic API

claude_code_version: "@anthropic-ai/claude-code@2.1.85"
generator: claude_code
model: "claude-opus-4-20250514"
use_vertex: false

setup:
  mcp_servers:
    "cloud-sql":
      httpUrl: "https://sqladmin.googleapis.com/mcp"
      authProviderType: google_credentials
      headers:
        X-Goog-User-Project: astana-evaluation

Run:

export ANTHROPIC_API_KEY="sk-ant-..."
export EVAL_CONFIG=datasets/claude-code-tools/example_run_config.yaml
./evalbench/run.sh

Troubleshooting

Error: Invalid MCP configuration: ... Does not adhere to MCP server configuration schema

The MCP server config didn't translate correctly. Common causes:

  • Missing both httpUrl/url and command — the server needs at least one
  • Stale generated mcp_servers.json — delete .venv/fake_home_claude/.claude/mcp_servers.json and re-run

There's an issue with the selected model (...). It may not exist or you may not have access to it.

  • Vertex AI: The model isn't enabled in your GCP project. Visit Model Garden in the Cloud Console and enable it for your vertex_region. Or pick a different model that's already enabled (claude-opus-4-6, claude-sonnet-4@20250514).
  • Direct API: Your ANTHROPIC_API_KEY doesn't have access to the model. Check your console at https://console.anthropic.com.

Not logged in · Please run /login

Claude Code can't find auth credentials in the sandboxed HOME. Either:

  • Use use_vertex: true (uses GCP ADC, no login needed), or
  • Set ANTHROPIC_API_KEY in env or model config, or
  • Make sure your real ~/.claude/ has valid credentials (the generator copies them to the fake home)

Error: --session-id can only be used with --continue or --resume if --fork-session is also specified.

Already fixed — the generator passes --fork-session automatically when resuming. If you still see this, make sure you're on a current version of claude_code.py.

When using --print, --output-format=stream-json requires --verbose

Already fixed — --verbose is added automatically. Check your version of claude_code.py.

Empty/zero results in the summary

The simulated user failed to initialize. Check EVAL_GCP_PROJECT_ID is set if your simulated user model uses Vertex AI:

export EVAL_GCP_PROJECT_ID=astana-evaluation

Scenarios appear to run in interleaved order

This is expected — the default agent_runners: 10 runs scenarios concurrently. To force sequential execution, add this to your run config:

runners:
  agent_runners: 1

MCP tool call fails with Incompatible auth server: does not support dynamic client registration

Symptom: the agent connects to the MCP server and can list its tools, but the first real tool call fails with Incompatible auth server: does not support dynamic client registration.

Root cause: this is a misleading error. Google API MCP endpoints (e.g. sqladmin.googleapis.com/mcp) leave initialize and tools/list unauthenticated but require a valid bearer token on the actual tool call. When the injected token is missing, expired, or the wrong kind, the tool call returns 401 with a WWW-Authenticate: Bearer resource_metadata=... challenge. Claude Code always attaches an OAuth authProvider to HTTP MCP servers, so on that 401 it tries to complete an OAuth 2.0 flow — which begins with dynamic client registration (RFC 7591). These Google endpoints don't support dynamic client registration, so the OAuth fallback dies with that error instead of surfacing the underlying 401.

Fixes:

  • Use ADC, not the user token. The generator now injects gcloud auth application-default print-access-token (ADC) rather than gcloud auth print-access-token. The plain gcloud user token is rejected by these endpoints ("Request had invalid authentication credentials"), which is what triggered the fallback. Make sure you ran gcloud auth application-default login (not just gcloud auth login).
  • Token expiry on long runs (handled automatically). Google access tokens expire after ~1 hour. To avoid later scenarios hitting the same 401 → DCR error, the generator also emits a headersHelper on the MCP server config — a command Claude Code runs on every connection to mint a fresh ADC token (its JSON stdout overrides the baked static header). The baked token remains as a fallback for when gcloud/ADC isn't available in Claude Code's environment. No action needed; if you see stale-token 401s, confirm gcloud is on PATH and ADC is valid in the run environment.

Limitation: OneMCP / Google API MCP endpoints do not support OAuth dynamic client registration. Static bearer-token auth (via authProviderType: google_credentials) is the only supported path — Claude Code's interactive OAuth login flow will not work against them.

MCP server fails with 401 Unauthorized (real Cloud SQL endpoint)

Usually a token problem (see the DCR entry above). Checklist:

  • Run gcloud auth application-default login with --scopes=https://www.googleapis.com/auth/cloud-platform.
  • Confirm your account has the required IAM roles (e.g., roles/cloudsql.admin).
  • Set the quota project header: headers: { X-Goog-User-Project: <project> }.
  • Verify directly: curl -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" -H "X-Goog-User-Project: <project>" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_instances","arguments":{"project":"<project>"}}}' https://sqladmin.googleapis.com/mcp

npm exec is slow on first run

npm exec --yes <package>@<version> downloads the package on first use (~30 sec). Subsequent runs use the cache.


See Also