Skip to main content
The bot supports Model Context Protocol (MCP) servers that extend Claude’s capabilities beyond basic file access and shell commands. Built-in MCP servers provide session control and user interaction, and you can add custom servers for Things, Notion, Typefully, and more.

Built-In MCP Servers

ask_user — Interactive Buttons

ask_user shows Telegram inline buttons in the active chat and pauses the turn so the user can pick an option. Tool input shape:
{
  "question": "Which file format do you prefer?",
  "options": ["PDF", "PNG", "HTML"]
}
  • question is required
  • options is required
  • At least 2 options are required (schema allows up to 10; 2-6 is recommended)
IPC mechanism (how it works internally):
  1. MCP server writes /tmp/ask-user-<id>.json with status: "pending" and the current chat_id
  2. The streaming handler scans /tmp, sends ❓ <question> with inline buttons, then marks the request as sent
  3. When the user taps a button, callback handling loads the same request file, validates the selected index, and deletes the file
  4. The selected option text is injected as the user’s next message, and the model continues from that choice
Return text to Claude:
  • Success: [Buttons sent to user. STOP HERE - do not output any more text. Wait for user to tap a button.]
  • Invalid input: throws question and at least 2 options required
Example:
You: Prepare a report
Claude: (calls ask_user with options PDF/PNG/HTML and stops)
You: [Tap PDF]
Claude: Generating PDF...

bot_control — Session Management

bot_control is the bot’s control-plane tool. It handles usage checks, model/driver switching, session management, and process restart. Tool input shape:
{
  "action": "switch_model",
  "params": {
    "model": "claude-sonnet-4-6",
    "effort": "high"
  }
}
  • action is required
  • params is optional and depends on action
IPC mechanism (how it works internally):
  1. MCP server writes a pending request file to /tmp/bot-control-<id>.json
  2. The streaming handler scans /tmp, executes the action, then writes result back into the same file
  3. MCP server polls every 100ms, up to 10 seconds, and returns the result text to Claude
  4. Request files are deleted after completion/error (best-effort cleanup)
Action reference (quick table):
ActionParamsWhat it doesReturns
usagenoneFetches Claude usage and, when enabled, Codex quotaUsage text block, or failure message
switch_modelmodel?: string, effort?: stringUpdates current session model/effortUpdated model/effort summary, or validation error
switch_driverdriver: "claude" | "codex"Switches active driver and resets running driver sessions"Switched to Codex" / "Switched to Claude Code", or error
new_sessionnoneStops and kills current session so next message starts fresh"Session cleared. Next message will start a fresh session."
list_sessionsnoneLists saved sessions with title/date/ID prefixNumbered list, or "No saved sessions."
resume_sessionsession_id: stringResumes a saved session by full ID or prefixResumed: "...", or validation/failure message
restartnoneStops active driver sessions and exits process after a short delay"Restarting bot..."
Per-action details and examples:

usage

  • Params: none
  • Example call:
    { "action": "usage" }
    
  • Return: USAGE DATA (show this to the user as-is, in a code block): ... or Failed to fetch usage data.

switch_model

  • Params:
    • model (optional): model ID or display name
    • effort (optional)
  • Effort values:
    • Claude driver: low, medium, high
    • Codex driver: minimal, low, medium, high, xhigh
  • Example call:
    { "action": "switch_model", "params": { "model": "claude-opus-4-6", "effort": "high" } }
    
  • Return: current or updated model/effort summary, or an Unknown model... / Invalid effort... message

switch_driver

  • Params: driver required (claude or codex)
  • Example call:
    { "action": "switch_driver", "params": { "driver": "codex" } }
    
  • Return:
    • Success: "Switched to Codex" or "Switched to Claude Code"
    • Validation: Invalid driver "...". Use: claude or codex
    • Availability failure: Cannot switch to Codex: ... if Codex is disabled/unavailable

new_session

  • Params: none

pino_logs — Pino Log Tail

pino_logs returns recent entries from the bot’s Pino log file with simple filters. Tool input shape:
{
  "level": "error",
  "limit": 50,
  "module": "claude"
}
  • level is the minimum severity (default: error)
  • levels (optional) is an exact list of levels like ["error","warn"] (overrides level)
  • limit controls how many entries to return (1-500)
  • module filters by module field (e.g., claude, streaming, bot)
IPC mechanism (how it works internally):
  1. MCP server writes /tmp/pino-logs-<id>.json with status: "pending" and the current chat_id
  2. The streaming handler scans /tmp, tails the log file, filters entries, and writes the result back
  3. MCP server polls every 100ms, up to 10 seconds, and returns the result text to Claude
Example calls:
{ "level": "error", "limit": 100 }
{ "levels": ["error", "warn"], "module": "streaming" }
  • Example call:
    { "action": "new_session" }
    
  • Return: "Session cleared. Next message will start a fresh session."

list_sessions

  • Params: none
  • Example call:
    { "action": "list_sessions" }
    
  • Return: numbered lines in the format "<index>. "<title>" (<date>) — ID: <prefix>..." or "No saved sessions."

resume_session

  • Params: session_id required (full ID or prefix)
  • Example call:
    { "action": "resume_session", "params": { "session_id": "a1b2c3d4" } }
    
  • Return:
    • Success: Resumed: "<title>"
    • Errors: Missing session_id parameter., No session found matching "...", or Failed: ...

restart

  • Params: none
  • Example call:
    { "action": "restart" }
    
  • Return: "Restarting bot..."

send_turtle — Emoji Kitchen Turtle Stickers

send_turtle sends a turtle mashup sticker to the current Telegram chat. It combines 🐢 with another emoji using Google’s Emoji Kitchen images. Tool input shape:
{
  "emoji": "😍",
  "caption": "Mood"
}
  • emoji is optional. It accepts either a Unicode emoji ("😍") or hex codepoint ("1f60d"). If omitted, it sends turtle + turtle.
  • caption is optional text.
What happens internally:
  1. MCP server resolves the emoji combo to a prebuilt Emoji Kitchen URL
  2. It writes /tmp/send-turtle-<id>.json
  3. The streaming handler sends the sticker to Telegram for the active chat
  4. If sticker upload fails, it falls back to sending the URL as a text message
Return text to Claude:
  • Success: [Turtle sticker sent to chat: 🐢 + <emoji>]
  • Missing combo: [No turtle combo found for emoji "..." (codepoint: ...). Try a different emoji ...]
Example:
You: Send me a turtle sticker with heart eyes
Claude: (uses send_turtle with emoji "😍")
Claude: 🐢 sticker sent

Custom MCP Servers

Adding Your Own Tools

The bot loads MCP server definitions from mcp-config.ts. This file connects Claude to external tools like:
  • Things 3 — Your to-do manager
  • Notion — Your notes and databases
  • Typefully — Social media scheduling
  • Slack — Team communication
  • GitHub — Repository management
  • Stripe — Payment processing
  • Custom tools you create

Setup: Create mcp-config.ts

Copy the example:
cd super_turtle/claude-telegram-bot
cp mcp-config.example.ts mcp-config.ts
Example configuration:
// mcp-config.ts
import type { McpServerConfig } from "./src/types";

export const MCP_SERVERS: Record<string, McpServerConfig> = {
  // Built-ins are auto-included

  // Example: Things 3
  things: {
    command: "npx",
    args: ["-y", "@soulmen/things-mcp"],
    env: {
      THINGS_DATABASE_PATH: "/Users/you/Library/CloudStorage/iCloud~com~culturedcode~things/Things Database.thingsdatabase",
    },
  },

  // Example: Notion
  notion: {
    command: "npx",
    args: ["-y", "@github/notion-mcp"],
    env: {
      NOTION_API_KEY: "secret_...", // Get from Notion settings
    },
  },

  // Example: Typefully
  typefully: {
    command: "npx",
    args: ["-y", "@typefully/mcp"],
    env: {
      TYPEFULLY_API_TOKEN: "...",
    },
  },
};
mcp-config.ts is gitignored — keep it local. Store secrets in environment variables, not in the file.

Server Configuration

Each MCP server config has:
FieldTypeDescription
commandstringCommand to run (e.g., npx, python, /path/to/binary)
argsstring[]Arguments passed to the command
envobjectEnvironment variables (credentials, configs)
timeoutnumber?Timeout in ms (default: 5000)
Example with all options:
myservice: {
  command: "python",
  args: ["/path/to/mcp-server.py"],
  env: {
    API_KEY: "sk-...",
    DEBUG: "true",
  },
  timeout: 10000, // 10 seconds
},

Things 3 (macOS)

Todo management via natural language:
You: Add "Review PRs" to my inbox
Claude: (uses Things MCP)
Claude: ✅ Added to Things
Setup:
things: {
  command: "npx",
  args: ["-y", "@soulmen/things-mcp"],
  env: {
    THINGS_DATABASE_PATH: `${require("os").homedir()}/Library/CloudStorage/iCloud~com~culturedcode~things/Things Database.thingsdatabase`,
  },
},

Notion

Query and update Notion databases:
You: What's on my agenda this week?
Claude: (reads from Notion)
Claude: You have: [lists items from your Notion calendar]
Setup:
notion: {
  command: "npx",
  args: ["-y", "@github/notion-mcp"],
  env: {
    NOTION_API_KEY: process.env.NOTION_API_KEY || "",
  },
},
Get your API key from Notion Settings → Integrations.

GitHub

Access repositories and pull requests:
You: Show me open PRs on my projects
Claude: (queries GitHub)
Claude: [lists PRs with review status]
Setup:
github: {
  command: "npx",
  args: ["-y", "@anthropics/github-mcp"],
  env: {
    GITHUB_TOKEN: process.env.GITHUB_TOKEN || "",
  },
},

Slack

Send messages and query channels:
You: Send a message to #engineering
Claude: (uses ask_user to confirm)
You: [Taps Send]
Claude: ✅ Message sent

Secrets Management

Never commit secrets to git. Use environment variables:
# .env (gitignored)
NOTION_API_KEY=ntn_...
THINGS_DATABASE_PATH=/path/to/database
GITHUB_TOKEN=ghp_...
Then reference them in mcp-config.ts:
export const MCP_SERVERS: Record<string, McpServerConfig> = {
  notion: {
    command: "npx",
    args: ["-y", "@github/notion-mcp"],
    env: {
      NOTION_API_KEY: process.env.NOTION_API_KEY || "",
    },
  },
};
Environment variables are passed to the MCP process but not logged or exposed. Keep sensitive values in .env only.

Troubleshooting

MCP server not connecting?
  1. Check the bot logs:
    tail -f /tmp/claude-telegram-bot-ts.log
    
  2. Verify the command runs:
    npx @soulmen/things-mcp
    
  3. Check environment variables are set:
    echo $NOTION_API_KEY
    
Claude won’t use the tool?
  • Ensure mcp-config.ts exists (not just the example)
  • Restart the bot after editing mcp-config.ts
  • Ask Claude explicitly: “Use my Notion integration to…”
  • Check /status to see which MCP servers loaded
Tool is slow or timing out?
  • Increase the timeout in the config (default: 5000ms)
  • Check if the external service is responding
  • Look at service logs for errors

Examples

Personal Assistant Setup

Connect your entire workflow:
export const MCP_SERVERS: Record<string, McpServerConfig> = {
  things: {
    command: "npx",
    args: ["-y", "@soulmen/things-mcp"],
    env: {
      THINGS_DATABASE_PATH: `${require("os").homedir()}/Library/CloudStorage/iCloud~com~culturedcode~things/Things Database.thingsdatabase`,
    },
  },

  notion: {
    command: "npx",
    args: ["-y", "@github/notion-mcp"],
    env: {
      NOTION_API_KEY: process.env.NOTION_API_KEY || "",
    },
  },

  github: {
    command: "npx",
    args: ["-y", "@anthropics/github-mcp"],
    env: {
      GITHUB_TOKEN: process.env.GITHUB_TOKEN || "",
    },
  },

  slack: {
    command: "npx",
    args: ["-y", "@slackhq/slack-mcp"],
    env: {
      SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN || "",
    },
  },
};
Now Claude can:
  • Check your to-dos (Things)
  • Query your notes (Notion)
  • Review PRs (GitHub)
  • Send team messages (Slack)
  • All from your phone

Workflow Example

You: Add "Review design specs" to my inbox and create a Notion reminder
Claude: (sees your request)
Claude: Adding to Things... Done
Claude: Creating Notion entry... Done
Claude: ✅ Added to Things and created Notion reminder

Next Steps