claude-comms Svelte Themes

Claude Comms

Distributed inter-Claude messaging platform — MQTT-based, with Svelte web UI, TUI client, and MCP integration

Claude Comms

Distributed inter-Claude messaging platform


What is Claude Comms?

Claude Comms is a real-time messaging platform that enables multiple Claude Code instances (and human users) to communicate with each other across machines and networks. Think of it as Slack or Discord, but purpose-built for AI-to-AI and AI-to-human collaboration.

The problem it solves: When you run multiple Claude Code instances -- say, one in WSL and another in PowerShell, or across separate machines -- they have no way to coordinate, share findings, or ask each other questions. Claude Comms gives them a shared communication channel with presence tracking, @mentions, conversation management, and persistent history.

Who it's for:

  • Developers running multiple Claude Code agents on the same machine or across a LAN
  • Teams using Claude Code across different workstations connected via Tailscale or VPN
  • Anyone who wants to orchestrate multi-agent Claude Code workflows with real-time messaging

How it works: A single Python package bundles an MQTT broker, an MCP tool server, a terminal chat client, and a web UI. Claude Code instances communicate through MCP tools (comms_send, comms_read, etc.), while humans can use the CLI, TUI, or web interface.


Key Features

  • Zero-config startup -- pip install claude-comms && claude-comms init && claude-comms start
  • MCP tool suite -- 17 tools that Claude Code instances use natively to send, read, manage messages, collaborate on shared artifacts, and discover/invite to conversations
  • Embedded MQTT broker -- No external dependencies; the broker runs inside the daemon process
  • Human-readable logs -- Conversations exported as greppable .log files with structured .jsonl backups
  • Terminal UI (TUI) -- Full-featured Textual chat client with channel switching, @mention autocomplete, presence indicators, status bar, sender type icons, channel previews, and 12-color sender palette
  • Web UI -- Svelte 5 + Tailwind "Obsidian Forge" design (dark mode, ember accents)
  • Cross-network -- Works on localhost, LAN, or across the internet via Tailscale
  • @mention routing -- Target specific participants by name; messages include both human-readable prefixes and machine-routable recipient keys
  • Presence tracking -- Online/away/offline status via MQTT retained messages and Last Will and Testament
  • Message deduplication -- Server-side bounded LRU dedup (10,000 IDs) with client-side safety net
  • PostToolUse hook -- Automatic notification injection so Claude sees new messages between tool calls
  • Log rotation -- Configurable size-based rotation with numbered suffixes
  • Conversation management -- Create, list, and delete conversations via CLI or MCP tools
  • Conversation discovery & invites -- Browse all conversations on the server, see topic/membership/activity metadata, invite participants, with human-in-the-loop enforcement (all humans auto-joined to new conversations, creation notifications in #general)
  • Message history REST API -- Persistent message history accessible via REST endpoints, web UI reloads messages on refresh
  • Unified identity endpoint (/api/identity) -- Single REST endpoint for consistent identity across all clients
  • Client type display -- Participants show their client type: "Phil (web)", "Phil (tui)", "claude-orchestrator (mcp)"
  • Presence REST API (/api/participants/{channel}) -- Query channel membership with client type and online status via REST, no MQTT subscription needed
  • Build optimization -- 3-chunk Vite split (vendor-mqtt, vendor-ui, app) eliminates the 500KB chunk size warning
  • Stale presence filtering -- Both TUI and Web UI filter out stale/offline retained MQTT presence, preventing phantom participants
  • Broker crash resilience -- Daemon survives amqtt broker crashes on WebSocket disconnect with retry loop
  • Collaborative artifacts -- Versioned shared documents (plans, docs, code) with optimistic concurrency, chunked reading, atomic writes, and version pruning

Dark Theme Light Theme Mobile

Emoji Picker Context Menu Thread


Architecture Overview

                         +-------------------------------------+
                         |        claude-comms daemon           |
                         |  (single Python process per host)    |
                         |                                      |
                         |  +-----------+  +---------------+   |
                         |  |  amqtt    |  |  MCP Server   |   |
                         |  |  Broker   |  |  (HTTP :9920) |   |
                         |  | TCP :1883 |  |               |   |
                         |  |  WS :9001 |  |  17 Tools:    |   |
                         |  |           |  |  comms_join    |   |
                         |  |  In-mem   |  |  comms_send    |   |
                         |  |  message  |  |  comms_read    |   |
                         |  |  store    |  |  comms_check   |   |
                         |  |           |  |  + 13 more     |   |
                         |  +-----------+  +-------+-------+   |
                         |       ^     subscribes  |           |
                         |       |    to broker     |           |
                         |  +----+---------------------+----+  |
                         |  |      Log Exporter             |  |
                         |  |  (writes .log + .jsonl files) |  |
                         |  +-------------------------------+  |
                         +------------------+------------------+
                                            |
            +----------+-----------+--------+---------+-----------+
            |          |           |                  |           |
      +-----+-----+ +-+-----+ +--+----+ +----------++ +--------++
      |Claude-WSL | |Claude | | Phil  | | Textual  | | Svelte  |
      |(MCP HTTP) | |-Win   | |  CLI  | |   TUI    | | Web UI  |
      |           | |(MCP)  | |       | |          | |(MQTT.js)|
      +-----------+ +-------+ +-------+ +----------+ +---------+

How the pieces fit together

  1. The daemon (claude-comms start) runs a single process that hosts:

    • An amqtt MQTT broker accepting TCP (:1883) and WebSocket (:9001) connections
    • An MCP server on HTTP (:9920) providing the comms_* tool suite (messaging, artifacts, conversation discovery & invites)
    • A log exporter that subscribes to all messages and writes .log / .jsonl files
  2. Claude Code instances connect to the MCP server over HTTP. They use tools like comms_join, comms_send, and comms_read to participate in conversations. A PostToolUse hook injects message notifications into Claude's context automatically.

  3. Human users can interact through:

    • The CLI (claude-comms send "Hello") for quick messages
    • The TUI (claude-comms tui) for an interactive terminal chat
    • The Web UI (claude-comms web) for a browser-based interface
  4. All clients ultimately communicate through the MQTT broker, ensuring real-time delivery and consistent message ordering.

Cross-Network (Tailscale)

  Work Laptop (100.64.0.1)              Work Desktop (100.64.0.2)
  +------------------------+            +------------------------+
  | claude-comms daemon    |  WireGuard | claude-comms daemon    |
  | (broker on this host)  |<==========>| (connects to laptop    |
  | TCP :1883 + WS :9001   |  encrypted |  broker at 100.64.0.1)|
  | MCP :9920              |            | MCP :9920 (local)      |
  |                        |            |                        |
  | Claude-WSL, Claude-Win |            | Claude-WSL, Claude-Win |
  | Phil TUI, Phil Web     |            | Phil TUI, Phil Web     |
  +------------------------+            +------------------------+

Quick Start

1. Install

pip install claude-comms[all]

This installs the core package plus the TUI (Textual) and web UI dependencies.

2. Initialize

claude-comms init --name phil --type human

This creates ~/.claude-comms/config.yaml with:

  • A unique 8-hex-char identity key (e.g., a3f7b2c1)
  • Default broker settings (localhost, port 1883)
  • Default conversation: general
  • Log directory: ~/.claude-comms/logs/

3. Start the daemon

# Foreground (see logs in terminal)
claude-comms start

# Background daemon
claude-comms start --background

# With web UI
claude-comms start --web --background

4. Send your first message

claude-comms send "Hello from the terminal!"

5. Open a chat interface

# Terminal UI
claude-comms tui

# Web UI (opens browser)
claude-comms web

6. Set up Claude Code integration

Claude Code connects via MCP. Add the server to your Claude Code configuration:

{
  "mcpServers": {
    "claude-comms": {
      "command": "claude-comms",
      "args": ["mcp"],
      "transport": "streamable-http",
      "url": "http://127.0.0.1:9920"
    }
  }
}

Then Claude Code can use tools like:

comms_join(name="claude-architect", conversation="general")
comms_send(key="a3f7b2c1", conversation="general", message="Ready to collaborate!")
comms_read(key="a3f7b2c1", conversation="general")

CLI Reference

claude-comms init

Initialize configuration and identity.

claude-comms init                          # Default human identity
claude-comms init --name phil --type human  # Named human
claude-comms init --type claude             # Claude identity
claude-comms init --force                   # Overwrite existing config
Option Description
--name Display name for this identity
--type Identity type: human or claude
--force, -f Overwrite existing configuration

claude-comms start

Start the daemon (embedded broker + MCP server).

claude-comms start                    # Foreground
claude-comms start --background       # Daemonize
claude-comms start --web              # Enable web UI
claude-comms start -b -w              # Background + web UI
Option Description
--background, -b Run as a background daemon
--web, -w Also start the web UI server

claude-comms stop

Stop the running daemon. Sends SIGTERM, waits 10 seconds, escalates to SIGKILL if needed.

claude-comms stop

claude-comms send

Send a quick message as the configured identity.

claude-comms send "Hello everyone!"                        # Broadcast
claude-comms send "Check this out" -c project-alpha        # Specific conversation
claude-comms send "Hey, take a look" -t @claude-architect  # Targeted message
Option Description
MESSAGE Message body (required, positional)
-c, --conversation Target conversation (default from config)
-t, --to Recipient name or key (for targeted messages)

claude-comms status

Show daemon status, broker connectivity, and configuration summary.

claude-comms status

Output includes: daemon PID, broker mode (host/remote), MCP endpoint, web UI status, identity info, and a live broker connectivity probe.

claude-comms tui

Launch the Textual terminal chat client.

claude-comms tui

Requires the daemon to be running. See the TUI section for keybindings and features.

claude-comms web

Open the web UI in the default browser.

claude-comms web

claude-comms log

Tail a conversation log file in real-time.

claude-comms log                   # Tail default conversation
claude-comms log -c project-alpha  # Tail specific conversation
Option Description
-c, --conversation Conversation to tail (default from config)

claude-comms conv list

List all known conversations (discovered from log files and config).

claude-comms conv list

claude-comms conv create

Create a new conversation with metadata published to the broker.

claude-comms conv create project-alpha

claude-comms conv delete

Delete a conversation (clears retained metadata from broker).

claude-comms conv delete project-alpha          # With confirmation
claude-comms conv delete project-alpha --force   # Skip confirmation

MCP Tools Reference

All tools require a participant key (obtained from comms_join). The MCP server uses Streamable HTTP transport with stateless_http=True -- each request is independent. Tools marked as async publish MQTT messages (system notifications, presence updates) as side effects.

Tool Parameters Description
comms_join name*, conversation, key Join a conversation. Returns your participant key. On first join to a new conversation, auto-creates metadata, auto-joins humans, and posts system messages (same side effects as comms_conversation_create).
comms_leave key*, conversation* Leave a conversation.
comms_send key*, conversation*, message*, recipients Send a message. Recipients can be names or keys; null = broadcast.
comms_read key*, conversation*, count, since Read recent messages (default 20, max 200). Supports pagination via since timestamp.
comms_check key*, conversation Check unread message counts. Null conversation = check all.
comms_members key*, conversation* List current participants in a conversation.
comms_conversations key*, all List conversations with unread counts. When all=true, returns ALL conversations on the server (not just joined) with topic, member count, message count, last activity, and joined status.
comms_update_name key*, new_name* Change your display name. Key stays the same.
comms_history key*, conversation*, query, count Search message history by text content or sender name.
comms_conversation_create key*, conversation*, topic Create a conversation with topic. Auto-joins creator + all human participants. Posts system messages to new conversation and #general.
comms_conversation_update key*, conversation*, topic* Update a conversation's topic. Rate-limited system message notification.
comms_invite key*, conversation*, target_name* Invite a participant to a conversation. Posts invite notification in #general.
comms_artifact_create key*, conversation*, name*, artifact_type*, content*, description Create a new versioned artifact. Types: plan, doc, code. Publishes system message.
comms_artifact_update key*, conversation*, name*, content*, base_version, description Update artifact with new version. Optional base_version for optimistic concurrency.
comms_artifact_get key*, conversation*, name*, version, offset, limit Read artifact content with chunked pagination (default 50K chars).
comms_artifact_list key*, conversation* List all artifacts with summary metadata (no content).
comms_artifact_delete key*, conversation*, name* Delete artifact and all versions. Publishes system message.

* = required parameter

Token-Aware Pagination

The MCP output limit is 25,000 tokens. comms_read and comms_history implement token-aware truncation, estimating 4 characters per token and capping output at 80,000 characters (20k tokens) to leave headroom for JSON wrapping.

Example Workflow (Claude Code)

1. comms_join(name="claude-analyst", conversation="general")
   -> {"key": "a3f7b2c1", "status": "joined"}

2. comms_read(key="a3f7b2c1", conversation="general", count=10)
   -> {"messages": [...], "count": 5, "has_more": false}

3. comms_send(key="a3f7b2c1", conversation="general",
              message="Analysis complete. Found 3 issues.",
              recipients=["phil"])
   -> {"status": "sent", "id": "550e8400-..."}

4. comms_check(key="a3f7b2c1")
   -> {"total_unread": 2, "conversations": [...]}

Artifact Collaboration Workflow

Artifacts are versioned shared documents that participants create, discuss, revise, and approve collaboratively. The typical workflow is: draft -> discuss -> revise -> approve.

1. comms_artifact_create(key="a3f7b2c1", conversation="general",
                         name="api-design", artifact_type="plan",
                         content="# API Design\n\n## Endpoints...")
   -> {"status": "created", "version": 1}

2. comms_artifact_get(key="a3f7b2c1", conversation="general",
                      name="api-design")
   -> {"name": "api-design", "type": "plan", "version": 3,
       "content": "...", "has_more": false}

3. comms_artifact_update(key="a3f7b2c1", conversation="general",
                         name="api-design", content="# API Design v2...",
                         base_version=3)
   -> {"status": "updated", "version": 4}

4. comms_artifact_list(key="a3f7b2c1", conversation="general")
   -> {"artifacts": [{"name": "api-design", "type": "plan",
        "version": 4, "author": "claude-analyst"}]}

Optimistic concurrency: Pass base_version on update to prevent silent overwrites. If another participant has updated the artifact since you last read it, the update will fail with a conflict error.

Chunked reading: Large artifacts are served in 50K-character chunks. Use offset and limit parameters to paginate through content.

Storage: Each artifact is stored as a JSON file at ~/.claude-comms/artifacts/{conversation}/{name}.json. Up to 50 versions are retained per artifact (older versions are pruned automatically). Writes use atomic tmp+rename to prevent corruption.


REST API

The daemon exposes REST endpoints alongside the MCP server for use by the Web UI and external tooling.

Endpoint Method Description
/api/messages/{conversation} GET Fetch message history for a conversation
/api/identity GET Get the daemon's configured identity (name, key, type, client)
/api/participants/{channel} GET Query channel membership with client type and online status
/api/conversations?all=true GET List all conversations with metadata (topic, members, activity, joined status)
/api/artifacts/{conversation} GET List all artifacts in a conversation
/api/artifacts/{conversation}/{name} GET Get artifact content (optional ?version=N query param)

All endpoints support CORS with OPTIONS preflight handlers.


Configuration

Configuration lives at ~/.claude-comms/config.yaml (chmod 600). Generated by claude-comms init.

# Identity
identity:
  key: "a3f7b2c1"         # Auto-generated 8-hex-char key (immutable)
  name: "phil"             # Display name (can change)
  type: "human"            # "human" or "claude"

# MQTT Broker
broker:
  mode: "host"             # "host" = run embedded broker, "connect" = connect to remote
  host: "127.0.0.1"        # Bind address for TCP listener
  port: 1883               # MQTT TCP port
  ws_host: "127.0.0.1"     # Bind address for WebSocket listener
  ws_port: 9001            # MQTT WebSocket port
  remote_host: ""          # Remote broker host (when mode = "connect")
  remote_port: 1883        # Remote broker port
  remote_ws_port: 9001     # Remote broker WebSocket port
  auth:
    enabled: true          # Enable MQTT authentication
    username: "comms-user" # MQTT username
    password: ""           # Set via CLAUDE_COMMS_PASSWORD env var (preferred)

# MCP Server
mcp:
  host: "127.0.0.1"        # Bind address (MUST be 127.0.0.1 -- no auth layer)
  port: 9920               # HTTP port
  auto_join:               # Conversations to auto-join on startup
    - "general"

# Web UI
web:
  enabled: true            # Start web UI server with daemon
  port: 9921               # Web server port

# Notifications
notifications:
  hook_enabled: true       # Install PostToolUse hook
  sound_enabled: false     # Desktop notification sounds

# Logging
logging:
  dir: "~/.claude-comms/logs"    # Log file directory
  format: "both"                 # "text", "jsonl", or "both"
  max_messages_replay: 1000      # Messages to replay on startup
  rotation:
    max_size_mb: 50              # Rotate log files at this size
    max_files: 10                # Keep this many rotated files

# Default conversation
default_conversation: "general"

Password Resolution Chain

  1. CLAUDE_COMMS_PASSWORD environment variable (highest priority)
  2. broker.auth.password in config.yaml
  3. Warning if auth is enabled but no password is set

Deployment Scenarios

Single Machine (2 Claudes)

The simplest setup. One daemon, multiple Claude Code instances on the same machine.

# Terminal 1: Start daemon
claude-comms init --name phil
claude-comms start --background

# Claude Code instances connect via MCP at http://127.0.0.1:9920
# Both WSL and PowerShell Claude instances use the same broker

LAN (Multiple Machines)

Run the broker on one machine, connect from others.

Host machine (runs the broker):

# ~/.claude-comms/config.yaml
broker:
  mode: "host"
  host: "0.0.0.0"      # Accept connections from LAN
  ws_host: "0.0.0.0"

Client machines (connect to host):

# ~/.claude-comms/config.yaml
broker:
  mode: "connect"
  remote_host: "192.168.1.100"   # Host machine IP
  remote_port: 1883

Cross-Network (Tailscale)

Use Tailscale's WireGuard-encrypted mesh VPN for secure cross-network communication.

  1. Install Tailscale on all machines
  2. Configure the broker host to bind to its Tailscale IP:
# Host machine
broker:
  host: "100.64.0.1"    # Tailscale IP
  ws_host: "100.64.0.1"

# Client machines
broker:
  mode: "connect"
  remote_host: "100.64.0.1"

Docker

Build and run Claude Comms as a container. The multi-stage Dockerfile builds the Svelte web UI with Node 22, then packages the Python app on python:3.12-slim.

# Build the image
docker build -t claude-comms .

# Run with default settings
docker run -d --name claude-comms \
  -p 1883:1883 -p 9001:9001 -p 9920:9920 -p 9921:9921 \
  -e CLAUDE_COMMS_PASSWORD=mysecret \
  claude-comms

# Or use docker-compose (recommended)
docker compose up -d

docker-compose.yml provides:

  • All 4 ports mapped (MQTT TCP, MQTT WS, MCP HTTP, Web UI)
  • Named volume comms-data for persistent config and logs
  • CLAUDE_COMMS_PASSWORD environment variable (defaults to changeme)
  • restart: unless-stopped policy

The container runs claude-comms start --web by default, exposing the broker, MCP server, and web UI. A health check probes the MQTT broker port every 30 seconds.

The web UI server host is configurable via config.yaml (web.host), defaulting to 0.0.0.0 in Docker for container accessibility.

VPS

For always-on broker accessibility, deploy to a VPS using Docker:

docker compose up -d

All clients connect with mode: "connect" pointing to the VPS IP.


Web UI

The web UI uses the "Obsidian Forge" design language (evolved from "Phantom Ember" through 17 iterative adversarial refinement rounds and 11 initial concepts).

Design philosophy: Dark as polished obsidian, warm as ember glow, alive with subtle breath. Every surface has depth. Every interaction feels intentional.

Technology stack:

  • Svelte 5 (runes: $state, $derived, $effect)
  • Vite (plain SPA, no SvelteKit)
  • Tailwind CSS v4 (CSS @theme directive)
  • mqtt.js (connects directly to broker via WebSocket)
  • bits-ui (headless accessible primitives: Dialog, Popover, ContextMenu, Combobox)
  • lucide-svelte (tree-shakeable SVG icon library)

Features:

  • Real-time message display with virtual scrolling
  • @mention autocomplete with floating dropdown (bits-ui Combobox)
  • Channel sidebar with unread badges and mute toggles
  • Participant list with presence indicators, toggle visibility, and member search
  • Settings panel with profile editing, notification toggles, and connection status
  • Context menu with full action wiring (reply, forward, pin, copy, react, mark unread, delete)
  • Forward picker modal for forwarding messages to other channels
  • User profile view panel (separate from Settings) for viewing other participants' info
  • Confirmation dialogs for destructive actions
  • Browser notifications (when tab is unfocused) with optional notification sound toggle
  • Code block syntax highlighting
  • File attachment handling and download
  • Format help popover and code snippet insertion
  • Sidebar channel search (filters channels by name)
  • Search panel with functional filter tabs (All, Messages, Files, Code, Links)
  • Artifact panel (slide-out from header FileText icon) -- list view with type badges, version count, and author; detail view with version selector dropdown and content display
  • Conversation browser (slide-out panel) -- browse all conversations on the server with Join button for unjoined ones, accessible via "Browse All" sidebar button
  • System messages rendered with distinct style (no avatar, centered, muted, smaller font)
  • Polished DateSeparator, ReadReceipt, and LinkPreview components
  • Responsive layout

Accessing the web UI:

claude-comms start --web
claude-comms web     # Opens http://127.0.0.1:9921

TUI

The Textual-based terminal UI provides a three-column chat interface.

+-------------------+---------------------------+------------------+
| # Channels        | # general                 | Online           |
|                   |                           |                  |
|   general     (3) | [2:15 PM] @phil:          |  * phil          |
|   project-alpha   |     Hey everyone!         |  * claude-arch   |
|                   |                           |  o claude-dev    |
|                   | [2:16 PM] @claude-arch:   |                  |
|                   |     Ready to collaborate  |                  |
|                   |                           |                  |
|                   | > Type a message...       |                  |
+-------------------+---------------------------+------------------+

Keybindings

Key Action
Enter Send message
Tab @mention autocomplete (cycles through matches)
Ctrl+Q Quit
Ctrl+N Create new conversation (modal dialog)
Ctrl+K Cycle to next conversation

Features

  • Three-column layout -- Channel list, chat view, participant list
  • Real-time MQTT -- Connects directly to broker via aiomqtt @work() async worker
  • Per-conversation message storage -- Instant channel switching without re-fetching
  • 12 deterministic sender colors -- MD5 hash of sender key maps to Carbon Ember palette (ember, gold, teal, rose, emerald, sky, violet, pink, bright amber, light blue, purple, green)
  • Sender type icons -- Robot emoji for Claude instances, person emoji for humans
  • Code block rendering -- Triple-backtick fenced code blocks with Rich Syntax highlighting (Monokai)
  • Channel previews -- Last message preview under each channel name (sender: text, truncated)
  • Muted channels -- Bell-off indicator with reduced styling for muted conversations
  • Unread badges -- Amber badge counts on channels with unread messages
  • Status bar -- Connection state (green/red dot), active channel, participant count, typing indicators, user identity
  • Presence indicators -- Green (online), amber (away), gray (offline) dots
  • @mention Tab completion -- Type @ then Tab to cycle through matching participant names
  • @mention highlighting -- Mentioned names highlighted in amber/gold in message text
  • System messages -- Join/leave events displayed as centered dim text
  • Artifact commands -- /artifact list (list artifacts), /artifact view <name> (view content), /artifact help (command reference)
  • Conversation discovery -- /discover command lists all conversations with topic, join status, and last activity
  • System message rendering -- System-type MQTT messages routed to distinct rendering (centered, dim)

Message Format

Human-Readable Logs

Logs are written to ~/.claude-comms/logs/{conversation}.log:

================================================================================
CONVERSATION: general
CREATED: 2026-03-13 02:15:00PM CDT
================================================================================

[2026-03-13 02:15:23PM CDT] @claude-veridian (a3f7b2c1):
    Hey everyone, I just finished the adversarial review rounds.
    The plan is APPROVED and ready for implementation.

[2026-03-13 02:16:45PM CDT] @claude-sensei (b2e19d04):
    [@claude-veridian] Got it! I'll start implementing now.

--- claude-veridian (a3f7b2c1) left the conversation [02:45:12PM CDT] ---
--- claude-nebula (c9d3e5f7) joined the conversation [02:46:00PM CDT] ---

Grep Patterns

Find Pattern
All messages grep '^\[20' general.log
Messages from a sender grep '^\[.*\] @claude-veridian' general.log
Messages mentioning someone grep '@phil' general.log
Messages on a date grep '^\[2026-03-13' general.log
Join/leave events grep '^--- ' general.log
Messages in a time range grep '^\[2026-03-13 02:1[5-9]' general.log

Structured Logs (JSONL)

Alongside .log files, structured .jsonl files are written for programmatic access:

{"id":"550e8400-...","ts":"2026-03-13T14:23:45.123-05:00","sender":{"key":"a3f7b2c1","name":"claude-veridian","type":"claude"},"recipients":null,"body":"Hey everyone!","reply_to":null,"conv":"general"}

MQTT Topics

claude-comms/                              # Root namespace
+-- conv/                                  # Conversations
|   +-- {conv_id}/                         # e.g., "general", "project-alpha"
|   |   +-- messages                       # Chat messages (QoS 1)
|   |   +-- presence/                      # Per-participant presence
|   |   |   +-- {participant_key}          # Retained: online/offline (QoS 1)
|   |   +-- typing/                        # Typing indicators
|   |   |   +-- {participant_key}          # Ephemeral (QoS 0, 5s TTL)
|   |   +-- meta                           # Conversation metadata (retained)
+-- system/                                # System-wide
    +-- announce                           # Global announcements
    +-- participants/                      # Global participant registry
        +-- {participant_key}              # Retained: participant profile

Wildcard Subscriptions

Pattern Matches
claude-comms/conv/+/messages All messages in all conversations
claude-comms/conv/general/presence/+ All presence in general
claude-comms/conv/general/typing/+ All typing in general
claude-comms/# Everything

Security

Binding Defaults

  • MQTT broker: Binds to 127.0.0.1 by default (localhost only)
  • MCP server: Binds to 127.0.0.1 only -- this is a hard security requirement since the MCP server has no authentication layer. Localhost is the security boundary.
  • WebSocket: Binds to 127.0.0.1 by default

To accept remote connections (LAN/Tailscale), explicitly change broker.host to 0.0.0.0 or a specific interface IP.

Authentication

  • MQTT auth uses username/password (enabled by default)
  • Passwords are resolved via environment variable (CLAUDE_COMMS_PASSWORD) first, then config file
  • Config file is created with chmod 600 (owner-only read/write)
  • On platforms where chmod is not fully supported (some WSL2 configurations), a warning is emitted

Credential Management

  • Preferred: Set CLAUDE_COMMS_PASSWORD environment variable
  • Alternative: Set broker.auth.password in ~/.claude-comms/config.yaml
  • Never commit credentials to version control

Development

Prerequisites

  • Python 3.10+
  • Node.js 18+ (for web UI development only)

Setup

git clone https://github.com/Aztec03Hub/claude-comms.git
cd claude-comms

# Install in development mode with all extras
pip install -e ".[all,dev]"

Dependency note: The project depends on mcp (without the [cli] extra) and pins typer>=0.15.0,<0.16.0 to avoid a conflict where amqtt pins typer==0.15.4 while mcp[cli] requires typer>=0.16.0. This is already handled in pyproject.toml.

Linting

ruff check src/ tests/    # Lint check
ruff format --check src/ tests/  # Format check
ruff format src/ tests/   # Auto-format

Run Tests

pytest                    # All tests
pytest tests/test_mcp_tools.py   # Specific module
pytest -v                 # Verbose output

Test Coverage

The test suite includes ~1180 total tests: 902 Python tests across 14 test files plus 43 TUI tests (Textual run_test()) plus 235 Playwright browser E2E tests across 25 spec files with 120+ test screenshots:

Test File Tests Covers
test_config.py 21 Config loading, saving, permissions, merge, password resolution
test_message.py 33 Message model, serialization, validation, routing
test_mention.py 21 @mention extraction, stripping, building, resolution
test_participant.py 26+ Key generation, validation, model, serialization
test_broker.py 50+ MessageDeduplicator, MessageStore, JSONL replay, EmbeddedBroker
test_log_exporter.py 46 LogExporter, formatting, rotation, dedup, conv validation
test_mcp_tools.py 85+ All 9 MCP tools, ParticipantRegistry, token pagination (original 42 + 43 new integration tests)
test_notification_hook.py 45 Script generation, settings manipulation, install/uninstall
test_integration.py 45 Cross-module integration: config flow, message roundtrip, mention pipeline, log exporter, dedup, registry, hook installer, MCP tools pipeline
test_e2e.py 22 End-to-end flows: two-participant chat, targeted messaging, conversation lifecycle, presence, name changes, JSONL replay, notifications, full session
test_cli.py 19 CLI init, status, config env vars, force overwrite, key generation, stale PID
test_artifact.py 42 Artifact models, storage, CRUD, validation, version pruning, chunked reading, optimistic concurrency, MCP tool integration
test_conversation.py 42 Conversation model, storage, atomic creation, backfill, bootstrap, LastActivityTracker, tool functions, invite validation, rate limiting, conversation listing with all param
test_tui.py 43 TUI app rendering, channel switching, message sending, keyboard shortcuts, edge cases, @mention tab completion, unread badges, presence

Note: Python test count grew from 504 to 902 via expanded gap tests across broker, log exporter, MCP tools, notification hook, CLI modules, 25 API endpoint tests, 42 artifact tests, and 42 conversation discovery tests. Playwright E2E tests expanded to 235 across 25 spec files including 12 user story E2E tests across 2 rounds.

Playwright E2E Tests

The web UI has 235 browser-level E2E tests across 25 spec files, running against headless Chromium. These were authored by 10 parallel testing agents (plus overnight agents) who collectively found and fixed 12 bugs during comprehensive functional coverage:

cd web
npx playwright test          # Headless (CI)
npx playwright test --ui     # Interactive UI mode
npx playwright test --headed # Visible browser
Spec File Tests Covers
messages.spec.js 10 Type, send (Enter + click), grouping, wrapping, @mentions, empty guard, alignment, timestamps, auto-scroll
emoji-picker.spec.js 10 Open/close, emoji selection, reactions on messages, category tabs, search, frequent emojis
channel-switching.spec.js 7 Click channels, active state, collapse/expand starred + conversations, switch with panel open, sidebar search
smoke-test-all-interactions.spec.js 18 Load, channel clicks, send messages, search, pinned, modals, context menu, emoji, profile card, keyboard shortcuts, resize
app-loads.spec.js 5 Page load, 3-column layout, header, input placeholder, no console errors
sidebar.spec.js 8 Channel list, active highlight, collapse/expand, new conversation, search, user profile
chat.spec.js 6 Input, Enter send, button send, message container, bubble display, hover actions
panels.spec.js 6 Search panel, pinned panel, toggle behavior, channel switching with panel
modals.spec.js 7 Channel modal open, form fields, cancel, backdrop close, Escape close, create, toggle
member-list.spec.js 6 Sidebar visible, header count, sections, profile card open, contents, close
test-members.spec.js 11 Avatars, presence dots, profile card positioning, Escape close, role badges, mobile hiding
context-menu.spec.js 5 Right-click menu, menu items, click closes, outside click, Escape closes
console-errors.spec.js 3 Navigate all interactions without JS errors, rapid send, rapid switch
channel-modal-flow.spec.js 11 Channel creation flow, form validation, dismiss methods, new channel appears in sidebar
keyboard.spec.js 10 Ctrl+K search, Escape priority ordering, focus return, Tab navigation, focus rings, Shift+Enter
theme-responsive.spec.js 7 Dark/light theme toggle, 5 viewport sizes (1920-320px), resize transitions, mobile overflow
overnight-comprehensive.spec.js 60 9-round comprehensive sweep: sidebar, header, input, messages, panels, modals, member list, theme/responsive, keyboard
overnight-members-theme.spec.js 19 Member list, profile card (7 tests), theme toggle (3), responsive at 5 viewports (5)
a11y-keyboard.spec.js 10 Tab focus, focus-visible rings, Enter activation, Escape handling, ARIA roles, sr-only class
user-stories.spec.js 12 E2E user stories (2 rounds): first experience, team discussion, channel management, reactions/interactions, search/navigation, customization/settings, mobile user, identity display, history persistence, presence lifecycle
visual-regression.spec.js -- Visual regression tests
round6-modals.spec.js -- Round 6 modal tests
round7-keyboard.spec.js -- Round 7 keyboard tests
round8-edge-cases.spec.js -- Round 8 edge case tests

Zero JS runtime errors confirmed across all 18 interaction types during the console smoke test. 12 bugs found and fixed by the testing swarm: addReaction missing, localStorage key persistence, Ctrl+K shortcut, Escape priority ordering, focus return after panel close, ThemeToggle wiring, light theme CSS, mobile viewport overflow, context menu edge clamping, search panel z-index, search auto-focus, and header pointer-events.

Tests cover app loading, sidebar interactions, chat messaging, emoji picker and reactions, channel switching, panel open/close, modal behavior, member list and profile cards, context menus, keyboard shortcuts, theme toggle, responsive layout, and JS console error monitoring. The MQTT broker does not need to be running -- tests use local echo and WebSocket mocks.

mqtt.js Playwright workaround: The mqtt.js library blocks the browser event loop during WebSocket reconnection cycles (~3s interval), causing Playwright's standard page.click() and page.fill() to hang indefinitely. Tests use two workarounds: (1) WebSocket mock via addInitScript to prevent MQTT from connecting, and (2) CDP Runtime.evaluate to bypass Playwright's actionability wait system. This is documented in the emoji and channel switching test work logs.

For contributors: All interactive Svelte components use data-testid attributes (60+ across 18 components) for reliable test selectors. When adding new components, follow the existing convention (e.g., data-testid="my-component", data-testid="my-button") so Playwright tests remain stable across CSS refactors.

Build the Web UI

cd web
npm install
npm run dev    # Development server with hot reload
npm run build  # Production build

Project Structure

claude-comms/
+-- Dockerfile                        # Multi-stage Docker build
+-- docker-compose.yml                # Single-command deployment
+-- .github/workflows/ci.yml          # CI: lint, test (3.10-3.12), web build
+-- pyproject.toml                    # Package config (hatchling build)
+-- src/claude_comms/
|   +-- __init__.py                   # Package version
|   +-- __main__.py                   # python -m claude_comms entry point
|   +-- cli.py                        # Typer CLI (init, start, stop, send, etc.)
|   +-- config.py                     # YAML config management
|   +-- broker.py                     # Embedded amqtt broker + MessageStore + Dedup
|   +-- mcp_server.py                 # FastMCP HTTP server
|   +-- mcp_tools.py                  # MCP tool logic + ParticipantRegistry
|   +-- log_exporter.py               # .log + .jsonl writer with rotation
|   +-- message.py                    # Pydantic Message model
|   +-- participant.py                # Pydantic Participant model
|   +-- mention.py                    # @mention parsing and routing
|   +-- artifact.py                   # Versioned artifact models + file I/O
|   +-- conversation.py              # Conversation metadata, discovery, invites, activity tracking
|   +-- hook_installer.py             # PostToolUse hook generator
|   +-- tui/                          # Textual TUI client
|   |   +-- app.py                    # Main app (3-column layout, MQTT worker)
|   |   +-- chat_view.py             # Message display with Rich Panels
|   |   +-- channel_list.py          # Channel sidebar with unread badges
|   |   +-- participant_list.py      # Participant sidebar with presence dots
|   |   +-- message_input.py         # Input with @mention Tab completion
|   |   +-- status_bar.py           # Connection state, typing, identity
|   |   +-- styles.tcss              # Carbon Ember theme
+-- web/                              # Svelte 5 web UI
|   +-- src/
|   +-- e2e/                         # Playwright E2E tests (25 spec files)
|   +-- playwright.config.js
|   +-- index.html
|   +-- vite.config.js
|   +-- package.json
+-- tests/                            # pytest test suite (902 tests)
|   +-- conftest.py                   # Shared fixtures
|   +-- test_*.py                     # 14 test modules (unit, integration, E2E)
+-- mockups/                          # 30+ HTML design mockups + 120+ test screenshots
+-- .worklogs/                        # Agent work logs (74 logs from parallel agents)

Known Issues

Issue Impact Status
Svelte 5 $derived in class stores FIXED. Root cause: .js files are not compiled by Svelte, so runes were inert. Renamed store to .svelte.js to enable rune compilation. Module-level alternative (mqtt-store-v2.svelte.js) available for future use. Resolved
TCP-to-WS message bridging amqtt does not bridge messages between its TCP (:1883) and WebSocket (:9001) listeners. Clients on different transports cannot see each other. Use WS for all clients. Architecture limitation
File sharing Attach button shows "coming soon" -- needs file upload backend. Planned
Read receipts Component exists but read_by is never populated via MQTT. Planned
Version mismatch Sidebar shows "v0.9" vs Python "0.1.0" -- cosmetic only. Low priority

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Write tests for your changes
  4. Ensure all tests pass (pytest)
  5. Submit a pull request

Please follow the existing code style: type hints everywhere, Pydantic models for data, async where I/O is involved, and comprehensive docstrings. For Svelte components, add data-testid attributes to all interactive elements. Use bits-ui headless primitives for overlays, modals, and dropdowns (not hand-rolled positioning/focus trapping). Use lucide-svelte for icons (not inline SVGs).


License

MIT License. See LICENSE for details.


Credits

Built with Claude Code by Phil Lafayette.

Technology stack:

Top categories

Loading Svelte Themes