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.

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

The last command 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.

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

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 identity, a run trace, the tools you invoked, and the context you retrieved. Each field is optional — add what you have. The verified identity always comes from your API key; a caller-supplied actor.agent_id is recorded as a claimed sub-agent until an admin verifies it.

  • 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.
  • 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.
{
  "request_type": "approval",
  "title": "Refund $4,200 to customer 8831",
  "source": { "integration": "claude-code" },
  "actor": { "agent_id": "billing-agent", "agent_name": "Billing Agent" },
  "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.

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