Pyclaudius
A Python implementation of an openclaw like setup but using Claude directly
Ask AI about Pyclaudius
Powered by Claude Β· Grounded in docs
I know everything about Pyclaudius. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
pyclaudius
A personal Telegram bot that relays messages to the Claude CLI as a subprocess, giving full tool/MCP access. Inspired by claude-telegram-relay.
Setup
Prerequisites
- Python 3.12+
- uv
- Claude CLI installed and authenticated (
claudein PATH) - A Telegram bot token from @BotFather
- Your Telegram user ID from @userinfobot
Install
git clone <repo-url> && cd pyclaudius
uv sync --all-extras
Configure
Copy the example env file and fill in your values:
cp .env.example .env
See .env.example for all available options.
Run locally
uv run pyclaudius
Session commands
/helpβ list all available commands/clearβ end the current session and start fresh/compactβ compact the conversation context to free up context window space/contextβ show current context window usage
MCP Tools
pyclaudius runs an in-process MCP server that gives Claude direct tool access. A FastMCP HTTP server starts alongside the bot on 127.0.0.1, and at startup pyclaudius registers it with Claude CLI via claude mcp add --transport http --scope project. This lets Claude call tools mid-conversation, see results, and reason over them.
Tools are registered conditionally based on feature flags (MEMORY_ENABLED, CRON_ENABLED, EMAIL_ENABLED, BACKLOG_ENABLED).
Available tools
| Tool | Feature flag | Description |
|---|---|---|
remember_fact | MEMORY_ENABLED | Remember an important fact about the user |
forget_memory | MEMORY_ENABLED | Forget memories matching a keyword or by index |
list_memories | MEMORY_ENABLED | List all stored memory facts |
add_cron_job | CRON_ENABLED | Add a recurring cron job (5-field cron expression) |
schedule_once | CRON_ENABLED | Schedule a one-time task at a specific datetime |
remove_cron_job | CRON_ENABLED | Remove a scheduled job by index |
list_cron_jobs | CRON_ENABLED | List all scheduled cron jobs |
download_new_mail | EMAIL_ENABLED | Download unseen emails as markdown |
delete_read_mail | EMAIL_ENABLED | Delete all read emails from server |
list_backlog | BACKLOG_ENABLED | List pending backlog items |
clear_backlog | BACKLOG_ENABLED | Clear all pending backlog items |
replay_one | BACKLOG_ENABLED | Pop a single backlog item and return its prompt |
replay_backlog | BACKLOG_ENABLED | Pop all backlog items and return their prompts |
Adding your own tools
You can extend Claude with custom MCP tools by writing a plain Python function and registering it in pyclaudius/mcp_tools/server.py.
1. Create a new module in pyclaudius/mcp_tools/ with your functions:
# pyclaudius/mcp_tools/timezone_tool.py
def get_active_timezone(*, bot_data: dict) -> str:
"""Return the user's configured timezone."""
tz = bot_data.get("user_timezone")
return f"Active timezone: {tz or 'UTC (default)'}"
Your functions can accept bot_data β a shared dict typed as BotData (see pyclaudius/bot_data.py for all available keys, including settings, memory, cron_jobs, backlog, user_timezone, and scheduler). This gives your tool full access to bot state and configuration.
2. Register it as an MCP tool in pyclaudius/mcp_tools/server.py inside create_mcp_server():
from pyclaudius.mcp_tools import timezone_tool
@mcp.tool()
async def get_active_timezone() -> str:
"""Return the user's configured timezone."""
return timezone_tool.get_active_timezone(bot_data=bot_data)
The @mcp.tool() decorator exposes the function to Claude via MCP. The docstring becomes the tool description that Claude sees. The bot_data dict is captured via closure from the enclosing create_mcp_server() function.
You can also register tools conditionally based on feature flags:
if settings.some_feature_enabled:
@mcp.tool()
async def my_tool() -> str:
...
That's it. The tool is automatically discovered by Claude CLI, added to --allowedTools via the mcp__pyclaudius__* wildcard, and available in every conversation.
Memory
pyclaudius supports persistent memory across sessions. When enabled, Claude can remember and forget facts using MCP tools (remember_fact, forget_memory).
You can also manage memory manually with Telegram commands:
/remember <fact>β store a fact/forget <keyword or number>β remove facts matching a keyword or by index/listmemoryβ list all stored facts
Enable it via environment variables:
MEMORY_ENABLED=true
MAX_MEMORIES=100 # optional, default 100
Memories are stored in ~/.pyclaudius-relay/memory.json and injected into every prompt. To clear memory, delete the file or edit it manually.
Scheduled Tasks (Cron)
pyclaudius supports recurring and one-time scheduled tasks. When enabled, Claude can create and manage jobs using MCP tools (add_cron_job, schedule_once, remove_cron_job, list_cron_jobs).
You can also manage jobs with Telegram commands:
/addcron <min> <hour> <day> <month> <weekday> <prompt>β add a recurring job/schedule <YYYY-MM-DD HH:MM> | <prompt>β schedule a one-time task/listcronβ list all scheduled jobs/removecron <number>β remove a job by number/testcron <number>β immediately test a job without waiting for its schedule
Enable it via environment variables:
CRON_ENABLED=true
Jobs are stored in ~/.pyclaudius-relay/cron.json and survive restarts.
Silent responses
When a scheduled job fires, Claude is instructed to respond with [SILENT] if there is nothing noteworthy to report. This suppresses the Telegram notification, avoiding spam from routine checks.
Email Integration
pyclaudius can fetch emails from a dedicated IMAP account (e.g. a Gmail address that receives forwarded mail) and save them as markdown files in the Claude working directory. This lets Claude read and reason over your emails.
Warning β pyclaudius can permanently delete emails from the server. The
/deleteallreadmailcommand (and thedelete_read_mailMCP tool) issues an IMAPEXPUNGEwith no undo. For this reason, the recommended setup is a dedicated Gmail account that only receives forwarded copies of emails you care about. That way you can grant pyclaudius full access without risking your primary inbox.
Telegram commands
/downloadnewmailβ download unseen emails and save as markdown files/deleteallreadmailβ delete all read emails from the server
MCP tools
When enabled, two MCP tools are also registered so Claude can fetch and clean mail autonomously:
| Tool | Description |
|---|---|
download_new_mail | Download unseen emails and save as markdown |
delete_read_mail | Delete all read (SEEN) emails from the server |
For forwarded emails, pyclaudius extracts and records the original sender rather than just the forwarder. It checks preserved headers (X-Original-From, Resent-From, X-Original-Sender) first, then falls back to scanning the body for Gmail / Outlook / Apple Mail forward boilerplate. The **Original sender:** field appears in the saved markdown when found.
Configuration
EMAIL_ENABLED=true
EMAIL_IMAP_HOST=imap.gmail.com # optional, default
EMAIL_IMAP_PORT=993 # optional, default
EMAIL_USER=your-llm-email@gmail.com
EMAIL_PASSWORD=your-app-password
For Gmail, use an App Password (not your regular password). Emails are saved to ~/.pyclaudius-relay/claude-work/emails/ as markdown files.
Gmail setup
-
Create a dedicated Gmail account (e.g.
yourname-ai@gmail.com). Never use your primary inbox β pyclaudius has delete access. -
Forward mail to it. In your primary Gmail: Settings β See all settings β Forwarding and POP/IMAP β Add a forwarding address. Add the new address and confirm. You can also create a filter (Settings β Filters β Create new filter) to forward only specific senders or subjects instead of everything.
-
Enable 2-Step Verification on the dedicated account. This is required before App Passwords are available: Google Account β Security β 2-Step Verification β Turn on.
-
Create an App Password: Google Account β Security β App passwords β choose Mail (or Other β custom name) β Generate. Copy the 16-character password shown β you won't see it again.
-
Enable IMAP in the dedicated Gmail: Settings gear β See all settings β Forwarding and POP/IMAP β IMAP access: Enable IMAP β Save Changes.
-
Set credentials in
.env:
EMAIL_USER=yourname-ai@gmail.com
EMAIL_PASSWORD=abcd efgh ijkl mnop # the 16-char App Password (spaces optional)
Timezone
Set your timezone so Claude sees the correct local time and scheduled jobs fire at the right local time:
/timezone <city>β set timezone with fuzzy matching (e.g./timezone Berlin)- Affects prompt time display and cron/schedule job scheduling
- Default is UTC if not set
The timezone is stored in ~/.pyclaudius-relay/timezone.json.
Allowed Tools
By default, Claude CLI in print mode (-p) does not have permission to use tools like WebSearch or WebFetch. To pre-approve tools, set the ALLOWED_TOOLS environment variable:
ALLOWED_TOOLS=["WebSearch","WebFetch"]
MCP tool names are automatically added to the allowed tools list.
Replay Backlog
When Claude CLI returns an authentication error (expired token, 401), pyclaudius saves your original message to a persistent backlog and notifies you. After re-authenticating with claude auth login, you can replay the saved messages.
This feature is enabled by default. To disable it:
BACKLOG_ENABLED=false
Telegram commands
/listbacklogβ show pending backlog items/clearbacklogβ clear all backlog items/replaybacklogβ replay all backlog items sequentially/replayone <number>β replay a single backlog item by index
How it works
- You send a message to Claude
- Claude CLI returns an authentication error
- pyclaudius saves your original message to
~/.pyclaudius-relay/backlog.json - You receive a notification with the pending count
- Re-authenticate:
claude auth login - Use
/replaybacklogto resend all saved messages
The backlog is also accessible via MCP tools (list_backlog, clear_backlog, replay_one, replay_backlog).
Tmux Keepalive
Claude CLI auth expires roughly every 15 hours. If you run Claude Code in a tmux session, pyclaudius can periodically send it a "hello" to keep the auth alive.
Set the tmux session name:
TMUX_SESSION=claude
When configured, an APScheduler job runs every 5 hours 10 minutes and executes tmux send-keys -t <session> "hello" Enter. This requires tmux to be installed and the named session to exist.
systemd note: The service unit uses PrivateTmp=true, which isolates /tmp. To let the keepalive reach your tmux socket, the unit includes a BindPaths= directive that bind-mounts /tmp/tmux-<UID>. The default UID is 1000; if yours differs, update the value in daemon/pyclaudius.service (check with id -u).
Deploying on a server (Hetzner, etc.)
1. Provision the server
Any VPS with Ubuntu 22.04+ works. A Hetzner CX22 (2 vCPU, 4 GB RAM) is more than enough.
ssh root@your-server-ip
apt update && apt upgrade -y
2. Create a dedicated user
adduser --disabled-password pyclaudius
su - pyclaudius
3. Install uv and the project
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.bashrc
git clone <repo-url> ~/pyclaudius && cd ~/pyclaudius
uv sync
4. Install and authenticate the Claude CLI
# Install Claude CLI (as the pyclaudius user)
npm install -g @anthropic-ai/claude-code
# Or download the standalone binary
# Authenticate
claude auth login
5. Create the .env file
cp ~/pyclaudius/.env.example ~/pyclaudius/.env
# Edit with your values
nano ~/pyclaudius/.env
chmod 600 ~/pyclaudius/.env
6. Set up as a daemon
Ready-to-use service files are in the daemon/ directory.
Linux (systemd):
Install the service (automatically replaces YOUR_USERNAME with your login name):
sed "s/YOUR_USERNAME/$USER/g" ~/pyclaudius/daemon/pyclaudius.service | sudo tee /etc/systemd/system/pyclaudius.service > /dev/null
sudo systemctl daemon-reload
sudo systemctl enable pyclaudius
sudo systemctl start pyclaudius
See the comments in daemon/pyclaudius.service for full instructions.
macOS (launchd):
Windows:
7. Manage the service
# Check status
sudo systemctl status pyclaudius
# View logs
sudo journalctl -u pyclaudius -f
# Restart after code changes
sudo -u pyclaudius bash -c "cd ~/pyclaudius && git pull && uv sync"
sudo systemctl restart pyclaudius
# Stop
sudo systemctl stop pyclaudius
Development
uv run pytest -v # run tests
uv run ruff check . # lint
uv run mypy pyclaudius/ # type check
License
MIT
