$ shellfirm

Claude Code Integration

Protect Claude Code with shellfirm hooks and MCP

shellfirm integrates with Claude Code through two complementary layers:

  • Hooks — automatic pre-tool-use interception that blocks risky Bash commands (exit code 2 = blocked)
  • MCP — on-demand tools that let Claude explain risks, suggest alternatives, and check project policies

Quick Start

1. Install shellfirm

brew tap kaplanelad/tap && brew install shellfirm

Or with Cargo:

cargo install shellfirm

2. Connect to Claude Code

shellfirm connect claude-code

This installs both hooks and MCP into ~/.claude/settings.json. You can also install them separately:

shellfirm connect claude-code --hooks-only   # hooks only
shellfirm connect claude-code --mcp-only     # MCP only
shellfirm connect claude-code --dry-run      # preview changes
shellfirm connect claude-code --uninstall    # remove everything

3. Restart Claude Code

After connecting, restart Claude Code so it picks up the new configuration.

How Hooks Work

Claude Code hooks are shell commands that fire automatically before and after tool calls. shellfirm registers a PreToolUse hook on the Bash tool:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "shellfirm check --stdin --format json --exit-code"
          }
        ]
      }
    ]
  }
}

When Claude Code is about to run a Bash command:

  1. The hook receives the command as JSON on stdin
  2. shellfirm runs the full analysis pipeline (pattern matching, context detection, policy checks)
  3. If the command is safe → exit code 0, Claude proceeds
  4. If the command is risky → exit code 2, Claude Code blocks execution

This happens automatically on every Bash command — Claude doesn't need to remember to call a tool first.

How MCP Works

The MCP server gives Claude access to four on-demand tools:

  • check_command — checks if a command is risky before executing it
  • suggest_alternative — gets safer alternatives for a risky command
  • get_policy — retrieves the current shellfirm configuration and project policies
  • explain_risk — provides detailed risk explanations

While hooks handle automatic blocking, MCP lets Claude proactively analyze commands and make informed decisions about alternatives.

Manual Configuration

If you prefer to configure manually, add to ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "shellfirm check --stdin --format json --exit-code"
          }
        ]
      }
    ]
  },
  "mcpServers": {
    "shellfirm": {
      "command": "shellfirm",
      "args": ["mcp"]
    }
  }
}

For per-project MCP configuration, add to .mcp.json in your project root:

{
  "mcpServers": {
    "shellfirm": {
      "command": "shellfirm",
      "args": ["mcp"]
    }
  }
}

Example interaction

You: Deploy the latest changes to production.

Claude Code:

Let me check if the deployment command is safe first.

[hook blocks git push --force origin main with exit code 2]

shellfirm blocked the force push:

  • git:force_push (High) — "Force push can overwrite remote history."
  • Alternative: git push --force-with-lease

I'll use the safer alternative instead: git push --force-with-lease origin main

Auto-deny for unattended use

When Claude Code runs autonomously, configure auto-deny to automatically block high-severity commands:

# ~/.shellfirm/settings.yaml
agent:
  auto_deny_severity: High

With this setting:

  • Commands matching patterns with severity High or Critical are automatically denied
  • Commands with Medium, Low, or Info severity are allowed to proceed
  • Claude sees the denial reason and can suggest alternatives

Combining with project policies

When you have a .shellfirm.yaml in your project, both hooks and MCP benefit from team-level policies. For example, if your team denies force pushes:

# .shellfirm.yaml
version: 1
deny:
  - "git:force_push"

The hook will block force push attempts, and Claude will see that force push is on the deny list via MCP.

The shellfirm check CLI

The hook uses shellfirm check with these flags:

  • --stdin — reads the command from stdin JSON (supports both {"command": "..."} and Claude Code's {"tool_input": {"command": "..."}} format)
  • --format json — outputs a structured RiskAssessment JSON to stdout
  • --exit-code — exit 0 if safe, exit 2 if risky/blocked

You can test it directly:

echo '{"command":"rm -rf /"}' | shellfirm check --stdin --format json --exit-code

Troubleshooting

  • shellfirm tools not appearing — verify that shellfirm is in your PATH by running which shellfirm
  • Hooks not firing — check that ~/.claude/settings.json contains the hooks.PreToolUse entry; restart Claude Code after changes
  • Commands not being blocked — verify your auto_deny_severity threshold in ~/.shellfirm/settings.yaml; the default is High
  • Connection errors — check that the MCP configuration JSON is valid and the command path is correct
  • JSON parse errors — ensure shellfirm is installed and accessible; run shellfirm check --help to verify