agent-crm
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
agent-crm
A small CRM your AI agent can run. It exposes an MCP endpoint so an agent can search contacts, run the call queue, log calls and move deals, without being able to delete anything or send email. A plain web app shows you everything it did.

FastAPI, sqlite, Jinja2, Docker. One container, one database file, no external services.
Status: 1.0. Breaking changes to the MCP tool set or schema are now called out in
CHANGELOG.mdunder a major version bump. Back updata/crm.dbbefore upgrading.
Who it's for
A good fit if you are one person (a solo consultant, a small clinic, or a one-person trade business) who sells through calls and follow-ups, and you want an agent to do the CRM busywork while you keep a clear view of the pipeline.
Not a fit if you need multiple users with roles and permissions, email sent
and received inside the CRM, reports and dashboards, or a native mobile app.
These are left out on purpose; see docs/SCOPE.md.
Related MCP server: Follow Up Boss MCP Server
Quick start
For trying it out. docker-compose.yml is a development setup with live reload;
see Production for a real deployment.
git clone https://github.com/sevasek/agent-crm.git && cd agent-crm
cp .env.example .env
docker compose up --build -d
docker compose exec app python scripts/create_admin.py you@example.com "Your Name"
docker compose exec app python scripts/seed_services.py # optional example servicesOpen http://localhost:8000 and log in.
Connect an agent
Generate a key, put it in .env as CRM_MCP_API_KEY, and recreate the container
(a plain restart does not reload .env):
python -c "import secrets; print(secrets.token_hex(32))"
docker compose up -dThen point any MCP client at http://localhost:8000/mcp (Streamable HTTP) with
Authorization: Bearer <key>. Check it works:
curl -sS http://localhost:8000/mcp \
-H "Content-Type: application/json" -H "Accept: application/json" \
-H "Authorization: Bearer $CRM_MCP_API_KEY" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'An agent can search contacts (search_partners), pull today's queue
(get_call_queue), log what happened (record_call_outcome, log_activity),
and move deals (set_deal_stage). It cannot delete records, send email, manage
users, or reshape the pipeline. Tools tell the agent to search before it creates,
and updates fill empty fields instead of overwriting yours.
Hosted connectors that need OAuth 2.1 with PKCE can use /oauth/authorize.
Full tool list, auth and limits: docs/MCP.md.
What's inside
Companies and people in one table; people link to their company.
Deals on a pipeline you define: stage names, order and roles are data.
A next action and date on every deal, with due and overdue flags and a daily check that logs each due follow-up on the contact's timeline.
A daily call list ranked out of 100 by a fixed formula you can read (deal value, source, contact completeness, recency).
A phone-first call view with tap-to-call, the offer to pitch, and one-tap outcomes.
Weighted fit rules, a service catalogue and priced offers you configure.
Lead ingest over an API or CLI, with duplicate matching.
Tags on deals (a campaign, a region, anything else you want to filter by), set from the deals page or from the agent.
Follow-on (post-sale) work is another deal linked with
parent_deal_id, plus delegated tasks — not a separate fulfilment module.Delegated tasks to hand work to another agent or a person, with an optional webhook.
An optional webhook when a deal enters a nurture stage.
An optional webhook when a deal is won, for invoice hand-off.
It starts empty apart from a starter pipeline: services, offers and fit rules are yours to define. Judgement lives in your agent, not in the CRM; scores are plain formulas, not a model.
Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --buildStanding up a separate client (its own database, secrets, admin, and
domain) is docs/CLIENT_DEPLOY.md. The app binds
to 127.0.0.1:${CRM_PORT:-8000} (default 8000). Put a
TLS-terminating reverse proxy in front and forward everything, including
/mcp and /oauth/*. Working Caddy and nginx configs, header forwarding, and
TRUSTED_PROXIES notes are in docs/DEPLOY.md. A host that
already runs Traefik adds docker-compose.traefik.yml
on top of the prod file. In .env:
SECRET_KEY: a real one, not the placeholder.SECURE_COOKIES=trueandBASE_URL=https://your.domain.TRUSTED_PROXIES: your proxy's IP, a CIDR (e.g. the compose network172.18.0.0/16), or a hostname, so rate limits use the real client IP.TZ: decides what "today" means for follow-ups.CRM_PORTandCOMPOSE_PROJECT_NAME: set these when more than one instance shares a host (see Multiple instances on one host).
Security defaults: login uses expiring session cookies with CSRF protection;
login and API endpoints are rate limited (failed auth is counted per IP;
authenticated MCP/API traffic has a larger per-key bucket); the lead-ingest,
stages and MCP keys are separate, and each endpoint refuses all requests until
its key is set. Set BOOTSTRAP_ADMIN_EMAIL and BOOTSTRAP_ADMIN_PASSWORD_FILE
(preferred; wins over BOOTSTRAP_ADMIN_PASSWORD) once to create the first
admin without shell access — the file is not visible in docker inspect.
That first password is one-time: it must be changed at first login (until
then the account can only reach the change-password form) and it expires
after ONE_TIME_PASSWORD_TTL_HOURS (default 24). Reset a lapsed one with
scripts/create_admin.py --reset <email>.
/health checks that sqlite is writable and returns 503 if it is not, and
also 503s ({"status": "low_disk_space"}) once free space on the DB's
filesystem drops below HEALTH_MIN_FREE_MB (default 200), ahead of an
actual write failure.
The staleness-cron service runs the follow-up check on start and daily at 08:00.
Production compose rotates json-file logs at 10 MB × 5 files and caps the app
at 256 MB RAM. docker stop / docker kill leave the container stopped under
restart: unless-stopped — a maintenance stop is not a crash, so start it
again with docker compose ... up -d when you are done.
You can run a published multi-arch image instead of building from the
working copy (build: . stays the default). How to cut a tag and what the
workflow publishes: docs/RELEASE.md.
# compose override (a third -f file, or docker-compose.override.yml)
services:
app:
image: ghcr.io/sevasek/agent-crm:vX.Y.Z
build: !reset
pull_policy: always
staleness-cron:
image: ghcr.io/sevasek/agent-crm:vX.Y.Z
build: !reset
pull_policy: alwaysThen docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
(no --build) so Compose pulls the published image instead of building locally.
See CHANGELOG.md for what changed between tags.
Backup and restore
All data is one sqlite file, ./data/crm.db, in WAL mode. Do not cp it while
the app runs: you can copy a torn database. Use the shipped online-backup
script (sqlite's backup API, safe under load; files are mode 0600 and land
in ./backups/, not ./data/):
./scripts/backup.shIt prefers host Python against the bind-mounted file, and falls back to
docker compose exec if the database is only reachable inside the container.
It verifies PRAGMA integrity_check and prunes snapshots older than
BACKUP_KEEP_DAYS (default 14).
Nothing leaves the host until you set an off-host hook — pick one:
# restic / any command; the backup path is appended (or substitute {})
export BACKUP_REMOTE_CMD='restic -r s3:s3.amazonaws.com/your-bucket backup'
# or rclone
export BACKUP_RCLONE_DEST='remote:crm-backups'
# or aws s3
export BACKUP_S3_URI='s3://your-bucket/crm'Do not put cloud credentials in CI; these are operator-only. Install a schedule from the examples (not just this README):
scripts/backup.cron.example— crontab //etc/cron.dscripts/crm-backup.service.example+scripts/crm-backup.timer.example— systemd, 03:00
Restore (stops the stack, drops WAL/SHM, copies the snapshot, starts):
./scripts/restore.sh backups/crm-YYYY-MM-DDTHHMMSSZ.dbThe entrypoint repairs ownership of ./data on first boot when it can
traverse the volume; later restarts skip that step so cap_drop: ALL
survives a restart. If start fails on an unreadable bind-mount, chown
the host ./data to APP_UID (see docs/DEPLOY.md). Check docker compose ps
shows healthy and you can log in.
Optional sub-minute RPO: run Litestream beside the
app to replicate data/crm.db to S3/GCS. It is not bundled and not required.
Multiple instances on one host
scripts/new-instance.sh NAME TZ EMAIL allocates the next free CRM_PORT
(from instances/ports.tsv, starting at 8000), writes a mode-600
instances/$NAME/.env with fresh secrets.token_hex(32) values, sets
COMPOSE_PROJECT_NAME=crm-$NAME, starts the stack with
docker-compose.port.yml + docker-compose.instance.yml, waits for
/health, creates the admin via create_admin.py (one-time password
printed once, expiring after ONE_TIME_PASSWORD_TTL_HOURS, default 24 —
BOOTSTRAP_ADMIN_PASSWORD is not left in the env), and
prints a Caddy site block.
CRM_PORT is interpolated as 127.0.0.1:${CRM_PORT:-8000}:8000.
COMPOSE_PROJECT_NAME keeps container and network names from colliding
when two instances share a host. TRUSTED_PROXIES is set to the compose
network CIDR (172.16.0.0/12). Use --dry-run to generate the env
without Docker.
Backup and restore default to ./data/crm.db and the default compose
project. For an instance, point them at that instance or they will copy
or down the wrong stack:
export CRM_DB_PATH=instances/acme/data/crm.db
export COMPOSE_PROJECT_NAME=crm-acme
export CRM_ENV_FILE=instances/acme/.env
export BACKUP_DIR=instances/acme/backups
./scripts/backup.sh
./scripts/restore.sh instances/acme/backups/crm-YYYY-MM-DDTHHMMSSZ.dbIngest leads
POST /api/v1/leads with X-API-Key: $CRM_API_KEY (header only), or from the CLI.
Each item may set "type": "lead" (default) or "type": "opportunity".
Default ingest creates a lead and does not create a partner. Jobs that
still need a partner and an opportunity must send "type": "opportunity"
or pass --type opportunity.
python scripts/inject_leads.py --dry-run tests/fixtures/leads/example.json
python scripts/inject_leads.py tests/fixtures/leads/example.json
python scripts/inject_leads.py --type opportunity tests/fixtures/leads/example.jsonservice_slug must already exist in your catalogue when the item needs a
service. Field meanings and matching rules are in
docs/DATA_MODEL.md. To load confirmed clients from
markdown files, see scripts/import_clients.py (still opportunity by
default; --type lead is opt-in).
Docs
USING.md the screens, in plain language ·
SCOPE.md what's in and out ·
DATA_MODEL.md entities, schema and ingest rules ·
MCP.md the agent interface ·
CLIENT_DEPLOY.md new client instance on a VPS ·
DEPLOY.md reverse proxy and TLS ·
OFFBOARDING.md removing a cancelled customer's instance ·
PRIVACY.md what the CRM collects, why, and how to ask about it ·
RELEASE.md how to cut a version tag
Those files also build into a static manual that uses the same CSS as the app. The running app does not use Node, and the image does not include the manual build. To build it locally:
npm ci
npm run docs:build # writes ./_site
npm run docs:serve # live-reload at http://localhost:8080
npm run docs:test # build and check the outputContributing
Open an issue before a pull request that adds a feature. The scope is narrow on purpose. Bug fixes can go straight to a pull request. Run the tests first:
pip install -r requirements-dev.txt
python -m pytest tests/ -qSupport and security
Questions and bugs: open an issue. To report a vulnerability, use the
private security report form
rather than a public issue (see SECURITY.md).
License
This server cannot be deployed
Maintenance
Related MCP Connectors
API-first CRM for LLMs - contacts, companies, deals and activities over a native MCP server.
The HubSpot MCP Server acts as a bridge that enables AI assistants and Large Language Models to securely interact with HubSpot CRM data through natural conversation, without requiring users to understand complex API structures. It provides read-only access to standard CRM objects (contacts, companies, deals, tickets, products, invoices, and more) and their associations, secured via OAuth 2.0, allowing AI agents to perform tasks like summarizing deals, fetching company updates, and looking up record changes.
MCP server enabling AI agents to manage Bitrix24 features via standardized protocol
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA comprehensive MCP server for Bitrix24 CRM integration that enables AI agents to manage contacts, deals, tasks, leads, and companies. It also provides advanced tools for sales team monitoring, performance analytics, and automated CRM search capabilities.2 npm-
- AlicenseCqualityCmaintenanceAn MCP server that gives AI agents full access to Follow Up Boss CRM via natural language, enabling CRUD operations on people, deals, appointments, and more through 160 tools.10033 npm1Elastic 2.0
- FlicenseNot gradedqualityDmaintenanceA governed MCP server for integrating AI agents with customer data, featuring role-based access control, field redaction, and human-in-the-loop approval for secure support operations.1-
- AlicenseAqualityDmaintenanceMCP server that connects to a sales CRM database, enabling AI agents to search prospects, view pipeline status, manage follow-ups, and update records using natural language commands.5121 npmISC