How to Get Notified When Claude Code Finishes a Task: 3 MCP Messaging Approaches

Claude Code ran for 20 minutes while you grabbed coffee. How do you know it finished? Three notification approaches compared: Hooks for quick scripts, Channels for two-way chat, and Hermes MCP for cross-platform bridge messaging.

How to Get Notified When Claude Code Finishes a Task: 3 MCP Messaging Approaches technical illustration for AI Workflow Pro readers
How to Get Notified When Claude Code Finishes a Task: 3 MCP Messaging Approaches technical illustration for AI Workflow Pro readers

Claude Code just ran for 20 minutes. You stepped away for coffee. When you come back, the task finished 10 minutes ago. Wasted time.

Claude Code product logo by Anthropic

This is the single most common friction point for anyone running long Claude Code sessions — SEO audits, code refactors, batch file processing. You need a reliable way to know when it is done, without babysitting the terminal.

Three approaches solve this today, each at a different complexity level:

Approach How It Works Two-Way? Works with Codex? Setup Effort
Hooks Stop event triggers a shell script One-way push No Minimal
Channels Bun plugin pushes events into a Claude Code session Full two-way chat No Low
Hermes MCP Bridge Claude Code calls a remote MCP server to send messages One-way (extensible to two-way) Yes Moderate

I have been running all three in production across multiple machines since early 2026. Here is what actually works, what breaks, and which one fits your workflow.


What Are Hooks, and When Do They Fall Short?

Hooks are Claude Code's built-in lifecycle event system, documented in Anthropic's official Hooks reference. The Stop event fires after Claude completes each response turn, triggering any shell command you configure.

Which Events Matter for Notifications?

Event When It Fires Notification Use
Stop Claude finishes a response turn Task completion (most common)
Notification Claude waits for user input "Come back and look" reminder
SubagentStop A sub-agent finishes Parallel branch completion
StopFailure API error causes termination Error alerting

How Does a Basic Hooks Notification Work?

Configure a Stop event hook in ~/.claude/settings.json pointing to a custom notification script:

Prompt: Generate Hooks Stop event config

Generate ~/.claude/settings.json Hooks configuration. Requirements:

  • Listen on the Stop event
  • Type command, execute bash ~/.claude/hooks/notify.sh
  • JSON structure follows Claude Code Hooks spec (hooks -> event name -> array -> hooks array -> single hook object)

The notification script ~/.claude/hooks/notify.sh receives Stop event JSON via stdin, checks whether to notify, then triggers a desktop alert:

Prompt: Generate macOS desktop notification script

Generate ~/.claude/hooks/notify.sh for Claude Code Stop Hook. Requirements:

  • Bash script, set -euo pipefail
  • Read JSON from stdin
  • Check stop_hook_active field — exit 0 immediately when true (prevents infinite loops; Claude Code force-overrides after 8 consecutive blocks)
  • Extract stop_reason (default "unknown") and stop_message (default "Claude stopped", truncate to 300 chars) via jq
  • Fire macOS desktop notification via osascript display notification, title "Claude Code", subtitle shows stop_reason
  • Play system alert sound /System/Library/Sounds/Hero.aiff via afplay in background

Five minutes, zero dependencies. But you must be at your computer to see the notification.

What Does the Stop Event JSON Look Like?

Understanding the input structure helps you write precise notification scripts. Claude Code pipes this JSON to your script via stdin:

{
  "session_id": "abc123",
  "stop_reason": "end_turn",
  "stop_message": "I finished all modifications, processed 47 files...",
  "stop_hook_active": false
}

Key fields:

  • stop_reason — why Claude stopped. end_turn means normal completion, max_tokens means output limit reached, tool_use means a tool call is needed. Notification scripts typically only push on end_turn
  • stop_message — the text content of Claude's last message. Truncate to ~300 characters for notification body
  • stop_hook_active — whether the hook is already inside a loop. If true, your script must exit immediately (exit 0), otherwise you trigger an infinite loop. Claude Code force-overrides after 8 consecutive blocks with no progress

On Linux, replace osascript with notify-send:

# Linux desktop notification
notify-send "Claude Code" "$MESSAGE" --icon=dialog-information

How Do You Push Hooks Notifications to Your Phone?

Add a Telegram Bot API call to the script:

Prompt: Generate Telegram push notification script

Generate a Claude Code Stop Hook Telegram push script. Requirements:

  • Bash script, set -euo pipefail
  • Read JSON from stdin, exit immediately if stop_hook_active is true
  • Extract stop_reason and stop_message (truncate to 300 chars) via jq
  • Define TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID variables (leave placeholders for user)
  • Async curl call to Telegram Bot API sendMessage, message format Claude Code [{reason}]: {message}
  • curl runs in background (&), stdout to /dev/null, never blocks Hook execution

Why Hooks Are Not Enough

From running Hooks for several months, these are the real limitations:

  1. One-way only — pushes out, no way to reply or control Claude from your phone
  2. Coarse trigger granularity — fires after every response turn, not just "real" task completions. Claude commits then pushes: you get two notifications
  3. Local shell dependency — scripts run on the Claude Code machine, need curl and jq pre-installed
  4. No message history — fire-and-forget, no conversation context
  5. Codex incompatible — Hooks are Claude Code exclusive; OpenAI Codex cannot use them
  6. Block limit — Claude Code force-overrides after 8 consecutive blocks with no progress

Best fit: solo developers who just need a basic reminder and never need to reply from their phone.


What Are Channels, and How Do They Enable Two-Way Chat?

Channels launched as a Research Preview in March 2026. The core idea: push external events into a running Claude Code session, enabling two-way conversation between your phone and your local Claude Code instance.

How Does the Architecture Differ from Standard MCP?

Standard MCP flow has Claude actively querying external data. Channels reverse that direction:

MCP architecture diagram of a host client connecting to local and remote servers
  • Standard MCP: User instruction -> Claude -> calls MCP server -> returns data -> Claude responds
  • Channels: External message -> Channel plugin (local Bun script) -> pushed into Claude Code session -> Claude processes -> replies through Channel

Each Channel is essentially a local MCP server that polls an external platform for messages and pushes them into Claude Code via the notifications/claude/channel event.

Which Platforms Does Channels Support?

Platform Launch Date Status
Telegram 2026-03-20 Research Preview
Discord 2026-03-20 Research Preview
iMessage 2026-03-27 Research Preview

How Do You Set Up Channels with Telegram?

# 1. Install the plugin
/plugin install telegram@claude-plugins-official

# 2. Configure your Bot Token
/telegram:configure <your-bot-token>

# 3. Start Claude Code with Channels enabled
claude --channels plugin:telegram@claude-plugins-official

# 4. Find your Bot in Telegram, send any message to get a pairing code

# 5. Pair in Claude Code
/telegram:access pair <code>

# 6. Lock down to allowlist only
/telegram:access policy allowlist

After pairing, messages you send in Telegram go directly into the Claude Code session. Claude's replies appear in the same Telegram chat.

What Can You Do with Channels?

  1. True two-way communication — send messages from your phone, get Claude's response in the same chat
  2. Full file system access — Channel messages enter your local Claude Code session with complete filesystem and Git permissions
  3. Remote permission approval — permission prompts forward to your phone for remote approve/deny
  4. Session context persistence — maintains conversation context for the session lifetime
  5. Event-driven — not limited to chat. Can receive CI webhooks, monitoring alerts, and other external events

Where Do Channels Fall Short?

  1. Research Preview — syntax and protocol may change
  2. Auth lock-in — requires claude.ai or Console API key authentication; no Bedrock or Vertex support
  3. Bun dependency — official plugins are Bun scripts
  4. Session-bound — events arrive only while the session is open, not a persistent background service
  5. Codex incompatible — Claude Code exclusive
  6. Single session binding — one Channel connects to exactly one Claude Code session

How Do Channels Compare to Other Remote Options?

Approach Purpose File Access
Channels Push external events into a local session Full (local filesystem)
Claude Code on Web Cloud sandbox async tasks Cloud sandbox
Remote Control Drive a local session from claude.ai Full (local)

If you need to send messages from your phone to a running Claude Code instance, Channels is the most direct path. If you need Claude Code to notify you after it finishes, Hooks or Hermes MCP are more straightforward.

Best fit: users who need remote control of Claude Code from their phone, want two-way conversation, and are all-in on the Anthropic ecosystem.


Hermes MCP Bridge Messaging Explained

Hermes Agent (open-sourced by Nous Research) is a multi-platform messaging gateway AI agent. Since v0.16.0, it exposes itself as an MCP server via hermes mcp serve. Claude Code and Codex connect to Hermes over SSH and call messages_send to push notifications to any connected messaging platform.

Model Context Protocol official architecture overview documentation page

This is a reverse bridge architecture: instead of external messages pushing into Claude, Claude actively calls Hermes tools to send messages out when needed.

How Does Hermes MCP Differ from Channels?

Dimension Channels Hermes MCP Bridge
Message direction External -> Claude Code Claude Code -> External
Two-way? Native two-way One-way notification (can read messages via events_poll)
Where it runs Plugin runs locally Hermes runs on a remote machine
Platform support Telegram / Discord / iMessage Discord / Telegram / Slack / WhatsApp / Signal / Matrix
Auth dependency Requires Anthropic auth None — any MCP client works
Persistence Dies with the session Hermes runs 24/7 as a background service
Tool count Channel protocol (send + reply) 10 full tools
Codex compatible? No Yes

How Does SSH stdio Bridging Work?

SSH stdio is a lightweight method to expose a remote MCP server to a local MCP client. Claude Code spawns an ssh subprocess, and SSH transparently bridges local and remote stdin/stdout:

MCP architecture diagram showing host, client, transport layer, and server pool
Local Claude Code
    |
    | Spawns: ssh [email protected] hermes mcp serve
    |
    |-- stdin  --> SSH tunnel --> remote hermes stdin  (JSON-RPC requests)
    |
    +-- stdout <-- SSH tunnel <-- remote hermes stdout (JSON-RPC responses)

Why this works well:

  • No extra ports needed — reuses SSH port 22
  • Reuses existing SSH key auth, no extra credentials
  • Encrypted transport
  • To Claude Code, Hermes looks like a local MCP server — completely transparent

Which Tools Does Hermes MCP Expose?

Hermes exposes 10 tools. Notification workflows primarily use the first four:

Tool Purpose Notification Usage
messages_send Send a message to a specific platform/channel Core — called every notification
channels_list List available messaging channels and targets Called during initial setup verification
conversations_list List all active conversations across platforms When you need to pinpoint a specific target
messages_read Read recent messages from a conversation Check context before sending a notification
events_poll Poll for new events (messages, approval requests) Used in two-way interaction scenarios
events_wait Long-poll for new events (blocks up to 30s) Used in two-way interaction scenarios
permissions_list_open List pending approval requests Remote permission management
permissions_respond Respond to an approval request Remote permission management

How Do You Call messages_send?

The target format is platform:identifier, with human-friendly channel name resolution:

# Send notification to a Discord channel
mcp__hermes__messages_send(
    target="discord:#headquarters",
    message="SEO audit complete. Found 12 broken links. Report saved."
)

# Send notification to a Telegram private chat
mcp__hermes__messages_send(
    target="telegram:6308981865",
    message="Code refactor done. 47 files updated. All tests pass."
)

How Do You Verify the Connection?

Use channels_list during initial setup to confirm everything works:

# List all Discord channels
mcp__hermes__channels_list(platform="discord")
# Returns: [{target: "discord:#headquarters", ...}, ...]

# List channels across all platforms
mcp__hermes__channels_list()

Setting Up Hermes MCP Bridge Step by Step

Prerequisites

  1. A persistent machine (Mac Mini, Linux VPS, or WSL) with Hermes Agent installed
  2. Passwordless SSH from your dev machine to the Hermes host
  3. Hermes connected to at least one messaging platform (Discord, Telegram, Slack, etc.)

If Hermes and Claude Code run on the same machine, skip SSH and use local stdio directly.

Claude Code documentation on connecting to tools through MCP servers

Step 1: Deploy Hermes Agent

pipx install hermes-agent[messaging]
hermes --version

After installation, initialize the config, connect a messaging platform, and start the gateway:

Prompt: Generate Hermes Agent initialization and gateway startup

Complete Hermes Agent initialization. Requirements:

  • Run hermes init to generate initial config
  • Configure messaging platform credentials in ~/.hermes/config.yaml (use Discord Bot Token as example)
  • Start the gateway: hermes gateway start
  • Verify: hermes gateway status

Step 2: Set Up SSH Key Authentication

# On your local dev machine
ssh-keygen -t ed25519 -C "claude-code-hermes"

# Copy public key to Hermes machine
ssh-copy-id [email protected]

# Verify passwordless login
ssh [email protected] "hermes mcp serve --help"

Step 3: Configure Claude Code MCP Server

Edit ~/.claude.json and add the Hermes SSH stdio config under mcpServers:

Prompt: Generate Claude Code Hermes MCP Server config

Generate the mcpServers.hermes config for ~/.claude.json. Requirements:

  • type set to stdio (stdin/stdout transport, JSON-RPC 2.0 communication)
  • command set to ssh
  • args array: -o StrictHostKeyChecking=no (local network; remove for public internet), SSH target [email protected], remote Hermes binary path (e.g., /Users/user/.local/bin/hermes), and mcp serve
  • For same-machine setup: skip SSH args, point command directly to the local Hermes path, args is just ["mcp", "serve"]

Step 4: Set Permission Allowlist

Edit ~/.claude/settings.json and add Hermes tools to the permission allowlist:

Prompt: Generate Hermes permission allowlist config

Add Hermes tool permissions to ~/.claude/settings.json under permissions.allow. Options:

  • List individual tools: mcp__hermes__messages_send, mcp__hermes__channels_list, mcp__hermes__conversations_list, mcp__hermes__messages_read, mcp__hermes__events_poll
  • Or use prefix wildcard "mcp__hermes" to allow all Hermes tools at once

Step 5: Configure Codex (Optional)

If you also use OpenAI Codex, add the Hermes MCP server config to ~/.codex/config.toml:

Prompt: Generate Codex Hermes MCP Server config

Generate [mcp_servers.hermes] in ~/.codex/config.toml. Requirements:

  • command set to ssh
  • args array matches Claude Code: -o StrictHostKeyChecking=no, SSH target, remote Hermes path, mcp serve
  • startup_timeout_sec = 15.0 (SSH connection + Hermes init takes time)
  • tool_timeout_sec = 60.0 (message delivery may need network time)

Codex needs no separate permission allowlist.

Step 6: Verify the Connection

In Claude Code, type:

List available Hermes messaging channels

Claude calls channels_list. If it returns your Discord/Telegram channel list, the connection is live.

Step 7: Trigger a Notification

Run an SEO audit and notify me on Discord when done

After completing the task, Claude Code automatically calls:

mcp__hermes__messages_send(
  target="discord:#headquarters",
  message="SEO audit complete. Found 12 broken links. Report saved."
)

Three Approaches Compared Side by Side

Dimension Hooks Channels Hermes MCP Bridge
Provider Anthropic (official) Anthropic (official) Nous Research (open source)
Maturity Stable release Research Preview v0.16.0
Direction Agent -> user (one-way) User <-> Agent (two-way) Agent -> platforms (extensible)
Platforms Any (shell script can reach) Telegram / Discord / iMessage Discord / Telegram / Slack / WhatsApp / Signal / Matrix
Codex support No No Yes
Setup complexity Minimal (one shell script) Low (Bun plugin) Moderate (standalone Hermes deploy)
Auth dependency None Anthropic auth required None (SSH keys)
Persistence Dies with session Dies with session 24/7 background service
Tool richness None (pure script) Channel protocol 10 complete tools
Remote permissions No Yes Yes
Trigger Automatic (event-driven) External push-in On-demand (user-instructed)
Context retention None (fire-and-forget) Yes (session-scoped) Yes (Hermes has independent memory)

Which Approach Fits Which Scenario?

Scenario A: 30-Minute SEO Audit While You Step Out

Step Hooks Channels Hermes MCP
Start task claude -p "Audit SEO dead links" claude --channels plugin:telegram -p "Audit SEO" claude -p "Audit SEO dead links, notify me on Discord when done"
Leave desk Walk away Walk away Walk away
Task completes Desktop notification Telegram gets full results Discord gets message
Review Must return to computer Read results on phone Read results on phone
Follow-up Must use terminal Ask follow-ups in Telegram Ask follow-ups via Hermes in Discord

Scenario B: 20-Minute Codex Code Refactor

Approach Available?
Hooks Codex does not support it
Channels Codex does not support it
Hermes MCP Fully available via config.toml

Hermes MCP bridge is the only on-demand notification method currently available for Codex.

Scenario C: Multi-Brand Content Distribution Notifications

In my workflow, I run multiple brand channels on Discord. Hermes routes notifications to the correct channel automatically — SEO updates go to #website-ops, content publishing goes to #content-pipeline. Hooks sends the same generic notification regardless. Channels pushes everything to one Telegram chat.

The channel isolation matters when you manage multiple projects. Without it, notifications from different workstreams pile up in a single thread, and you lose context.


What Is the Best Practice for Triggering Notifications?

Hermes MCP uses an on-demand design: Claude Code only calls messages_send when you explicitly say "notify me." This zero-overhead approach means:

  • Short tasks (under 2 minutes): skip notifications, just wait for the result
  • Medium tasks (2-10 minutes): personal preference, a sound alert may suffice
  • Long tasks (over 10 minutes): configure phone notifications

What Format Does the Message Target Use?

The messages_send target parameter follows platform:identifier:

discord:#channel-name
telegram:chat-id
slack:#channel-name

Human-friendly channel names resolve automatically. Run channels_list first to see all available targets.

How Does Hermes Compare to Other MCP Notification Servers?

The community has built several notification-focused MCP servers, each with a different focus:

Server Focus Best For
ntfy-mcp-server ntfy push, cross-device topic subscriptions Simplest possible deployment
omni-notify-mcp Multi-platform notifications + two-way + TTS Multiple channels without a full agent
telegram-notification-mcp Telegram-only, Cloudflare Workers deployment Telegram-exclusive users
Hermes MCP Full agent + messaging gateway + knowledge base integration Agent capabilities + multi-platform routing

How Do You Troubleshoot Common Issues?

MCP Server Connection Timeout on Startup

Symptom: Claude Code reports MCP server hermes failed to start or times out.

Debug steps:

# 1. Verify SSH connection
ssh [email protected] "echo ok"

# 2. Verify remote Hermes is executable
ssh [email protected] "hermes --version"

# 3. Verify MCP serve mode
ssh [email protected] "hermes mcp serve --help"

# 4. Check Hermes path (pipx installs to ~/.local/bin/)
ssh [email protected] "which hermes"

Common causes: SSH key not configured for passwordless login (hangs waiting for password), Hermes install path mismatches the path in ~/.claude.json, or firewall blocks SSH port 22.

messages_send Succeeds but No Notification on Phone

Symptom: Claude Code shows successful tool call, but Discord/Telegram has no new message.

Debug steps:

# 1. Check Hermes gateway status
hermes gateway status

# 2. Check messaging platform connections
hermes status

# 3. Review gateway logs
tail -30 ~/.hermes/logs/gateway.log

# 4. Check error logs
cat ~/.hermes/logs/gateway.error.log

Common causes: Hermes gateway not running (hermes gateway start), Discord Bot Token expired or lacks permissions, Telegram Bot banned or Chat ID incorrect.

First Message Takes ~20 Seconds

Symptom: First Hermes MCP call is slow; subsequent calls are fast.

Cause: Cold start — SSH connection + Hermes process initialization + model API endpoint probing.

Fixes:

  1. Set explicit model endpoint URLs in Hermes .env to skip auto-probing
  2. Increase startup_timeout_sec to 15-20 seconds in Codex config.toml
  3. Enable SSH connection multiplexing:

Prompt: Generate SSH connection multiplexing config

Add Hermes server connection multiplexing to ~/.ssh/config. Requirements:

  • Host alias hermes-server
  • ControlMaster auto (reuse existing connections)
  • ControlPath ~/.ssh/sockets/%r@%h-%p (ensure ~/.ssh/sockets/ directory exists)
  • ControlPersist 600 (auto-close after 10 minutes idle)

Hermes Tools Not Visible in Claude Code

Symptom: ~/.claude.json configured but Hermes tools are not callable.

Checklist:

  1. Validate ~/.claude.json JSON syntax
  2. Restart Claude Code to reload config
  3. Confirm ~/.claude/settings.json includes "mcp__hermes" in permissions.allow
  4. Run /mcp inside Claude Code to list connected MCP servers

How Should You Choose Between Hooks, Channels, and Hermes MCP?

Do you need task completion notifications from Claude Code / Codex?
|-- Yes
|   |-- Using Codex?
|   |   |-- Yes --> Hermes MCP (only option)
|   |   +-- No (Claude Code)
|   |       |-- Need two-way chat (control Claude from your phone)?
|   |       |   |-- Yes --> Channels (Telegram / Discord / iMessage)
|   |       |   +-- No (one-way notification only)
|   |       |       |-- Want it running in 5 minutes? --> Hooks + shell script
|   |       |       +-- Need multi-platform notifications? --> Hermes MCP
|   |       +-- Need external events to trigger Claude Code actions?
|   |           |-- Yes --> Channels (event-driven architecture)
|   |           +-- No --> follow the tree above
+-- No --> no configuration needed

The bottom line:

  • Fastest setup: Hooks — one script, desktop notifications in 5 minutes
  • Official two-way: Channels — remote control Claude Code from your phone
  • Most flexible: Hermes MCP — cross-client, cross-platform, programmable routing

All three are non-conflicting. Run them simultaneously if your workflow demands it.



Frequently Asked Questions

Can I use Hooks, Channels, and Hermes MCP at the same time?

Yes. All three are independent. A practical combination: Hooks handles local afplay sound alerts, Channels provides Telegram two-way chat, Hermes MCP routes notifications to specific Discord channels. Pick what fits each use case.

Does Hermes MCP affect Claude Code performance?

No. hermes mcp serve starts only when called via SSH. Claude Code never contacts Hermes on its own — it fires only when you explicitly say "notify me." Zero-overhead design.

Is the SSH stdio connection secure?

SSH tunnels are end-to-end encrypted, matching standard SSH session security. The critical factor is SSH key management: ensure only authorized machines can connect to the Hermes host. Use StrictHostKeyChecking=no on local networks; enable strict verification for public networks.

Do I need a separate server to run Hermes MCP?

No. Hermes Agent runs on any Python 3.12+ machine — Linux VPS, WSL, another Mac. You can also run Hermes and Claude Code on the same machine. Skip SSH entirely: set command to the local Hermes path and args to ["mcp", "serve"].

Can Codex use any notification method?

OpenAI Codex does not support Hooks or Channels. Hermes MCP reverse bridge is the only on-demand notification method available for Codex today. Configure it through ~/.codex/config.toml for identical notification capability.

Will the Stop Hook fire too often?

Yes. The Stop event fires after every Claude response turn, not just real task completions. Three solutions:

  1. Check stop_reason in your script — only push on end_turn
  2. Add a duration threshold — only notify after tasks exceeding N minutes
  3. Switch to Hermes MCP — it only fires when you say "notify me," eliminating false triggers entirely

— Leo

Successfully subscribed! Check your inbox for confirmation.

Successfully subscribed! Check your inbox for confirmation.

Successfully subscribed! Check your inbox for confirmation.

Successfully subscribed! Check your inbox for confirmation.

Done.

Cancelled.