zulip-mcp
Integrates with a Zulip stream to let agents share and manage a work log. Provides tools to check stream status, read recent messages, post updates to topics with duplicate suppression, search messages, view mentions, list active topics, and add emoji reactions.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@zulip-mcppost my work log to the feature/login topic"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
zulip-mcp
A small MCP 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, which has up to 60 tools.
Tools
Tool | Result |
| One line: message count, last sender and age, recent mentions of this bot |
| Last 5 messages at 300 chars; |
| Posts to a topic; an identical repeat within 120 s is not posted again |
| Full-text search across the stream, 10 hits at 200 chars |
| The 10 most recent messages that mention this bot |
| Most recently active topics |
| Adds an emoji reaction to a message id |
Related MCP server: coordination_bus/mcp_server.py
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
uv tool install git+https://github.com/teddybuild/zulip-mcpCreate 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 |
| yes | Server URL, such as |
| yes | The bot's email |
| yes | The bot's API key |
| yes | The one stream this server uses |
| no |
|
| no |
|
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:
claude mcp add --scope user zulip -- zulip-mcp-launchCodex:
codex mcp add zulip -- zulip-mcp-launchAntigravity, in ~/.gemini/config/mcp_config.json:
{ "mcpServers": { "zulip": { "command": "zulip-mcp-launch", "args": [] } } }OpenCode 2.x, in ~/.config/opencode/opencode.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:andBlocked:.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
uv sync
scripts/check.shLicense
Available Tools
7 toolszulip_mentionsARead-only
The 10 most recent messages in the stream that mention this bot, at 300 chars each.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint, so the safety profile is covered. The description adds genuinely useful behavior beyond that: a hard cap of 10 results and 300-character truncation per message, which tells the agent to expect short, lossy content and no pagination.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler; the result cap and truncation length are the two facts that matter and both appear immediately.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read tool with annotations and no output schema, the description covers what is returned in broad terms (count, truncation, mention filter). It stops short of saying which stream or how results are ordered, which leaves a small gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate at the schema level; baseline 4 applies. The fixed limits are stated in the description rather than exposed as inputs, which is consistent.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource and scope: the 10 most recent messages that mention this bot, truncated to 300 chars. That distinguishes it from zulip_read and zulip_search by content type (mentions of the bot). The phrase 'in the stream' is slightly ambiguous without a stream parameter, keeping it short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to prefer this over zulip_read or zulip_search, and no note that it cannot be filtered or paginated. The agent must infer that this is the lightweight 'did anyone ping me' call rather than a general message retriever.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zulip_reactA
Add an emoji reaction, by name such as "thumbs_up", to a message id.
| Name | Required | Description | Default |
|---|---|---|---|
| emoji | Yes | ||
| message_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, destructiveHint=false, idempotentHint=false, and openWorldHint=true, so the safety profile is covered. The description adds nothing about the non-idempotent behavior that matters here (what happens when the same emoji is added twice, whether it errors or duplicates) nor about required permissions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler. Minor deduction because the nested quoting around "thumbs_up" is slightly awkward and the emoji-name hint would read more cleanly as a separate clause.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter, non-destructive tool with no output schema, the description is serviceable but thin: it omits duplicate-reaction behavior, permission requirements, and the message_id format. An agent can probably invoke it correctly, but edge cases are undocumented.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must carry parameter meaning and only partially does. The 'by name such as "thumbs_up"' clause genuinely helps, telling the agent to pass a Zulip emoji name rather than a unicode character, but message_id is left entirely unexplained and the exact naming convention (aliases, punctuation) remains ambiguous.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Add') and resource ('emoji reaction') against a named target ('a message id'). This is clearly distinct from every sibling (zulip_send, zulip_read, zulip_search), so an agent can select it without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the action itself: react to an existing message rather than post a new one. However, there is no explicit statement of when to prefer this over zulip_send (e.g., acknowledging a message without a reply) and no exclusions or prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zulip_readCRead-only
Last 5 messages of a topic at 300 chars each. full=True gives 15, untruncated. Posts with Did:, Gate:, Next: and Blocked: lines are never truncated. The newest one among the last 50 comes first when it is older than the messages shown.
| Name | Required | Description | Default |
|---|---|---|---|
| full | No | ||
| path | No | ||
| topic | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint, so the safety profile is covered, and the description adds genuinely useful behavior: default 5-message/300-char truncation, the full=True expansion to 15 untruncated, and the exemption for posts containing Did:/Gate:/Next:/Blocked: lines. The ordering sentence ('newest one among the last 50 comes first when it is older than the messages shown') is real behavioral detail but is phrased ambiguously enough that an agent may misread the sort order.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is compact and front-loads the return-shape information, with no filler sentences. However, the telegraphic fragments ('at 300 chars each', 'full=True gives 15') and the convoluted final sentence about the newest of the last 50 hurt readability more than the length does.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description does well to explain truncation and ordering, but for a read tool whose whole purpose is scoped retrieval it leaves the scoping parameters (path, topic) undefined. An agent cannot reliably decide what to pass to fetch a specific topic versus a default. The result-shape coverage does not compensate for the missing input semantics.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across three parameters, so the description must carry the load, and it only covers 'full' (15 messages, untruncated). 'path' and 'topic' are entirely unexplained — the agent cannot tell whether topic is required, how path relates to topic, or what happens when both are null. One of three parameters documented at zero schema coverage is a significant gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description conveys that it returns the last 5 messages of a topic, truncated to 300 chars, which implies a read of topic messages. But it is written as a fragment with no verb or resource framing, and it never distinguishes itself from siblings like zulip_search, zulip_topics, or zulip_mentions. An agent can guess the purpose but must infer it from output behavior rather than a stated scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The only conditional guidance is internal to the tool ('full=True gives 15, untruncated'), which explains a parameter rather than when to choose this tool. There is no statement of when to prefer it over zulip_search or zulip_topics, nor any prerequisite or exclusion. The agent gets no routing help among six siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zulip_searchARead-only
Full-text search across every topic in the stream. 10 hits at 200 chars each.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds real behavior beyond the annotations: results are capped at 10 hits and each snippet is truncated to 200 characters. That is genuinely useful for an agent planning a search. It stops short of 5 because it does not say whether more results can be retrieved (no pagination or broadening hint).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with purpose and followed by the result-shape constraint. Every sentence carries information and there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read tool with no output schema, the description covers purpose and result size, which is reasonable. It omits how to obtain results beyond the 10-hit cap and what 'the stream' refers to, leaving gaps an agent would hit on a second query.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the description says nothing about query syntax, operators, or scoping. It does imply that the single 'query' parameter is a full-text search string, which is the minimum needed for a self-evidently named parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Full-text search across every topic in the stream.' This clearly separates it from read-oriented siblings like zulip_read and zulip_topics, though the phrase 'the stream' is undefined and leaves the scope slightly ambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use or when-not-to-use guidance, and no sibling is named as an alternative. Usage is only implied by 'full-text search' versus browsing topics with zulip_topics or reading with zulip_read.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zulip_sendA
Post a message. Pass topic, or path set to the repository being worked on, whose repo and branch name the topic. An identical repeat within 120 s is not posted again. handoff=True marks an end-of-work post, which must meet the server's post rules if on.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | ||
| topic | No | ||
| content | Yes | ||
| handoff | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by disclosing the 120-second duplicate-suppression window and the handoff/server post-rule constraint — both non-obvious behaviors for an agent. This partially softens the idempotentHint=false annotation (a repeat is effectively a no-op inside the window), but does not contradict it outright; the description still omits error/rate-limit behavior and any auth requirements for an openWorld tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Short and front-loaded with the core action, but the middle sentence is a compressed run-on ('...whose repo and branch name the topic') that takes a re-read to parse. Nothing is redundant, yet the grammar costs clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an openWorld, non-idempotent write with zero schema descriptions and no output schema, the description covers the important quirks (dedup window, handoff rules) but is silent on the topic/path precedence, what a successful call returns, and failure modes. Adequate to call it, not quite enough to call it confidently.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the load, and it does explain topic, path (repo/branch-derived topic naming), and handoff. It adds real meaning but leaves 'content' entirely undefined and does not say what happens when both topic and path are supplied or when neither is.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Post a message'), which cleanly separates it from the read-oriented siblings (zulip_read, zulip_search, zulip_mentions, zulip_topics) and from zulip_react. It does not name a sibling explicitly, but the post-vs-read split is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives field-level guidance ('Pass topic, or path set to the repository being worked on') and a mode cue ('handoff=True marks an end-of-work post'), which implies when to use it. However, it never states when to prefer zulip_react or a different channel/topic, and gives no prerequisites or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zulip_statusBRead-only
One line: a topic's message count, last sender and age, plus recent mentions of this bot. Topic defaults to path's repo and branch, then the server's folder.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | ||
| topic | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so safety and network access are covered. The description adds the default-resolution chain for topic selection and the shape of the returned line, but says nothing about authentication requirements, failure modes, or how recent the "recent mentions" window is, which matter for an open-world read.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences with no filler, and the output content is front-loaded before the defaulting rule. The opening phrase "One line:" is slightly cryptic but functions as a useful format hint.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and no annotations beyond read-only and open-world, the description must convey returns and defaults, which it does at a summary level. It still leaves gaps on the meaning of the "age" and "recent mentions" windows and on prerequisites for reaching the server, so an agent could call it but not fully predict the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and both parameters are nullable with no descriptions, so the description carries the burden; it does so by explaining that topic falls back to the path's repo and branch and then to the server's folder. That fallback chain is exactly the meaning an agent needs to decide whether to pass path or topic, though path's own semantics are only implicit.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the tool returns a one-line topic summary: message count, last sender, age, and recent bot mentions. That is a specific resource and content set, but there is no clear verb ("status" is not clarified as a read/report action), and nothing explicitly separates it from zulip_mentions or zulip_topics, which overlap with the mention and topic elements.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use or when-not-to-use guidance, and no sibling is named as an alternative. The defaulting chain for topic selection is useful context but is a parameter behavior, not usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zulip_topicsCRead-only
The stream's most recently active topics, newest first. limit is capped at 100.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds real behavioral value by disclosing result ordering (newest first) and the hard cap of 100 on limit, but it does not say whether an over-limit value is clamped or rejected, nor whether results are paginated or truncated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the resource and ordering before the constraint. Nothing is redundant, though the extreme terseness leaves the ambiguities noted elsewhere unaddressed rather than merely avoided.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple (one optional parameter, no output schema), yet the description refers to 'the stream's' topics while the schema exposes no stream parameter, leaving the scope undefined. For a read tool with no output schema, the description should at least say which stream is used and roughly what each returned topic contains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the bare 'limit' integer with default 20 carries no meaning on its own; the description partially compensates by stating the maximum of 100. It still omits what limit counts (topics) and what happens at the boundary, so the single parameter is only half-explained.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description identifies the resource (a stream's most recently active topics) and the ordering (newest first), which loosely distinguishes it from message-oriented siblings like zulip_read and zulip_search. However, it is a noun phrase with no explicit verb (list/retrieve), and it never names the siblings or clarifies the relationship to them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance at all: nothing says to call this to discover which topics exist before reading messages, and no alternative (zulip_read, zulip_search) is mentioned as a substitute or complement. The agent must infer the tool's role from the name alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v0.1.0- First observed
zulip_mentions - First observed
zulip_react - First observed
zulip_read - First observed
zulip_search - First observed
zulip_send - First observed
zulip_status - First observed
zulip_topics
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.
Maintenance
Related MCP Connectors
Work journal for coding agents: worklogs, tasks, project state and costs across projects.
Shared team record of decisions, changes and handoffs that every coding agent reads and writes.
Coordination hub for AI coding agents: message teammates, ask humans, audit every event.
One shared project for a team's coding agents: decisions, a task board and handoffs.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides a local chat room for coordinating work across Git worktrees, coding agents (Claude, Codex), and humans, with tools to start, wake, stop, message, and search agent sessions.Apache 2.0
- FlicenseNot gradedqualityCmaintenanceEnables MCP-capable coding agents to coordinate via authenticated task creation, claiming, messaging, and review approval over a secure local event log.-
- AlicenseNot gradedqualityBmaintenanceEnables teams of coding agents to share one live plan synced through their existing git remote, so each agent receives teammates' contract, decision, and task changes at its next tool call. Provides tools for publishing contracts, logging decisions, adding and claiming tasks, and reviewing the plan for contradictions.456 npm4MIT
- AlicenseNot gradedqualityBmaintenanceGives a hackathon team's AI coding agents and their humans a shared task board and inbox, so agents can atomically claim tasks, move them between todo/doing/blocked/done, post messages to teammates or the whole team, and read new messages plus board events since they last checked. It also exposes member and agent activity, letting multiple agents working in the same repo coordinate without duplicating work.MIT