mercury-agent
v0.7.0
Published
Personal AI assistant for chat platforms (WhatsApp, Slack, Discord, Telegram)
Maintainers
Readme
Mercury is a personal AI assistant that lives where you chat. It connects to WhatsApp, Slack, Discord, and Telegram, runs agents inside containers for isolation, and uses pi as the runtime.
Prerequisites
- Node.js >= 18 — Required for
npm install -g mercury-agent - Bun >= 1.2 — JavaScript runtime used by Mercury
- Docker — Required for running agent containers. On Linux, also install
docker-buildx(apt-get install docker-buildx) — Docker Desktop includes it, but Docker Engine on Linux does not. - Windows users: Mercury runs best under WSL2. Install WSL2 with
wsl --install, then install Bun and Docker inside it.
Quick Start
npm install -g mercury-agent
mkdir my-assistant && cd my-assistant
mercury setup # Interactive guided setup (recommended)
# -- or for manual setup: --
mercury initAuthenticate:
mercury auth login # Interactive OAuth (Anthropic, GitHub Copilot, etc.)
mercury auth login anthropic # Or specify provider directly
mercury auth status # Check what's configuredOr set an API key in .env:
MERCURY_ANTHROPIC_API_KEY=sk-ant-...Configure identity and adapters in .env:
MERCURY_BOT_USERNAME=Mercury
MERCURY_TRIGGER_PATTERNS=@Mercury,Mercury
# Enable adapters (pick one or more)
MERCURY_ENABLE_WHATSAPP=true
MERCURY_ENABLE_DISCORD=true
MERCURY_DISCORD_BOT_TOKEN=your-bot-token
MERCURY_ENABLE_TELEGRAM=true
MERCURY_TELEGRAM_BOT_TOKEN=your-bot-tokenStart:
mercury run
# or install as a background service (macOS/Linux only):
mercury service install
mercury runruns in the foreground — on Windows (WSL2) this is the only option. On macOS/Linux, prefermercury service installfor long-running use — it runs in the background and auto-restarts.
Set up spaces and conversations
Mercury discovers conversations from incoming traffic. They start unlinked — you assign them to spaces (memory boundaries).
# Create spaces
mercury spaces create main
mercury spaces create work
mercury spaces create family
# Send a message from WhatsApp/Discord/Slack, then:
mercury conversations # See discovered conversations
mercury conversations --unlinked # See unlinked ones
mercury link <id> main # Link a conversation to a spaceMultiple conversations can point at the same space — they share memory, session, and vault.
Updating Mercury
# 1. Stop Mercury
# macOS/Linux: mercury service uninstall
# Windows/WSL2: Ctrl+C in the terminal running mercury run
# 2. Update the package
npm update -g mercury-agent
# 3. Rebuild the agent container image
mercury build
# 4. Start again
# macOS/Linux: mercury service install
# Windows/WSL2: mercury runmercury build rebuilds the base agent image from the installed package source. The derived image (base + your extensions) rebuilds automatically on startup when the base changes.
How It Works
┌──────────────────────────────────────────────────────────────┐
│ Host Process │
│ │
│ ┌──────────┐ ┌─────────┐ ┌─────────┐ ┌───────────────┐ │
│ │ WhatsApp │ │ Slack │ │ Discord │ │ Scheduler │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │ (cron tasks) │ │
│ └───┬──────┘ └────┬────┘ └───┬─────┘ └──────┬────────┘ │
│ └──────────────┴───────────┴───────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Router/Queue │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ SQLite DB │ │
│ └────────┬────────┘ │
└──────────────────────────────┼───────────────────────────────┘
│
┌─────────▼──────────┐
│ Docker Container │
│ ┌──────────────┐ │
│ │ pi CLI │ │
│ └──────────────┘ │
│ /spaces/<space-id> │
└────────────────────┘Each space is a user-defined memory boundary with its own workspace and pi session. Incoming platform conversations are discovered automatically, then linked into spaces.
Features
| Feature | Description | Docs | |---------|-------------|------| | Multi-platform | WhatsApp, Slack, Discord, Telegram | docs/pipeline.md | | Memory | Obsidian-compatible vault per space | docs/memory.md | | Scheduled Tasks | Cron-based recurring prompts | docs/scheduler.md | | Permissions | Role-based access control | docs/permissions.md | | Media | Images, documents, voice notes | docs/media/overview.md | | KB Distillation | Extract lasting knowledge from chats | docs/kb-distillation.md | | Dashboard | Web UI for managing spaces, tasks, permissions, extensions | docs/dashboard.md | | Extensions | TypeScript plugins for CLIs, skills, jobs, hooks | docs/extensions.md | | DM Auto-Space | Auto-create isolated spaces per user DM | docs/rate-limiting.md |
Default capabilities vs. extensions
Out of the box, Mercury is a capable chat assistant with conversation context, scheduled tasks, role-based permissions, multi-space management, message recall, and sub-agent delegation. It does not include web search, web browsing, a knowledge vault, voice, or third-party service integrations — those are added via extensions.
Install extensions from the dashboard (Features page), the CLI, or during setup with a profile:
# From the CLI
mercury add web-browser # Web browsing & search (uses Brave Search, no API key needed)
mercury add napkin # Obsidian-style knowledge vault
# Or pick a profile that bundles what you need
mercury setup --profile researchRun mercury extensions list or visit the dashboard's Features page to see what's available.
Workspaces
Each space gets an isolated workspace:
.mercury/spaces/<space-id>/
├── AGENTS.md # Space instructions
├── .mercury.session.jsonl # pi session
├── inbox/ # Media received from users
└── outbox/ # Files produced by the agentMemory/vault structure (for example .obsidian/, knowledge/, daily/) is created by installed extensions.
Multiple platform conversations can point at the same space.
Agent Profiles
Profiles are shareable templates that bundle agent instructions, extensions, and defaults. Use them to start from a preconfigured assistant type.
Built-in profiles:
| Profile | Description |
|---------|-------------|
| general | General-purpose assistant — helpful, concise, chat-friendly |
| coding | Code-focused with sub-agent delegation and web search |
| research | Web search and knowledge distillation focused |
# Create from built-in profile
mercury setup --profile coding
# Create from git repo
mercury setup --profile https://github.com/user/mercury-stock-agent
# Create from local directory
mercury setup --profile ./my-profiles/ml-assistant
# List available profiles
mercury profiles list
# Export current project as a profile
mercury profiles export ./my-profileCLI
mercury (host CLI)
# setup + runtime
mercury setup # Interactive guided setup (recommended for new projects)
mercury setup --profile <name> # Create from a profile (general, coding, research, or path/URL)
mercury init # Manual init (creates .env, .mercury structure)
mercury run
mercury build # rebuild agent container image (required after updates)
mercury status
mercury doctor # preflight check — validates Docker, Bun, credentials, adapters
# profiles (agent templates)
mercury profiles list # List built-in profiles
mercury profiles show <name> # Show profile details
mercury profiles export <dir> # Export current project as a reusable profile
# auth
mercury auth login [provider]
mercury auth logout [provider]
mercury auth status
mercury auth whatsapp
# chat + routing
mercury chat "hello"
mercury chat --file photo.jpg "what's in this?"
mercury chat --space work "check status"
echo "summarize" | mercury chat
mercury spaces list
mercury spaces create <id>
mercury conversations
mercury conversations --unlinked
mercury link <conversation-id> <space-id>
# extensions
mercury add ./path/to/extension
mercury add npm:<package>
mercury add git:<repo-url>
mercury add user/repo # GitHub shorthand (e.g. mercury add user/mercury-ext-ml)
mercury add user/repo#subdir # GitHub repo with subdirectory
mercury remove <name>
mercury extensions list
mercury ext create <name> # Scaffold a new extension
mercury ext validate <name> # Validate extension structure and load
mercury ext test <name> # Dry-run load extension
# service (macOS/Linux only — uses launchd/systemd)
mercury service install
mercury service uninstall
mercury service status
mercury service logs [-f]mrctl (in-container API CLI)
mrctl whoami
mrctl tasks list|create|pause|resume|run|delete
mrctl roles list|grant|revoke
mrctl permissions show|set
mrctl config get|set
mrctl spaces list|name|delete
mrctl conversations list
mrctl stop
mrctl compactFor full command docs, run mercury --help, mercury <command> --help, or mrctl help.
Extensions
Mercury supports TypeScript extensions that add CLIs, skills, background jobs, lifecycle hooks, config keys, and dashboard widgets.
Creating Extensions
mercury ext create my-extension # Scaffold a new extension
mercury ext validate my-extension # Check structure and load
mercury ext test my-extension # Dry-run load.mercury/extensions/
├── napkin/
│ ├── index.ts
│ └── skill/SKILL.md
└── my-extension/
└── index.tsEach extension exports a setup function:
export default function(mercury) {
mercury.cli({ name: "napkin", install: "bun add -g napkin-ai" });
mercury.permission({ defaultRoles: ["admin", "member"] });
mercury.env({ from: "MERCURY_NAPKIN_API_KEY" });
mercury.skill("./skill");
mercury.on("workspace_init", async ({ workspace, containerWorkspace }) => { ... });
mercury.on("before_container", async ({ workspace, containerWorkspace }) => {
return { env: { MY_VAR: containerWorkspace + "/data" } };
});
}Extensions with CLIs get auto-installed into a derived Docker image. Skills are symlinked for agent discovery. Permissions integrate with the existing RBAC system.
See docs/extensions.md for the extension system guide.
Configuration
Optional project file mercury.yaml (or mercury.yml) supplies non-secret defaults; any set MERCURY_* variable overrides it. See docs/configuration.md and resources/templates/mercury.example.yaml.
Environment Variables
Core:
| Variable | Default | Description |
|----------|---------|-------------|
| MERCURY_DATA_DIR | .mercury | Data directory |
| MERCURY_MAX_CONCURRENCY | 2 | Max concurrent runs |
| MERCURY_PORT | 8787 | API port |
| MERCURY_BOT_USERNAME | mercury | Bot display name |
| MERCURY_LOG_LEVEL | info | Log level |
Security:
| Variable | Description |
|----------|-------------|
| MERCURY_API_SECRET | Shared secret for /api/* routes. When set, requires Authorization: Bearer <secret>. Auto-generated by mercury setup. |
| MERCURY_CHAT_API_KEY | Optional API key for /chat endpoint. When set, requires Bearer token. When unset, /chat is open (for local use). |
Auth:
Credentials are resolved in this order:
- OAuth credentials from
mercury auth login(saved to.mercury/global/auth.json) - API keys from
.env(e.g.,MERCURY_ANTHROPIC_API_KEY)
Supported OAuth providers: Anthropic, GitHub Copilot, Google Gemini CLI, Antigravity, OpenAI Codex.
Model:
| Variable | Default | Description |
|----------|---------|-------------|
| MERCURY_MODEL_PROVIDER | anthropic | Provider |
| MERCURY_MODEL | claude-opus-4-8 | Model |
| MERCURY_ANTHROPIC_API_KEY | — | API key |
| MERCURY_ANTHROPIC_OAUTH_TOKEN | — | OAuth token (alternative) |
Adapters:
| Variable | Description |
|----------|-------------|
| MERCURY_ENABLE_WHATSAPP | Enable WhatsApp |
| MERCURY_WHATSAPP_AUTH_DIR | Auth storage path |
| MERCURY_ENABLE_TELEGRAM | Enable Telegram |
| MERCURY_TELEGRAM_BOT_TOKEN | Telegram bot token |
| MERCURY_ENABLE_DISCORD | Enable Discord |
| MERCURY_DISCORD_BOT_TOKEN | Discord bot token |
| MERCURY_ENABLE_SLACK | Enable Slack |
| MERCURY_SLACK_BOT_TOKEN | Slack bot token |
| MERCURY_SLACK_SIGNING_SECRET | Slack signing secret |
Container:
| Variable | Default | Description |
|----------|---------|-------------|
| MERCURY_AGENT_IMAGE | ghcr.io/avishai-tsabari/mercury-agent:latest | Container image |
| MERCURY_CONTAINER_TIMEOUT_MS | 300000 | Container timeout (5 min) |
| MERCURY_CONTAINER_RUNTIME | runc | runc (default) or runsc (gVisor) |
| MERCURY_CONTAINER_BWRAP_DOCKER_COMPAT | false | Set true on Linux Docker Engine (see note below) |
| MERCURY_AGENT_ID | — | Unique ID for this assistant (required when running multiple assistants on the same Docker daemon) |
Multiple assistants on the same machine: Mercury builds a derived Docker image (
mercury-agent-ext:<hash>) from the base image plus your extensions. When a new image is built, older tags in the same repo are pruned. If two assistants share a Docker daemon without distinctMERCURY_AGENT_IDvalues, they share the same image repo and one will silently prune the other's image, causing container launch failures. SetMERCURY_AGENT_IDto a unique value per project (e.g. in.env). Cloud console deployments set this automatically.
Linux Docker Engine: Mercury uses bubblewrap for in-container sandboxing. On Linux Docker Engine (not Docker Desktop), bwrap cannot mount
/procwithout extra privileges. Either setcontainer_bwrap_docker_compat: trueinmercury.yaml(adds--privilegedtodocker run), or install gVisor and setMERCURY_CONTAINER_RUNTIME=runscto skip bwrap entirely.
KB Distillation:
| Variable | Default | Description |
|----------|---------|-------------|
| MERCURY_KB_DISTILL_INTERVAL_MS | 0 (disabled) | Distillation interval |
Triggers:
| Variable | Default | Description |
|----------|---------|-------------|
| MERCURY_TRIGGER_MATCH | mention | mention, prefix, always |
| MERCURY_TRIGGER_PATTERNS | @Mercury,Mercury | Trigger patterns |
| MERCURY_ADMINS | — | Pre-seeded admin user IDs |
DM Auto-Space
Auto-create isolated spaces for each user who DMs your bot. Each user gets their own space with persistent context, configurable permissions, and rate limits.
# mercury.yaml
dm_auto_space:
enabled: true
admin_ids: # platform IDs — auto-link to "main" space
- "24417056866472" # WhatsApp LID, Telegram user ID, etc.
default_system_prompt: "You are a helpful assistant."
default_member_permissions: "prompt,prefs.get" # restrict users to chat only
runtime:
rate_limit_daily_member: 20 # global daily cap for members (0 = unlimited)
rate_limit_daily_admin: 0 # global daily cap for admins (0 = unlimited)admin_ids values are platform-specific identifiers — check the dashboard Conversations page to see the format (e.g. WhatsApp LID digits, Telegram numeric user ID).
Auto-created spaces are seeded with trigger.match=always, context.mode=context, and the configured member permissions. Changing rate_limit_daily_member in mercury.yaml propagates to all auto-created spaces immediately — no per-space update needed. Explicit per-space overrides (set via dashboard or API) still take precedence.
Broadcast
Send a message to all auto-created DM spaces at once (e.g. maintenance notices, promotions):
curl -X POST http://localhost:8787/api/broadcast \
-H "Authorization: Bearer $API_SECRET" \
-H "X-Mercury-Caller: <your-admin-id>" \
-H "X-Mercury-Space: main" \
-H "Content-Type: application/json" \
-d '{"text": "Maintenance tonight 10pm-2am."}'Returns { total, delivered, failed, errors }. Only global admins (admins or dm_auto_space.admin_ids) may broadcast. Messages are sent as literal text — no LLM processing.
Bot Character
Global admins can set a bot-wide character (tone, greeting style, personality) that applies to all spaces — including every DM auto-space. Stored in the database; survives profile deploys.
The recommended flow is conversational: tell the bot to change its character, it drafts the update, confirms with you, then saves via mrctl character set --file <path>. Programmatic access:
# Set
curl -X PUT http://localhost:8787/api/character \
-H "Authorization: Bearer $API_SECRET" \
-H "X-Mercury-Caller: <admin-id>" \
-H "X-Mercury-Space: main" \
-H "Content-Type: application/json" \
-d '{"text": "Be warm and professional. Greet with שלום וברכה."}'
# Get current character
curl http://localhost:8787/api/character ...
# Clear
curl -X DELETE http://localhost:8787/api/character ...Only global admins may set the character (same gate as broadcast). Per-space tone adjustments use the existing per-space system_prompt in dashboard settings.
Per-space Config
Conversations are discovered from incoming traffic. Unlinked conversations stay idle until you attach them to a space via mercury link <conversation-id> <space-id> or the dashboard.
mrctl config set trigger_match always
mrctl config set trigger_patterns "@Bot,Bot"Docs
- Platform setup: WhatsApp · Discord · Slack
- Configuration (mercury.yaml + env) · PRD: config load
- Authentication
- Message pipeline
- Memory system
- Scheduled tasks
- Permissions
- Media handling
- KB distillation
- Container lifecycle
- Graceful shutdown
- Rate limiting
- Dashboard
- Extensions
Credits
Mercury was originally created by Michael Livshits. Full credit for the foundational architecture, extension system, and platform adapters goes to him.
Original repository: github.com/Michaelliv/mercury (archived)
This fork is maintained by Avishai Tsabari.
License
MIT — see LICENSE
Third-party licenses
The WhatsApp adapter depends on @whiskeysockets/baileys, which transitively includes libsignal (GPL-3.0) for Signal Protocol encryption. This component runs inside an isolated Docker container and is loaded as a runtime dependency, not statically linked into Mercury's codebase. If your use case has strict copyleft concerns, consult legal counsel or omit the WhatsApp adapter.
