> ## Documentation Index
> Fetch the complete documentation index at: https://superturtle.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Tools

> Model Context Protocol (MCP) tools for extending Claude's capabilities

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:**

```json theme={null}
{
  "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:**

```json theme={null}
{
  "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):**

| Action           | Params                              | What it does                                                       | Returns                                                       |
| ---------------- | ----------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------- |
| `usage`          | none                                | Fetches Claude usage and, when enabled, Codex quota                | Usage text block, or failure message                          |
| `switch_model`   | `model?: string`, `effort?: string` | Updates current session model/effort                               | Updated model/effort summary, or validation error             |
| `switch_driver`  | `driver: "claude" \| "codex"`       | Switches active driver and resets running driver sessions          | `"Switched to Codex"` / `"Switched to Claude Code"`, or error |
| `new_session`    | none                                | Stops and kills current session so next message starts fresh       | `"Session cleared. Next message will start a fresh session."` |
| `list_sessions`  | none                                | Lists saved sessions with title/date/ID prefix                     | Numbered list, or `"No saved sessions."`                      |
| `resume_session` | `session_id: string`                | Resumes a saved session by full ID or prefix                       | `Resumed: "..."`, or validation/failure message               |
| `restart`        | none                                | Stops active driver sessions and exits process after a short delay | `"Restarting bot..."`                                         |

**Per-action details and examples:**

#### `usage`

* **Params:** none
* **Example call:**
  ```json theme={null}
  { "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:**
  ```json theme={null}
  { "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:**
  ```json theme={null}
  { "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:**

```json theme={null}
{
  "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:**

```json theme={null}
{ "level": "error", "limit": 100 }
```

```json theme={null}
{ "levels": ["error", "warn"], "module": "streaming" }
```

* **Example call:**
  ```json theme={null}
  { "action": "new_session" }
  ```
* **Return:** `"Session cleared. Next message will start a fresh session."`

#### `list_sessions`

* **Params:** none
* **Example call:**
  ```json theme={null}
  { "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:**
  ```json theme={null}
  { "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:**
  ```json theme={null}
  { "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:**

```json theme={null}
{
  "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:

```bash theme={null}
cd super_turtle/claude-telegram-bot
cp mcp-config.example.ts mcp-config.ts
```

Example configuration:

```typescript theme={null}
// 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: "...",
    },
  },
};
```

<Note>
  `mcp-config.ts` is **gitignored** — keep it local. Store secrets in environment variables, not in the file.
</Note>

### Server Configuration

Each MCP server config has:

| Field     | Type      | Description                                               |
| --------- | --------- | --------------------------------------------------------- |
| `command` | string    | Command to run (e.g., `npx`, `python`, `/path/to/binary`) |
| `args`    | string\[] | Arguments passed to the command                           |
| `env`     | object    | Environment variables (credentials, configs)              |
| `timeout` | number?   | Timeout in ms (default: 5000)                             |

Example with all options:

```typescript theme={null}
myservice: {
  command: "python",
  args: ["/path/to/mcp-server.py"],
  env: {
    API_KEY: "sk-...",
    DEBUG: "true",
  },
  timeout: 10000, // 10 seconds
},
```

## Popular MCP Servers

### Things 3 (macOS)

Todo management via natural language:

```
You: Add "Review PRs" to my inbox
Claude: (uses Things MCP)
Claude: ✅ Added to Things
```

**Setup:**

```typescript theme={null}
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:**

```typescript theme={null}
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](https://www.notion.so/my-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:**

```typescript theme={null}
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:

```bash theme={null}
# .superturtle/.env (gitignored)
NOTION_API_KEY=ntn_...
THINGS_DATABASE_PATH=/path/to/database
GITHUB_TOKEN=ghp_...
```

Then reference them in `mcp-config.ts`:

```typescript theme={null}
export const MCP_SERVERS: Record<string, McpServerConfig> = {
  notion: {
    command: "npx",
    args: ["-y", "@github/notion-mcp"],
    env: {
      NOTION_API_KEY: process.env.NOTION_API_KEY || "",
    },
  },
};
```

<Warning>
  Environment variables are passed to the MCP process but not logged or exposed. Keep sensitive values in `.superturtle/.env` only.
</Warning>

## Troubleshooting

**MCP server not connecting?**

1. Check the bot logs:
   ```bash theme={null}
   superturtle logs loop
   ```

2. Verify the command runs:
   ```bash theme={null}
   npx @soulmen/things-mcp
   ```

3. Check environment variables are set:
   ```bash theme={null}
   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:

```typescript theme={null}
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

* [Personal Assistant Guide](/docs/bot/personal-assistant) — Full setup for using Claude as your assistant
* [Security](/docs/config/security) — How secrets are handled safely
* [Environment Variables](/docs/config/environment-variables) — Full config reference
