Wrap Claude Code in a deterministic shell.
Agent tools, pre-action guards, and post-action checks β declared in TypeScript, enforced by hooks, observable by default.
Status: Early. Architecture stable; the public API may still evolve.
The three core harness primitives β tools (what the agent can call), guards (what it can't do), checks (what it must verify) β written in TypeScript and wired into Claude Code's hooks.
New to harness engineering? See Anthropic or Fowler.
npm install agent-harness-sdk
npx harness initRestart Claude Code from the project directory and approve the new MCP server and hooks when prompted. You now have:
my-project/
βββ harness/
β βββ harness.config.ts β declarative config: which tools/guards/checks are active
β βββ harness.lock β synced-content manifest (git-tracked; updated by /harness update)
β βββ tools/ β MCP tools the agent can call
β βββ guards/ β PreToolUse filters
β βββ checks/ β PostToolUse validators
βββ .claude/
β βββ settings.json β PreToolUse + PostToolUse + SessionStart hooks
β βββ rules/harness.md β universal conventions + authoring contracts (synced)
β βββ commands/harness.md β /harness slash command (synced)
βββ .mcp.json β MCP server registration
βββ .harness/ β gitignored audit log + evolve state
Scaffold a harness primitive β from inside Claude Code:
/harness add tool fetch-weather
/harness add guard block-pushes
/harness add check validate-routes
Or from your shell:
npx harness add tool fetch-weather
npx harness add guard block-pushes
npx harness add check validate-routesAudit the harness:
/harness evolve
Audits your codebase and harness side by side. Surfaces tiered suggestions: patterns worth enforcing, dead components to remove, drift to fix, and architectural smells worth a human look. Read-only β nothing scaffolds without your approval.
The harness manages three primitives directly β declared in harness.config.ts, enforced at runtime by hooks and the MCP server:
| Primitive | What it is | Where it lives | Enforced by |
|---|---|---|---|
| Tool | Deterministic MCP operation the agent calls | harness/tools/<name>.ts |
MCP server (auto-instrumented) |
| Guard | Pre-action filter β vetoes a tool call before it runs | harness/guards/<name>.ts |
PreToolUse hook |
| Check | Post-action validator β fails with feedback after a tool runs | harness/checks/<name>.ts |
PostToolUse hook |
All registered in one place β harness/harness.config.ts:
import { defineHarness, protectEnvFiles } from "agent-harness-sdk";
import myTool from "./tools/my-tool";
import { myGuard } from "./guards/my-guard";
import { myCheck } from "./checks/my-check";
export default defineHarness({
tools: [myTool],
guards: [protectEnvFiles, myGuard],
checks: [myCheck],
});Every tool call, guard fire, and check run auto-emits a JSONL line to .harness/log.jsonl (gitignored):
{"ts":"2026-05-10T12:34:57Z","event":"pre-tool-use.denied","tool_name":"Edit","file_path":".env","denied":[{"name":"protect-env-files","reason":"..."}]}Ask Claude "what has the harness been doing this week?" β it'll call the bundled harness_status tool to aggregate the log.
Env vars (shell or .env):
HARNESS_LOG_DISABLED=1β turn off loggingHARNESS_LOG_PATH=/custom/pathβ redirect output