An MCP server for Atomic CRM β a fantastic open-source CRM built by the folks at Marmelab. Huge thanks to them for building and open-sourcing it.
This package lets you talk to your Atomic CRM instance through natural language β manage contacts, companies, deals, notes, tasks, and file attachments β from Claude Code, Claude Desktop, or any MCP-compatible client.
Built with the MCP TypeScript SDK and Supabase.
Heads up: this project is roughly 95% vibe-coded. It works for my use case, but it hasn't been battle-tested. Expect rough edges, and please don't rely on it for anything critical without giving it a good look first.
Marmelab already published their own MCP server: marmelab/atomic-crm-mcp. It's the official one β go check it out.
This project is just a different approach. Where their server exposes two generic tools (get_schema + query) that let the LLM write raw SQL, this one exposes ~30 domain-specific tools (one per entity and action).
Neither approach is strictly better β it depends on what you need. This one trades flexibility for simplicity: the LLM doesn't need to know SQL, and each call only passes the fields that are actually relevant.
Pick the authentication method that fits your setup:
Option A β Email + password (recommended for personal use):
claude mcp add atomic-crm \
-e SUPABASE_URL="https://your-crm.example.com" \
-e SUPABASE_ANON_KEY="your-anon-key" \
-e SUPABASE_EMAIL="you@example.com" \
-e SUPABASE_PASSWORD="yourpassword" \
-- npx -y @nykk/atomic-crm-mcpOption B β Service role key (admin access, no user login needed):
claude mcp add atomic-crm \
-e SUPABASE_URL="https://your-crm.example.com" \
-e SUPABASE_SERVICE_ROLE_KEY="your-service-role-key" \
-- npx -y @nykk/atomic-crm-mcpOption C β OAuth / SSO provider:
Note: the OAuth login flow has only been tested with Keycloak. It might work with other providers (Authelia, Google, GitHubβ¦) since it uses standard PKCE, but I haven't verified it personally. Proceed with caution and let me know if it works for you.
First run the one-time login command β it opens a browser, authenticates, and saves the session locally:
SUPABASE_URL="https://your-crm.example.com" \
SUPABASE_ANON_KEY="your-anon-key" \
SUPABASE_OAUTH_PROVIDER="keycloak" \
npx -y @nykk/atomic-crm-mcp loginThen register the server (no credentials in the config β the saved session is reused automatically):
claude mcp add atomic-crm \
-e SUPABASE_URL="https://your-crm.example.com" \
-e SUPABASE_ANON_KEY="your-anon-key" \
-- npx -y @nykk/atomic-crm-mcpOAuth callback URL: the login command starts a local server on
http://localhost:54321/callback. You must add this URL to the Additional Redirect URLs list in your Supabase project (Authentication β URL Configuration) before runninglogin.
| Tool | Description |
|---|---|
crm_list_contacts |
List and search contacts with pagination and filtering |
crm_get_contact |
Get a contact enriched with tags, recent deals, pending tasks, and recent notes |
crm_create_contact |
Create a new contact |
crm_update_contact |
Update contact fields, including tag assignment |
crm_assign_contact_to_company |
Link a contact to a company |
| Tool | Description |
|---|---|
crm_list_companies |
List and search companies with pagination and filtering |
crm_get_company |
Get full details of a company by ID |
crm_create_company |
Create a new company |
crm_update_company |
Update company fields |
| Tool | Description |
|---|---|
crm_list_deals |
List and search deals, filter by stage, company, or sales person |
crm_get_deal |
Get a deal enriched with company info, associated contacts, and recent notes |
crm_create_deal |
Create a new deal |
crm_update_deal |
Update deal fields (stage, amount, closing date, archiveβ¦) |
| Tool | Description |
|---|---|
crm_list_contact_notes |
List all notes for a contact |
crm_create_contact_note |
Add a note to a contact |
crm_update_contact_note |
Edit a contact note |
crm_delete_contact_note |
Delete a contact note |
crm_list_deal_notes |
List all notes for a deal |
crm_create_deal_note |
Add a note to a deal |
crm_update_deal_note |
Edit a deal note |
crm_delete_deal_note |
Delete a deal note |
| Tool | Description |
|---|---|
crm_list_tasks |
List tasks, filter by contact or sales person (pending only by default) |
crm_create_task |
Create a task linked to a contact |
crm_complete_task |
Mark a task as completed |
crm_delete_task |
Delete a task |
| Tool | Description |
|---|---|
crm_list_tags |
List all available tags (id, name, color) |
| Tool | Description |
|---|---|
crm_upload_attachment |
Upload a file (base64, max ~7.5 MB) to a note and store it in Supabase Storage |
crm_download_attachment |
Download a file from a note (images returned inline, others as base64) |
crm_delete_attachment |
Remove a file from a note and delete it from storage |
| Tool | Description |
|---|---|
crm_list_sales |
List sales team members (use to find sales_id) |
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL |
Always | Your Atomic CRM URL (e.g. https://crm.example.com) |
SUPABASE_ANON_KEY |
Auth options A & C | Supabase anon/publishable key (safe to share) |
SUPABASE_EMAIL |
Auth option A | User email for non-interactive login |
SUPABASE_PASSWORD |
Auth option A | User password for non-interactive login |
SUPABASE_SERVICE_ROLE_KEY |
Auth option B | Service role key β skips user auth, grants admin access |
SUPABASE_OAUTH_PROVIDER |
Auth option C (login only) |
OAuth provider slug as configured in Supabase (e.g. keycloak) |
Where to find these values: in your Supabase project dashboard under Settings β API.
Three authentication strategies are supported. The server tries them in order β the first one that works is used:
| Priority | Method | How to configure |
|---|---|---|
| 1 | Service role key | Set SUPABASE_SERVICE_ROLE_KEY. Admin access, no user login needed. Good for automation. |
| 2 | Email + password | Set SUPABASE_EMAIL + SUPABASE_PASSWORD. Non-interactive, credentials stay in the MCP config. |
| 3 | Saved session | Reuses the refresh token from a previous login run. Auto-refreshed on every start. |
| 4 | Interactive login | Fallback: prompts email + password via stderr on first run, then saves the session. |
For SSO providers, use the login subcommand once to authenticate via the browser. It implements the OAuth 2.0 PKCE flow and saves a session to ~/.config/atomic-crm-mcp/session.json.
SUPABASE_URL="https://your-crm.example.com" \
SUPABASE_ANON_KEY="your-anon-key" \
SUPABASE_OAUTH_PROVIDER="keycloak" \
npx @nykk/atomic-crm-mcp loginRequirements:
SUPABASE_OAUTH_PROVIDERmust match the provider slug configured in Supabase.http://localhost:54321/callbackmust be listed in Authentication β URL Configuration β Additional Redirect URLs in your Supabase project.
After a successful login the saved session is reused automatically on every subsequent MCP start and refreshed on each run, so you only need to run login once (or again when the refresh token expires).
Add to claude_desktop_config.json:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Email + password:
{
"mcpServers": {
"atomic-crm": {
"command": "npx",
"args": ["-y", "@nykk/atomic-crm-mcp"],
"env": {
"SUPABASE_URL": "https://your-crm.example.com",
"SUPABASE_ANON_KEY": "your-anon-key",
"SUPABASE_EMAIL": "you@example.com",
"SUPABASE_PASSWORD": "yourpassword"
}
}
}
}Service role key:
{
"mcpServers": {
"atomic-crm": {
"command": "npx",
"args": ["-y", "@nykk/atomic-crm-mcp"],
"env": {
"SUPABASE_URL": "https://your-crm.example.com",
"SUPABASE_SERVICE_ROLE_KEY": "your-service-role-key"
}
}
}
}OAuth / SSO (after running npx @nykk/atomic-crm-mcp login once):
{
"mcpServers": {
"atomic-crm": {
"command": "npx",
"args": ["-y", "@nykk/atomic-crm-mcp"],
"env": {
"SUPABASE_URL": "https://your-crm.example.com",
"SUPABASE_ANON_KEY": "your-anon-key"
}
}
}
}git clone https://github.com/nykk-io/atomic-crm-mcp
cd atomic-crm-mcp
npm installSUPABASE_URL=https://crm.example.com \
SUPABASE_ANON_KEY=your-anon-key \
SUPABASE_SERVICE_ROLE_KEY=your-key \
npm run devInspect with MCP Inspector (browser UI at http://localhost:5173)
SUPABASE_URL=https://crm.example.com \
SUPABASE_ANON_KEY=your-anon-key \
SUPABASE_SERVICE_ROLE_KEY=your-key \
npx @modelcontextprotocol/inspector npx tsx src/index.ts# List all registered tools
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \
| SUPABASE_URL=... SUPABASE_ANON_KEY=... SUPABASE_SERVICE_ROLE_KEY=... \
npx tsx src/index.ts 2>/dev/null
2>/dev/nullsilences auth log messages (they go to stderr).