OpenTeleCRM MCP Server
by iamsrishanth
README.md
# OpenTeleCRM
**The self-hosted, drop-in replacement for TeleCRM.** A telecalling-first sales CRM for teams that live on the phone โ leads, dialer, WhatsApp, automation โ with your data on your hardware and an API your existing integrations already speak.









---
## Table of contents
- [What is OpenTeleCRM?](#what-is-opentelecrm)
- [See it in action](#see-it-in-action)
- [Features](#features)
- [Architecture](#architecture)
- [Quick start](#quick-start)
- [TeleCRM parity](#telecrm-parity)
- [Roadmap](#roadmap)
- [Documentation](#documentation)
- [Tunnel mode](#tunnel-mode)
- [Contributing](#contributing)
- [License](#license)
---
## What is OpenTeleCRM?
OpenTeleCRM is a **1:1 open-source clone of TeleCRM** โ the telecalling-first sales
CRM used by thousands of Indian sales teams โ rebuilt clean-room against the
documented API surface, self-hosted **natively** (no Docker), and multi-tenant
from line one.
It answers the three questions every telecalling operation asks:
| | |
|---|---|
| **๐ Your data, your rules** | Runs on your hardware โ Linux (Debian/Ubuntu, systemd) or macOS (Homebrew, launchd), native binaries, no containers. Every table is tenant-isolated by PostgreSQL Row-Level Security, `FORCE`d so even the DB owner can't cross tenants. |
| **๐ Wire-compatible, not look-alike** | The REST (`/autoupdate/v2`) and MCP surfaces match TeleCRM's API shapes, so existing integrations, scripts, and Postman/Bruno collections keep working โ verified by a 27-request, 26-assertion Bruno collection that runs green against the live API. |
| **๐ Live channels, not mocks** | Real WhatsApp outbound through a standalone, deploy-anywhere Baileys bridge. Real calls through a source-built Asterisk 21 PBX over ARI, with caller-ID, callbacks, and TRAI-window-aware dialer scoring. |
The parity promise is **compatible surface, not bug-compatible behavior**: where
TeleCRM silently drops async writes, caps tokens at 3, or expires MCP tokens in
30 days with no renewal, OpenTeleCRM deliberately fixes it (see
[docs/PARITY.md ยง Divergences](docs/PARITY.md)).
## See it in action
Agent desk (dark theme) โ dashboard, callbacks queue, automation rules, all
rendered against the seeded demo workspace:



## Features
| Area | What you get |
|------|--------------|
| **Multi-tenant foundation** | 28 RLS-`FORCE`d tenant tables, `withTenant()` transaction wrapper, deterministic seed (1 enterprise ยท 5,000 leads ยท 3 users ยท 2 pipelines ยท 20 custom fields) |
| **Sync API** | TeleCRM-parity `POST/GET/PUT/DELETE` under `/autoupdate/v2` โ leads CRUD + upsert-by-identifier + search, actions batch CRUD, team members, custom fields/actions โ with per-item `CREATED\|IGNORED\|UPDATED\|REJECTED` status + `remarks[]` |
| **Async API** | Fire-and-forget `autoupdatelead` โ `requestId`, `?validate=true` dry-run (zero writes), `X-Strict-Mode` 422, ingest log + per-field outcomes |
| **MCP server** | 13 TeleCRM-parity tools (Streamable HTTP, RLS-scoped) โ drive the CRM from Cursor, Claude, or any MCP client |
| **WhatsApp** | Send, unified inbox with auto lead-attribution, templates, broadcasts with consent ledger, drip sequences โ plus a **standalone bridge** (Baileys 7.x, own session + queue) you can deploy on any Linux box |
| **Telephony** | Smart dialer (score + TRAI-window + DND suppression), live caller-ID, follow-up callbacks, call tracking, recordings (signed URLs) โ **live calls via Asterisk ARI** |
| **Automation** | Pure-TS rule engine, 9 event kinds, 10 action executors, lead distribution (round-robin / least-loaded / skill-match), HMAC-authenticated webhooks (X-OT-Signature) + replay, 60s scheduler, per-tenant quota metering, 10 seeded templates, **React Flow visual builder** |
| **Web agent desk** | Next.js app: dashboard with real stats, leads (search/filter/score), dialer call pad, WhatsApp inbox, automations + builder, sequences, templates, broadcasts, callbacks, webhooks, settings |
| **Mobile app** | Kotlin-native Android client (Compose): offline-first Room cache + outbox, caller-ID heads-up, dialer with dispositions, WhatsApp inbox, UnifiedPush, F-Droid metadata โ verified on-device |
## Architecture
A pnpm + Turborepo monorepo, ESM-only, Node โฅ 22. One Postgres database, one
tenant-scoping discipline:
```mermaid
flowchart LR
WEB[Next.js agent desk<br/>apps/web ยท :3007] --> API[NestJS API<br/>services/api ยท :3005]
MOB[Kotlin mobile app<br/>apps/mobile] --> API
MCPC[MCP clients] -->|JSON-RPC /mcp| MCP[MCP server<br/>services/mcp ยท :3100]
API --> DB[(PostgreSQL 16/17<br/>28 RLS-FORCE tenant tables)]
MCP --> DB
API --> BRIDGE[WhatsApp bridge<br/>services/whatsapp-bridge ยท :3098]
BRIDGE -.->|Baileys 7.x| WA[WhatsApp]
API --> ARI[Asterisk 21 LTS ยท ARI<br/>infra/asterisk ยท :8088 loopback]
ARI -.->|SIP| PSTN[PSTN / SIP trunk]
```
Every request resolves a token โ tenant, then reads through `withTenant(eid)` โ
a transaction that sets `app.enterprise_id` so RLS returns only that tenant's
rows. Missing tenant context returns **zero rows**, not a leak.
Full detail โ C4 diagrams, data flow, ports, future containers:
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
## MCP client setup
The MCP server (`services/mcp`) exposes 13 TeleCRM-parity tools over
Streamable HTTP (`POST /mcp`, loopback bind by default). In production it
**refuses to boot** without `MCP_ENTERPRISE_ID` and either `MCP_BEARER_TOKEN`
or an explicit `MCP_ALLOW_UNAUTHENTICATED=true`; set `MCP_HOST=0.0.0.0` only
when you also set a bearer token (a public bind without one is rejected).
Server env (`services/mcp`):
```bash
MCP_PORT=3100
MCP_ENTERPRISE_ID=<tenant-uuid>
MCP_BEARER_TOKEN=<long-random-token> # recommended
MCP_HOST=127.0.0.1 # default; loopback only
```
Clients that support auth-header streamable HTTP configs: Claude Desktop,
Cursor, VS Code MCP, MCP Inspector, Continue โ all use the same shape:
**Claude Desktop** โ `claude_desktop_config.json`:
```json
{
"mcpServers": {
"opentelecrm": {
"type": "http",
"url": "http://127.0.0.1:3100/mcp",
"headers": { "Authorization": "Bearer <MCP_BEARER_TOKEN>" }
}
}
}
```
**Cursor** โ `.cursor/mcp.json`:
```json
{
"mcpServers": {
"opentelecrm": {
"type": "http",
"url": "http://127.0.0.1:3100/mcp",
"headers": { "Authorization": "Bearer <MCP_BEARER_TOKEN>" }
}
}
}
```
Point `url` at the tunnel/ingress host when the server is remote, and keep the
bearer token out of anything you commit (`.gitignore` already covers `*.mcp`).
## Quick start
**Prerequisites:** Debian/Ubuntu (or any Linux), Node.js โฅ 22 (corepack pnpm),
PostgreSQL 16 or 17, ~10 minutes.
```bash
git clone https://github.com/iamsrishanth/OpenTeleCRM.git
cd OpenTeleCRM
make setup # provision system deps + pnpm install + db-init + db-migrate + db-seed
make dev # API on :3005 (web desk: pnpm --filter @opentelecrm/web dev โ :3000)
pnpm test # 151 tests across API / MCP / whatsapp / telephony
```
`make setup` is a one-shot: it provisions native deps, installs JS deps,
creates the DB role, runs all 8 migrations, and seeds a demo workspace (5,000
leads, 3 users, 2 pipelines). Step-by-step targets (`make provision`, `make
install`, `make db-init`, `make db-migrate`, `make db-seed`) exist if you'd
rather run them separately.
**Bruno collection** (wire-compatibility proof):
```bash
cd collections/opentelecrm
bash ../../scripts/bruno-bootstrap-jwt.sh # inject a dev JWT
npx @usebruno/cli run --env local -r . # 27 requests, 26 assertions
```
## TeleCRM parity
| Area | Status |
|------|--------|
| Foundation (multi-tenant, RLS, seed) | โ
|
| Lead CRUD + search + upsert | โ
|
| Action logging (note, call, WhatsApp) | โ
|
| Async autoupdate + validation | โ
|
| API token management (20-token cap, D2 fix) | โ
|
| Custom fields / team / workspace settings | โ
|
| WhatsApp (send, inbox, templates, broadcasts, sequences) | โ
live outbound |
| Telephony (calls, dialer, caller-ID, callbacks, recordings) | โ
live ARI dialing |
| Automation (rules, schedule, distribution, webhook, quota) | โ
|
| Web app + mobile app | โ
|
| Widget / browser extension | ๐ง planned |
| AI & voice / reports / billing / migration tooling | ๐ง roadmap (P5โP10) |
Full matrix with per-feature test IDs: [docs/PARITY.md](docs/PARITY.md)
## Roadmap
Shipped: **P0โP4** (foundation โ core CRM โ WhatsApp โ telephony โ automation),
**P4b** (web desk, visual builder, sequences, quota metering, live
Asterisk/WhatsApp wiring), **P8 mobile** (ahead of plan, as Kotlin native).
Next up: **P5 lead capture** (connector SDK, persistent ingest log, webhook /
CSV / missed-call / email / FB Lead Ads connectors). Then P6 analytics, P7
AI & voice, P9 admin/migration/SaaS, P10 hardening & launch.
Per-phase scope + exit criteria: [docs/ROADMAP.md](docs/ROADMAP.md)
## Documentation
| Doc | Contents |
|-----|----------|
| [docs/README.md](docs/README.md) | Documentation index + conventions |
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | C4 architecture, data flow, ports |
| [docs/PARITY.md](docs/PARITY.md) | TeleCRM parity matrix + divergences |
| [docs/ROADMAP.md](docs/ROADMAP.md) | What's shipped / what's next (P0โP10) |
| [docs/DECISIONS.md](docs/DECISIONS.md) | ADR log (ADR-0001 โ ADR-0031) |
| [docs/RISKS.md](docs/RISKS.md) | Risk register (WhatsApp ToS, recording privacy, RLS) |
| [docs/LICENSES.md](docs/LICENSES.md) | License posture for every component |
| [services/whatsapp-bridge/README.md](services/whatsapp-bridge/README.md) | Deploy-anywhere WhatsApp bridge |
| [infra/asterisk/README.md](infra/asterisk/README.md) | Asterisk 21 PBX scaffold |
| [infra/macos/README.md](infra/macos/README.md) | Cross-platform runbook: portable launchers, systemd + launchd, brew provisioning |
| [apps/mobile/README.md](apps/mobile/README.md) | Android client (modules, build, F-Droid) |
## Networking & tunnel mode
The web desk resolves its API base **at runtime from the browser's own origin**
(`apps/web/src/lib/config.ts` `getApiBase()`), so it works from every surface
without a build-time flag:
| Web UI served at | API base the browser calls |
|------------------|----------------------------|
| `http://localhost:3007` / `http://127.0.0.1:3007` | `http://localhost:3005/autoupdate/v2` |
| `http://<lan-ip>:3007` | `http://<lan-ip>:3005/autoupdate/v2` |
| `http://<tailnet-ip>:3007` or `http://<host>.ts.net:3007` | `http://<same-host>:3005/autoupdate/v2` |
| `https://crm.srishanth.com` (Cloudflare) | `https://api.srishanth.com/autoupdate/v2` |
The Cloudflare tunnel (optional โ demos / remote teams) splits web and API
onto two hostnames; both are managed by `scripts/tunnel.py` through the
Cloudflare API:
```bash
make tunnel # ensure DNS CNAMEs + ingress: crm.srishanth.com -> :3007 (web),
# api.srishanth.com -> :3005 (API); write apps/web/.env.local
make untunnel # revert to local mode
```
Tunnel origins, zone, and all Cloudflare credentials live **only** in
gitignored files (`.env`, `apps/web/.env.local`, `/etc/cloudflared/token`) โ
never committed, never printed. Because the API base is derived at runtime,
no `next build` time flag or baked origin is involved โ the same bundle serves
every surface. While the tunnel is up the API is publicly reachable, so never
run it with production credentials.
## Cross-platform supervision
The web + API servers are supervised identically on Linux and macOS through
**one portable launcher pair** (`infra/launchers/launch-api.sh`,
`infra/launchers/launch-web.sh` โ resolve `node` dynamically, source `.env`,
no watch flag):
- **Linux (systemd):** `infra/systemd/opentelecrm-{api,web}.service`
(`sudo systemctl enable --now opentelecrm-api opentelecrm-web`).
- **macOS (launchd):** `infra/macos/com.opentelecrm.{api,web}.plist`
(`launchctl load ~/Library/LaunchAgents/โฆ`).
Full runbook + portability matrix: [`infra/macos/README.md`](infra/macos/README.md).
## Contributing
Contributions are welcome โ this is a young project and the roadmap is long.
1. **Fork โ branch โ PR** (conventional commits).
2. **The gate is green or the PR doesn't land:** `pnpm test` (151), `make
typecheck`, `pnpm lint` (Biome for services/packages; eslint for `apps/web`).
3. **Docs update in the same commit as the feature** โ test counts, tables,
ports, and statuses in README/PARITY/ROADMAP must not drift (see
[docs/README.md](docs/README.md) conventions).
4. **No Docker, ever** โ native provisioners only: `scripts/provision/debian.sh`
on Linux, `infra/macos/provision-brew.sh` on macOS (ADR-0001).
5. **No secrets** โ `.env*`, tunnel hostnames, `*.mcp` tokens, and the mobile
release keystore are gitignored; a PR that commits any of them will be
rejected.
## License
**AGPL-3.0** โ see [LICENSE](LICENSE) and [docs/LICENSES.md](docs/LICENSES.md).
OpenTeleCRM is **not affiliated with TeleCRM**. It is an independent,
clean-room implementation of TeleCRM's documented API surface for self-hosted
use. TeleCRM is a trademark of its respective owner.
---
<p align="center"><sub>Built for teams that live on the phone. ๐</sub></p>
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues