Guide · MCP
Connect the Ooperon CRM MCP to any LLM
Twenty-one tools that let any MCP-capable model actually work your CRM — find people, fix records, move deals, book meetings, answer conversations and clean up duplicates. One endpoint, scoped credentials, and a dry run for anything you are not sure about.
By the Ooperon team · Updated September 2026 · 9 min read
This is the second of two servers
Ooperon runs two Model Context Protocol servers, and picking the right one is the first decision you make:
| Endpoint | What it is for |
|---|---|
/api/mcp-crm | This guide. Operating the CRM: people, deals, tasks, calendars, conversations. |
/api/mcp | Building automations. Covered in its own guide. |
They are split on purpose. What makes a model pick the wrong tool or invent an id is the size and similarity of the list it is shown, not the number of servers it is connected to — two coherent lists of twenty beat one list of forty near-duplicates. Authentication is shared, literally: both endpoints import the same module, so one oop_live_… key or one OAuth token works on both. You can connect either, or both.
dryRun, and why the scopes below are worth reading properly rather than granting all of them.The endpoint
https://ooperon.com/api/mcp-crmTransport is streamable HTTP, stateless — one JSON-RPC request and response per call, no session to keep alive. Clients that only speak stdio reach it through the mcp-remote bridge, shown per client below.
Authenticating, and the one gotcha
An API key (recommended here)
Open Admin → API Keys in the app and create one. You will see the full oop_live_… secret exactly once — only its hash is stored, so nobody, us included, can show it to you again. Pass it as a bearer token:
Authorization: Bearer oop_live_xxxxxxxxGrant only the scopes you want the model to have. Each tool checks its own, so a key with only contacts:read is genuinely read-only — it can list your contacts and will be refused an opportunity:
| Scope | Unlocks |
|---|---|
contacts:read | Everything read-only — and the scope that opens the door to this server at all. |
contacts:write | Create, update, organise and merge contacts, log activity — and SEND MESSAGES to them. |
companies:read | Companies in search results and crm_get_record. |
opportunities:read / opportunities:write | Read deals; create them and move them through a pipeline. |
tasks:write | Create, complete and reopen tasks. |
calendars:read, appointments:write | List calendars and free slots; book one of them. |
The key is scoped to one workspace and reaches every business inside it. A key from one workspace can never read or write another's data.
Or OAuth, if your client asks for scopes
Hit the endpoint with no credential and it answers with a pointer to its own discovery document, which names the authorization server and advertises the CRM scopes. A spec-compliant client reads that, asks for those scopes, shows you a consent screen listing each one in plain language, and you are connected — no key to copy or rotate.
automations:read automations:write — and this server will refuse it with a 403 naming contacts:read. That is deliberate, not a bug: a bare connect must never silently hand a model the ability to merge your contacts and message your customers. If your client does not negotiate scopes, use an API key.Choosing a business
A workspace can hold several businesses, so every tool takes an optional businessId. Omit it and a single-business workspace just works; a multi-business one asks you to name which, rather than guessing. The ids come from crm_describe_workspace.
Client setup
Claude Code (CLI)
claude mcp add ooperon-crm --transport http https://ooperon.com/api/mcp-crm \
--header "Authorization: Bearer oop_live_xxxxxxxx"Note that --scope in that command means something else entirely — where the config is stored (local, user or project), not OAuth scope. You never pass CRM scopes on the command line; they come from the key, or from the consent screen.
Claude.ai and Cowork
Remote connectors are configured once against your Claude account and are shared across Claude clients, so adding this in one place covers Claude on the web, Claude Desktop and Cowork. On Pro or Max, go to Customize → Connectors, choose + → Add custom connector, and paste https://ooperon.com/api/mcp-crm. On Team or Enterprise an owner adds it under Organization settings → Connectors → Add → Custom → Web, and members then connect to it from Customize → Connectors.
Claude Desktop (stdio bridge)
If you would rather run it locally with a key than through a connector, add it to claude_desktop_config.json:
{
"mcpServers": {
"ooperon-crm": {
"command": "npx",
"args": ["mcp-remote", "https://ooperon.com/api/mcp-crm",
"--header", "Authorization: Bearer oop_live_xxxxxxxx"]
}
}
}Cursor
Create .cursor/mcp.json in your project, or the global one in ~/.cursor/:
{
"mcpServers": {
"ooperon-crm": {
"url": "https://ooperon.com/api/mcp-crm",
"headers": { "Authorization": "Bearer oop_live_xxxxxxxx" }
}
}
}GitHub Copilot (VS Code)
Add .vscode/mcp.json. The inputs block makes VS Code prompt for the key and keeps it out of the file you commit:
{
"inputs": [
{ "id": "ooperon-key", "type": "promptString", "description": "Ooperon API key", "password": true }
],
"servers": {
"ooperon-crm": {
"type": "http",
"url": "https://ooperon.com/api/mcp-crm",
"headers": { "Authorization": "Bearer ${input:ooperon-key}" }
}
}
}OpenAI Codex (CLI)
Codex speaks stdio, so bridge it in ~/.codex/config.toml:
[mcp_servers.ooperon_crm]
command = "npx"
args = ["mcp-remote", "https://ooperon.com/api/mcp-crm", "--header", "Authorization: Bearer oop_live_xxxxxxxx"]openClaw
Recent openClaw builds talk to remote streamable-HTTP servers directly, no bridge needed. Add it from the CLI:
openclaw mcp add ooperon-crm \
--url https://ooperon.com/api/mcp-crm \
--transport streamable-httpOr in ~/.openclaw/openclaw.json, under mcp.servers:
{
"mcp": {
"servers": {
"ooperon-crm": {
"url": "https://ooperon.com/api/mcp-crm",
"transport": "streamable-http",
"enabled": true,
"headers": { "Authorization": "Bearer oop_live_xxxxxxxx" }
}
}
}
}openClaw's own guidance is to keep credentials out of config literals and put the bearer token in its secret storage instead — worth doing, since this key can send messages. There is also a Settings → MCP screen in the Control UI if you prefer clicking.
Hermes
Add an entry under mcp_servers in ~/.hermes/config.yaml. Remote servers use url and headers; streamable HTTP is the default transport, so you do not need to name it:
mcp_servers:
ooperon_crm:
url: "https://ooperon.com/api/mcp-crm"
headers:
Authorization: "Bearer oop_live_xxxxxxxx"Then restart Hermes, or run /reload-mcp in an active session and the crm_* tools appear alongside the built-in ones.
Anything else
Two rules cover every remaining client. If it accepts a remote HTTP server, give it the URL and an Authorization: Bearer header. If it only launches local stdio processes, give it this:
npx mcp-remote https://ooperon.com/api/mcp-crm --header "Authorization: Bearer oop_live_xxxxxxxx"That is the universal fallback — any MCP client that can start a process can reach the server through it.
Checking it worked, without a client
One raw call lists the tools. The Accept header needs both media types; leaving one out is the usual reason a first manual call fails:
curl -s -X POST https://ooperon.com/api/mcp-crm \
-H "Authorization: Bearer oop_live_xxxxxxxx" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'The twenty-one tools
Always start with crm_describe_workspace. It hands the model every real id and vocabulary word in your workspace, which is what stops it inventing a status value or a stage id and then failing three calls later.
| Tool | What it does |
|---|---|
crm_describe_workspace | Start here. Every real id and piece of vocabulary in the workspace: businesses, pipelines and stages, the statuses / lead sources / owners actually in use, tags, folders, custom fields. |
crm_search | Find contacts, companies or deals by name, email, phone or company text. Returns typed ids. |
crm_list_contacts | The filtered worklist, cursor-paginated. `missing` finds records with a gap in a field, which is how you audit data quality. |
crm_get_record | One record and everything attached: tags, folders, custom fields, notes, tasks, activity, open deals, and the conversation id. |
crm_upsert_contact | Add someone without risking a duplicate — matches on email then phone, and updates instead of creating. |
crm_update_records | Apply one change across contacts you have already listed. |
crm_organize | Tag, folder, bookmark, archive, restore. `targetName` creates a missing tag or folder for you. |
crm_log_activity | Record a note, call, email or meeting on a contact. |
crm_merge_contacts | Fold duplicates together. Destructive — dry-run it first. |
crm_manage_opportunity | Create a deal, or move it to another stage. |
crm_manage_task | Create, complete or reopen a task. |
crm_find_slots | Which calendars exist, and when they are actually free. |
crm_book_appointment | Book one of those slots. |
crm_list_conversations | The inbox, and where conversation ids come from. `awaitingReply` finds the threads waiting on you. |
crm_get_thread | Read a conversation. |
crm_get_channels | What can reach this contact — and why not, where it cannot. |
crm_send_message | Send SMS, email or Instagram. Takes a contactId when there is no thread yet. |
crm_set_do_not_disturb | Mute or unmute the agent for one contact. |
crm_find_duplicates | Who looks like the same person, graded by confidence. |
crm_stats | Contact counts, pipeline value, tasks, and data-quality gaps. |
crm_import_contacts | Many people at once, still without duplicating. |
What keeps it from wrecking your data
Writes here are real, so the safety is in the tools themselves rather than a review gate. Five things are worth knowing before you point a model at a live workspace.
Dry-run anything large
Every write tool accepts dryRun. It reports the exact change it would make and writes nothing — propose, inspect, then commit as a second, deliberate call. It is the closest thing here to the automation server's draft, and the right reflex before any merge or bulk update.
Retries do not double-apply
Pass an idempotencyKey and repeating the call returns the original result instead of applying it twice — the same 24-hour window the public API has used since v1. Reusing a key with different arguments is an error rather than a silent replay, which is what you want when a model reuses a key by accident.
Duplicates are graded, not equal
crm_find_duplicates marks a group high when the contacts share an email or phone, and likely when only the name looks similar. Merging is destructive and there is no undo — do not merge a likely group without looking at it yourself.
Sending respects do-not-disturb, and the agent
A contact on do-not-disturb is refused unless you explicitly pass force. And if an AI agent already owns a thread, your message is delivered into that run rather than sent alongside it — so the contact never receives two different replies to the same question.
Names are not overwritten on a match
crm_upsert_contact updates every field except the name when it matches an existing contact, because an incoming name is usually worse than the one already on file — “ada” arriving for “Dr. Ada Lovelace”. Pass updateNameOnMatch if you really do mean to rename.
Underneath all of that, every write is recorded in the audit trail as mcp.crm.* with the key that made it, and every single query is scoped to your workspace — including any businessId you pass, which is checked against your workspace before it is used rather than trusted.
Try these first
Good opening prompts once you are connected:
- “What does this CRM look like? Give me the stats.”
- “Find any duplicate contacts and tell me which ones you're confident about.”
- “Which contacts are missing an email address?”
- “Add Jane Doe, jane@acme.com, as a Lead — but check she isn't already there.”
- “Move the Acme deal to Qualified.”
- “Read my conversation with Sam and draft a reply — don't send it.”
When you want the model to build something rather than operate it — a follow-up sequence, a qualification flow, a booking automation — that is the other server, at https://ooperon.com/api/mcp. Connect both and one model can work your pipeline in the morning and automate what it learned in the afternoon.
Put your CRM behind your favourite model
Connect once, then ask in plain language. The agents keep the conversations running and the CRM keeps itself current.
