Framework guides

Add human approval to Claude Code with Contro1

Route selected Claude Code actions to a manager or other eligible reviewer before they run, with production deployment as a practical quickstart example.

Updated Jul 20, 2026

Add accountable human approval to deploys, sensitive edits, infrastructure changes, database operations, privileged MCP tools, or any selected Claude Code tool action.

Use the integration skill

Copy this skill link into your code agent to add Claude Code and Contro1 to your system.

Copy skill link

Key takeaways

  • Production deploy is the quickstart example, not the limit of the connector.
  • Use deploy mode for recognized deployment commands or all mode for every call from selected tools.
  • Use an approval request for approve/reject and a separate free_text request when Claude needs a human response.
  • A project hook is a convenience gate; Claude Code managed settings make selected actions organization-controlled.
  • The reviewer sees machine-observed facts separately from the agent's justification.

Connect your Claude Code agent to Contro1

Claude Code runs on a computer, so a Contro1 service there holds its key, which cannot be exported. The agent itself never holds a credential.

  • 1. Install the CLI and run contro1 auth login.
  • 2. Run contro1 connect, approve the agent at the link it prints, and check with contro1 doctor.
  • 3. To let it use applications: turn on "Let it use applications" on the agent page, give it Actions in the capsule, then run contro1 apps enable once on the computer.

Connect an agent: every path, in full

Set up Claude Code

Everything the agent can do still comes from Access. Connecting grants nothing by itself.

terminal
contro1 auth login
contro1 connect claude-code          # shows what changes; run again with --yes
contro1 doctor claude-code           # every line green
claude mcp add --scope user contro1 -- contro1 mcp serve

When to reach for Contro1 with Claude Code

Use this connector whenever a Claude Code action should require an accountable person before it runs: production deployment, sensitive file changes, infrastructure mutation, database access, external side effects, or a privileged MCP tool. Route by the role that owns the decision, such as engineering manager, CTO, security, finance, or data owner.

The production deploy flow below is the default quickstart because it is concrete and safe to demonstrate. In deploy mode the connector gates recognized deploy-like Bash commands. In all mode it gates every call from the selected tool names at PreToolUse. It does not intercept private model reasoning or actions performed outside Claude Code.

Approval and Respond are two separate flows

When Claude proposes a sensitive action, the connector creates an approval request. The reviewer must approve or reject the exact reviewed action. A decision comment can explain the choice, but it does not replace the action input and it is not a Respond decision.

When Claude needs information from a person, use a free_text request. The operator replies with decision_type=respond and the returned string becomes input to the agent. In the default deploy mode, Claude can call contro1 ask without creating an approval request because the command is not a deploy.

Ask a human from a Claude workflow
contro1 ask "Which production region should I use?" \
  --role platform-owner \
  --wait \
  --format json

Install and configure the developer setup

Two ways to give the hook its identity. The recommended one keeps no key on the laptop: contro1 connect claude-code asks the accountable owner to approve this Claude Code once, and a local Contro1 service holds the key. The hook only needs the path of the mapping file.

The quick one uses a token: the last command of the token tab prints a live agent-scoped token. Copy it into user-level Claude settings, then clear the terminal. Do not commit the token or place it in a project .centcom.json file. Configure one or the other; the hook refuses both at once.

Connect (no key)
curl -fsSL https://contro1.com/install.sh | sh
npm install -g @contro1/claude-code
contro1 auth login
contro1 connect claude-code        # the owner approves once
contro1 doctor claude-code         # every check should be ok
~/.claude/settings.json
{
  "env": {
    "CONTRO1_PLATFORM_MAPPING_FILE": "/etc/contro1/platforms/claude-code.json",
    "CENTCOM_GATE_MODE": "deploy",
    "CENTCOM_REQUIRED_ROLE": "cto",
    "CENTCOM_FALLBACK": "deny"
  }
}
Install (token)
curl -fsSL https://raw.githubusercontent.com/contro1-hq/contro1-cli/main/install.sh | sh
npm install -g @contro1/claude-code
contro1 auth login --mode agent
contro1 init --name "Claude Code - developer laptop"
contro1 auth print-access-token --yes
~/.claude/settings.json
{
  "env": {
    "CENTCOM_API_KEY": "cc_live_your_key",
    "CENTCOM_GATE_MODE": "deploy",
    "CENTCOM_REQUIRED_ROLE": "cto",
    "CENTCOM_ENVIRONMENT": "production",
    "CENTCOM_TARGET": "billing-api",
    "CENTCOM_FALLBACK": "deny",
    "CENTCOM_TIMEOUT": "900000"
  }
}

Add the project convenience hook

Commit only the hook configuration. Keep the scoped agent token in user-level settings or a managed secret channel. The project hook is intentionally a convenience gate: a developer who controls the repository or machine can remove it.

.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "centcom-claude-code",
            "timeout": 910,
            "statusMessage": "Waiting for production deploy approval"
          }
        ]
      }
    ]
  }
}

Gate any selected tool action

Set CENTCOM_GATE_MODE to all when approval should apply to every call from selected tools. List the same tool names in CENTCOM_TOOLS and in the PreToolUse matcher. This can cover Write, Edit, Bash, or privileged MCP tools—not only deployments.

PreToolUse runs before matching tool calls even when Claude Code would not otherwise show a permission dialog, including non-interactive flows. It does not intercept private model reasoning or actions performed outside Claude Code. Keep the selection narrow enough that reviewers do not learn to rubber-stamp.

~/.claude/settings.json
{
  "env": {
    "CENTCOM_GATE_MODE": "all",
    "CENTCOM_TOOLS": "Bash,Write,Edit,mcp__production_database__execute",
    "CENTCOM_REQUIRED_ROLE": "engineering-manager",
    "CENTCOM_POLICY_REASON": "Selected sensitive Claude Code actions require manager approval"
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|Write|Edit|mcp__production_database__execute",
        "hooks": [
          {
            "type": "command",
            "command": "centcom-claude-code",
            "timeout": 910
          }
        ]
      }
    ]
  }
}

Send context the reviewer can trust

A request that only says "Approve this tool call?" forces the reviewer to rubber-stamp. Send three things with every gated call: the exact tool input your gate intercepted (machine-observed fact), the agent's own justification (make reason a required parameter of the risky tool, so the model produces it at decision time), and the trigger - the user message or event that started the run.

Keep the two kinds apart in context: facts your code observed versus text the model wrote. Agent-written justification is agent-reported evidence: it helps the reviewer decide, but it must never change routing, risk_level, or approval policy, because a prompt-injected agent writes very persuasive reasons. If a high-risk request arrives without this context, fail closed and reject it instead of asking a human to guess.

Reviewer context and trust rules

Case continuity

Set CENTCOM_CORRELATION_ID to a stable session or project identifier so every gated action in the same coding session appears in one case timeline.

The request id stays tied to one gated tool call; correlation_id ties the surrounding Claude Code timeline together.

permission_hook.ts
// hookPayload is the PreToolUse JSON Claude Code sends on stdin.
// Redact secrets before storage, and hash the original input so execution can
// remain bound to what Claude Code actually proposed.
const request = await client.createProtocolRequest({
  title: `Approve Claude Code tool: ${hookPayload.tool_name}`,
  request_type: 'approval',
  source: { integration: 'claude-code', workflow_id: 'permission-hook' },
  context: {
    action: { tool: hookPayload.tool_name, input: redactedToolInput },
    environment: 'production',
    machine_observed: {
      cwd: hookPayload.cwd,
      command_sha256: sha256(originalCommand),
      tool_input_sha256: sha256(stableStringify(hookPayload.tool_input)),
      git_commit: currentCommit,
      workspace_state_hash: currentWorkspaceHash,
    },
    agent_reported: { justification: agentExplanation },
  },
  routing: { required_role: 'cto', priority: 'urgent', sla_minutes: 10 },
  risk_level: 'critical',
  policy_trigger: 'Production deploys require CTO authorization',
  continuation: { mode: 'decision' },
  external_request_id: `claude-code:${toolCallId}`,
  correlation_id: process.env.CENTCOM_CORRELATION_ID, // set per session
});

Audit records and cases reference

Gate the tool before it executes

The connector is invoked before each matching Claude Code tool call. The quickstart selects deploy-like Bash commands; all mode can instead gate every call from selected tools such as Write, Edit, Bash, or a named MCP tool. It does not claim to intercept private reasoning or an execution path outside Claude Code.

Rejection, timeout, invalid configuration, authentication failure, and Contro1 API failure return deny. Approval allows the original input bound to the reviewed hash. If you need a different command, reject and submit a new request rather than editing the approved action.

Tell the agent the policy

CLAUDE.md helps the agent behave predictably and explain the deploy, but it is not an enforcement mechanism. Keep routing, risk, quorum, and timeout in trusted connector or organization policy.

CLAUDE.md
## Approval policy (Contro1)

Actions selected by the organization approval policy require Contro1 approval from the configured reviewer role. Examples include production deploys, sensitive edits, infrastructure changes, database operations, and privileged external tools. Do not bypass, remove, weaken, or work around the approval hook.

Before a gated action, state the target, environment, intended action, and reason in one sentence. The reviewer can approve, reject, and add a policy-controlled comment; the reviewer cannot edit the proposed action. If rejected, stop and report the decision. Do not retry a modified action unless the user starts a new attempt.

If the approval service is unreachable, STOP. Do not guess permission.

Company applications through Contro1 (MCP)

The hook governs what Claude Code stops for. To let it use company applications such as mail, calendar or tickets, give it the Contro1 MCP server. Contro1 then decides which application actions it may use, asks a person where your policy says so, and records every call.

Run contro1 connect claude-code first. After that there is nothing to copy: the connection is published on this computer and contro1 mcp serve reads it. That matters for more than convenience. Without it, Claude Code would sign in through the browser instead and act as YOU, a person-bound connection that Contro1 records as claimed rather than verified. With it, it acts as the agent whose owner approved it on this specific machine.

terminal
# In Contro1: open the agent, allow applications, choose what it may use.
claude mcp add contro1 -- contro1 mcp serve

# Only if this computer has more than one connection:
contro1 mcp endpoint            # lists the agents to choose between
claude mcp add contro1 -- contro1 mcp serve --agent <agent-id>

Move from convenience to enterprise enforcement

A project hook protects against accidental or autonomous actions inside that configured Claude Code project. It does not stop a developer from editing the hook or performing the same action through another client.

For the Claude Code path, distribute the PreToolUse hook through server-managed or endpoint-managed settings, install the connector at an administrator-controlled path, allow only managed hooks and permission rules, disable bypass-permissions mode, and enable forceRemoteSettingsRefresh when startup must fail closed without fresh remote policy. Do not add a matching ask rule unless you intentionally want a second local confirmation after Contro1 approval. For external systems such as production, keep credentials behind protected CI or a service-side broker as an additional boundary.

Call the setup non-bypassable only for the governed execution and identity path. Repository administrators, identity administrators, and deployment-policy administrators remain privileged trust boundaries and should be protected and audited separately.

Production deploy gate architecture and CI template · Complete managed settings example

Send full agent traceability

Beyond the approval call, attach a run trace, the tools you invoked, and the context you retrieved. Each field is optional; add what you have. Identity always comes from your credential. When a named part of a multi-agent system made the call, say which with the Contro1-Sub-Agent header (as_sub_agent in the SDKs): Contro1 registers it under this agent, and it never has more authority than this agent.

  • trace_id / parent_trace_id - link one run (and sub-agent runs) into a single trace.
  • tool_calls[] - what the agent tried to do, so reviewers see the actions. A hook can report every tool call for you.
  • retrieved_context[] - the data the decision was based on (RAG provenance).
  • Then export a signed evidence packet from GET /requests/:id/evidence.
Send full traceability
// POST /api/centcom/v1/requests - add these fields to the body you already send.
// Optional header for a named part of this agent: Contro1-Sub-Agent: billing
{
  "request_type": "approval",
  "title": "Refund $4,200 to customer 8831",
  "source": { "integration": "claude-code" },
  "trace_id": "trc_<run id>",
  "tool_calls": [{ "name": "lookup_order", "outcome": "success" }],
  "retrieved_context": [{ "source": "policy:refunds", "uri": "kb://policy/refunds" }],
  "continuation": { "mode": "decision" }
}

Agent identity, traceability & signed evidence

Put this integration in the enterprise control model

The connector creates the runtime gate. The operating model around it defines which actions pause, which owner receives the decision, how a timeout escalates, and where the outcome is recorded across agent frameworks.

runtime human approval for AI agents · enterprise AI agent control plane

Frequently asked questions

Should I gate every Claude Code action?

No. Gate the actions that can change code, systems, or data in ways that matter to your organization. Read-only tools, searches, and plan-only steps should never be gated.

Is the project hook non-bypassable?

No. It is a developer-controlled convenience gate. Managed settings can make the Claude Code client path resistant to user and project overrides, but production enforcement also requires credentials to stay behind protected CI or a deployment broker.

Can I use the Claude Code hook without storing an API key on the laptop?

Yes. Run contro1 connect claude-code and set CONTRO1_PLATFORM_MAPPING_FILE in user settings instead of CENTCOM_API_KEY. The accountable owner approves the connection once, a local Contro1 service keeps the key, and revoking the connection in Contro1 stops that laptop immediately.

How do I audit every gated action later?

Every Contro1 request is stored with its question, context, operator, decision, and correlation metadata. You can export or stream them to your SIEM for long-term audit.

Related resources

Best AI Agent Control Plane Tools: 2026 Enterprise Comparison

Compare the best AI agent control plane tools for 2026, including Contro1, Microsoft Agent 365, Galileo Agent Control, ValidMind, Permit.io, and observability platforms. See what each layer controls before an agent acts, from MCP and API access to human approvals and agent skills.