Stay connected to your Claude Code sessions from anywhere. Get notifications via Email or Telegram when tasks complete, approve operations remotely, and respond to prompts from any device.
When Claude Code needs your input—a question, approval, or notification—you shouldn't have to be tethered to your terminal.
| Scenario | Without | With Claude Code Anywhere |
|---|---|---|
| Task completes | Sit and wait, or miss it | Get notified instantly |
| Claude asks a question | Session blocks until you notice | Reply from your phone |
| Destructive operation | Must be at terminal to approve | Approve via email: "Y" |
| Long-running task | Keep checking back | Do other things, get pinged |
The result: Run background tasks with confidence. Walk away. Stay connected.
Click to expand
| Feature | Description |
|---|---|
| Multi-Channel | Email, Telegram, or both simultaneously |
| Bidirectional | Send notifications AND receive replies |
| Multi-Session | Track multiple Claude Code sessions independently |
| Easy Toggle | /cca on/off (per-session) or /cca on --all/off --all (global) |
| Provider Flexible | Any SMTP/IMAP email provider |
| 85% Test Coverage | Production-ready with comprehensive tests |
%%{init: {'theme': 'default'}}%%
flowchart LR
subgraph Claude Code
CC[Claude Code] --> Hook[Hook Event]
end
subgraph Bridge Server
Hook --> |HTTP POST| Server[Bridge Server]
Server --> CM[Channel Manager]
CM --> Email[Email Channel]
CM --> TG[Telegram Channel]
end
subgraph User Devices
Email --> |SMTP| Inbox[Email Inbox]
TG --> |Bot API| Phone[Telegram App]
Inbox --> |Reply| Email
Phone --> |Reply| TG
end
Email --> |IMAP Poll| Server
TG --> |Long Poll| Server
Server --> |Response| CC
%%{init: {'theme': 'dark'}}%%
sequenceDiagram
participant CC as Claude Code
participant BS as Bridge Server
participant CH as Channel (Email/Telegram)
participant U as User
CC->>BS: Hook triggered (notification/approval)
BS->>CH: Send via all enabled channels
CH->>U: Notification delivered
Note over U: User reads and replies
U->>CH: Reply message
CH->>BS: Poll detects reply
BS->>CC: Return response to hook
Note over CC: Claude continues with user input
claude /plugin add github.com/blueraai/claude-code-anywhereOn first session, you'll see a one-time message explaining the two notification modes:
| Mode | Description | Best For |
|---|---|---|
| Session-Only | Run /cca on in each terminal |
Trying it out, occasional use |
| Global | All sessions automatically connected | Daily use, multiple projects |
What's the difference?
Session-Only (default):
/cca onenables notifications for the current session only/cca offdisables only the current session (server keeps running for others)- Use
/cca on --allor/cca off --allfor global enable/disable - Server can be shared across multiple sessions
Global:
- Notifications work in ALL Claude sessions automatically
- Background daemon runs persistently (survives reboots)
- Reply from your phone → routes to the correct session
- Installs: PATH shim + background service + one line in .zshrc/.bashrc
To enable global mode:
/cca-installCreate ~/.claude/claude-code-anywhere/config.json:
{
"telegram": {
"botToken": "123456789:ABCdef...",
"chatId": "123456789"
},
"email": {
"user": "claude-cca@gmail.com",
"pass": "xxxx-xxxx-xxxx-xxxx",
"recipient": "you@example.com"
}
}You can configure one or both channels. See Configuration for details.
/cca on # Starts server (session-only mode)
/cca-test # Sends test message to all configured channels
/cca-doctor # Diagnose installation and show current modeGmail Setup
Step 1: Create a Dedicated Account
Create a new Gmail account for Claude (e.g., my-claude-cca@gmail.com). Using a dedicated account keeps your personal inbox clean and is more secure.
Step 2: Enable 2-Factor Authentication
- Go to Google Account Security
- Enable 2-Step Verification
Step 3: Create App Password
- Go to Google App Passwords
- Select "Mail" and your device
- Copy the 16-character password (no spaces)
Step 4: Add to .env
EMAIL_USER=my-claude-cca@gmail.com
EMAIL_PASS=abcdefghijklmnop # The app password (no spaces)
EMAIL_RECIPIENT=your-real-email@example.comOther Email Providers
Outlook/Office 365:
SMTP_HOST=smtp.office365.com
SMTP_PORT=587
IMAP_HOST=outlook.office365.com
IMAP_PORT=993Yahoo:
SMTP_HOST=smtp.mail.yahoo.com
SMTP_PORT=465
IMAP_HOST=imap.mail.yahoo.com
IMAP_PORT=993Custom SMTP/IMAP:
SMTP_HOST=mail.example.com
SMTP_PORT=587
IMAP_HOST=mail.example.com
IMAP_PORT=993Telegram Setup Guide
Step 1: Create a Bot
- Open Telegram and search for @BotFather
- Send
/newbotand follow the prompts:- Choose a display name (e.g., "Claude Code Anywhere")
- Choose a username ending in
bot(e.g.,my_claude_cca_bot)
- BotFather will reply with your bot token:
Use this token to access the HTTP API: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz - Copy the entire token (including the colon)
Step 2: Get Your Chat ID
- Search for @userinfobot and start a chat
- It will immediately reply with your user info:
Id: 123456789 - Copy the numeric ID
Step 3: Start Your Bot
- Search for your new bot by its username
- Click "Start" or send
/start— required before the bot can message you
Step 4: Add to .env
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
TELEGRAM_CHAT_ID=123456789Replying to Notifications
Approval requests (PreToolUse events): Tap the YES or NO button directly — no typing needed! The buttons encode the session ID, so they always target the correct session even with multiple concurrent sessions.
Other notifications: Send a text message to respond.
- Single session: Just send a message — it links to the most recent notification.
- Multiple concurrent sessions: Use Telegram's reply feature (swipe left on mobile, or right-click → Reply on desktop) to target the correct session. Without explicit reply, your message goes to whichever session sent the most recent notification.
Why the difference from Email? Email automatically threads via subject line (
[CC-xxx]) and In-Reply-To headers. Telegram doesn't have threading for regular messages, so it relies on inline keyboard buttons for approvals, or explicit replies /[CC-xxx]prefix for text responses.
Show current server and channel status, or toggle notifications.
Example output
Notification Status
| Status | |
|---|---|
| Server | ✅ Running |
| Uptime | ~8 minutes |
| Active sessions | 1 |
| Pending responses | 0 |
Channels
| Channel | Config | Enabled | Connected | Last Activity | Error |
|---|---|---|---|---|---|
claudecode@gmail.com → you@example.com |
✅ | ✅ | - | - | |
| Telegram | Chat ID: 123456789 |
✅ | ✅ | - | - |
Enable notifications for the current session. Starts the server if not already running.
Use /cca on --all to enable notifications for all sessions.
Example output
Notifications enabled for this session.
Notification Status
| Status | |
|---|---|
| Server | ✅ Running |
| Uptime | just started |
| Active sessions | 1 |
| Pending responses | 0 |
Channels
| Channel | Config | Enabled | Connected | Last Activity | Error |
|---|---|---|---|---|---|
claudecode@gmail.com → you@example.com |
✅ | ✅ | - | - | |
| Telegram | Chat ID: 123456789 |
✅ | ✅ | - | - |
Disable notifications for the current session. The server keeps running for other sessions.
Use /cca off --all to disable notifications for all sessions and stop the server.
Example output
Notifications disabled for this session. Server still running for other sessions.
Install global notification support for all Claude sessions.
What it does
- Creates a PATH shim at
~/.claude-code-anywhere/bin/claude - Adds the shim to your PATH (in .zshrc/.bashrc)
- Installs the plugin to
~/.claude-code-anywhere/plugins/ - Sets up a background daemon (launchd on macOS, systemd on Linux)
After installation, restart your shell. All future claude sessions will automatically have notifications enabled.
macOS Note: You'll see a system notification: "Software from 'Jarred Sumner' can run in the background." This is expected — Jarred Sumner is the creator of Bun, the JavaScript runtime we use. Allow it in System Settings → Login Items to keep the daemon running.
Windows: Global installation is not yet supported. Use session-only mode with /cca on.
Remove global installation and revert to session-only mode.
What it removes
- PATH shim at
~/.claude-code-anywhere/bin/claude - PATH entry from .zshrc/.bashrc
- Background daemon (launchd/systemd)
- Plugin copy at
~/.claude-code-anywhere/plugins/
Your .env configuration is preserved as a backup.
Send a test message to all configured channels.
Example output
Test notification sent!
Sent to 2 channels:
- ✅ Telegram
Check your inbox/Telegram for the test message.
Add or remove the CCA status indicator from your Claude Code statusline.
/cca-statusline add # Add indicator to statusline
/cca-statusline remove # Remove indicator from statusline
Example output
CCA indicator added to statusline.
Your statusline will now show:
- CCA (green) when notifications are active (global enabled OR session enabled)
- cca (dim gray) when notifications are inactive for this session
Restart Claude Code or wait for statusline refresh to see the change.
Diagnose installation and configuration issues. Especially useful after global installation.
Example output
╭─────────────────────────────────────────╮
│ Claude Code Anywhere - Diagnostics │
╰─────────────────────────────────────────╯
## PATH Check
✅ Shim is first in PATH: ~/.claude-code-anywhere/bin/claude
Real claude: ~/.local/bin/claude
## Service Status
✅ Daemon running (launchd: com.claude.code-anywhere)
API: http://localhost:<dynamic-port>
## Plugin Installation
✅ Plugin installed: vX.Y.Z
Path: ~/.claude-code-anywhere/plugins/claude-code-anywhere
## Channels
✅ Email: you@example.com
✅ Telegram: Chat ID 123456789
For production use, store credentials in ~/.claude/claude-code-anywhere/config.json:
{
"telegram": {
"botToken": "123456789:ABCdefGHIjklMNOpqrsTUVwxyz",
"chatId": "123456789"
},
"email": {
"user": "claude-cca@gmail.com",
"pass": "xxxx-xxxx-xxxx-xxxx",
"recipient": "you@example.com"
}
}You can configure one or both channels. The config file takes priority over environment variables.
Environment variables can be used for development (via .env file) or to override the config file:
| Variable | Required | Default | Description |
|---|---|---|---|
| Email Channel | |||
EMAIL_USER |
† | - | Sending account (Claude's email) |
EMAIL_PASS |
† | - | App password |
EMAIL_RECIPIENT |
† | - | Your email for notifications |
SMTP_HOST |
No | smtp.gmail.com | SMTP server |
SMTP_PORT |
No | 587 | SMTP port |
IMAP_HOST |
No | imap.gmail.com | IMAP server |
IMAP_PORT |
No | 993 | IMAP port |
EMAIL_POLL_INTERVAL_MS |
No | 5000 | Reply check interval |
| Telegram Channel | |||
TELEGRAM_BOT_TOKEN |
† | - | Bot token from @BotFather |
TELEGRAM_CHAT_ID |
† | - | Your chat ID |
| Server | |||
BRIDGE_PORT |
No | dynamic | Bridge server port (auto-selects available) |
BRIDGE_URL |
No | auto | Bridge URL (derived from port) |
| Logging | |||
LOG_MAX_SIZE_MB |
No | 10 | Max log file size before rotation (MB) |
LOG_MAX_ROTATED_FILES |
No | 5 | Number of rotated logs to keep |
† At least one channel must be fully configured (all † vars for that channel), either via config file or env vars.
Settings persist in ~/.claude/claude-code-anywhere/state.json:
{
"enabled": true,
"hooks": {
"Notification": true,
"Stop": true,
"PreToolUse": true,
"UserPromptSubmit": false
}
}On first session, a one-time message explains SESSION-ONLY vs GLOBAL modes. To suppress this:
| Method | How | Persists |
|---|---|---|
| Environment variable | export CLAUDE_CCA_AUTO=0 |
Per-shell |
| Sentinel file | touch ~/.config/claude-code-anywhere/disable-autoinstall |
Permanent |
| Event | When It Fires | Notification |
|---|---|---|
Notification |
Status updates, info messages | Sent immediately |
Stop |
Session/task ends | Sent immediately |
PreToolUse |
Before Bash/Write/Edit | Waits for your approval |
UserPromptSubmit |
Claude needs input | Waits for your response |
| Measure | Description |
|---|---|
| App Passwords | Never use your main email password |
| Dedicated Account | Use a separate email for Claude |
| Sender Verification | Only processes replies from EMAIL_RECIPIENT |
| Session Isolation | Session IDs prevent cross-session interference |
| Auto-Expiry | Sessions timeout after 30 minutes |
| No Secrets | Never sends credentials via notification |
Email Not Sending
- Verify config file exists:
cat ~/.claude/claude-code-anywhere/config.json - Ensure you're using an app password, not your main password
- For Gmail, verify 2FA is enabled
- Check logs:
tail -f ~/.claude/claude-code-anywhere/logs/*.log
Telegram Not Working
- Did you click "Start" on your bot? (Required!)
- Verify bot token and chat ID are correct
- Check logs for API errors
- Try
/cca-testto diagnose
Response Not Received
- For email: Reply from the correct address (
EMAIL_RECIPIENT) - For Telegram: Send a message in the bot chat (or use explicit reply)
- Check server logs for polling errors
- Ensure session hasn't expired (30 min timeout)
Server Won't Start
- Check if another instance is running:
lsof -i :$(cat ~/.claude-code-anywhere/plugins/claude-code-anywhere/port 2>/dev/null) - Server auto-selects available ports; check
portfile for actual port - Verify at least one channel is configured
PreToolUse Hook Blocking (Can't Type Commands)
If the PreToolUse approval hook is blocking and you can't interact with Claude (waiting for approval that never comes), use one of these fixes from a separate terminal:
Option 1: Enable global notifications
PORT=$(cat ~/.config/claude-code-anywhere/port) && curl -s -X POST "http://localhost:$PORT/api/enable"Option 2: Edit state file directly
# Enable global notifications
sed -i '' 's/"enabled": false/"enabled": true/' ~/.claude/claude-code-anywhere/state.jsonOption 3: Kill the server (disables all hooks)
pkill -f "bun run server"Why this happens: If global notifications are disabled but session is enabled, and the notification channels (Email/Telegram) aren't working, the hook waits for approval that never arrives. This was fixed in v0.6.22 but may occur if channels are misconfigured.
Global Installation Issues
-
Daemon not starting:
- macOS:
launchctl list | grep claude - Check logs:
ls ~/.claude-code-anywhere/plugins/claude-code-anywhere/logs/ - View latest:
cat ~/.claude-code-anywhere/plugins/claude-code-anywhere/logs/*.log | tail -50
- macOS:
-
Shim not first in PATH:
- Run
which claude— should show~/.claude-code-anywhere/bin/claude - Restart shell:
exec $SHELL - Verify PATH:
echo $PATH | tr ':' '\n' | head -5
- Run
-
Service won't load (macOS):
launchctl load ~/Library/LaunchAgents/com.claude.code-anywhere.plist launchctl print gui/$(id -u)/com.claude.code-anywhere
-
Run diagnostics:
/cca-doctor
Claude Code Remote is a feature-rich notification system that inspired some of our design. Here's how they compare:
| Feature | Claude Code Anywhere | Claude Code Remote |
|---|---|---|
| Channels | Email, Telegram | Email, Telegram, LINE, Desktop |
| Integration | Native hooks + HTTP | PTY relay + scripts |
| Setup | .env file |
Interactive wizard |
| Codebase | TypeScript, 85% tested | JavaScript |
| Telegram mode | Polling (simpler) | Webhook |
| Public URL needed | No | Yes (for webhooks) |
| Group support | 1:1 only | Groups supported |
| Focus | Clean, minimal, reliable | Feature-rich, flexible |
Both projects solve the same problem. We use Claude Code's native hook system; Claude Code Remote uses terminal injection for more direct control.
When to use Claude Code Anywhere:
- You want simple
.envconfiguration - Email and/or Telegram are sufficient
- You don't want to set up a public URL for webhooks
- You prefer a typed, tested codebase
- Plugin-based installation is preferred
When to use Claude Code Remote:
- You need LINE or desktop notifications
- You need group/team notifications
- You have a public URL for webhooks
- You want direct terminal control
- You prefer interactive setup wizards
git clone https://github.com/blueraai/claude-code-anywhere.git
cd claude-code-anywhere
bun install
bun run build| Command | Description | When to Use |
|---|---|---|
bun run build |
Compile TypeScript to dist/ | After code changes |
bun run dev |
Watch mode compilation | During active development |
bun run test |
Run tests in watch mode | During TDD/development |
bun run test:run |
Run tests once | Quick verification |
bun run test:coverage |
Run tests with coverage | Before committing |
bun run lint |
Run ESLint | Check code style |
bun run typecheck |
TypeScript type checking | Verify type safety |
bun run precommit |
Full validation (lint, types, tests, build) | Runs automatically on commit |
bun run version:patch |
Bump patch version (0.0.x) | Bug fixes |
bun run version:minor |
Bump minor version (0.x.0) | New features |
bun run version:major |
Bump major version (x.0.0) | Breaking changes |
bun run release |
Auto-detect version + tag + push | Release (recommended) |
bun run release:patch |
Force patch + tag + push | Override: bug fixes |
bun run release:minor |
Force minor + tag + push | Override: new features |
bun run release:major |
Force major + tag + push | Override: breaking changes |
claude-code-anywhere/
├── .claude-plugin/
│ └── plugin.json # Plugin manifest (canonical location)
├── commands/
│ ├── cca.md # /cca - toggle notifications
│ ├── cca-install.md # /cca-install - global mode setup
│ ├── cca-uninstall.md # /cca-uninstall - remove global mode
│ ├── cca-statusline.md # /cca-statusline - terminal indicator
│ ├── cca-test.md # /cca-test - send test notification
│ └── cca-doctor.md # /cca-doctor - diagnostics
├── hooks/
│ ├── hooks.json # Hook definitions
│ └── check-install.sh # SessionStart guidance hook
├── scripts/
│ ├── install.sh # Global installation script
│ └── uninstall.sh # Global uninstallation script
├── src/
│ ├── server/
│ │ ├── index.ts # Server entry
│ │ ├── channels.ts # Channel manager
│ │ ├── email.ts # Email channel
│ │ ├── telegram.ts # Telegram channel
│ │ ├── routes.ts # HTTP routes
│ │ └── sessions.ts # Session management
│ ├── shared/
│ │ ├── channel.ts # Channel interface
│ │ ├── config.ts # Configuration
│ │ └── types.ts # Type definitions
│ └── cli.ts # CLI entry
├── tests/ # Test files (85% coverage)
└── dist/ # Compiled output
Develop this plugin while using it with Claude Code:
claude --plugin-dir . # assumes running from repo rootChanges take effect on Claude Code restart (no reinstall needed).
Safety: Hook scripts check if the bridge server is running first (1 second timeout). If not running, they exit silently and don't block your workflow.
| Mode | Server | Behavior |
|---|---|---|
| Develop | Off | Hooks do nothing, develop normally |
| Test | Running + /cca on |
Full notification flow |
| Pause | Running + /cca off |
Hooks check and exit early |
${CLAUDE_PLUGIN_ROOT} is NOT available in most command contexts. This is a known Claude Code limitation (#9354, #12541).
| Context | CLAUDE_PLUGIN_ROOT Available |
|---|---|
| Hook scripts (directly invoked) | Yes |
Dynamic context (! backtick) |
No (tested) |
Plugin frontmatter (allowed-tools) |
Yes (v2.1.0+) |
| Bash commands from skill instructions | No |
Workaround: Bootstrap resolver script (community solution).
On first use in a project, Claude will run the bootstrap skill to create .claude/cpr.sh:
# The resolver script finds the plugin via:
# 1. CLAUDE_PLUGIN_ROOT (if available)
# 2. ~/.claude/plugins/installed_plugins.json lookup
# 3. Python fallback
# Commands then use:
PLUGIN_ROOT=$(.claude/cpr.sh)
cd "$PLUGIN_ROOT" && bun run server# Auto-detect version from conventional commits (recommended)
bun run release
# Or force a specific version bump
bun run release:patch # Force 0.0.x
bun run release:minor # Force 0.x.0
bun run release:major # Force x.0.0Auto-detection uses conventional commits: fix: → patch, feat: → minor, feat!:/BREAKING CHANGE: → major.
What happens automatically:
- Version bumped in package.json, plugin.json
- CHANGELOG.md generated from commits
- Git tag created and pushed
- GitHub Actions creates release
- Marketplace updated with new version
Note: The dist/ directory must be committed (plugins have no build step during installation).
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests (maintain 80%+ coverage)
- Submit a pull request
MIT — See LICENSE for details.
- Issues: GitHub Issues
- Changelog: CHANGELOG.md