Modular bridge between chat platforms and AI agents
Project description
Agent Bridge
Modular bridge service that connects chat platforms to AI agents. The architecture cleanly separates platform concerns from agent concerns, making it easy to add new platforms or agents.
Currently supports Slack as the chat platform and Claude Code as the AI agent backend.
Architecture
┌─────────────────────────┐
│ Platform (Slack) │ Defines session semantics (thread = session)
│ platforms/slack/ │ Manages per-session locking & flow control
│ - adapter.py │ Renders agent events (stream updates, final reply)
│ - config.py │
└──────────┬──────────────┘
│ session_key, text, context
▼
┌─────────────────────────┐
│ Bridge │ Pure routing — no platform or agent knowledge
│ bridge.py │ session_key → session_id (via SessionManager)
│ session.py │ Forwards to agent, yields BridgeEvents back
│ events.py │ TextDelta | StatusUpdate | Completion
│ protocols.py │ AgentController + PlatformAdapter protocols
└──────────┬──────────────┘
│ session_id, prompt, is_new, context
▼
┌─────────────────────────┐
│ Agent (Claude Code) │ Purely invoked: load session + input → output
│ agents/claude/ │ Translates Claude stream-json → BridgeEvents
│ - controller.py │ Does not define sessions or care about rendering
│ - events.py │
│ - config.py │
└─────────────────────────┘
Design Principles
Platform defines session semantics. A Slack thread is a session. A Discord channel might be a session. This is platform knowledge — the bridge and agent don't care how sessions are defined.
Agent is purely invoked. It receives (session_id, prompt, is_new, context), loads the session, executes, and yields events. It doesn't know where the session came from or how results will be rendered.
Bridge is pure routing. It resolves session keys to session IDs and forwards requests/events. No platform-specific or agent-specific logic.
Generic Event Model
Platforms consume three event types — the common language between any agent and any platform:
| Event | Description |
|---|---|
TextDelta |
Incremental text from the agent |
StatusUpdate |
Agent is performing an action (tool use, thinking, etc.) |
Completion |
Agent finished responding (with cost, duration, error status) |
Agent-internal events (init, thinking, tool results) are translated to these generic types within each agent module.
Data Flow
- User sends a message in Slack (via
@mentionin channel or direct message) - Slack Adapter receives the event, constructs a session key (
slack:{channel}:{thread_ts}) - Slack Adapter acquires per-session lock (prevents concurrent processing)
- Bridge resolves session key → session ID via SessionManager
- Agent (Claude Controller) spawns
claude -pwith the session, yieldsBridgeEvents - Slack Adapter renders events as real-time message updates (throttled to avoid rate limits)
Session Management
- Each Slack thread maps to one agent session (defined by the platform)
- The bridge stores the mapping:
session_key → {session_id, created_at, last_used} - Mappings are persisted in a JSON file
- Sessions have a configurable TTL (default 72 hours) — expired sessions are automatically purged
Tech Stack
| Component | Choice | Reason |
|---|---|---|
| Language | Python 3.12+ | Type union syntax (X | Y), match statements, modern asyncio |
| Package manager | uv | Fast, supports pyproject.toml natively |
| Slack SDK | slack-bolt | Official Slack SDK, async support, Socket Mode |
| Async HTTP | aiohttp | Required by slack-bolt for async Socket Mode |
| Env config | python-dotenv | Load .env files |
| Testing | pytest + pytest-asyncio | Standard Python testing |
| Claude CLI | claude -p with --output-format stream-json |
Non-interactive mode with real-time streaming |
Project Structure
agent-bridge/
├── pyproject.toml
├── .env.example
├── src/
│ └── agent_bridge/
│ ├── __init__.py # Entry point: wires platform + bridge + agent
│ ├── config.py # BridgeConfig (session store, TTL)
│ ├── bridge.py # Pure routing: session resolve → agent call
│ ├── events.py # TextDelta, StatusUpdate, Completion
│ ├── session.py # SessionManager (key → session_id mapping)
│ ├── protocols.py # AgentController + PlatformAdapter protocols
│ ├── agents/
│ │ └── claude/
│ │ ├── config.py # ClaudeConfig (work_dir, permissions, timeout)
│ │ ├── controller.py # Claude Code subprocess controller
│ │ └── events.py # Claude stream-json parser + BridgeEvent converter
│ └── platforms/
│ └── slack/
│ ├── config.py # SlackConfig (bot_token, app_token)
│ └── adapter.py # Slack adapter (session def, locking, rendering)
└── tests/
├── test_events.py # Claude event parsing + BridgeEvent conversion
└── test_session.py # Session manager tests
Setup
Prerequisites
- Python 3.12+
- uv
- Claude Code CLI installed and authenticated
Install
git clone <repo-url>
cd agent-bridge
uv sync
Slack App Configuration
- Create a Slack App at api.slack.com/apps
- Enable Socket Mode and generate an App-Level Token (
xapp-...) - Add the following Bot Token Scopes under OAuth & Permissions:
app_mentions:read— receive @mention eventschat:write— send and update messagesim:history— read DM messagesim:read— access DM channels
- Subscribe to these Events under Event Subscriptions:
app_mentionmessage.im
- Install the app to your workspace and copy the Bot User OAuth Token (
xoxb-...)
Environment Variables
cp .env.example .env
Edit .env:
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_APP_TOKEN=xapp-your-app-level-token
CLAUDE_WORK_DIR=/path/to/your/project
CLAUDE_PERMISSION_MODE=acceptEdits
SESSION_STORE_PATH=./sessions.json
SESSION_TTL_HOURS=72
| Variable | Required | Default | Description |
|---|---|---|---|
SLACK_BOT_TOKEN |
Yes | — | Slack Bot User OAuth Token |
SLACK_APP_TOKEN |
Yes | — | Slack App-Level Token (Socket Mode) |
CLAUDE_WORK_DIR |
No | . |
Working directory for Claude Code |
CLAUDE_PERMISSION_MODE |
No | acceptEdits |
Claude permission mode |
SESSION_STORE_PATH |
No | ./sessions.json |
Path to session mapping file |
SESSION_TTL_HOURS |
No | 72 |
Session TTL in hours |
Run (Local)
uv run agent-bridge
Run (Docker)
cp .env.example .env
# Edit .env with your tokens (including ANTHROPIC_API_KEY for Docker)
docker compose up --build
Test
uv run pytest tests/ -v
Usage
- Channel: Mention the bot —
@AgentBridge help me refactor this function - DM: Send a direct message — the bot responds in the same conversation
- Thread continuity: Reply in the same Slack thread to continue the agent session
Extending
Adding a new agent
Create agents/<name>/ with config.py, controller.py, events.py. Implement the AgentController protocol — your run() method yields BridgeEvents. Wire it up in __init__.py.
Adding a new platform
Create platforms/<name>/ with config.py, adapter.py. Define your own session key logic (e.g., discord:{guild}:{channel}), manage per-session locking, consume BridgeEvents from bridge.handle_message(). Wire it up in __init__.py.
Neither change requires modifying the bridge, the other agent, or the other platform.
Design Decisions
One-shot per message (vs. long-running process)
Each user message spawns a new claude -p process that exits after completion. Session continuity is handled by Claude Code's built-in --resume flag.
Why: Simpler process lifecycle, no idle resource consumption, graceful handling of crashes.
Per-session locking (platform-owned)
An asyncio.Lock per session key prevents concurrent agent processes for the same session. This is managed by the platform adapter, not the bridge, because locking strategy may vary by platform.
Throttled Slack updates
Slack message updates are throttled to 1.5-second intervals during streaming.
Why: Slack's API rate limits are ~1 request/second per method.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ai_agent_bridge-0.1.0.tar.gz.
File metadata
- Download URL: ai_agent_bridge-0.1.0.tar.gz
- Upload date:
- Size: 61.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b276eee2e455315e38d69153cede23e3e4b3631a3dde9fe5bec0a4fed3fddfe
|
|
| MD5 |
7d472aee42b6bd26effaff185ab7a4e6
|
|
| BLAKE2b-256 |
2e269061f82c55e5e0943ffb71bfb09f8a3d756f5946a9da59c301e36f48af0a
|
File details
Details for the file ai_agent_bridge-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ai_agent_bridge-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5187379f6ed6fee4296b7820693580f2c5bcf1352ec41e5091782f71c130190
|
|
| MD5 |
ddc5672fad1fccd2ba7c358392d0683b
|
|
| BLAKE2b-256 |
058e71b7e2362c20525d8bdcddea7b78e71c3ef7f46aa3826c9688e14e092a5f
|