Tools, resources and prompts: three different things
An MCP tool is a function an AI assistant calls to do something in your CRM: it takes arguments, performs an action, and returns a result. An MCP resource is a read-only feed addressed by a URI that the assistant loads into its context without choosing any arguments at all. An MCP prompt is a parameterized template the server hands the client, so a person can pick a named workflow from a menu instead of describing it from scratch every time. Those three sentences are the whole mental model, and almost every confusion about the Model Context Protocol dissolves once they are separated.
The practical difference is who decides. A tool call is the assistant deciding: it looked at your request, picked crm_search_contacts, invented a query string, and now owns the consequences of that choice. A resource read is the server deciding: crm://tasks/today means one specific thing and returns the same shape every time, so there is no argument for the model to get wrong. A prompt is you deciding: you clicked daily-briefing in your client and the server supplied the wording.
That distinction has real consequences for cost and reliability. Tools are expressive and therefore risky, because an assistant that misunderstands the question makes a wrong call rather than no call. Resources are narrow and therefore safe, and they are the cheapest way to give an assistant a great deal of accurate context in a single request. Prompts are how you stop re-typing the same three paragraphs every Monday morning.
CRM Solid publishes 62 tools, 21 resources and 15 prompts on one endpoint. Of the tools, 37 are annotated read-only and 25 are annotated as writes. Everything below is the complete catalogue, grouped so that the groups match the way you would actually hand out access. If you want the protocol-level picture of how the server itself works, that lives on the MCP server overview; if you want the setup steps for a specific client, those live on connect Claude to your CRM.
How to read this reference
Every tool in this reference carries four things: the exact name your assistant will call, one plain sentence on what it does, the single scope the call requires, and whether it reads or writes. Those four columns are the entire contract. There is no hidden fifth condition, no per-tool pricing tier and no separate permission model layered on top of scopes.
Names are exact and case-sensitive. Every tool is prefixed crm_, and the rest of the name reads as a verb plus a noun: crm_list_deals, crm_send_social_message, crm_set_lead_score. If a name in your client does not appear on this page, it is not part of this server. The server itself is always the final authority: a tools/list call returns the live catalogue with the input schema for each tool, which is what your client reads at connection time.
curl -sS https://api.crmsolid.com/mcp \
-H "Authorization: Bearer csk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Scopes are the second column that matters. Each tool declares exactly one required scope, written as an area and a verb joined by a colon: contacts:read, deals:write, telegram:send. The ordering is deliberate and worth memorizing, because writing it the other way round is the single most common mistake people make when configuring a key. There is no read:contacts; there is only contacts:read.
Scopes are attached to the API key, not to the assistant, not to the session and not to the individual call. You mint a key in the panel, tick the scopes that key is allowed to use, and hand the key to one client. Two assistants with different jobs should hold two different keys, because that is the only way their permissions can differ.
When a call needs a scope the key does not hold, the server answers with JSON-RPC error code -32002 and names the missing scope in the error data. It does not fail generically and it does not silently return an empty result, both of which are far worse failure modes for an AI agent that will otherwise conclude you have no deals.
{
"jsonrpc": "2.0",
"id": 7,
"error": {
"code": -32002,
"message": "...",
"data": {
"requiredScope": "deals:write",
"granted": ["contacts:read", "deals:read"]
}
}
}That shape makes the fix mechanical. Read requiredScope from the error, compare it to granted, and decide whether the assistant should have it. Frequently the answer is no, and the right response is to rephrase the request rather than widen the key. An assistant that keeps asking for webhooks:write is telling you something about how it interpreted your instruction.
The third column is the read or write annotation. It comes from the tool definition itself, so any MCP client that shows a confirmation dialog before write operations gets that signal from the server rather than from a guess about the name. One tool is annotated destructive on top of being a write: crm_delete_webhook. One tool is annotated as a write even though it changes nothing in your CRM: crm_run_agent, because each run spends AI credits.
Two practical notes before the catalogue. First, MCP responses are camelCase, which differs from the REST v1 API where fields are PascalCase, so code that consumes both needs to know which surface it is talking to. Second, the MCP server is a Business plan feature: a valid key on a lower plan is refused with HTTP 402 rather than a scope error, which is a different problem with a different fix. Pricing details are on the pricing page.
Contacts and CRM depth (11 tools)
Eleven tools cover the contact record itself and the four things layered on top of it: tags, lead score, ownership and the activity timeline. Four of them read and seven of them write, which makes this the single largest write surface in the catalogue and the group most worth thinking about before you hand out a key.
The split matters because the read half is harmless and the write half is not. An assistant holding only contacts:read can answer any question about who a person is and what was said to them. Add contacts:write and the same assistant can retag your database, reassign owners and overwrite lead scores. Those are recoverable actions, but recovering them by hand across a few hundred contacts is a bad afternoon.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_search_contacts | Finds people by name, username or phone number and returns the 25 most recently contacted matches. | contacts:read | Read |
crm_get_contact | Opens one contact by id and includes the last ten messages exchanged, so the assistant has real context before it says anything. | contacts:read | Read |
crm_get_contact_activity | Returns the contact timeline newest first: notes, stage moves, tag changes, score changes and assignments. | contacts:read | Read |
crm_list_tags | Lists the workspace tag dictionary along with how many contacts each tag is attached to. | contacts:read | Read |
crm_add_contact_note | Appends a timestamped note. The notes field holds 500 characters, so a long note pushes the oldest text off the front and can remove earlier notes. | contacts:write | Write |
crm_update_contact_stage | Moves a contact along the pipeline: lead, conversation, proposal, negotiation, then won or lost. | contacts:write | Write |
crm_create_tag | Adds a tag to the dictionary without attaching it to anybody. Asking twice for the same name returns the existing tag rather than a duplicate. | contacts:write | Write |
crm_tag_contact | Attaches an existing tag to a contact by tag id or exact name and logs a TagAdded activity. An unknown name is rejected instead of quietly creating a tag. | contacts:write | Write |
crm_untag_contact | Detaches a tag from a contact and logs a TagRemoved activity. Running it twice changes nothing the second time. | contacts:write | Write |
crm_set_lead_score | Sets a lead score from 0 to 100 by hand, marks it as a manual override of the AI score and logs a ScoreChanged activity. | contacts:write | Write |
crm_assign_contact | Gives a contact an owner on your team, or clears the owner when no user id is supplied. Logs an Assigned or Unassigned activity. | contacts:write | Write |
What you would actually ask for
The natural-language shape here is a question about a person, not a question about a table. You name somebody and the assistant works out that it needs to search first, open the record second and read the timeline third. Tagging requests are the other common shape, and they are the reason the tag dictionary is a separate read tool: an assistant that cannot list your tags will invent one, and an invented tag name gets rejected rather than created.
- "Who is Selin Kaya, what have we talked about, and has anyone on the team been assigned to her?"
- "Tag every contact I spoke to this week about the enterprise plan with the enterprise-interest tag, and tell me first which tags already exist."
Two rules save you from surprises. First, crm_tag_contact never creates a tag, so a workflow that invents tag names has to call crm_create_tag before it. Second, the notes field is a fixed 500 characters and older text falls off the front, so treat notes as a rolling scratchpad and put durable facts in tags, score or a deal.
Conversations and messaging (5 tools)
Five tools cover the one-to-one messaging history stored in the CRM and the two direct sends that reach a person on Telegram or X. Three read, two write, and the two writes are the ones that put words in front of a human being, so they sit behind their own scopes rather than sharing a general write scope.
The separation is deliberate. Reading conversation history needs contacts:read, the same scope that reads the contact record, because a message thread is part of the contact. Sending needs telegram:send or twitter:send, one scope per channel, so you can build an assistant that reads everything and sends on exactly one network.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_list_recent_conversations | Lists recently active contacts with a preview of the last message, newest activity first. The fastest answer to who needs a reply. | contacts:read | Read |
crm_get_conversation | Returns one contact thread newest first, up to 50 messages per call. Pass a message id in before to page further back. | contacts:read | Read |
crm_search_twitter_messages | Searches the X direct messages already stored in your CRM by free text and returns the 25 most recent matches. | contacts:read | Read |
crm_send_telegram_message | Queues an outbound Telegram message from one of your connected accounts, addressed by contact id or username. It is queued, not sent inline. | telegram:send | Write |
crm_send_twitter_dm | Sends an X direct message synchronously through your connected X session. The contact must already exist on the twitter platform with an X user id. | twitter:send | Write |
What you would actually ask for
Almost every messaging request starts as a triage question and ends as a drafting question. You want to know who is waiting, then you want the reply written, and only sometimes do you want it sent. Because the two send tools are separately scoped, you can leave the last step to yourself for weeks and still get the value: the assistant reads, ranks and drafts, and you paste. When you are ready to close the loop, add one send scope, not both.
- "Show me everyone who messaged in the last two days without a reply from us, ranked by how long they have been waiting."
- "Read my whole thread with this contact and draft a short Telegram follow-up that answers the pricing question they asked in March."
Telegram sends are asynchronous. crm_send_telegram_message returns once the job is queued, and the jobs tools further down this page are how you confirm the message actually left. X direct messages go out synchronously, so a failure surfaces in the tool result itself.
Email inbox (4 tools)
Four tools cover the shared email inbox, and none of them sends mail. Two read threads, two set workflow state, and every one of them is explicitly non-sending. That is the whole design: an assistant can read the mailbox, summarize a thread, close it, hand it to a colleague, and still be structurally incapable of emailing a customer.
The read tools also carry work that has already been done for you. A thread returned by crm_get_email_thread includes any AI summary and lead score computed when the mail arrived, so an assistant asking for context does not have to re-read forty messages to rank a lead.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_search_email_threads | Searches inbox threads by subject and preview text, with filters for status, contact and unread state. Read only, and it does not send mail. | email:read | Read |
crm_get_email_thread | Returns one thread as plain-text messages oldest first, plus any AI summary and lead score already stored against it. | email:read | Read |
crm_set_email_thread_status | Moves a thread between open, pending and closed. It changes workflow state only and sends nothing. | email:write | Write |
crm_assign_email_thread | Hands a thread to a teammate, or unassigns it when no user id is given. It sends nothing. | email:write | Write |
What you would actually ask for
Email requests are usually about the queue rather than a single message. People ask what is still open, who has been ignored longest, and which threads can be closed because the customer already got an answer somewhere else. The assignment tool turns that into something useful for a team: a morning pass that reads every open thread, decides who owns it and files it, without ever writing a word to a customer.
- "Summarize every open email thread from the last week, say what the customer is actually asking for, and flag the three that look most likely to churn."
- "Close the threads where the customer already replied thank you, and assign anything mentioning invoices to the finance owner."
email:write governs status and assignment only. The advanced email:send scope exists and is left unchecked by default when you mint a key, so a routine inbox assistant never gets accidental sending rights.
Deals (4 tools)
Four tools cover the sales pipeline: two read the deal list and one deal in detail, two create a deal and move it between stages. This is the clearest example of the safe-write boundary on the whole surface, because one specific transition is missing on purpose.
crm_update_deal_stage accepts lead, qualified, proposal, negotiation and lost. It does not accept won. Marking a deal won books an income entry in the ledger, and booking revenue is a decision a person makes in the panel, not something an assistant does because a conversation sounded positive. Creating a deal already closed is refused for the same reason.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_list_deals | Lists deals ordered by stage and then value, each with a count of its open tasks. | deals:read | Read |
crm_get_deal | Returns one deal with its linked tasks and the resolved contact name. | deals:read | Read |
crm_create_deal | Creates a deal in an active stage. A title is required, and a deal cannot be created already won or lost. | deals:write | Write |
crm_update_deal_stage | Moves a deal to lead, qualified, proposal, negotiation or lost. Moving to won is deliberately not supported here. | deals:write | Write |
What you would actually ask for
Pipeline questions are forecasting questions. You want the shape of the funnel, the deals that have gone quiet, and the one action that would move the biggest number. Because deal reads are cheap and deal writes are narrow, this is a good group to grant in full: an assistant with deals:read and deals:write can keep the board honest all week and still cannot invent revenue.
- "What is in my pipeline right now, grouped by stage, and which three deals have had no activity in two weeks?"
- "Create a deal for the agency I spoke to yesterday at fifteen thousand in the qualified stage, and move the stalled one to lost with a note explaining why."
Tasks (3 tools)
Three tools cover to-dos and reminders: one lists them, one creates them, one changes their status. Tasks are the cheapest place in the CRM for an assistant to write, because a wrong task costs you five seconds to delete and a wrong message costs you a customer.
That makes tasks:write the first write scope worth granting when you are still nervous about giving an assistant any write access at all. It gives you a real feedback loop: you can watch what the assistant decided to create, judge whether its reasoning was sound, and decide from evidence whether to widen the key.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_list_tasks | Lists tasks sorted by due date and then priority, which is what answers what is due and what is overdue. | tasks:read | Read |
crm_create_task | Creates a task with a required title, plus an optional contact link, deal link, due date and priority. | tasks:write | Write |
crm_complete_task | Marks a task done and stamps the completion time, or reopens it by passing an open or in-progress status. | tasks:write | Write |
What you would actually ask for
The useful pattern is turning a conversation into commitments. An assistant that has just read a thread knows what you promised, and creating three tasks with due dates is a far better record of that than a note nobody reads. The reverse works too: an end-of-day pass that closes everything already done keeps the list credible enough that you keep looking at it.
- "Read yesterday’s conversations and create a task for every promise I made, with a due date matching what I said."
- "What is overdue, what is due today, and in what order should I work through it?"
Pipelines (2 tools)
Two read-only tools describe the shape of your CRM rather than its contents: which boards exist, what stages they have, in what order, and how many contacts are sitting in each stage. There is no write tool here at all, because board structure is a configuration decision rather than a daily operation.
These are orientation tools. An assistant that calls crm_list_pipelines before it moves anybody gets stage names from your workspace instead of guessing generic ones, which is the difference between a stage move that lands and a stage move that gets rejected.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_list_pipelines | Lists every pipeline board with its ordered stage columns and the live contact count in each stage. | pipelines:read | Read |
crm_get_pipeline | Returns one board by id with the same ordered stages and per-stage contact counts. | pipelines:read | Read |
What you would actually ask for
You rarely ask for a pipeline directly. You ask a question about your funnel and the assistant reaches for these first to find out how your funnel is actually named. The one time you do ask directly is when you are diagnosing a bottleneck, because per-stage counts across every board is a picture you cannot get from a single deal list.
- "How are my boards set up, and which stage has the most contacts stuck in it?"
- "Before you move anyone, list my pipeline stages so we agree on what qualified actually means here."
pipelines:read is a small, safe scope worth granting to almost any assistant. Without it, an assistant guesses stage names, and guessed stage names produce rejected write calls that look like bugs.
Finance (4 tools)
Four tools, all read-only, all under finance:read. This group is reporting and nothing else. It reads the ledger, invoices and revenue-source configuration, and it cannot move, charge, settle or refund a single unit of currency.
That constraint is worth stating plainly because finance is where people are rightly most nervous about AI access. crm_list_invoices returns no payment link. crm_list_transactions cannot create, edit, refund or settle an entry. crm_revenue_sources_summary never returns a key or credential. Nothing in this group can be chained into a payment.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_finance_summary | Reports realized income, expense and net per currency, outstanding totals and the largest expense categories over a window. Reads the ledger, moves nothing. | finance:read | Read |
crm_list_transactions | Lists ledger entries newest first with optional filters. It cannot create, edit, refund or settle a transaction. | finance:read | Read |
crm_list_invoices | Lists invoices newest first with an outstanding summary per currency. It cannot create, send, void or pay an invoice, and it returns no payment link. | finance:read | Read |
crm_revenue_sources_summary | Lists configured external revenue sources with last-sync status, total ingested count and last event time. Keys and credentials are never returned. | finance:read | Read |
What you would actually ask for
Finance questions over MCP are the ones that would otherwise mean exporting a spreadsheet on a Friday afternoon. Because the entire group is read-only, you can grant finance:read to an analysis assistant without the usual argument about blast radius. The worst case for a leaked finance:read key is that somebody sees numbers, which is a real risk but a bounded one, and it is not the same category as a key that can spend.
- "How did we do last month by currency, what is still outstanding, and which expense category grew fastest?"
- "Which invoices are overdue, how much is owed in total, and which customers do they belong to?"
A finance:write scope exists on the key form and is left unchecked by default. Nothing in the MCP catalogue on this page requires it, which is the clearest possible signal that finance over MCP is a reporting surface.
Sequences (4 tools)
Four tools cover outbound sequences: two report on them and two control whether they are running. Notice what is not here. There is no tool to create a sequence, edit its steps, change its message copy or enroll a contact into it. The MCP surface can start and stop the machine, and it cannot rebuild it.
That is the right boundary for automated outreach. Pausing a live campaign because the AI noticed an error spike is exactly the kind of judgement you want an assistant making at three in the morning. Rewriting the copy that goes to two thousand people is not.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_list_sequences | Lists outbound sequences with status, target counts and progress, which answers what campaigns are running. | sequences:read | Read |
crm_get_sequence_status | Returns a deep report on one sequence: status, target, processed, successful and failed counts, its message steps and recent job activity. | sequences:read | Read |
crm_pause_sequence | Stops a sequence queueing new messages. Jobs already in flight still run, and pausing a paused sequence does nothing. | sequences:write | Write |
crm_resume_sequence | Starts a paused sequence queueing again. | sequences:write | Write |
What you would actually ask for
The high-value request is a safety net rather than a campaign builder. You ask the assistant to watch failure rates and pull the handbrake, and because pause is idempotent it can do that without worrying about double-firing. The reporting half answers the weekly question of which campaign is worth its slot.
- "Check every running sequence and pause anything whose failure rate went above one in five, then tell me what you paused and why."
- "Which of my sequences is converting worst, and what does its step list look like compared to the best one?"
Pause is not a cancel. In-flight jobs still run, so the correct verification after a pause is to watch the outbound jobs list until it drains rather than assuming the queue emptied instantly.
Social inbox (7 tools)
Seven tools cover direct messages across every connected network: Instagram, Facebook, X, LinkedIn, TikTok, YouTube, Threads, Pinterest, Reddit, Bluesky, Telegram and WhatsApp. Five read and two write. This is the largest read group after contacts, and for a very practical reason: an assistant answering a stranger on Instagram needs far more context than one replying to a known contact.
The one to reach for first is crm_social_inbox_summary. A single call returns conversation and unread totals overall and per network, plus the threads still waiting on a reply, oldest first. That is a whole triage pass in one request instead of a dozen, which matters when your budget is 60 requests a minute.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_list_social_accounts | Lists the social accounts connected to the workspace with platform, handle and posting limits. Call it first to learn which account id to use. | social:read | Read |
crm_social_inbox_summary | One call for the whole inbox: conversation and unread totals overall and per network, plus the threads still waiting on a reply, oldest first. | social:read | Read |
crm_list_social_conversations | Lists DM threads most recently active first, with participant, unread count and last-message preview. Also how you find a conversation id. | social:read | Read |
crm_get_social_conversation | Returns one thread with the participant details, the bridged CRM contact and the last ten messages. Read this before drafting a reply. | social:read | Read |
crm_list_social_messages | Pages through a thread, oldest first inside a page. Voice notes carry their transcript and translated messages carry both wordings. Pass beforeMessageId to walk back. | social:read | Read |
crm_send_social_message | Sends a DM into an existing thread. It reaches a real person immediately, pauses AI auto-reply for the linked contact and records the send on the CRM timeline. | social:write | Write |
crm_mark_social_conversation_read | Clears a thread unread badge on the network as well as in the CRM. Marking an already-read thread changes nothing. | social:write | Write |
What you would actually ask for
The request people actually make is some version of who is waiting and what should I say. That resolves into a summary call, a thread read for the two or three that matter, and a draft. Whether the draft goes out is a separate decision governed by a separate scope, and plenty of teams run this loop for months with social:read only, using the assistant as a very fast reader and writer while a human presses send.
- "Which social conversations are still waiting on us, across every network, and who has been waiting longest?"
- "Read this Instagram thread and draft a reply in the same language the customer used, matching how long their messages are."
crm_send_social_message is the tool that most obviously touches the outside world, and it carries platform rules with it. WhatsApp only allows a free-form reply within 24 hours of the customer’s last message, so a thread that has gone quiet for two days will refuse a casual follow-up no matter how the request was phrased.
Social posts (6 tools)
Six tools cover scheduled and published content: three read the calendar and its results, three schedule, edit and cancel. Publishing is the other place besides direct messaging where a tool call becomes visible to the public, so the read and write halves sit under separate scopes, posts:read and posts:write.
One detail changes how you phrase requests. crm_schedule_social_post creates one post per target account, so asking for the same thing on Instagram and LinkedIn produces two posts, not one post with two destinations. Per-platform rules are enforced at schedule time: TikTok and YouTube need a video, Instagram needs media, and X caps the text at 280 characters.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_list_social_posts | Lists scheduled and published posts, newest scheduled time first, with status, published URL and any failure reason. | posts:read | Read |
crm_get_social_post | Returns one post with its full content, media, schedule, status and published URL. | posts:read | Read |
crm_social_post_stats | Reports publishing volume and outcome over a recent window, by status overall and per network, plus the last publish time. | posts:read | Read |
crm_schedule_social_post | Schedules a post on one or more connected accounts, creating one post per account. scheduledAt is required unless publishNow is set, which publishes to a real audience straight away. | posts:write | Write |
crm_update_social_post | Edits the text, media or scheduled time of a post that has not gone out. Anything published, in flight, failed or cancelled is rejected. | posts:write | Write |
crm_cancel_social_post | Cancels a scheduled post so it never publishes. A post that has already gone out cannot be withdrawn from here. | posts:write | Write |
What you would actually ask for
Content requests come in two flavors. The planning flavor asks what is already queued and where the gaps are, which is pure posts:read. The production flavor asks for a week of posts to be written and scheduled, which needs posts:write plus social:read for the account list, because the account roster lives with the social inbox rather than with posts.
- "What is going out this week, on which networks, and where are the empty days?"
- "Draft five posts about the new pricing page and schedule them across LinkedIn and X for weekday mornings next week."
Watch publishNow. Everything else in this group is reversible up to the moment of publication, and publishNow removes exactly that window. A safe house rule is that an assistant may schedule but only a person may publish immediately.
Webhooks (4 tools)
Four tools cover outbound webhook endpoints: two read the registry and its delivery history, two register and remove endpoints. This group contains the only hard delete in the entire catalogue, and it is annotated as destructive so a well-behaved client can warn before it runs.
The delivery-history tool is the quietly useful one. crm_list_webhook_deliveries returns status, attempt count, last response code and last error for one endpoint, newest first, which turns the worst question in integration work into a single tool call.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_list_webhooks | Lists registered endpoints with URL, subscribed event types and health. Signing secrets are never returned, only a short preview. | webhooks:read | Read |
crm_list_webhook_deliveries | Lists recent delivery attempts for one endpoint with status, attempt count, last response code and last error, newest first. | webhooks:read | Read |
crm_create_webhook | Registers an HTTPS endpoint, optionally narrowed to specific event types. The signing secret is returned once and cannot be retrieved later. | webhooks:write | Write |
crm_delete_webhook | Permanently removes an endpoint and stops every future delivery to it. This is the one call in the catalogue that cannot be undone. | webhooks:write | Write |
What you would actually ask for
This group answers debugging questions, and it answers them fastest when the assistant can see both halves: what is registered and what happened to the last deliveries. Ninety percent of webhook problems are one of three things, an endpoint that was never subscribed to the event, a receiver returning a non-2xx status, or an endpoint that was deleted and forgotten. All three are visible from webhooks:read alone.
- "My webhook stopped firing. Which endpoints do I have, what events are they subscribed to, and what did the last twenty deliveries return?"
- "Register an endpoint on my staging server for message events only, and show me the signing secret so I can store it."
Two things to remember. The signing secret from crm_create_webhook appears once in that response and is never retrievable afterwards, so an assistant that creates an endpoint has to hand you the secret in the same turn. And crm_delete_webhook is genuinely permanent, which is why webhooks:write is the scope least suited to a general-purpose chat assistant.
AI agents and outbound jobs (4 tools)
Four tools sit at the boundary between your own automation and the assistant looking at it. Two describe the AI auto-reply agents already running in your workspace, and two follow outbound message jobs through the send queue. This is how an assistant answers the question that follows every send: did it actually leave.
crm_run_agent deserves its own paragraph. It runs one of your agents against a sample inbound message and returns the reply it would have sent. Nothing is delivered, no contact hears from you and no CRM record is created, changed or deleted. It is annotated as a non-read-only tool for one reason only: each run calls an external model provider and spends AI credits.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_list_agents | Lists your AI auto-reply agents with status, channels, trigger and response mode, model and a 24-hour run count. | agents:read | Read |
crm_run_agent | Test-runs an agent against a sample message and returns the reply it would send. Nothing is delivered and no record changes. Flagged as a write only because each run spends AI credits. | agents:run | Write |
crm_list_jobs | Lists outbound Telegram send jobs newest first with status and error visibility, which answers whether your messages actually went out. | jobs:read | Read |
crm_get_job | Returns one job with its full text, target, status and last error, for diagnosing a specific send failure. | jobs:read | Read |
What you would actually ask for
The agent tools turn prompt tuning into something you can iterate on in a chat window: feed a real awkward customer message, read what the agent would say, adjust, run again. The job tools turn delivery from a hope into a fact. Together they cover the two things people worry about most when they automate replies, which is whether the wording is right and whether the message left the building.
- "Run my support agent against this angry message about a late refund and show me what it would send, without sending it."
- "Did everything I queued this morning go out? List anything that failed and show me the actual error."
agents:run is the one scope whose real cost is money rather than risk. It cannot damage your data, but a loop that runs an agent a hundred times spends a hundred model calls, so it belongs on assistants you supervise rather than on unattended schedules.
Analytics and connected accounts (4 tools)
Four read-only tools give an assistant the numbers and the account roster it needs to orient itself. Three sit under analytics:read and one, the account list, sits under contacts:read. That last placement surprises people, so it is worth saying out loud: crm_list_accounts is a contacts:read tool.
It matters because crm_list_accounts is the prerequisite for sending anything. Every send tool needs an account id, and this is where account ids come from. A key that has telegram:send but not contacts:read can send only to an account id you hardcode, which is occasionally what you want and usually a bug in your scope plan.
| Tool | What it does | Required scope | Mode |
|---|---|---|---|
crm_dashboard_summary | Headline numbers for the workspace: total contacts, messages queued and sent today, and connected accounts. A ready-made daily standup. | analytics:read | Read |
crm_messaging_stats | Counts sent, failed and queued messages over a 1, 7 or 30 day window, optionally for a single account. | analytics:read | Read |
crm_top_contacts | Ranks the most-messaged contacts of the last 30 days by outbound volume, which is a decent proxy for who is warmest. | analytics:read | Read |
crm_list_accounts | Lists connected Telegram and X accounts with id, type, name, status and most recent activity. Required before sending, because it supplies the account id. | contacts:read | Read |
What you would actually ask for
These are the tools an assistant reaches for when you open a chat and say good morning. One dashboard call plus one stats call is enough for a useful standup, and the account list tells it which identities it is allowed to speak as. The trend question is the more valuable one, because a raw count today means nothing without the seven days behind it.
- "Give me a two-line standup: what happened yesterday, what is queued today, and is anything failing more than usual?"
- "How does this week compare with last week, and is one of my connected accounts responsible for most of the failures?"
The 21 resources, and when to read one instead of calling a tool
A resource is a fixed, read-only view of your workspace addressed by a crm:// URI, and it takes no arguments. That single property is what makes resources valuable. A tool call requires the assistant to decide what to pass, and every decision is a chance to be wrong. A resource read requires no decision at all: the URI is the whole request, and the server returns the same well-shaped answer every time.
Read a resource instead of calling a tool whenever three conditions hold. The question is general rather than about one specific record. The default window or default ordering is what you wanted anyway. And you are at the start of a session, where the goal is to give the assistant enough context to reason well rather than to answer one narrow question. Priming a conversation with two or three resources is the highest-value request-per-token move available on this server.
Reach for a tool instead when you need a specific record by id, a non-default time window, a filter, or pagination. crm://deals/open gives you the ranked opportunity list; crm_get_deal gives you one deal with its tasks. crm://jobs/recent tells you whether sends are failing; crm_get_job tells you why one specific send failed. The resource answers the question you asked out loud, and the tool answers the follow-up.
{"jsonrpc":"2.0","id":2,"method":"resources/list"}
{"jsonrpc":"2.0","id":3,"method":"resources/read",
"params":{"uri":"crm://social/inbox"}}| Resource URI | What it returns | When to read it |
|---|---|---|
crm://me | Identity, plan and a summary of connected accounts for the key holder. | At the start of any session, so the assistant knows whose workspace it is looking at before it says anything about it. |
crm://accounts | Every connected Telegram and X account with type, status and last activity. | Before any send, in place of crm_list_accounts. The account id an assistant needs is right here with no arguments to guess. |
crm://sequences | Outbound sequences with status, daily limit and target progress. | For a campaign overview, in place of crm_list_sequences. Drop to the tool only when you need one sequence in depth. |
crm://recent-conversations | The last 20 active contacts with name, platform, last-message preview and unread flag. | To open a triage conversation, in place of crm_list_recent_conversations. It is the cheapest way to make an assistant useful in one turn. |
crm://plan | Current subscription plan, period, daily message limits and remaining seats. | Before queueing a large batch of sends, so the assistant knows the daily ceiling instead of finding it by hitting it. |
crm://kpis/7d | Sent, failed and queued message counts for the last seven days plus a per-day breakdown. | For trend questions, in place of crm_messaging_stats. The per-day series is what makes an answer about direction possible. |
crm://finance/summary | Realized income, expense and net per currency for the last 30 days. | For a quick read on financial health, in place of crm_finance_summary when the default 30-day window is what you wanted anyway. |
crm://deals/pipeline | Deal count and total value per pipeline stage. | For funnel shape rather than individual deals. One read answers where value is concentrated without paging a deal list. |
crm://tasks/today | Open tasks due today or already overdue, ordered by due date. | For a morning planning turn, in place of crm_list_tasks with filters the assistant would otherwise have to invent. |
crm://inbox | Up to 20 open email threads with subject, preview and unread count. | To find out who is waiting on an email reply, in place of a crm_search_email_threads call with an empty query. |
crm://pipelines | All contact pipelines with ordered stages and the live contact count in each stage. | To prime an assistant with your board structure before it moves anyone, in place of crm_list_pipelines. |
crm://jobs/recent | The last 20 outbound message jobs with status, target, account and a trimmed error summary. | For a delivery check after a batch of sends, in place of crm_list_jobs. Drop to crm_get_job only for a specific failure. |
crm://finance/invoices | Open invoices summarized per currency, plus the most overdue ones. | For collections questions, in place of crm_list_invoices. The most-overdue list is already sorted the way you would sort it. |
crm://tasks/overdue | Open tasks strictly past their due date, oldest first. | When you want only what has slipped. It is deliberately narrower than the today feed, which also includes tasks due later today. |
crm://agents | AI auto-reply agents with status, channels, response mode and rate limits, plus a seven-day run count. No persona text, no secrets. | To let an assistant see what is already automating replies before it suggests automating more of them. |
crm://webhooks | Outbound webhook endpoints with subscribed event types, active flag and recent delivery health. Signing secrets are never included. | To start a webhook investigation, in place of crm_list_webhooks. Reach for the deliveries tool only once you know which endpoint is suspect. |
crm://deals/open | Open deals that are neither won nor lost, ordered by value, with stage, win probability and expected close date. | For forecasting, in place of crm_list_deals. Value ordering and probability in one read is most of a weighted forecast. |
crm://social/accounts | Every connected social account with handle, timezone and daily post limit. | Before scheduling anything, in place of crm_list_social_accounts. Timezone and daily limit are exactly what a scheduling decision needs. |
crm://social/inbox | Unread totals per network plus the 20 most recently active social DM conversations with participant, preview and unread count. | To open a social triage turn. One read replaces a summary call plus a conversation list call. |
crm://social/posts/scheduled | Posts queued to go out, soonest first, with network, scheduled time and a content preview. | For content planning, in place of crm_list_social_posts filtered to pending. It is the upcoming calendar with nothing else in the way. |
crm://social/posts/published | The most recently published posts with their live URLs, plus anything that failed and why. | For a publishing retrospective, and for catching a failed post you would otherwise notice a week later. |
A pattern worth copying: pick the three resources that match the assistant you are building and load them at the start of every session. A sales assistant wants crm://deals/open, crm://tasks/today and crm://recent-conversations. A social manager wants crm://social/inbox, crm://social/accounts and crm://social/posts/scheduled. An operator on call wants crm://jobs/recent, crm://webhooks and crm://agents. Three reads, and the assistant is oriented.
Notice which resources are narrower than their tool equivalents rather than wider. crm://tasks/overdue is strictly past due; crm://tasks/today also includes what is due later today. Reading both is redundant, and asking for the wrong one produces an answer that is technically correct and practically useless. Pick by the question: what has slipped, or what is on the plate.
The 15 prompts, and what each one is for
A prompt is a named, parameterized template that the server publishes and the client presents to a person, usually as a slash command or a menu item. The server supplies the wording and pulls the relevant CRM data into it; you supply whichever argument the template needs, if it needs one at all. Most of the fifteen take no argument, because they operate over the whole workspace; the rest need you to name one contact, one deal, one email thread, one conversation or one connected account.
Prompts are worth more than they look. The wording of a recurring analysis request is the part people get wrong and then never revisit, so a prompt that already asks for a weighted forecast and the three deals most at risk produces better output than the version you would type at nine on a Monday. It is also consistent week to week, which is the only way a recurring report becomes comparable.
The argument column below describes what each prompt needs rather than the literal field name, because the exact argument names come back from a prompts/list call and any client that supports prompts will show them when you pick one. Two prompts are explicit about their own limits: summarize-email-thread does not draft or send a reply at all, and dm-reply-draft drafts but never sends.
{"jsonrpc":"2.0","id":4,"method":"prompts/list"}
{"jsonrpc":"2.0","id":5,"method":"prompts/get",
"params":{"name":"daily-briefing"}}| Prompt | What it produces | Needs | Where it earns its place |
|---|---|---|---|
summarize-contact | A summary of the relationship with one contact plus a concrete proposed next step. It pulls the last ten messages exchanged. | The contact you are asking about. | Two minutes before a call with someone you last spoke to in April and cannot remember why the conversation stopped. |
draft-followup-message | A Telegram follow-up written for the contact’s current pipeline stage and their recent conversation, with an optional tone selector. | The contact, and optionally the tone you want. | Clearing a follow-up list where each message has to reference something specific rather than reading like a template. |
daily-briefing | A morning standup digest built from today’s queued, sent and failed jobs together with the contact pipeline. | Nothing. It reads the workspace as it stands. | The first message you send your assistant every morning, ideally saved as a one-click prompt in your client. |
audit-account-health | A risk assessment of one connected account from seven days of job statistics: rate-limit pressure, error spikes and similar warning signs. | The connected account to audit. | A Telegram account has started failing sends and you want to know whether you are being throttled before you send more. |
weekly-finance-report | A weekly finance report from the last seven days of realized income and expense, grouped per currency. | Nothing. | A recurring Friday report for a business that invoices in more than one currency and does not want to reconcile by hand. |
deal-next-step | The single best next action for one deal, reasoned from its stage, value, probability and open tasks. | The deal in question. | A deal that has been in negotiation for three weeks and you cannot decide between another call, a discount or walking away. |
summarize-email-thread | A summary of one email thread, the customer’s open question and one recommended next action. It does not draft or send a reply. | The email thread. | Inheriting a forty-message support thread from a colleague and needing the actual unanswered question inside a minute. |
triage-inbox | A ranking of who to reply to first and why, built from the most recently active contacts with unread threads first. | Nothing. | Coming back from two days off to an inbox where everything looks equally urgent and nothing is. |
pipeline-review | A weighted forecast from open deals grouped by stage with value and probability, plus the three deals most at risk. | Nothing. | The Monday pipeline meeting, where the useful output is the at-risk list rather than the total. |
task-prioritize | One prioritized action list for today, built from overdue tasks plus everything due in the next 48 hours. | Nothing. | A task list that has grown past the point where due-date sorting is enough to tell you what to do first. |
outreach-plan | Next week’s outreach focus from active sequences and their target progress, with one improvement per under-performing sequence. | Nothing. | Planning campaign effort for the coming week when three sequences are running and only one is working. |
lost-deal-postmortem | A likely root cause for one lost deal and a concrete re-engagement play, reasoned from the deal data and its task history. | The lost deal. | A quarterly review of closed-lost deals, where the point is finding the pattern rather than relitigating one loss. |
social-inbox-triage | A ranking of which social DMs to answer first and why, across every connected network. | Nothing. | A brand account collecting messages on Instagram, WhatsApp and LinkedIn at once with one person answering all three. |
weekly-content-plan | A concrete posting plan for the coming week, combining connected accounts, what is already scheduled and how the last 30 days of posting performed. | Nothing. | Monday content planning where the hard part is remembering what already went out and which network you neglected. |
dm-reply-draft | A reply to one social DM thread using the real history, the participant’s language and the platform’s length conventions. It drafts only, never sends. | The social conversation to reply in. | Answering a customer who wrote in a language you read slowly, where getting the register right matters as much as the facts. |
Four of the fifteen are worth setting up as recurring habits rather than occasional requests: daily-briefing every morning, task-prioritize right after it, pipeline-review before your weekly sales meeting, and weekly-finance-report on Friday. Those four alone cover most of what people build custom dashboards for, and unlike a dashboard they explain the numbers.
The remaining eleven are situational, and the situations are specific. You reach for audit-account-health when sends start failing, for lost-deal-postmortem during a quarterly review, for summarize-contact two minutes before a call. That is the mark of a well-scoped prompt library: each entry has an obvious moment rather than being a general-purpose ask with a different name.
Read, write, and where the write tools stop
Of the 62 tools, 37 are annotated read-only and 25 are annotated as writes, and the write set deliberately stops short of irreversible money and identity actions. That boundary is a design decision rather than an implementation gap, and it is the part of this server most worth understanding before you decide how much access to grant.
Here is what the write tools will not do, stated as flatly as possible. They will not create a ledger transaction. They will not pay, send, void or create an invoice, and no tool returns a payment link. They will not move a deal to won, because winning a deal books revenue. They will not create a deal that is already closed. They will not send email, even from the two tools that manage email threads. And crm_run_agent drafts a reply and never delivers it, no matter how the request was phrased.
The reasoning behind each of those is the same: the action either moves money or reaches a person in a way that cannot be taken back, and the cost of an assistant getting it wrong is not proportionate to the convenience of it getting it right. Booking revenue from a chat window because a conversation sounded positive is a bad trade even when the assistant is right nine times out of ten.
There is exactly one hard delete in the catalogue. crm_delete_webhook permanently removes an endpoint, stops every future delivery to it, and cannot be undone. It is annotated destructive so a client that asks for confirmation before dangerous operations will ask before this one. Everything else in the write set is either additive or reversible from the panel.
Three write tools reach a human being directly: crm_send_telegram_message, crm_send_twitter_dm and crm_send_social_message. A fourth reaches an audience: crm_schedule_social_post publishes immediately when publishNow is set. Those four are the ones to think hardest about, and they are spread across four separate scopes precisely so you can grant one without granting the rest.
Several write tools are idempotent, which matters more for AI agents than for human-driven code because agents retry. Pausing a paused sequence is a no-op. Marking an already-read conversation read changes nothing. Cancelling an already-cancelled post is a no-op. Creating a tag that exists returns the existing tag rather than a duplicate. Tagging and untagging are both idempotent. That set of guarantees is what makes a retrying agent safe to run against them.
One write tool has a side effect worth knowing about in advance. crm_add_contact_note appends to a field that holds 500 characters, and when it fills up the oldest text is dropped from the front. It is a rolling scratchpad, not an archive. Durable facts belong in tags, lead score, a deal or a task, all of which have their own tools and none of which overwrite anything.
A useful way to think about the whole boundary: the MCP surface can do everything that a competent assistant could reasonably do on your behalf during a working day, and none of the things you would want a second person to confirm. If you disagree with where that line sits, the scopes are the adjustment mechanism, and the next section is how to plan them.
Rate limits, batching and getting a lot done in 60 requests
Rate limiting is per API key: 60 requests per minute by default, with a ceiling of 300 on the Business plan. Going over returns HTTP 429 with a Retry-After header saying how long to wait. Every tool call is one request, every resource read is one request, and the protocol handshake costs a request too, so the budget disappears faster than people expect once an assistant starts working through a list.
The failure mode is worth picturing. You ask an assistant to review fifty contacts. A naive plan is one search call, then fifty crm_get_contact calls, then fifty crm_get_contact_activity calls. That is 101 requests for one question, and the assistant will be sitting in a 429 backoff halfway through while you watch a spinner. The same answer is usually available in three or four requests if you ask for it differently.
Four habits keep you comfortably inside the budget:
- Prefer a resource over repeated tool calls. One read of
crm://recent-conversationsreplaces a listing call plus several detail calls when all you needed was who is waiting. One read ofcrm://social/inboxreplaces a summary call plus a conversation listing. - Ask for one summary rather than fifty lookups. Requesting a ranked shortlist with reasons produces a plan that fits in a handful of calls. Requesting a per-record report invites the assistant to fetch every record. Phrase the request around the decision you want to make, not the data you think it needs.
- Use the tools that already aggregate.
crm_social_inbox_summaryreturns per-network totals plus the waiting threads in a single call.crm_dashboard_summaryreturns headline KPIs in one.crm_finance_summaryreturns net per currency plus top expense categories in one. These exist so an assistant does not have to reconstruct them from primitives. - Give long-running automation its own key. The limit is per key, so a scheduled nightly job and an interactive assistant on the same key compete for the same 60 requests. Two keys with different scopes and different limits is both safer and faster.
When you do hit a 429, honor Retry-After rather than retrying immediately. A retry loop that ignores it will keep failing and burn its budget on failures, which is how a one-minute pause turns into a five-minute one. Well-behaved MCP clients handle this for you, but an agent framework you wrote yourself probably does not unless you made it.
For genuinely high-volume work, MCP is the wrong tool and that is fine. A nightly export of every contact belongs on the REST API, where you control pagination and concurrency directly. MCP is optimized for an assistant asking a few well-chosen questions during a conversation, not for bulk extraction. The trade-offs are laid out in detail on MCP versus REST API versus Zapier.