zulip-mcp
# zulip-mcp
A small [MCP](https://modelcontextprotocol.io) server that lets coding agents share a work log in one Zulip stream.
It has 7 tools, no database and no background process. Each agent runs its own copy with its own bot, so Zulip's sender
column shows which agent posted.
If you need scheduled messages, file uploads, approvals or DMs, use
[zulipchat-mcp](https://github.com/akougkas/zulipchat-mcp), which has up to 60 tools.
## Tools
| Tool | Result |
|---|---|
| `zulip_status` | One line: message count, last sender and age, recent mentions of this bot |
| `zulip_read` | Last 5 messages at 300 chars; `full=true` gives 15, untruncated. The newest post with all 4 labels is shown whole and stays visible, if it is among the last 50 |
| `zulip_send` | Posts to a topic; an identical repeat within 120 s is not posted again |
| `zulip_search` | Full-text search across the stream, 10 hits at 200 chars |
| `zulip_mentions` | The 10 most recent messages that mention this bot |
| `zulip_topics` | Most recently active topics |
| `zulip_react` | Adds an emoji reaction to a message id |
## Topics
Topics default to `<repo> / <branch>`, the format Zulip's git integrations use, so agent posts and push events share a
topic. The repo name comes from the `origin` URL, so every worktree of a repo shares one topic.
`zulip_send` needs `topic`, or `path` set to the repository being worked on. MCP clients choose the server's working
directory, and it may be a different repo from the one being worked on. Read tools may fall back to that directory.
`path` gives the local branch. A push from `feature` to remote `main` needs an explicit `topic`.
## Install
```bash
uv tool install git+https://github.com/teddybuild/zulip-mcp
```
Create a generic bot in Zulip and subscribe it to your stream.
## Configuration
All configuration comes from environment variables. Bad values stop startup with exit code 2.
| Variable | Required | Meaning |
|---|---|---|
| `ZULIP_SITE` | yes | Server URL, such as `https://chat.example.com` |
| `ZULIP_EMAIL` | yes | The bot's email |
| `ZULIP_API_KEY` | yes | The bot's API key |
| `ZULIP_STREAM` | yes | The one stream this server uses |
| `ZULIP_INSECURE_TLS` | no | `1` skips certificate checks, for self-signed servers. Default `0` |
| `ZULIP_POST_RULES` | no | `1` applies the post rules below to handoff posts. Default `0` |
Prefer a launch script that pulls the key from your secret store and then runs `zulip-mcp`, so the key stays out of client
config files. The examples below use a script called `zulip-mcp-launch`.
## Client setup
Claude Code:
```bash
claude mcp add --scope user zulip -- zulip-mcp-launch
```
Codex:
```bash
codex mcp add zulip -- zulip-mcp-launch
```
Antigravity, in `~/.gemini/config/mcp_config.json`:
```json
{ "mcpServers": { "zulip": { "command": "zulip-mcp-launch", "args": [] } } }
```
OpenCode 2.x, in `~/.config/opencode/opencode.json`:
```json
{ "mcp": { "servers": { "zulip": { "type": "local", "command": ["zulip-mcp-launch"] } } } }
```
## Post rules
Ordinary posts are free-form, so agents can post often. With `ZULIP_POST_RULES=1`, `zulip_send` with `handoff=true` rejects a
message unless:
- It has lines starting `Did:`, `Gate:`, `Next:` and `Blocked:`.
- It is at most 600 characters.
- It has no relative paths, because a reader in another repo cannot resolve them.
`zulip-post-validate` runs the same check on stdin (exit 0 or 2), so shell scripts share one copy of the rules.
## Errors
Tool error text carries a code: `config`, `validation`, `topic`, `auth_failed`, `rate_limited`, `unreachable`, `timeout`,
`ambiguous_write` or `api_error`. Reads retry up to 3 times. Writes never retry. `ambiguous_write` means Zulip may have saved
the message, so read the topic before resending.
## Development
```bash
uv sync
scripts/check.sh
```
## License
MIT. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
TDQS
Scored across 7 tools
Most tools have distinct roles: read, send, search, topics, mentions, react, and status. There is some overlap because zulip_status includes recent mentions and topic recency, while zulip_read and zulip_search both retrieve message content, but the descriptions make the intended boundaries reasonably clear.
All tools use a consistent zulip_ snake_case prefix, which makes them easy to recognize as a set. The main minor deviation is that some suffixes are verbs (read, send, search, react) while others are nouns (status, mentions, topics).
Seven tools is well scoped for a Zulip integration focused on topic messaging and bot workflow. Each tool corresponds to a useful operation, and there are no obviously redundant or filler tools.
The surface covers the core lifecycle for this focused workflow: status, reading messages, sending, searching, listing topics, mentions, and reactions. Minor gaps remain, such as editing/deleting messages or removing reactions, but the main agent workflows are supported.