Reading history by channel and topic rather than by channel alone means an assistant can pull one conversation instead of an interleaved firehose — the reason Zulip is organised that way. Posting works the same, so a reply lands in the thread it belongs to. The bot's own subscriptions are the boundary of what it can see, and there is a tool to change that, so a permission error usually means one missing subscription rather than a wrong key.
An MCP server over the Zulip API that runs as a bot in your organization. It covers Zulip's stream-and-topic model rather than flattening it: messages are posted into a named topic within a channel, and history is read the same way.
- Channels listed with their ids, with private and web-public streams included or excluded as you choose — `zulip_list_channels`
- The topics inside a channel, by channel id — `zulip_get_topics`
- Recent messages from a specific channel and topic, with a message count and an anchor to page backwards from — `zulip_get_channel_history`
- A message posted into a channel and topic, and direct messages sent to one or more people by email or user id — `zulip_post_message`, `zulip_send_direct_message`
- An emoji reaction added to a message by id — `zulip_add_reaction`
- The organization's users listed with their basic information — `zulip_get_users`
- The bot subscribed to a channel it needs to reach — `zulip_subscribe_to_channel`
A Zulip bot, created under Settings → Personal → Bots as a generic bot. That gives you the three values it needs: `ZULIP_EMAIL` for the bot's address, `ZULIP_API_KEY` for its key, and `ZULIP_URL` for the instance. The npm package name is `@modelcontextprotocol/server-zulip` (0.1.0) and it runs via `npx`, or from the `mcp/zulip` Docker image you build from the repository. Bots start with limited permissions: subscribe the bot to every channel you expect it to read, or history calls will come back empty. Licensed Apache 2.0.
