Skip to main content
Glama
README.md
# zulip-mcp

Minimal, secure, **read-only** MCP server for reading Zulip chats from Claude Code
(or any MCP client). Browse streams, topics, messages, and users — nothing can be
created, modified, or deleted.

## Why this exists

A security review of an existing Zulip MCP server surfaced serious issues
(`eval()`-based remote code execution, arbitrary local file read/write, SSRF).
This is a clean, minimal alternative built around a strict read-only,
no-code-execution design. See [Security](#security).

## Requirements

- Python 3.10+
- A Zulip account and an API key (see [below](#get-your-zulip-api-key))
- [`uv`](https://docs.astral.sh/uv/) (recommended) or `pip`

## Install

Clone the repo:

```bash
git clone https://github.com/shreyan-gupta/zulip-mcp.git
cd zulip-mcp
```

Then install with **either** uv or pip:

```bash
# Option A — uv (recommended; also installs the right Python)
uv sync

# Option B — pip + venv
python3 -m venv .venv
.venv/bin/pip install -e .
```

## Get your Zulip API key

1. Open your Zulip instance (e.g. `https://your-org.zulipchat.com`).
2. Avatar → **Personal settings** → **Account & privacy**.
3. Under **API key**, click **Manage your API key** and copy it.

## Add to Claude Code

Run this from inside the cloned repo (`$(pwd)` expands to its absolute path):

```bash
# Option A — uv
claude mcp add zulip \
  -e ZULIP_EMAIL=you@example.com \
  -e ZULIP_API_KEY=your-api-key \
  -e ZULIP_SITE=https://your-org.zulipchat.com \
  -- uv run --directory "$(pwd)" zulip-mcp

# Option B — venv entry point
claude mcp add zulip \
  -e ZULIP_EMAIL=you@example.com \
  -e ZULIP_API_KEY=your-api-key \
  -e ZULIP_SITE=https://your-org.zulipchat.com \
  -- "$(pwd)/.venv/bin/zulip-mcp"
```

Restart Claude Code, then try:

```
> List the streams I'm subscribed to
> Show messages in #engineering about "sync redesign"
> Summarize my last week of messages in #general
```

Tip: ask it to call `get_own_profile` first to confirm the connection works.

## Tools

| Tool                 | Description                                    |
| -------------------- | ---------------------------------------------- |
| `get_own_profile`    | Verify connection, see authenticated user info |
| `list_subscriptions` | List channels you're subscribed to             |
| `list_streams`       | List all visible streams in the org            |
| `get_stream_id`      | Look up a stream's ID by name                  |
| `list_topics`        | List topics in a stream                        |
| `get_messages`       | Fetch messages with stream/topic/sender/search |
| `get_message`        | Fetch a single message by ID                   |
| `get_user`           | Get user profile by ID or email                |
| `list_users`         | List all users in the org                      |

`get_messages` supports anchor-based pagination and combines filters (stream,
topic, sender, full-text search) into a single query.

## Security

This server is designed to be safe by construction:

- **Read-only** — cannot create, modify, or delete any Zulip data.
- **No code execution** — no `eval()`, `exec()`, `subprocess`, or dynamic imports.
- **No filesystem access** — tools never read or write local files.
- **No telemetry** — every network request goes exclusively to your Zulip instance.
- **Stdio only** — no HTTP listener, no open ports.
- **Credentials stay local** — read from env vars, never logged or returned in output.

## Configuration

Configuration is via environment variables only (no `.env` auto-loading):

| Environment Variable | Required | Description                          |
| -------------------- | -------- | ------------------------------------ |
| `ZULIP_EMAIL`        | Yes      | Your Zulip login email or bot email  |
| `ZULIP_API_KEY`      | Yes      | API key from Zulip settings          |
| `ZULIP_SITE`         | Yes      | Base URL of your Zulip instance      |

## Development

```bash
uv sync --extra dev        # or: pip install -e ".[dev]"
ruff check .
pytest
```

The test suite is offline — it never contacts a real Zulip server.

## License

[MIT](LICENSE)

TDQS

A4.2/5.0

Scored across 9 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: get_message vs get_messages differ in cardinality, get_user vs get_own_profile differ in target, list_streams vs list_subscriptions differ in scope. No overlap or ambiguity.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern using snake_case (e.g., get_message, list_streams). There are no deviations or mixed conventions.

Tool Count5/5

9 tools is a reasonable number for a Zulip MCP server focused on reading data. Each tool serves a distinct function without being excessive or insufficient for the apparent scope.

Completeness2/5

The server is read-only, missing core write operations such as sending messages, creating streams, or subscribing. This is a significant gap for a chat platform, limiting agent capability.

Maintenance

ActivityInactive
ResponsivenessNo issues