Skip to main content
Glama
iamsrishanth

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.

![License](https://img.shields.io/github/license/iamsrishanth/OpenTeleCRM)
![Node](https://img.shields.io/badge/node-%E2%89%A5%2022-339933?logo=nodedotjs&logoColor=white)
![TypeScript](https://img.shields.io/badge/TypeScript-5.7-3178C6?logo=typescript&logoColor=white)
![NestJS](https://img.shields.io/badge/NestJS-10-E0234E?logo=nestjs&logoColor=white)
![Next.js](https://img.shields.io/badge/Next.js-16-000000?logo=nextdotjs&logoColor=white)
![PostgreSQL](https://img.shields.io/badge/PostgreSQL-16%2F17-4169E1?logo=postgresql&logoColor=white)
![Android](https://img.shields.io/badge/Android-Kotlin%202.0-7F52FF?logo=kotlin&logoColor=white)
![Tests](https://img.shields.io/badge/tests-151-2ea44f)
![Self-hosted](https://img.shields.io/badge/self--hosted-native-blueviolet)

---

## 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:

![OpenTeleCRM agent desk โ€” dashboard with live stats](assets/screenshots/dashboard.png)

![OpenTeleCRM agent desk โ€” follow-up callbacks queue](assets/screenshots/callbacks.png)

![OpenTeleCRM agent desk โ€” automation rules with visual builder](assets/screenshots/automations.png)

## 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>