Claude Code skips bash guards in agentic mode

Claude Code's agentic bash guard is off by default. Covers Telegram control planes and broker API safety for trading.

Claude Code skips bash guards in agentic mode
Share

Security docs can promise a guardrail that the runtime quietly disables. That is exactly the gap at the heart of running Claude Code as a live trading operator: the "dangerous Bash pattern" filter you read about in the docs does not run in the mode most agentic deployments actually use.

What the bash filter does — and what turns it off

Screenshot of https://raw.githubusercontent.com/RichardAtCT/claude-code-telegram/main/docs/tools.md

The dangerous-Bash pattern filter is a string-matching layer that inspects shell commands before execution and rejects known-risky patterns. Richard Atkinson's claude-code-telegram bridge documents this check in its security reference alongside traversal checks, secret-file blocking, and rate limits. But the bridge's own tools reference adds the caveat the title names: the pattern layer is "classic mode only" and is not active in agentic mode.

"The dangerous-Bash-pattern layer is classic mode only," — claude-code-telegram tools reference (source: docs/tools.md).

In an agentic deployment, then, Bash runs without that interception. Your remaining barriers are OS sandboxing, Claude Code's permission rules, and the order-gate code you write yourself — nothing about the bridge stops a shell call from reaching a broker on its own. The platform-level control that replaces the string filter is the Agent SDK's evaluation order, documented in Claude Code's permissions reference: hooks → deny rules → permission mode → allow rules → canUseTool. Because hooks run first, a PreToolUse hook is the practical replacement — it can block a shell command before it executes.

The corollary is blunt: an agent enforces only what is written down. A 14-session Bybit futures build published May 2026 — 961 tool calls across 59 files — recorded Claude self-halting in Session 5 when it detected BYBIT_TESTNET=false . It refused to trade — but only because that constraint lived in CLAUDE.md. Nothing intrinsic caught it.

The snippet below is illustrative (not executed — it needs an authenticated Claude Code login) and shows the shape of the problem: launching with bypassPermissions and a narrow allowedTools is not a safety boundary.

import subprocess
import sys

prompt = (
    "Use Bash to run exactly this command and report the output: "
    "python3 -c 'print(\"BASH_GUARD_SKIPPED\")'"
)

cmd = [
    "claude",
    "-p",
    "--bare",
    "--permission-mode",
    "bypassPermissions",
    "--allowedTools=Bash(echo *)",
    prompt,
]

try:
    result = subprocess.run(cmd, text=True, capture_output=True, timeout=20)
except subprocess.TimeoutExpired:
    print("Claude Code timed out before demonstrating whether Bash(echo *) was enforced.")
    sys.exit(124)

print(result.stdout, end="")
if result.stderr:
    print(result.stderr, end="")
sys.exit(result.returncode)

Telegram: Channels or open bridge?

Telegram: Channels or open bridge? (source: cdn.quasa.io)

Two paths connect a running Claude Code session to Telegram, and they trade convenience for control. The official route is Claude Code Channels, a plugin Anthropic shipped on March 20, 2026 that lets a local session receive and reply to Telegram and Discord messages. It needs Claude Code v2.1.80 or newer. Setup is short: create a bot through BotFather, install the Channels plugin, store the token in .claude/channels/telegram/.env, then relaunch with claude --channels plugin:telegram@claude-plugins-official. During the research preview, --channels only accepts plugins from that one allowlist. Anthropic's listing page reported 100,332 installs at the time of research.

The self-hosted alternative is Richard Atkinson's claude-code-telegram — the most complete community control plane found. Its main branch reports version 1.6.0, Python ≥3.11, python-telegram-bot ^22.6, and claude-agent-sdk ^0.1.39, and adds per-user session persistence, SQLite audit logs, allowlist auth, webhook HMAC, and directory sandboxing. The README install snippet still recommends pinning the v1.3.0 tag until main stabilizes, so choose a release deliberately before deploying.

Either way, the transport layer is python-telegram-bot, whose v22.7 (released March 16, 2026) supports Bot API 9.6, async webhooks and polling, and inline keyboards for one-tap approve/cancel flows. The HumbledTrader IBKR build wraps every Telegram call in try/except so a failed notification can never abort trade logic [HumbledTrader, 2026-05].

Treat Telegram as a console, not a durable authorization layer. Callback data is ephemeral, delivery is best-effort, and the preview allowlist is claude-plugins-official only. Plan for a Telegram outage from day one — keep order state and confirmations server-side.

From Telegram to fill: Robinhood, Alpaca, or CCXT

From Telegram to fill: Robinhood, Alpaca, or CCXT

The order-entry layer is where three routes diverge: a broker-native MCP server, a paper-trading REST API, or a crypto exchange SDK. Robinhood's Agentic Trading, launched May 27, 2026 as an equities-only beta, is the lowest-wiring path — you add it in one command and skip custom broker code entirely:

claude mcp add robinhood-trading --transport http https://agent.robinhood.com/mcp/trading

The agent gets a dedicated agentic account with real-time P&L and disconnect/pause controls, and can place trades only inside that account [Robinhood support]. Read Robinhood's own warning carefully: if you instruct the agent to act without approval, trades may execute without confirmation, and you remain responsible.

Alpaca is the safest place to soak a strategy. Paper trading is free, defaults to a $100k paper account, and lives at a separate endpoint, https://paper-api.alpaca.markets [Alpaca docs]. It supports market, limit, stop, bracket, OCO, and OTO orders keyed by client_order_id. The catch: it explicitly does not model slippage, queue position, latency, or regulatory fees, so paper results overstate live performance.

For crypto futures, builders reach for CCXT or a native SDK such as Bybit's. Generate API keys with withdrawal permissions disabled, always. And note that ErrCode 10001 (Hedge vs One-way mode mismatch) surfaces only at order submission, not at connection time — one build diary hit it mid-run, so validate on testnet first [dev.to build diary].

IBKR is the most capable and the most operationally demanding. Its Client Portal Web API constraints matter for any agent loop:

ConstraintValue
Brokerage sessionOne active per user; ~6-minute auth timeout without /tickle; up to 24-hour lifetime with midnight reset
Global rate cap10 req/s; /iserver/account/orders GET at 1 per 5s; 15-minute penalty box for violators
TWS paper port7497
IB Gateway ports4002 (paper) / 4001 (live)

Pick by tolerance for wiring: Robinhood for the least custom code, Alpaca for free paper soak, CCXT/IBKR for control at the cost of session and pacing discipline [HumbledTrader, 2026-05].

How to compensate: kill button, drawdown cap, idempotency

With the dangerous-Bash-pattern layer inactive in agentic mode, every brake has to live in config the agent reads at startup — not in conversation. The reference builds converge on a small set of externalized guardrails: a red kill flag that issues a global cancel-and-flatten, a circuit breaker that cuts position size at ~2% daily loss, a force-flatten at ~3% daily loss or 10% drawdown, and a lock file that requires manual resume before trading restarts .

Write the numeric limits down where the agent must honor them — a rules.json or CLAUDE.md. The IBKR reference build caps max concurrent positions at 5, risk-per-trade at 1%, and max position size at 10%, enforces a MAX_TRADES_PER_DAY limit, and adds a startup guard that refuses to boot when the PAPER_TRADING flag disagrees with the connected port number . The mechanism works: in a 14-session build, Claude self-halted in Session 5 — refusing to trade after detecting BYBIT_TESTNET=false — but only because that constraint was written into CLAUDE.md .

Authorization has to be durable. Use broker-generated order IDs and idempotency keys in every submission, and store risk-decision logs and broker confirmations server-side — Telegram callback data alone is not durable authorization, since a tapped button leaves no auditable, replay-safe record.

"If you ask the agent to act without requiring your approval, trades may execute without additional confirmation, and you remain responsible for them," — Robinhood, Agentic Trading documentation (source: Robinhood).

On the platform side, PreToolUse hooks are the Agent SDK's closest analog to the missing Bash filter: the SDK evaluates hooks first, then deny rules, permission mode, and allow rules, so a hook can intercept a Bash call, log the intended command with a timestamp before execution, and gate "place order," "modify order," and "cancel all" through an explicit approval check in code rather than conversational consent . The snippet below is illustrative — not executed — and shows why the filter can be bypassed at all:

import subprocess
import sys

prompt = (
    "Use Bash to run exactly this command and report the output: "
    "python3 -c 'print(\"BASH_GUARD_SKIPPED\")'"
)

cmd = [
    "claude",
    "-p",
    "--bare",
    "--permission-mode",
    "bypassPermissions",
    "--allowedTools=Bash(echo *)",
    prompt,
]

try:
    result = subprocess.run(cmd, text=True, capture_output=True, timeout=20)
except subprocess.TimeoutExpired:
    print("Claude Code timed out before demonstrating whether Bash(echo *) was enforced.")
    sys.exit(124)

print(result.stdout, end="")
if result.stderr:
    print(result.stderr, end="")
sys.exit(result.returncode)

The takeaway: treat the agent as untrusted. Externalize kill switch, drawdown caps, and idempotent order gates into config and deterministic hooks — the agent only applies brakes that are written down.

Frequently asked questions

Is the dangerous-Bash filter disabled in all agentic deployments, or is this specific to claude-code-telegram?

This is documented behavior in the claude-code-telegram community bridge specifically: its tools doc states the dangerous-Bash-pattern layer is "classic mode only" and is not active in agentic mode . That pattern filter is one layer. It is independent of Anthropic's own controls: the Claude Agent SDK evaluates hooks, then deny rules, permission mode, allow rules, then canUseTool, and PreToolUse can block dangerous operations regardless of which bridge you run . Treat them as two separate layers — losing the bridge's filter does not remove SDK-level bash permissions and hooks, and neither substitutes for broker-side controls.

What is the difference between Claude Code Channels and the claude-code-telegram community bridge?

Channels is Anthropic's official plugin, shipped March 20, 2026, requiring Claude Code v2.1.80+ and, during the research preview, accepting only plugins from the claude-plugins-official allowlist via a roughly six-step setup . The claude-code-telegram bridge is self-hosted — its main pyproject.toml reports version 1.6.0 — and adds audit logs, HMAC/Bearer webhook auth, directory sandboxing, and per-user session persistence . Because the README install snippet recommends tag v1.3.0 while main is at 1.6.0, pin to v1.3.0 until you have vetted a specific release.

Does Robinhood's agentic account support crypto trading yet?

Not yet. As of the May 27, 2026 launch, Robinhood's Agentic Trading is equities-only beta, running through AI-native MCP servers with dedicated agentic accounts and disconnect/pause controls . A July 1, 2026 announcement confirmed that US agentic crypto accounts are rolling out soon, but no firm date was given . If you need crypto today, wire an exchange directly through CCXT or a native SDK instead of waiting on the Robinhood path.

What happens if Telegram goes down while a position is open?

Nothing should break, because Telegram is a console, not a safety system. Safety-critical state must live server-side with idempotency keys, order IDs, timestamps, and broker confirmations — callback data alone is not durable authorization. A time-based force-close (for example, 3:51 ET) and a lock file that survives a restart let the agent halt safely without chat connectivity, and the reference IBKR build wraps every Telegram call in try/except so a notification failure can never interrupt trade logic . Design so that losing the chat layer degrades alerts, never risk enforcement.

Can I use IBKR instead of Robinhood or Alpaca?

Yes, but it is operationally heavier. In the local route the bot connects to TWS on port 7497 for paper, while production guides insist on headless IB Gateway (ports 4002 paper / 4001 live) . The Client Portal Web API adds more friction: sessions time out after roughly 6 minutes without a /tickle, global pacing is 10 req/s, /iserver/account/orders GET is capped at 1 per 5 seconds, and violators land in a 15-minute penalty box . IBKR suits serious production use more than quick iteration; for fast prototyping, Alpaca's free paper account or Robinhood's zero-wiring MCP route is lighter.

Enjoyed this article? Subscribe to get new stories by email whenever they're published.

Subscribe