by op7418
Host-agnostic bridge connecting Claude Code SDK to IM platforms (Telegram, Discord, Feishu)
# Add to your Claude Code skills
git clone https://github.com/op7418/Claude-to-IMEnglish | 中文
Claude-to-IM is a host-agnostic bridge library that connects Claude Code SDK to IM platforms, allowing users to interact with Claude through Telegram, Discord, and Feishu (Lark).
This library handles all IM-side complexity — message routing, streaming previews, permission approval flows, Markdown rendering, chunking, retry, rate limiting — while delegating persistence, LLM calls, and permission resolution to the host application through a set of dependency injection interfaces.
If you want a ready-to-use desktop application without writing any integration code, check out CodePilot — a desktop GUI client for Claude Code with built-in IM bridge support. CodePilot implements all the host interfaces for you and provides a complete UI for managing sessions, settings, and bridge connections.
Claude-to-IM was extracted from CodePilot as a standalone library for developers who want to embed the IM bridge capability in their own applications.
No comments yet. Be the first to share your thoughts!
IM Platform (Telegram / Discord / Feishu)
|
| InboundMessage
v
+-----------+ +------------------+
| Adapter |---->| Bridge Manager | (orchestrator)
+-----------+ | |- Channel Router -> session binding
| |- Conversation Engine -> LLM streaming
| |- Permission Broker -> tool approval flow
| |- Delivery Layer -> chunking, retry, dedup
+------------------+
|
| Host Interfaces (DI)
v
+------------------+
| Host Application | (implements BridgeStore,
| | LLMProvider, etc.)
+------------------+
All bridge modules access host services through a DI context (getBridgeContext()), never through direct imports. This means you can plug the bridge into any Node.js application by implementing four interfaces.
npm install claude-to-im
Or clone this repo and install dependencies:
git clone https://github.com/op7418/Claude-to-IM.git
cd Claude-to-IM
npm install
The bridge requires four interfaces. See docs/development.md for the full specification of each interface.
import { initBridgeContext } from 'claude-to-im/context';
import type { BridgeStore, LLMProvider, PermissionGateway, LifecycleHooks } from 'claude-to-im/host';
const store: BridgeStore = { /* your persistence layer (~30 methods) */ };
const llm: LLMProvider = { /* wraps Claude Code SDK streamChat */ };
const permissions: PermissionGateway = { /* resolves pending tool permissions */ };
const lifecycle: LifecycleHooks = { /* optional start/stop callbacks */ };
initBridgeContext({ store, llm, permissions, lifecycle });
import * as bridgeManager from 'claude-to-im/bridge-manager';
await bridgeManager.start();
const status = bridgeManager.getStatus();
// { running: true, adapters: [{ channelType: 'telegram', running: true, ... }] }
A self-contained example with in-memory store and echo LLM is included:
npx tsx src/lib/bridge/examples/mock-host.ts
All settings are read through BridgeStore.getSetting(key). Your host application decides how to store and surface these values (database, env vars, config file, UI settings panel, etc.).
| Key | Description |
|-----|-------------|
| remote_bridge_enabled | Master switch — "true" to enable the bridge |
| bridge_{adapter}_bot_token | Bot token for the platform (e.g. bridge_telegram_bot_token) |
| bridge_{adapter}_allowed_users | Comma-separated user IDs authorized to use the bridge |
| Key | Description | Default |
|-----|-------------|---------|
| bridge_auto_start | Auto-start bridge on app launch | "false" |
| bridge_{adapter}_enabled | Per-adapter toggle | "false" |
| bridge_{adapter}_stream_enabled | Enable streaming previews | "true" |
| bridge_default_cwd | Default working directory for new sessions | $HOME |
| bridge_model | Default Claude model | Host decides |
Replace {adapter} with telegram, discord, or feishu.
Before adopting this library, be aware of the following constraints:
This is a library, not a standalone application. You need to provide:
BridgeStore — A persistence layer with ~30 methods covering settings, sessions, messages, channel bindings, audit logs, dedup tracking, permission links, and channel offsets. This is the largest integration surface. See the full interface definition in src/lib/bridge/host.ts.LLMProvider — A wrapper around your LLM client that returns a ReadableStream<string> of SSE-formatted events. The stream format must match the Claude Code SDK's event protocol (text, tool_use, tool_result, permission_request, status, result events). See docs/development.md for the full event format spec.PermissionGateway — A way to resolve pending tool permissions from the Claude Code SDK.LifecycleHooks — Optional callbacks for bridge start/stop events.The LLMProvider.streamChat() must return SSE-formatted strings matching the Claude Code SDK's event protocol. If you are not using the Claude Code SDK, you need to adapt your LLM client's output to match this format. This is not a generic "chat completion" interface.
The bridge does not bundle any database driver. You provide all persistence through BridgeStore. This gives you full control but means you need to implement storage for sessions, messages, bindings, audit logs, dedup keys, permission links, and channel offsets.
The bridge uses a session lock mechanism (acquireSessionLock / renewSessionLock / releaseSessionLock) to serialize messages within the same session. Your BridgeStore implementation must provide atomic lock operations. For single-process deployments, in-memory locks work fine. For multi-process deployments, you need distributed locking (e.g., database-backed).
You still need to create bots on each platform and obtain tokens:
| Document | Description | |----------|-------------| | Development Guide | Host interface specs, SSE format, adapter development, step-by-step integration tutorial | | Architecture | Module dependency graph, message flows, design decisions | | Security | Threat model, mitigations, deployment recommendations | | Contributing | Dev setup, code style, testing guide | | Migration | Before/after import patterns for migrating from direct imports |
src/
lib/bridge/
context.ts # DI container (initBridgeContext / getBridgeContext)
host.ts # Host interface definitions (BridgeStore, LLMProvider, etc.)
types.ts # Shared type definitions (messages, bindings, status)
bridge-manager.ts # Orchestrator — start/stop, message dispatch, session locks
channel-adapter.ts # Abstract base class + adapter registry
channel-router.ts # ChannelAddress -> ChannelBinding resolution
conversation-engine.ts # LLM stream processing, SSE consumption
delivery-layer.ts # Reliable outbound delivery (chunk, retry, dedup, audit)
permission-broker.ts # Tool permission forwarding and callback handling
adapters/
telegram-adapter.ts # Telegram Bot API long polling
discord-adapter.ts # Discord.js Gateway WebSocket
feishu-adapter.ts # Feishu/Lark WSClient
telegram-media.ts # Telegram file download/attachment handling
telegram-utils.ts # Telegram API helpers
index.ts # Side-effect imports for adapter self-registration
markdown/
ir.ts # Intermediate representation for Markdown AST
render.ts # Generic IR -> platform string renderer
telegram.ts # Markdown -> Telegram HTML
discord.ts # Markdo