Copilot for CRM

An MCP server that plugs AI into your CRM

It reads the email and proposes. You approve. Nothing is written blind.

A sales email arrives. The copilot reads it, proposes the CRM record updates it implies, and drafts a reply — each proposed field carrying a confidence score and the exact words it was read out of. It never writes. You approve every change.

  • No API key
  • Runs on fixtures
  • Evidence for every field

How it works

From an inbound email to a change you approved.

Three steps, and a human stands between the model and your data at the only step that writes.

01

The email arrives

Inbound sales mail lands and is treated as untrusted input. The copilot reads the message — it is never handed the keys to your CRM, only the text of the email.

02

Fields are extracted, with proof

Each implied change is filed as a proposal carrying a confidence score and the exact span of text it was read out of. Anything below 0.7 is flagged, not hidden.

03

A human approves

Proposals wait in a queue. You approve, edit or reject each one — and only then is anything applied. The copilot has no tool that can write, so it never does.

Why you can trust it

Every claim shows its proof. The write capability isn't on the socket.

Two properties do the work here, and both are structural rather than a promise about a prompt.

Every field links to the sentence it came from

A proposal cannot be constructed without a quote, and that quote is verified against the stored email byte-for-byte before a human ever sees it. Click a proposed field and the highlighter jumps to the exact words in the source.

…we have finished the pilot review and we are ready to move forward. Legal has the MSA…

Evidence.text === source[start:end]

Write-safety by tool scope

The crm-copilot server exposes exactly these 4 tools to a model. There is no write, delete or export tool — so even a fully compromised model, driven by a hostile client, cannot change one CRM field.

search_contactsread-only
get_dealread-only
propose_updatewrites a proposal
draft_replyread-only
0tools can write to the CRM. Counted from the tool list, not typed in — the API refuses to serve its tool surface if that number is ever non-zero.

Deliberately not built

delete_contactexport_contactsupdate_contactsend_email

What's in the box

Built to be believed, not just demoed.

Evidence for every field

No proposal exists without a quote, and every quote is checked against the stored message. A span the model invented fails the check and is dropped.

text === source[start:end]

Confidence gating

Confidence is required, never defaulted. Anything under the threshold is flagged for the reviewer rather than quietly hidden — the human decides, not a cutoff.

confidence < 0.7 → is_low_confidence

Write-safety by tool scope

The model's reach is the tool list, and no tool on it writes to the CRM. The one function that applies a change lives behind the approval endpoint.

crm_write_tools = 0

Prompt-injection refusal

Instruction-override phrasing in untrusted email is refused at the input boundary with an audit trail. A heuristic catch, not a solved problem — no bypass rate is claimed because none has been measured.

OWASP LLM01 · LLM06

Idempotent proposals

Every email carries an RFC 5322 Message-ID, and that is the queue's idempotency key. The same message processed twice does not produce a second pile of proposals.

key = Message-ID

Runs on fixtures, no account

This whole demo is a static export over 14 invented emails, 15 contacts and 14 deals. No CRM is connected, no model is called, and there is no key to enter.

output: export