Skip to content

feat(zulip): add Zulip channel plugin#22308

Closed
emadomedher wants to merge 24 commits intoopenclaw:mainfrom
emadomedher:feature/zulip-channel
Closed

feat(zulip): add Zulip channel plugin#22308
emadomedher wants to merge 24 commits intoopenclaw:mainfrom
emadomedher:feature/zulip-channel

Conversation

@emadomedher
Copy link
Copy Markdown

@emadomedher emadomedher commented Feb 21, 2026

Summary

Adds first-party support for Zulip, the open-source threaded team chat.

Features

  • Real-time messaging: Receive messages from Zulip streams and DMs via event queue long-polling
  • Send messages: Send to streams (with topics) or direct messages
  • Media support: Upload and send files/attachments
  • Multiple accounts: Configure multiple Zulip bots
  • Security policies: DM allowlists, group policies, mention requirements

Configuration

{
  "channels": {
    "zulip": {
      "enabled": true,
      "baseUrl": "https://your-org.zulipchat.com",
      "email": "[email protected]",
      "apiKey": "your-api-key",
      "requireMention": true,
      "dmPolicy": "pairing"
    }
  }
}

Implementation

Follows the same patterns as the Mattermost extension:

  • Config schema with Zod validation
  • Event queue long-polling for real-time messages
  • Standard channel plugin interface
  • Environment variable support

Testing

Tested with self-hosted Zulip instance with 9 agent accounts.


Happy to make any changes needed!

Greptile Summary

Adds first-party Zulip channel plugin following the established Mattermost extension patterns. The implementation provides real-time message polling via Zulip's event queue API, stream and DM support with topics, media uploads, and multiple account configuration.

Key Changes:

  • New Zulip channel plugin in extensions/zulip/ with complete TypeScript implementation
  • Event queue long-polling for real-time message reception
  • Stream and DM message routing with topic support
  • HTTP Basic Auth with Zulip API
  • Config schema with Zod validation matching other channel plugins
  • Multiple account support with environment variable fallback

Note on PR Scope:
This PR includes unrelated upstream changes (MCP client extension, Mumble extension, DuckDuckGo search provider, TTS providers, and other core changes). These are from upstream syncs (be214626 and earlier commits) merged into the author's feature branch. The actual Zulip implementation is isolated to commit 4a4c8b9e which only touches extensions/zulip/ files.

Areas for verification:

  • Message routing in monitor.ts:171-174 uses enqueueSystemEvent but doesn't explicitly pass sender IDs or check security policies (dmPolicy, groupPolicy, requireMention) before routing. The README claims these policies are supported, but the monitor implementation doesn't show where mention detection or allowlist filtering occurs. This differs from Mattermost's approach which explicitly checks mentions and policies before routing (see extensions/mattermost/src/mattermost/monitor.ts:550-573).
  • Consider verifying that OpenClaw's routing system automatically applies the channel plugin's security configuration, or if explicit checks are needed in the monitor.

Confidence Score: 3/5

  • This PR is safe to merge with verification of security policy enforcement
  • The Zulip implementation follows established patterns from Mattermost and has clean, well-structured code. However, the score is moderate because: (1) security policy enforcement (dmPolicy, groupPolicy, requireMention) isn't visibly implemented in the monitor, requiring verification that the routing system handles it, and (2) the PR includes significant unrelated upstream changes (MCP client, Mumble, DuckDuckGo search, TTS providers) merged from main, though the actual Zulip commit is clean and isolated
  • Pay close attention to extensions/zulip/src/zulip/monitor.ts for security policy enforcement verification

Last reviewed commit: 4a4c8b9

Adds support for Whisper STT (https://github.com/openai/whisper),
OpenAI's open-source speech recognition model.

Features:
- Self-hosted Whisper support (whisper-asr-webservice, faster-whisper, etc.)
- OpenAI-compatible API endpoint
- Automatic language detection or explicit language hints
- Multiple model sizes (tiny, base, small, medium, large, large-v2, large-v3)
- Works with cloud OpenAI Whisper or local instances

Configuration example:
```yaml
tools:
  media:
    audio:
      enabled: true
      models:
        - provider: whisper
          baseUrl: http://localhost:8200/v1
          model: whisper-1
          language: en
```

Default baseUrl: http://localhost:8200/v1
Default model: whisper-1
Adds support for Chatterbox TTS (https://github.com/resemble-ai/chatterbox),
a state-of-the-art open-source text-to-speech system that runs locally.

Features:
- OpenAI-compatible API endpoint support
- Voice cloning via voice library
- Multilingual support (23+ languages)
- Configurable parameters: exaggeration, CFG weight, speed
- Supports Chatterbox, Chatterbox-Turbo, and Chatterbox-Multilingual models

Configuration example:
```yaml
messages:
  tts:
    provider: chatterbox
    chatterbox:
      enabled: true
      baseUrl: http://localhost:4123
      voice: default
      model: chatterbox-turbo
```

Closes #XXX
Adds support for Piper TTS (https://github.com/rhasspy/piper),
a fast, local, and privacy-focused neural text-to-speech system.

Features:
- OpenAI-compatible API endpoint support
- High-quality neural voices (60+ languages)
- Lightweight and fast inference
- Runs completely offline/local
- Configurable voice parameters (speed, noise, etc.)

Configuration example:
```yaml
messages:
  tts:
    provider: piper
    piper:
      enabled: true
      baseUrl: http://localhost:8101
      voice: en_US-lessac-high
      lengthScale: 1.0
```
@vector-im/matrix-bot-sdk returns {data: Buffer, contentType: string}
instead of raw Buffer. Extract the data property and preserve contentType.
- Full voice conversation loop (audio reception, STT, agent response, TTS, playback)
- Proactive speaking via HTTP endpoint (POST /mumble/speak)
- Voice parameter support for different TTS voices
- Sender allowlist for multi-bot environments
- High-quality audio (128kbps Opus, 10ms frames, 'audio' mode)
- Timeout-based silence detection (500ms)
- Comprehensive README with configuration examples

Technical details:
- Uses @tf2pickup-org/mumble-client (fork with full audio support)
- Whisper STT integration (OpenAI-compatible API)
- Kokoro TTS integration (67 voices, blending support)
- @discordjs/opus for native Opus encoding
- Linear interpolation resampling (24kHz → 48kHz)

Tested with Mumble v1.5.857, Node.js v24
Resolved conflicts:
- extensions/matrix/src/matrix/monitor/media.ts: accepted upstream's cleaner response handling
- src/media-understanding/providers/index.ts: merged both whisperProvider and zaiProvider
- Renamed skyline-mcp → mcp-client for generic MCP support
- Supports multiple simultaneous MCP server connections
- Compatible with any MCP server (filesystem, GitHub, Postgres, Slack, Skyline, etc.)
- Per-server tool prefixes to avoid naming conflicts
- Auto-reconnection support
- Updated plugin API for OpenClaw v2026.2.9 compatibility
  - Changed handler → execute method
  - Added toolCallId parameter
  - Fixed return format (content + details)

Tested with Skyline MCP server (31 tools loaded successfully).

Example config:
{
  "servers": {
    "skyline": { "command": "skyline-mcp", ... },
    "filesystem": { "command": "mcp-server-filesystem", ... }
  }
}

This enables OpenClaw to connect to the entire MCP ecosystem.
Major Features:
- Multi-server support with error isolation
- Auto ext_ prefix prevents native tool collisions
- Pre-flight command validation (no uncaught exceptions)
- Health monitoring with auto-recovery (60s intervals)
- Rate limiting (concurrent + per-minute)
- Metrics & observability (/mcp-metrics command)
- Protocol completeness (resources, prompts)
- Hot reload capability (/mcp-reload command)
- Comprehensive test suite (13 test cases)

P0 Features (Critical):
✅ Auto ext_ prefix for collision prevention
✅ MCP-to-MCP collision detection
✅ Pre-flight command check
✅ Error isolation (one bad server != crash)
✅ Tool discovery (/mcp command)
✅ Graceful degradation

P1 Features (Should Have):
✅ Expanded config validation
✅ Health monitoring & auto-restart
✅ Resource cleanup (no zombie processes)
✅ Basic test coverage

P2 Features (Nice to Have):
✅ Rate limiting (maxConcurrent, maxPerMinute)
✅ Metrics endpoint (/mcp-metrics)
✅ Resources & Prompts support
✅ Hot reload (/mcp-reload)

Testing:
- Crash-tested with invalid configurations
- Verified error isolation (3/4 servers working)
- Confirmed zombie process cleanup
- Production deployment verified (51 tools loaded)

Documentation:
- Comprehensive README (666 lines)
- Implementation summary with lessons learned
- Troubleshooting guide
- Configuration examples

Files:
- index.ts (995 lines)
- index.test.ts (219 lines, 13 tests)
- README.md (updated)
- IMPLEMENTATION-SUMMARY.md (new)

Status: Production Ready ✅
feat: Production-ready MCP client plugin
- Add duckduckgo as 4th search provider (no API key required)
- Uses official ddgs CLI (pip install ddgs)
- Supports Tor proxy with 'tor' convenience alias
- Add region, safesearch, and timelimit configuration options
- Maintain same caching and output format as other providers
- Auto-detects ddgs CLI in common installation paths

Configuration example:
  tools:
    web:
      search:
        provider: duckduckgo
        duckduckgo:
          proxy: tor
          region: wt-wt
          safesearch: moderate

Benefits:
- Zero-cost search option (vs Brave $5-15/month)
- Privacy-first with built-in Tor integration
- No API key registration required
- Same UX as existing providers

Technical details:
- Searches: ~/.local/bin/ddgs, ~/.openclaw/.venv/bin/ddgs, system paths
- Calls ddgs CLI with JSON output (-o flag)
- Maps ddgs output format to OpenClaw format
- Adds duckduckgo config schema validation
Use proper TypeScript types instead of 'as any' casts.
Keep both DuckDuckGo integration and upstream Grok improvements
Changes:
- extensions/mumble/package.json: @tf2pickup-org/mumble-client ^0.12.5
- Updated pnpm-lock.yaml

This makes the fork buildable without requiring a local mumble-client-fork directory.
Adds first-party support for Zulip, the open-source threaded team chat.

Features:
- Real-time message polling via Zulip event queue API
- Stream and DM support with topics
- Media upload and attachments
- Multiple account configuration
- DM/group allowlist policies
- @-mention requirement in streams
- Full config schema with environment variable support

Follows the same patterns as mattermost extension.
@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling size: XL labels Feb 21, 2026
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

37 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +171 to +174
runtime.system.enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
sessionKey,
contextKey: `zulip:message:${chatId}:${message.id}`,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that security policies (dmPolicy, groupPolicy, requireMention) are being enforced. The message is routed via enqueueSystemEvent without explicitly passing sender ID or checking policies. Mattermost's monitor explicitly checks resolveRequireMention and filters before routing (extensions/mattermost/src/mattermost/monitor.ts:550-573). Verify the routing system automatically applies channel security configs based on the sessionKey.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/zulip/src/zulip/monitor.ts
Line: 171-174

Comment:
Check that security policies (dmPolicy, groupPolicy, requireMention) are being enforced. The message is routed via `enqueueSystemEvent` without explicitly passing sender ID or checking policies. Mattermost's monitor explicitly checks `resolveRequireMention` and filters before routing (`extensions/mattermost/src/mattermost/monitor.ts:550-573`). Verify the routing system automatically applies channel security configs based on the sessionKey.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +56 to +58
"type": "git",
"url": "https://github.com/myka/openclaw-zulip.git"
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repository URL points to myka/openclaw-zulip but this PR is being submitted to openclaw/openclaw. Update to match the actual repository or remove if this will be published as part of the main openclaw repo.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/zulip/package.json
Line: 56-58

Comment:
Repository URL points to `myka/openclaw-zulip` but this PR is being submitted to `openclaw/openclaw`. Update to match the actual repository or remove if this will be published as part of the main openclaw repo.

How can I resolve this? If you propose a fix, please make it concise.

const fetchImpl = params.fetchImpl ?? fetch;

// Zulip uses HTTP Basic Auth with email:api_key
const authHeader = `Basic ${Buffer.from(`${email}:${apiKey}`).toString("base64")}`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Buffer in client code assumes Node.js environment. Consider using btoa() or a platform-agnostic base64 encoding approach for better compatibility, or document that this plugin requires Node.js runtime.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/zulip/src/zulip/client.ts
Line: 110

Comment:
Using `Buffer` in client code assumes Node.js environment. Consider using `btoa()` or a platform-agnostic base64 encoding approach for better compatibility, or document that this plugin requires Node.js runtime.

How can I resolve this? If you propose a fix, please make it concise.

@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle bot added the stale Marked as stale due to inactivity label Feb 28, 2026
@openclaw-barnacle
Copy link
Copy Markdown

Please make this as a third-party plugin that you maintain yourself in your own repo. Docs: https://docs.openclaw.ai/plugin. Feel free to open a PR after to add it to our community plugins page: https://docs.openclaw.ai/plugins/community

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling r: third-party-extension size: XL stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants