Connect any AI agent runtime to any messaging platform.
Existing chatbot frameworks run their own agent loop — they call LLM APIs, parse tool calls, execute tools locally, and manage context. When you connect them to a managed agent runtime like Claude Managed Agents, they have to bypass their entire infrastructure just to pipe messages through.
HarnessGate takes a different approach: no local agent loop. It's a pure bridge that delegates all intelligence to the provider runtime. The gateway just routes messages between platforms and the agent.
This means:
- Claude Managed Agents features work out of the box (tool confirmation, custom tools, multi-agent threads, extended thinking)
- Any future agent runtime plugs in with 4 methods
- No competing agent loops, no bypassed infrastructure, no wasted abstractions
[Telegram] [Discord] [Slack] [WhatsApp] [Teams] [Web UI]
| | | | | |
+----+----+----+---+-------+----------+------+
| |
PlatformAdapter ABC (per platform)
| |
+----+----+
|
Bridge (orchestrator)
SessionStore + StreamManager
|
Provider ABC
|
+----------+----------+
| Claude | Custom |
| Managed | (your |
| Agents | class) |
+----------+----------+
- Provider-agnostic — Claude Managed Agents or bring your own
- 6 platform adapters — Telegram, Discord, Slack, WhatsApp, Teams, Web UI
- Multi-app — run multiple bot instances per platform, each mapped to a different agent
- Session management — automatic session creation, in-memory persistence, multi-turn conversations
- Buffer-then-send — accumulates agent responses, sends as one message per turn
- Auto-split — respects per-platform message length limits
- Fully async — built on asyncio with
async/awaitthroughout
pip install harnessgateimport asyncio
from harnessgate import Bridge, BridgeConfig
from harnessgate.providers import ClaudeProvider
from harnessgate.platforms import TelegramAdapter
async def main():
provider = ClaudeProvider(api_key="sk-ant-...")
bridge = Bridge(
provider=provider,
config=BridgeConfig(platforms={"telegram": {"enabled": True}}),
)
# Route users to agents
async def resolve_user(sender, platform, message):
return {
"user_id": sender.id,
"agent_id": "agent_01XXXX",
"environment_id": "env_01XXXX",
}
bridge.set_user_resolver(resolve_user)
bridge.add_platform(TelegramAdapter())
await bridge.connect("telegram", {"bot_token": "123:ABC..."})
await bridge.start()
asyncio.run(main())| Platform | Library | Max text | Markdown | Threads | Typing | Attachments |
|---|---|---|---|---|---|---|
| Telegram | aiogram 3.x | 4096 | Yes | Yes | Yes | Yes |
| Discord | discord.py | 2000 | Yes | Yes | Yes | Yes |
| Slack | slack-bolt | 4000 | Yes | Yes | No | Yes |
| Cloud API (aiohttp) | 4096 | No | No | No | Yes | |
| Teams | Bot Framework SDK | 28000 | Yes | Yes | Yes | Yes |
| Web | Built-in (aiohttp) | 100000 | Yes | No | Yes | No |
Detailed setup instructions for each platform, including SaaS multi-tenant distribution:
- Telegram Setup — BotFather, privacy settings, multi-instance
- Discord Setup — Developer Portal, intents, OAuth2 invite links
- Slack Setup — Socket Mode, scopes, App Directory distribution
- WhatsApp Setup — Meta Business, webhook config, Embedded Signup
- Teams Setup — Azure Bot, manifest, Teams App Store
- Web Setup — HTTP endpoints, SSE streaming
from harnessgate.providers import ClaudeProvider
provider = ClaudeProvider(api_key="sk-ant-...")Connects to Claude Managed Agents. Full support for streaming, tool confirmation, custom tools, extended thinking, and multi-agent threads.
For Claude, agent_id and environment_id come from your UserResolver, not static provider config:
async def resolve_user(sender, platform, message):
return {
"user_id": sender.id,
"agent_id": "agent_01XXXX",
"environment_id": "env_01XXXX",
}Implement the Provider ABC:
from harnessgate import Provider
class MyProvider(Provider):
id = "my-provider"
capabilities = ProviderCapabilities(
interrupt=False,
tool_confirmation=False,
custom_tools=False,
thinking=False,
)
async def create_session(self, opts): ...
async def send_message(self, session_id, message): ...
async def stream(self, session_id): ... # AsyncIterator[ProviderEvent]
async def destroy_session(self, session_id): ...Every platform adapter supports running multiple app instances simultaneously. Each app connects to the platform and receives a platform-assigned appId.
# Add multiple Telegram bots at runtime
support_id = await bridge.connect("telegram", {"bot_token": SUPPORT_TOKEN})
sales_id = await bridge.connect("telegram", {"bot_token": SALES_TOKEN})
# Route based on which bot received the message
async def resolve_user(sender, platform, message):
agent_id = await db.get_agent_for_bot(message.app_id)
environment_id = await db.get_environment_for_bot(message.app_id)
return {"user_id": sender.id, "agent_id": agent_id, "environment_id": environment_id}| Platform | Source | Example value |
|---|---|---|
| Telegram | bot.id |
"123456789" |
| Discord | client.application_id |
"1098765432101234567" |
| Slack | auth_test().bot_id |
"A0123456789" |
| WABA phone number ID | "106540352267890" |
|
| Teams | activity.recipient.id |
"28:abc123..." |
| Web | N/A (single instance) | -- |
Each conversation context gets its own provider session:
| Context | Session scope | Example key |
|---|---|---|
| DM | Per user | telegram:direct:123:u:user99 |
| Group/Channel | Shared (all users) | slack:group:ch1 |
| Thread | Per thread | discord:thread:ch1:t:thread99 |
harnessgate-py/
├── src/harnessgate/
│ ├── __init__.py # Public API exports
│ ├── bridge.py # Orchestrator
│ ├── messages.py # InboundMessage, OutboundMessage, Sender, Attachment
│ ├── platform.py # PlatformAdapter ABC
│ ├── provider.py # Provider ABC + ProviderEvent types
│ ├── session.py # SessionStore, MemorySessionStore
│ ├── stream.py # StreamManager
│ ├── platforms/
│ │ ├── telegram.py # aiogram 3.x
│ │ ├── discord.py # discord.py
│ │ ├── slack.py # slack-bolt
│ │ ├── whatsapp.py # Cloud API (aiohttp)
│ │ ├── teams.py # Bot Framework SDK
│ │ └── web.py # Built-in HTTP + SSE
│ └── providers/
│ └── claude.py # Claude Managed Agents
├── tests/ # pytest test suite
├── docs/ # Platform setup guides
├── examples/ # Starter projects
└── pyproject.toml
- Python >= 3.11
- uv (recommended) or pip
uv sync
uv run pytest
uv run pyright # type checkingMIT