Integrate your agent

Two ways to route a real agent through Axtary: the Claude Code hook (gates Claude Code's file tools) and the MCP wrapper (gates any MCP client — Claude Code, Cursor, Codex). Both use the same policy file and the same hash-chained ledger.

Option 1 — Claude Code PreToolUse hook

Gates Claude Code's Read/Glob/Grep/Write/Edit tools by normalizing each into an Axtary content action and asking the local proxy for a decision.

1. Start the proxy in your project (leave it running):

axtary proxy --config axtary.yml

2. Register the hook in the project's .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Glob|Grep|Write|Edit|NotebookEdit",
        "hooks": [
          {
            "type": "command",
            "command": "node /ABS/PATH/axtary/packages/cli/dist/bin.js hook claude-code --proxy http://127.0.0.1:7331 --owner user:you@example.com --repo your-org/your-repo",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

3. In a Claude Code session in that project, ask it to read .env.production — it's denied with the blocked prefixes named, and Claude self-corrects. Every decision is a ledger record (.axtary/actions.jsonl).

What each decision does — and where it's enforced

Axtary decisionClaude Code CLIClaude Code VS Code extensionNotes
allowtool runstool runs
denyblockedblockedworks everywhere — secrets/denied paths can't be read or written
step_upaskprompts for approval⚠️ not prompted yetthe tool currently proceeds in the VS Code extension

Step-up caveat (important, honest): Axtary returns step_up correctly and records it in the ledger, and the Claude Code CLI honors it (prompts you). But the Claude Code VS Code extension currently ignores a PreToolUse ask decision and falls back to its own permission rules (anthropics/claude-code#13339) — so the edit can proceed without an approval prompt. deny is unaffected. For guaranteed, payload-bound step-up, use the dashboard approval queue / proxy-executed path (axtary run workflow … --hosted-approval), which binds approval to the exact payload hash rather than relying on the editor's prompt.

Known scope

  • Bash is not gated by the hook — an agent could cat .env through it. Claude Code's own Bash permissioning applies; shell normalization is a follow-up.
  • The hook governs file tools as repo content actions. MCP tool calls are gated by the wrapper below, not the hook.

Option 2 — MCP wrapper (any MCP client)

axtary mcp serve is a stdio MCP server. Every tools/call is normalized, checked against policy with the tool's definition hash pinned (drift = deny), pass-signed, executed through the wrapped upstream, and ledgered.

Run it (demo mode exposes a deterministic read_fixture tool — no upstream needed):

axtary mcp serve --config axtary.yml
# or wrap a real upstream:
axtary mcp serve --config axtary.yml --wrap 'npx -y @modelcontextprotocol/server-everything'

Connect it — pick the path that matches your client

Claude Code CLI (most reliable):

claude mcp add axtary -- node /ABS/PATH/axtary/packages/cli/dist/bin.js mcp serve --config /ABS/PATH/axtary.yml
claude mcp list      # confirm "axtary" is registered

Claude Code VS Code extension / Cursor — project-scoped config. Create .mcp.json (Cursor: .cursor/mcp.json) in the project root:

{
  "mcpServers": {
    "axtary": {
      "command": "node",
      "args": ["/ABS/PATH/axtary/packages/cli/dist/bin.js", "mcp", "serve", "--config", "/ABS/PATH/axtary.yml"]
    }
  }
}

MCP onboarding gotcha: a project .mcp.json is only loaded when the editor's workspace is that project folder, and you must approve the server when prompted (or via the client's MCP panel / /mcp). Opening a different folder, or adding .mcp.json to an already-open session, means the server won't appear. If your client has no claude/CLI on PATH, use the project-config route and open the folder directly.

Then ask the agent: "Call the axtary read_fixture tool with fixtureKey repo_primer." Confirm it landed:

tail -3 .axtary/actions.jsonl   # "tool":"mcp.tool.call", decision "allow"

The tool-poisoning block (deterministic, no client needed)

axtary mcp drift-demo            # exit 0 iff the drift was blocked

The tool runs at its reviewed hash, then the definition mutates (a hidden exfiltration instruction) and the identical call is denied with mcp_definition_hash_not_allowed before the upstream runs — a name-based allowlist would have executed the mutated tool.

MCP step-up note

step_up decisions for MCP tools currently deny at the wrapper (there's no interactive approval channel inside MCP). Route step-up workflows through the dashboard approval queue instead.

Which to use

  • Claude Code, file edits in an editor: the hook (with the step-up caveat above).
  • Any MCP client, or you want definition-hash provenance: the MCP wrapper.
  • Guaranteed payload-bound human approval: the dashboard approval queue (works regardless of editor).