Skip to main content
Glama
nitindermohan

pumble-mcp

README.md
<div align="center">

<img src="image.png" alt="pumble-mcp — Full Pumble Platform Access for AI Agents" width="100%"/>

<br/>

[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
[![MCP](https://img.shields.io/badge/MCP_Protocol-1.29-8B5CF6)](https://modelcontextprotocol.io)
[![Node.js](https://img.shields.io/badge/Node.js-22_LTS-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
[![Pumble SDK](https://img.shields.io/badge/Pumble_SDK-1.1-FF6B35)](https://github.com/CAKE-com/pumble-node-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Tests](https://img.shields.io/badge/Tests-175_passing-brightgreen)](#testing)

A standalone MCP server that gives AI agents complete access to the [Pumble](https://pumble.com) messaging platform.<br/>
Built on the official [Pumble SDK](https://github.com/CAKE-com/pumble-node-sdk) and the [Model Context Protocol](https://modelcontextprotocol.io).

[Getting Started](#getting-started) · [Tools](#-tools) · [Real-Time Events](#-real-time-events) · [Architecture](#architecture) · [Configuration](#configuration) · [Development](#development)

</div>

---

## Why pumble-mcp?

The [existing Pumble MCP server](https://www.npmjs.com/package/@shoutkol/pumble-mcp-server) wraps ~8 API endpoints with API-key auth. It can't touch files, receive events, manage channels, search messages, or schedule anything.

**pumble-mcp** exposes the *entire* platform — 33 tools across messaging, files, channels, users, search, scheduling, and real-time events — using OAuth2 and the full Pumble SDK for capabilities the REST API alone cannot provide.

| Capability | Existing MCP | pumble-mcp |
|---|:---:|:---:|
| Messages (send, read, edit, delete, threads) | Partial | **Full** |
| File operations (upload, download, list) | — | **SDK-exclusive** |
| Real-time events (subscribe, poll) | — | **WebSocket** |
| Channel management (create, add/remove members) | — | **Full** |
| Search with text/channel/user/date filters | — | **Full** |
| Scheduled messages (create, edit, cancel) | — | **Full** |
| User status and groups | — | **Full** |
| HTTP transport for N8N / webhooks | — | **Streamable HTTP** |
| Auth model | API key | **OAuth2** |
| Total tools | ~8 | **33** |

---

## Getting Started

### Prerequisites

- **Node.js 22+** (LTS)
- A [Pumble](https://pumble.com) workspace where you have admin access

### 1. Clone and install

```bash
git clone https://github.com/nitindermohan/pumble-mcp.git
cd pumble-mcp
npm install
```

### 2. Register a Pumble app

```bash
npx pumble-cli login     # Authenticate with your Pumble account
npx pumble-cli create    # Register a new bot app
```

This creates `.pumbleapprc` with your app credentials (`APP_ID`, `APP_KEY`, `CLIENT_SECRET`, `SIGNING_SECRET`).

### 3. Configure environment

```bash
cp .env.example .env
```

Fill in the credentials from `.pumbleapprc`:

```env
PUMBLE_APP_ID=your_app_id
PUMBLE_APP_KEY=your_app_key
PUMBLE_APP_CLIENT_SECRET=your_client_secret
PUMBLE_APP_SIGNING_SECRET=your_signing_secret
```

### 4. Authorize and register events

Run once via `pumble-cli` to authorize the bot in your workspace and push event subscriptions to Pumble's platform:

```bash
npx pumble-cli
```

Wait for `Websocket connected` and `App is updated`, then `Ctrl+C`.

### 5. Build and run

```bash
npm run build
node dist/index.js
```

### 6. Connect to your AI client

<details>
<summary><strong>Claude Code</strong></summary>

Add to `.mcp.json` in your project root:

```json
{
  "mcpServers": {
    "pumble": {
      "command": "node",
      "args": ["dist/index.js"],
      "cwd": "/path/to/pumble-mcp"
    }
  }
}
```

</details>

<details>
<summary><strong>Claude Desktop</strong></summary>

Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "pumble": {
      "command": "node",
      "args": ["/absolute/path/to/pumble-mcp/dist/index.js"],
      "cwd": "/absolute/path/to/pumble-mcp"
    }
  }
}
```

</details>

<details>
<summary><strong>N8N (HTTP transport)</strong></summary>

Set `MCP_TRANSPORT=http` (or `both`) in `.env`, optionally set `MCP_BEARER_TOKEN`, then point N8N's HTTP Request node at:

```
POST http://localhost:3456/mcp
Authorization: Bearer YOUR_TOKEN
```

</details>

---

## 🔧 Tools

### Messaging — Read, write, and manage messages across channels and threads

| Tool | Description |
|------|-------------|
| `pumble_send_message` | Send a message to a channel. Accepts `#channel-name` or channel ID. |
| `pumble_reply_message` | Reply to a specific message in a thread. |
| `pumble_list_messages` | List recent messages in a channel with cursor-based pagination. |
| `pumble_get_message` | Fetch a single message by its timestamp ID. |
| `pumble_get_thread_replies` | Fetch all replies in a message thread. |
| `pumble_edit_message` | Edit a message the bot previously sent. |
| `pumble_delete_message` | Delete a message the bot previously sent. |

### Direct Messages — Private conversations with individuals or groups

| Tool | Description |
|------|-------------|
| `pumble_send_dm` | Send a direct message to a user. Accepts email address or user ID. |
| `pumble_send_group_dm` | Send a group DM to multiple users at once. |

### Channels — Create and manage workspace channels

| Tool | Description |
|------|-------------|
| `pumble_list_channels` | List all channels the bot belongs to, with pagination. |
| `pumble_get_channel` | Get channel details by `#name` or ID. |
| `pumble_create_channel` | Create a new PUBLIC or PRIVATE channel. |
| `pumble_add_channel_members` | Add one or more users to a channel. |
| `pumble_remove_channel_member` | Remove a user from a channel. |

### Users — Workspace user information and bot identity

| Tool | Description |
|------|-------------|
| `pumble_list_users` | List all users in the workspace with roles and status. |
| `pumble_get_bot_identity` | Get the bot's own user ID, display name, and workspace info. |
| `pumble_list_user_groups` | List user groups and their members. |
| `pumble_set_status` | Set the bot's custom status with emoji, text, and optional expiration. |

### Reactions — Emoji reactions on messages

| Tool | Description |
|------|-------------|
| `pumble_add_reaction` | Add an emoji reaction (e.g. `:thumbsup:`) to any message. |
| `pumble_remove_reaction` | Remove a previously added emoji reaction. |

### Files — Upload, download, and browse files (SDK-exclusive)

| Tool | Description |
|------|-------------|
| `pumble_upload_file` | Upload a file (base64-encoded) to a channel with an optional message. |
| `pumble_download_file` | Download a file by URL and return its base64 content. |
| `pumble_list_files` | List files shared in a channel. |

> **Note:** File operations are only available through the Pumble SDK with OAuth2 auth. The REST API alone does not expose file endpoints.

### Search — Find messages across the workspace

| Tool | Description |
|------|-------------|
| `pumble_search_messages` | Full-text search with filters for channel, user, date range, and pagination. |

### Scheduled Messages — Send messages at a future time

| Tool | Description |
|------|-------------|
| `pumble_create_scheduled_message` | Schedule a message for future delivery to any channel. |
| `pumble_list_scheduled_messages` | List all pending scheduled messages. |
| `pumble_edit_scheduled_message` | Edit the content or timing of a pending scheduled message. |
| `pumble_delete_scheduled_message` | Cancel a scheduled message before it sends. |

### Real-Time Events — Subscribe and poll for live workspace activity

| Tool | Description |
|------|-------------|
| `pumble_subscribe_events` | Subscribe to one or more event types. Returns a subscription ID for polling. |
| `pumble_poll_events` | Poll for events buffered since the last poll for a given subscription. |
| `pumble_list_subscriptions` | List all active event subscriptions with their types and cursor positions. |
| `pumble_unsubscribe_events` | Remove a subscription and stop buffering its events. |

### System

| Tool | Description |
|------|-------------|
| `pumble_health_check` | Check server version, auth status, workspace name, and connection health. |

---

## 📡 Real-Time Events

pumble-mcp connects to Pumble via socket mode WebSocket and buffers incoming events in an in-memory ring buffer (1000 events, oldest evicted first). AI agents subscribe to event types and poll for new events at their own pace — no push endpoint or public URL required.

```
  AI Agent                   pumble-mcp                     Pumble
     │                           │                             │
     │── subscribe(NEW_MESSAGE) ─▶│                             │
     │◀── subscription_id ───────│                             │
     │                           │◀── WebSocket: NEW_MESSAGE ──│
     │                           │   (buffered in EventStore)  │
     │── poll(subscription_id) ──▶│                             │
     │◀── [event1, event2] ──────│                             │
```

**Supported event types:**

| Event | Triggered when... |
|-------|-------------------|
| `NEW_MESSAGE` | A message is posted in any channel the bot belongs to |
| `UPDATED_MESSAGE` | A message is edited |
| `REACTION_ADDED` | Someone adds an emoji reaction to a message |
| `CHANNEL_CREATED` | A new channel is created in the workspace |
| `APP_UNINSTALLED` | The bot app is uninstalled from the workspace |
| `APP_UNAUTHORIZED` | A user revokes the bot's authorization |
| `WORKSPACE_USER_JOINED` | A new user joins the workspace |

**Example flow:**

```jsonc
// 1. Subscribe to events
pumble_subscribe_events({ event_types: ["NEW_MESSAGE", "REACTION_ADDED"] })
// → { subscription_id: "sub_a1b2c3d4", event_types: [...] }

// 2. Poll periodically
pumble_poll_events({ subscription_id: "sub_a1b2c3d4" })
// → { events: [{ eventType: "NEW_MESSAGE", payload: {...}, ... }], count: 3 }

// 3. Unsubscribe when done
pumble_unsubscribe_events({ subscription_id: "sub_a1b2c3d4" })
// → { unsubscribed: true }
```

---

## Architecture

```
                      ┌──────────────────────────────────────────────┐
                      │                 pumble-mcp                   │
                      │                                              │
  Claude/CLI ──stdio──▶  McpServer ──▶ 33 Tools ──▶ PumbleClient ──▶── Pumble API
                      │      │                          │            │
  N8N/HTTP ───http──▶  McpServer     EventStore ◀── SDK WebSocket ◀─── Pumble Events
                      │              (ring buffer)                   │
                      └──────────────────────────────────────────────┘
```

**Key design decisions:**

- **Dual transport** — stdio for Claude Desktop/Code, Streamable HTTP for N8N and remote consumers. Same 33 tools on both.
- **EventStore singleton** — One ring buffer shared across all transports. Subscribe on stdio, poll from HTTP — it just works.
- **Socket mode** — The Pumble SDK handles WebSocket connection, ping/pong keepalive, message ack, and automatic reconnection. No public URL or tunnel needed.
- **stderr-only logging** — stdout is reserved exclusively for MCP JSON-RPC. The Pumble SDK's internal `console.log` calls are redirected to stderr at process start.
- **OAuth2 with token mutex** — Concurrent API calls serialize through a mutex to prevent race conditions on single-use refresh tokens.

---

## Customizing the Bot Identity

Edit `manifest.json` to change how the bot appears in your Pumble workspace:

```json
{
  "name": "my-custom-bot",
  "displayName": "My Custom Bot",
  "botTitle": "Team Assistant"
}
```

Then sync to Pumble:

```bash
npx pumble-cli    # Pushes manifest changes to Pumble
# Wait for "App is updated", then Ctrl+C
```

---

## Configuration

### Environment Variables

| Variable | Required | Default | Description |
|----------|:--------:|---------|-------------|
| `PUMBLE_APP_ID` | **Yes** | — | App ID from `.pumbleapprc` |
| `PUMBLE_APP_KEY` | **Yes** | — | App key for WebSocket authentication |
| `PUMBLE_APP_CLIENT_SECRET` | **Yes** | — | OAuth2 client secret |
| `PUMBLE_APP_SIGNING_SECRET` | **Yes** | — | Request signature verification secret |
| `PUMBLE_TOKEN_STORE_PATH` | No | `.pumble-tokens.json` | Path to OAuth2 token storage |
| `MCP_TRANSPORT` | No | `stdio` | Transport mode: `stdio`, `http`, or `both` |
| `MCP_HTTP_PORT` | No | `3456` | Port for HTTP transport |
| `MCP_BEARER_TOKEN` | No | — | Bearer token for HTTP transport auth (strongly recommended) |
| `LOG_LEVEL` | No | `info` | Logging verbosity: `error`, `info`, `debug` |

### Required Bot Scopes

These scopes are configured in `manifest.json` and registered during app creation:

```
messages:read    messages:write    messages:edit    messages:delete
channels:list    channels:read    channels:write
users:list       user:read
reaction:read    reaction:write
workspace:read   files:write       attachments:write   status:write
```

---

## Development

```bash
npm run dev           # Run with tsx (hot reload)
npm run build         # Compile TypeScript
npm test              # Run all 175 tests
npm run test:watch    # Watch mode
npm run test:coverage # Code coverage report
npm run typecheck     # Type check without emitting
```

### Testing

| Suite | Tests | Coverage |
|-------|------:|----------|
| **Unit** | 39 | EventStore, event listeners, auth, config, logger |
| **Integration** | 128 | All 33 tools via in-process MCP server + InMemoryTransport |
| **Live** | 8 | End-to-end against a real Pumble workspace |

```bash
npm test                              # Unit + integration (no network)
npx tsx tests/live/run-live-tests.ts  # Live tests (requires credentials)
```

### Project Structure

```
src/
  index.ts              Entry point — boot sequence, console.log redirect
  server.ts             MCP server setup (stdio + Streamable HTTP transports)
  config.ts             Environment validation with Zod schemas
  logger.ts             stderr-only logger (stdout reserved for MCP JSON-RPC)
  main.ts               pumble-cli entry point for event registration
  pumble/
    auth.ts             OAuth2 setup, token mutex, SDK event handler wiring
    client.ts           PumbleClient — unified wrapper for all SDK operations
    resolvers.ts        Smart resolution: #channel-name → ID, email → user ID
  events/
    event-store.ts      Ring buffer (1000 capacity) with subscription-cursor polling
    event-listeners.ts  Bridges SDK event callbacks → EventStore.push()
  tools/
    index.ts            Tool registration hub (routes to all modules below)
    health.ts           pumble_health_check
    channels.ts         5 channel management tools
    users.ts            4 user and bot identity tools
    messages.ts         5 message read tools
    messages-mutate.ts  2 message write tools (edit, delete)
    reactions.ts        2 reaction tools
    dms.ts              2 direct message tools
    files.ts            3 file operation tools (SDK-exclusive)
    search.ts           1 search tool with multi-filter support
    scheduled.ts        4 scheduled message tools
    events.ts           4 real-time event subscription tools
tests/
  setup.ts              Mock PumbleClient and EventStore factories
  unit/                 Pure logic tests — no network, no SDK
  integration/          Full MCP pipeline tests via InMemoryTransport
  live/                 Real Pumble workspace end-to-end tests
```

---

## Tech Stack

| Component | Technology | Version |
|-----------|------------|---------|
| Runtime | Node.js | 22 LTS |
| Language | TypeScript | 5.9 |
| MCP Protocol | `@modelcontextprotocol/sdk` | 1.29.x |
| Pumble Platform | `pumble-sdk` | 1.1.x |
| HTTP Transport | Express + `@modelcontextprotocol/express` | 4.x |
| Schema Validation | Zod | 4.x |
| Testing | Vitest | 4.x |
| Auth | OAuth2 via `pumble-cli` | — |

---

## Disclaimer

This project was largely **vibe-coded with [Claude Code](https://claude.ai/code)** (Anthropic's AI coding agent). The entire development lifecycle — architecture, implementation, testing, debugging, and documentation — was driven through conversational AI-assisted development. All code was reviewed, tested (175 automated tests), and validated against a live Pumble workspace.

---

## License

[MIT](LICENSE)