What managing social media over MCP means in practice
Managing social media over MCP means your assistant holds the inbox and the calendar directly, so the unit of work becomes a sentence you type rather than a sequence of clicks you perform. You stop being the transport layer between what you decided and where the decision has to be entered.
That is the whole of it, and it is worth being precise about what it is not. It is not a chatbot answering your customers. It is not a scheduler with a caption generator bolted on the side. It is a connection between an assistant that can reason about context and a system of record that can act, with a permission boundary in between that you control at the level of individual capabilities.
Three things change the day you connect it, and they are worth naming separately because people usually expect only the first.
The inbox stops being a place you go
The ordinary way to work a social inbox is to open it, scroll, and decide. The problem is not the scrolling, it is that the deciding happens with almost no context: you are looking at a preview line and a handle, and everything you know about that person lives in a different tool. An assistant with social:read reverses that. It opens with crm_social_inbox_summary, which returns conversation and unread totals overall and per network plus the threads still waiting on a reply, oldest first. Then it opens only the shortlist, and each one it opens comes back with the participant, the bridged CRM contact and the last ten messages attached. The context arrives with the thread instead of being something you go and look up.
The calendar stops being a form you fill in
Scheduling a week of content in a normal tool is a data-entry exercise: eight or ten times through the same modal, choosing accounts, pasting text, uploading media, picking a time. Over MCP the same week is one instruction and a review. The assistant reads what is already queued with crm_list_social_posts, reads how the last month went with crm_social_post_stats, proposes the slots, and calls crm_schedule_social_post once it has your agreement. You review the plan, which is the part that actually needs you, and skip the part that never did.
The context survives across both
This is the shift people do not anticipate. In a normal stack the DM inbox and the publishing calendar are separate products with separate logins, so nobody ever asks whether the posts that produced DMs are the posts you kept making. An assistant holding social:read and posts:read in the same session can answer that in one turn, because both halves are tools on the same server behind the same key. The question stops being expensive, so you start asking it.
What does not change
The networks still own their rules, and the assistant does not get to negotiate with them. WhatsApp still only allows free-form replies inside the twenty four hour window after the customer wrote to you. X still caps a post at 280 characters. TikTok and YouTube still require a video, Instagram still requires media. Those constraints are enforced on the way in, which is better than discovering them at publish time, but they are not removed.
A person also still owns the decision. Every write tool is annotated as a write tool, and crm_send_social_message says on its own face that the reply reaches a real person immediately. MCP clients use that annotation to ask before they act. The assistant is fast at gathering context and drafting; it is not authorized to be brave.
The twelve channels, and the honest split between them
Twelve channels are covered: Instagram, Facebook, X, LinkedIn, TikTok, YouTube, Threads, Pinterest, Reddit, Bluesky, Telegram and WhatsApp. Both crm_list_social_accounts and the crm://social/accounts resource enumerate the same twelve, and for each account you have actually connected they return the platform, the handle, the timezone and the posting limit.
Here is the part most vendor pages skip. A list of twelve logos does not tell you that all twelve do the same two jobs, and pretending otherwise is how people end up disappointed in week two. Networks differ in what they permit, account types differ inside a single network, and what your specific account can do depends on how it is set up on the network side. So the honest answer to "does channel X support Y" is not a static matrix on a marketing page. It is a tool call.
The rule to give your assistant is: call crm_list_social_accounts first and treat what comes back as the truth. That call is cheap, it is read-only, and its result is specific to your workspace rather than to a category. An assistant that plans a week of content without knowing which accounts exist and what their posting limits are will produce a plan that fails at publish time.
Conversation-first channels
Some networks earn their place in the stack because of what arrives, not what you publish. WhatsApp is the clearest example, and the platform rule is documented on the tool itself: free-form replies are only allowed within twenty four hours of the customer's last message. That single constraint reshapes the whole workflow. On a conversation-first channel, response latency is not a nice-to-have metric, it is a hard gate, and a thread that sits unanswered overnight can become a thread you are no longer permitted to answer the ordinary way.
This is exactly the workload MCP is good at, because the expensive part is triage rather than typing. An assistant can read every waiting thread in a fraction of the time it takes a person to open them, and it can tell you which three are about to age out of a reply window.
Publishing-first channels
Other networks are where the artifact matters. You can tell which ones the product treats as publishing surfaces from the rules crm_schedule_social_post enforces before it accepts a call: TikTok and YouTube require a video, Instagram requires media, and X caps text at 280 characters. Those are not house style rules invented by the CRM. They are the networks' own constraints, checked at the door so that a malformed post fails immediately and loudly instead of quietly at publish time.
The practical consequence for an assistant is that "post this everywhere" is never a single object. It is a fan-out, and the tool is explicit about it: one post is created per target account, so two platforms means two posts. That is the correct model, because the same words rarely belong on Pinterest and LinkedIn without adjustment, and having two records means you can edit one without disturbing the other.
Channels that do both
Most of the twelve sit in the middle: you publish there and people reply there, and the reply is often worth more than the post. This is the case that makes a combined surface worth having at all. When the inbox and the queue are separate tools, the loop between "we published this" and "these five people wrote to us afterwards" is never closed by anybody, because closing it means exporting from one product and matching by hand in another.
Over MCP that loop is two tool calls in the same conversation. Which is why the most valuable playbooks further down this page are the ones that cross the line between publishing and conversation rather than staying on one side of it.
What we will not claim: this page does not publish a per-network capability matrix, because the tool surface does not expose one and a matrix that is right today would be wrong after the next platform policy change. Connect the accounts you care about and read crm_list_social_accounts. That answer is current by construction.
The unified social inbox over MCP
The social inbox is exposed as five read tools and two write tools, and the read tools are deliberately shaped so that a triage loop starts with one call rather than twelve. Everything an assistant needs to decide who to answer first is available before it opens a single thread.
The five read tools, and what each is for
crm_social_inbox_summary is the opening move. One call returns conversation and unread totals overall and per network, plus the threads still waiting on a reply, oldest first. If you only ever use one social tool, use this one.crm_list_social_conversations lists DM conversations most-recently-active first, with the participant, the unread count and the last-message preview. Use it to answer questions like who wrote to us on Instagram, or to find the conversation id you need before replying.crm_get_social_conversation fetches one conversation with the participant's details, the bridged CRM contact and the last ten messages. This is the tool to call before drafting anything, because a reply written without the last ten messages is a reply that repeats a question the customer already answered.crm_list_social_messages pages through a thread, oldest-first inside the page, with beforeMessageId to walk further back. Two details make it more useful than a plain history dump: voice notes carry their transcript, and translated messages carry both wordings.crm_list_social_accounts is technically an accounts tool but it belongs in every inbox workflow, because it tells the assistant which handles exist and which account owns which conversation.
Alongside those, the server preloads crm://social/inbox as a resource: unread totals per network plus the twenty most-recently-active conversations with participant, preview and unread count. A client that reads resources on connect starts the conversation already knowing who is waiting, which removes a round trip from the first turn of every session.
What a triage loop actually looks like
A good triage loop is summary-first and shortlist-driven. Written out as steps, the loop an assistant should run is:
- Call
crm_social_inbox_summary once. Read the per-network unread totals and the list of threads waiting longest. - Choose a shortlist. Not the whole inbox: the threads that are old, unread, or on a channel with a reply window.
- Call
crm_get_social_conversation for each thread on the shortlist. Each call returns the participant, the bridged CRM contact and the last ten messages. - Where ten messages are not enough, walk further back with
crm_list_social_messages and beforeMessageId. Do this only for the threads that need it, because every page is a request against your rate budget. - Sort into three buckets: needs a human now, can be answered with a draft you approve, and noise.
- For the draftable ones, produce the reply and send it with
crm_send_social_message after you approve. - For the noise, call
crm_mark_social_conversation_read so the badge clears on the network as well as in the CRM. - For the ones that need a person, create a task rather than leaving them in the assistant's head. That crosses into the CRM tools covered further down.
The shape of that loop is the whole performance story. A naive assistant opens every conversation and burns thirty requests to learn what the summary told it in one. On a sixty-requests-per-minute key that is the difference between a triage pass that finishes and one that starts returning HTTP 429.
How an assistant decides what needs a human
The honest answer is that it decides from context you can inspect, not from a score it invented. The inputs available to it are all readable:
- Age. The summary returns threads waiting on a reply oldest first, so time-since-last-inbound is explicit rather than inferred.
- Channel. A WhatsApp thread approaching the twenty four hour free-form window is more urgent than a Reddit thread with the same age, because the cost of being late is different.
- Thread content. The last ten messages come back with the conversation, so an unanswered direct question is visible without a second call.
- Who they are. The conversation carries the bridged CRM contact, so an assistant with
contacts:read can pull the lead score, tags, stage and activity timeline before it decides how much the thread is worth. - Whether a bot already replied.
crm_list_agents shows which AI agents are live on which channels and how many times they ran in the last twenty four hours.
What you should not do is ask the assistant to invent an urgency number and then trust it as if it were data. Ask it to rank and to say why, in a sentence per thread, and read the reasons. A ranking you can argue with is worth more than a score you cannot.
How replies are actually sent
crm_send_social_message sends a direct message into an existing conversation, and four things happen when it runs. The reply is delivered through the account that owns the thread, so the customer sees an answer where they wrote rather than an unexplained new message from a different handle. The AI auto-reply is paused for the linked contact, so an agent does not talk over a human decision. The CRM timeline records the send, so the conversation history stays complete. And platform windows still apply, which on WhatsApp means the free-form reply has to be inside twenty four hours of the customer's last message.
The tool is annotated as a write tool and its description states plainly that it reaches a real person immediately. That annotation is the mechanism your client uses to prompt you before it runs. Keep that prompt on. The failure mode worth avoiding is not a bad draft, it is a good draft sent to the wrong thread.
Note also what this tool does not do: it sends into an existing conversation. It is a reply tool, not a cold-outreach tool. Outbound-first messaging on Telegram and X has its own tools and its own scopes (crm_send_telegram_message behind telegram:send, crm_send_twitter_dm behind twitter:send), and a key scoped only for social inbox work cannot call them. That separation is deliberate.
How threads get marked read
crm_mark_social_conversation_read clears a conversation's unread badge on the network as well as in the CRM, and it is idempotent, so marking an already-read conversation changes nothing. That second property matters more than it sounds: it means a triage loop can be safely re-run after an interruption without any special handling for what it already processed.
The network-side clearing is the part that makes automated triage tolerable in practice. A tool that only clears its own badge leaves your phone showing forty unread conversations that were dealt with an hour ago, which trains you to ignore the badge, which is the end of the badge being useful.
The detail that saves the most time: transcripts
crm_list_social_messages returns voice notes with their transcript attached, and translated messages with both wordings. On Instagram and WhatsApp a long voice note is frequently the entire substance of a lead, and any text-based triage that cannot read it silently drops the most valuable threads in the inbox. Being able to say "summarize the last three voice notes from this person" and get an answer without opening an audio player is, in day-to-day use, one of the largest single time savings on this list.
Publishing over MCP
Publishing is exposed as three read tools and three write tools, and the write tools are shaped around a queue: you schedule into it, you edit while the post is still pending, and you cancel before it goes out. Once something has published, MCP can tell you about it but cannot take it back.
Reading what is queued and what published
crm_list_social_posts lists scheduled and published posts, newest scheduled time first, with status, published URL and any failure reason. This is how you answer what is going out this week.crm_get_social_post fetches one post with its full content, media, schedule, status and published URL. Use it when you need the exact text rather than a list row.crm_social_post_stats returns publishing volume and outcome over a recent window: totals by status overall and per network, plus the last publish time.
Two resources cover the same ground without a tool call: crm://social/posts/scheduled is the upcoming calendar, soonest first, with network, scheduled time and a content preview, and crm://social/posts/published is what actually went out, with live URLs plus anything that failed and why.
Be precise about what the stats tool is. It reports delivery, not engagement. It tells you how many posts were scheduled, published or failed per network, and when the last publish happened. It does not return likes, impressions, watch time or follower growth, and this page is not going to imply otherwise. That is a real limitation and it changes how you build a retrospective, which is covered in the playbooks below.
Scheduling
crm_schedule_social_post schedules a post on one or more connected accounts. Four properties of that tool are worth internalizing before you let an assistant drive it.
- It fans out. One post is created per target account, so two platforms means two posts. Ask for a cross-post and you get two independent records you can edit separately.
- A time is required unless you say now.
scheduledAt is mandatory unless publishNow is true, and publishNow publishes to a real audience straight away. There is no third state where a post drifts out at some unspecified point. - Per-platform rules are enforced. TikTok and YouTube need a video, Instagram needs media, X caps at 280 characters. A call that breaks one of those is rejected rather than accepted and quietly failed later.
- Accounts have posting limits.
crm_list_social_accounts returns them, and the crm://social/accounts resource carries the daily post limit per account. A plan that ignores them is a plan that will partly not happen.
The timezone detail, stated plainly
A scheduled post is pinned to a moment in time, and a bare wall clock is not a moment. "Tuesday at nine" means nothing until you say nine where. This is the single most common way a confidently scheduled week comes out wrong, and it is entirely avoidable.
Every connected account carries its own timezone, returned by crm_list_social_accounts and preloaded in crm://social/accounts. The instruction to give an assistant is therefore explicit rather than trusting:
Before you schedule anything:
1. Call crm_list_social_accounts and tell me the timezone of each account
you are about to post from.
2. When I give you a time, restate it back to me as an unambiguous instant
(date, time, and the offset or zone you resolved it against).
3. Only then call crm_schedule_social_post.
If my instruction is ambiguous about the zone, ask instead of guessing.
That prompt is three sentences and it removes an entire class of error. The assistant is good at arithmetic across zones; what it cannot do is know which zone you meant when you did not say. Make it restate the resolved instant and you will catch the mistake while the post is still a draft.
Editing and cancelling
crm_update_social_post edits a post that has not gone out yet: its text, media or scheduled time. Only pending posts can be edited, and anything already published, in flight, failed or cancelled is rejected. That refusal is a feature. It means an assistant cannot silently rewrite history, and it means the state of a post is unambiguous at any moment.
crm_cancel_social_post cancels a scheduled post so it is never published, and it is idempotent, so cancelling an already-cancelled post is a no-op. A post that has already gone out cannot be withdrawn from here. If you need it gone from the network, that is an action on the network, not a tool call.
The combination of those two boundaries is what makes it reasonable to hand the queue to an assistant at all. The blast radius of any mistake is bounded by "things that have not happened yet", and the things that have already happened are read-only.
Seven worked playbooks
Each playbook below gives the prompt, the tools it triggers and the output you should expect. They are written to be pasted and adjusted rather than admired. Where a playbook needs scopes beyond the social four, the extra scope is named so you can decide whether that key should hold it.
Playbook 1: daily DM triage
The workhorse. Run it once in the morning and once before you stop. Scopes needed: social:read and social:write.
Give me the social inbox in three buckets.
First call crm_social_inbox_summary and tell me the unread totals per network
and how many threads are waiting on a reply.
Then open the ten threads that have been waiting longest with
crm_get_social_conversation.
Sort them into: (a) needs me personally, (b) you can draft a reply I approve,
(c) noise. One sentence of reasoning per thread, and say which network
each is on.
Do not send anything yet. Do not mark anything read yet.
Tools triggered: crm_social_inbox_summary, then up to ten calls to crm_get_social_conversation. Eleven requests against a sixty-per-minute budget, which is comfortable.
Output: a ranked list with reasons, not a wall of message previews. You then approve the (b) drafts one at a time, and each approval runs crm_send_social_message. Finish by asking it to run crm_mark_social_conversation_read across the (c) bucket so the badges clear on the networks too.
The two "do not" lines at the end of the prompt matter more than anything else in it. Without them a capable assistant will helpfully start sending, and you will lose the review step that is the entire reason this is safe.
Playbook 2: lead qualification from DMs into the CRM
This is the one that turns a social inbox into a pipeline. Scopes needed: social:read, plus contacts:read and contacts:write.
Work through the unread social DMs from the last three days.
For each one:
- Open it with crm_get_social_conversation so you have the last ten messages
and the bridged CRM contact.
- Pull the contact's history with crm_get_contact_activity.
- Decide whether this is a real buying signal, a support question, or noise.
For the real buying signals only:
- Make sure a tag exists with crm_create_tag, then attach it with
crm_tag_contact.
- Set a lead score with crm_set_lead_score and tell me the number and why.
- Move the stage with crm_update_contact_stage if the conversation justifies it.
- Add one short note with crm_add_contact_note quoting the sentence that
convinced you.
Show me the full list of changes before you make any of them.
Tools triggered: crm_list_social_conversations, crm_get_social_conversation, crm_get_contact_activity, then crm_create_tag, crm_tag_contact, crm_set_lead_score, crm_update_contact_stage and crm_add_contact_note.
Two things to know before you run it. First, crm_tag_contact never creates a tag: an unknown name is rejected, which is why crm_create_tag comes first. That is deliberate, and it is what stops an assistant from inventing eleven variants of the same tag over a month. Second, crm_add_contact_note writes into a 500 character field, and once full the oldest text is dropped from the front to make room. So a note tool used carelessly can remove earlier note text. Tell the assistant to write one short line, not a summary.
Output: a set of contacts that are tagged, scored, staged and annotated, with the sentence that justified each one recorded on the timeline. The lead score is marked as a manual override rather than an AI score, and each change logs an activity you can audit later with crm_get_contact_activity.
Playbook 3: a week of posts planned and queued
Scopes needed: posts:read, posts:write and social:read for the account list.
Plan next week's posts.
1. crm_list_social_accounts: which accounts exist, what are their timezones
and daily post limits.
2. crm_list_social_posts: what is already scheduled for next week, so we
do not double-book a slot.
3. crm_social_post_stats for the last 30 days: how many posts went out per
network and how many failed.
Then propose a plan as a table: date, local time, account, the post text,
and what media it needs. Respect the per-network rules (video for TikTok
and YouTube, media for Instagram, 280 characters on X) and respect the
daily post limits.
Show me the table. Schedule nothing until I say go.
Tools triggered: crm_list_social_accounts, crm_list_social_posts, crm_social_post_stats, then one crm_schedule_social_post call per slot once you approve.
Output: a reviewable table first, a queue second. Read it back afterwards with crm://social/posts/scheduled or another crm_list_social_posts call and check the count matches what you approved. Remember the fan-out rule: if a row targets three accounts, that is three posts, and the read-back should show three.
When something in the plan turns out wrong, and something always does, fix it with crm_update_social_post rather than cancelling and rescheduling. Editing a pending post keeps one record with one history instead of leaving a cancelled ghost next to its replacement.
Playbook 4: comment-to-DM follow-up
First, the honest boundary, because this playbook is often sold dishonestly: the MCP surface does not read public comment threads. There is no comments tool among the sixty two. crm_get_social_post gives you the post, its status and its published URL, not the replies underneath it. What MCP sees is the moment a conversation moves into DM.
That is still the important moment, because a comment that becomes a DM is a comment that became a lead. Scopes needed: posts:read, social:read, social:write.
We published a post on Tuesday. I want to catch the conversations it caused.
1. crm_list_social_posts: find the posts published in the last 4 days and
give me their published URLs and exact publish times.
2. crm_list_social_conversations: list the DM threads whose latest activity
started after each publish time.
3. For each of those threads, open it with crm_get_social_conversation and
tell me whether the person mentions the post, the offer in it, or the
topic of it.
Give me the ones that look post-driven, with the quote that shows it.
Draft a reply for each. Send nothing until I approve.
Tools triggered: crm_list_social_posts, crm_list_social_conversations, crm_get_social_conversation, then crm_send_social_message per approved draft.
Output: the shortlist of DMs that plausibly came from the post, each with the quote that supports the claim. The timing correlation is circumstantial and the assistant should say so rather than assert attribution it cannot prove. Tag the ones that hold up with a campaign tag via crm_create_tag and crm_tag_contact, and you have something you can count next month.
If public comments matter to your operation, handle them where they live and treat MCP as the layer that picks up the thread once it becomes a private conversation. Pretending otherwise would cost you more in surprise than the feature would have saved.
Playbook 5: campaign retrospective
Scopes needed: posts:read and social:read, plus deals:read if you want the commercial half.
Retrospective on the last 30 days of publishing.
1. crm_social_post_stats over 30 days: totals by status overall and per
network, and the last publish time.
2. crm_list_social_posts: everything published or failed in that window,
with published URLs and failure reasons.
3. For every failure, crm_get_social_post: what exactly failed and why.
4. crm_social_inbox_summary: current inbox load per network.
Answer four questions:
- Which networks did we actually keep up with, and which did we abandon?
- What is our failure rate, and is it concentrated on one network?
- Are the failures a content problem (wrong media type, too long) or a
connection problem?
- Where is the inbox load heaviest relative to how much we publish there?
Be explicit about what you cannot see: you do not have likes, impressions
or reach. Do not estimate them.
Tools triggered: crm_social_post_stats, crm_list_social_posts, crm_get_social_post, crm_social_inbox_summary, and optionally crm_list_deals and crm_get_deal if the key holds deals:read.
Output: a delivery and consistency review, not an engagement report. That distinction is the point. The last instruction in the prompt is not decoration: without it, a helpful model will happily estimate reach it has no access to, and an estimate that reads like a measurement is worse than no number at all.
What this retrospective is genuinely good at is catching the boring failures that quietly cost the most. A network where four posts in a row failed on a media rule. A handle nobody has published to in three weeks. A day of the week where everything queued and nothing went out. Those are all visible here, and none of them are visible in an engagement dashboard.
Playbook 6: a content calendar built from what already performed
Competitor-free, in the literal sense: this playbook reads nothing but your own history. No scraping, no competitor handles, no borrowed benchmarks. Scopes needed: posts:read, posts:write, social:read.
Since crm_social_post_stats reports delivery rather than engagement, "what performed" has to be defined against signals you actually hold. The strongest one available is inbound conversation: posts that were followed by DMs did something the posts that were not did not do.
Build next month's calendar from our own history only.
1. crm_list_social_posts: every post published in the last 60 days, with
network, publish time and text.
2. crm_list_social_conversations: DM threads whose first inbound message
landed within 48 hours of one of those publish times.
3. Group the posts into themes based on their text. For each theme, count
how many posts and how many DMs followed.
Now give me:
- The three themes that were most often followed by inbound DMs.
- The themes that published cleanly but produced nothing.
- The networks where we published least relative to the DM volume they
generate.
Then propose next month as a table (date, local time, account, theme,
draft text). Do not use any external benchmark, any competitor, or any
metric you cannot read from these tools. Say when the sample is too
small to conclude anything.
Tools triggered: crm_list_social_posts, crm_list_social_conversations, and then one crm_schedule_social_post per approved slot.
Output: a plan whose justification you can check, because every input is a record in your own workspace. The final instruction about small samples is essential. Sixty days of posting on five networks is not a lot of data, and a model that has been asked to find three themes will find three themes whether or not three themes exist. Ask it to say when it is guessing and it usually will.
Playbook 7: handing a thread from the AI agent back to a person
This one exists because of a specific, verified behavior: crm_send_social_message pauses the AI auto-reply for the linked contact. That makes a clean handoff possible without touching agent configuration. Scopes needed: social:read, social:write, plus agents:read to inspect and agents:run if you want to test.
Which conversations should come off auto-reply today?
1. crm_list_agents: which agents are live, on which channels, and how many
times did they run in the last 24 hours.
2. crm_social_inbox_summary, then crm_get_social_conversation for the threads
waiting longest.
3. Flag any thread where the agent has replied more than twice and the
customer is still asking the same thing, or where the customer has asked
for a human, or where money or a complaint is mentioned.
For each flagged thread, draft the human reply. When I approve and you send
it with crm_send_social_message, auto-reply pauses for that contact, which
is exactly what we want.
Tools triggered: crm_list_agents, crm_social_inbox_summary, crm_get_social_conversation, crm_send_social_message.
Output: a short list of threads where automation has stopped helping, each with a drafted human reply. If you want to see what the agent would say before deciding, crm_run_agent is a test-run tool: it returns the reply the agent would send against a sample message, the reply is never delivered to any contact, and no CRM record is created, changed or deleted. It is flagged as a non-read-only tool purely because each run calls an external model provider and spends AI credits.
Where social meets the CRM
The reason to run social inside a CRM rather than beside one is that a DM can become a contact, a contact can become a deal, and an unanswered thread can become a task, all inside the same assistant turn. Nothing has to be exported, matched or reconciled.
A DM becomes a contact
The bridge already exists in the data: crm_get_social_conversation returns the bridged CRM contact alongside the participant and the last ten messages. From there an assistant with contacts:read can call crm_get_contact for the full record including the last ten messages exchanged, crm_search_contacts to find related records by name, username or phone, and crm_get_contact_activity for the timeline of notes, stage changes, tags, score changes and assignments.
That timeline is the piece people underestimate. It is what lets an assistant answer "have we been here before with this person" instead of treating every inbound DM as a first contact. A returning customer who churned six months ago is a different conversation from a stranger, and the difference is knowable in one call.
A contact gets a lead score and tags
With contacts:write, four tools cover the classification work:
crm_create_tag creates a tag in the workspace dictionary. It is idempotent: an existing tag with the same name is returned unchanged rather than duplicated.crm_tag_contact attaches an existing tag and logs a TagAdded activity. It never creates a tag, so an unknown name is rejected. Pair it with the previous tool.crm_untag_contact detaches a tag and logs TagRemoved. Also idempotent.crm_set_lead_score sets a score from 0 to 100, marks it as a manual override rather than an AI score, and logs a ScoreChanged activity.
The manual-override flag on the score is a small design decision with a large consequence. It means the number an assistant wrote is distinguishable from the number the product's own scoring produced, so you can always tell which is which when they disagree. Read more about how scoring works on the contact lead scoring guide.
crm_assign_contact rounds out the set by giving a contact an owner, or removing one, and logging the change. On a team, assignment is usually the action that actually gets a thread answered, more than any tag.
A qualified thread becomes a deal
With deals:read and deals:write: crm_list_deals returns the pipeline ordered by stage then value with open-task counts, crm_get_deal fetches one deal with its linked tasks and resolved contact name, crm_create_deal creates a new deal in an active stage, and crm_update_deal_stage moves it between stages.
There is a hard boundary here and it is worth understanding rather than working around. crm_update_deal_stage allows lead, qualified, proposal, negotiation and lost. Moving a deal to won is intentionally not supported over MCP, because winning a deal books an income ledger entry. That is a money action, so it happens in the panel, by a person. For the same reason crm_create_deal cannot create a deal that is already won or lost.
Pipelines themselves are readable with crm_list_pipelines and crm_get_pipeline, which return the boards with their ordered stage columns and per-stage contact counts. An assistant should call one of them before it starts moving anything, because guessing your stage names is a good way to produce confident nonsense.
An unanswered thread becomes a task
This is the closing move of every triage session and the one most people skip. With tasks:write, crm_create_task creates a task or reminder, with a required title and optional links to a contact and a deal plus a due date and priority. crm_complete_task sets a task's status, defaulting to done with a completion timestamp, and can reopen one by passing a status. crm_list_tasks reads them back sorted by due date then priority.
The reason this matters: an assistant's memory of "these three threads need a person" ends when the session does. A task outlives the session, appears in crm://tasks/today and crm://tasks/overdue, and shows up in the panel where the rest of your team can see it. Make the final line of every triage prompt "create a task for anything you could not resolve" and the loop actually closes.
If you want the deeper version of this workflow outside of MCP, the unified inbox setup guide and the deals and tasks guide cover the same ground from the panel side.
Guardrails that matter for social specifically
Four constraints shape how a social assistant should be built: request rate, scope separation, write annotations and the safe-write boundary, and the platforms' own limits. Three of them are ours and one of them is not, and the one that is not is the one people forget.
Rate limits, and why triage is the workload that hits them
The limit is sixty requests per minute per key by default, with a ceiling of three hundred on the Business plan. Exceeding it returns HTTP 429 with a Retry-After header:
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Most CRM work never approaches that. Social triage does, because it is naturally fan-out shaped: one summary call, then one call per thread, then one call per page of older history, then one call per reply. Thirty threads with a page of history each is ninety requests before a single reply is drafted.
The mitigations are all design choices, not settings:
- Start with
crm_social_inbox_summary. One call that replaces a per-network sweep is the largest saving available. - Shortlist before you open. Ten threads opened deliberately beat forty opened exhaustively.
- Use resources for context that does not change within a session.
crm://social/accounts and crm://social/inbox are preloaded, so an assistant that reads them starts a turn already oriented. - Page history only where the last ten messages were genuinely not enough.
- Run batch work on its own key so a slow retrospective cannot starve the key your interactive assistant uses.
Scope separation: a publishing assistant cannot read finance
Scopes are the reason it is reasonable to give an assistant this much reach. The four that make a complete social key are social:read, social:write, posts:read and posts:write. That key can work the inbox and the calendar. It cannot read the ledger, because financial reporting sits behind finance:read, and it was never granted.
A refusal is explicit rather than silent. A tool call outside the granted set comes back as JSON-RPC error -32002, and the error data names both what was required and what you hold:
{
"jsonrpc": "2.0",
"id": 7,
"error": {
"code": -32002,
"data": {
"requiredScope": "posts:write",
"granted": ["social:read", "social:write", "posts:read"]
}
}
}
The human-readable message is omitted above because the two fields worth reading are requiredScope and granted. That pair makes a scope problem diagnosable in one glance, which matters when the symptom is an assistant saying it cannot do something and you cannot tell whether that is a permission or a limitation.
The practical recommendation is one key per job, not one key per person. A drafting assistant that reads the inbox and writes nothing gets social:read alone. A scheduling assistant gets posts:read and posts:write and no inbox access at all. A full social operator gets the four. Nobody gets finance:write, email:send or keys:manage unless there is a specific reason, and those three are offered but left unticked by default for exactly that reason.
Write annotations and the safe-write boundary
Every tool declares whether it is read-only or a write, and clients use that declaration to decide what to confirm with you. crm_send_social_message is the one to watch, and its own description says why: the reply reaches a real person immediately.
More interesting is what the write tools deliberately refuse. The boundary is drawn around irreversible money and identity actions:
- No ledger transaction can be created over MCP. Finance tools are reporting only.
- No invoice can be paid, sent, voided or created, and no payment link is returned.
- No deal can be forced to won, because winning books revenue. Lead, qualified, proposal, negotiation and lost are all reachable; won is not.
crm_run_agent is a dry run. The reply it produces is never delivered and no record is created, changed or deleted.- On the publishing side, a published post cannot be withdrawn and a non-pending post cannot be edited.
- Deleting a webhook endpoint is the one hard delete on the whole server, and it is annotated as destructive. A social key has no reason to hold
webhooks:write at all.
None of that is a substitute for reviewing what an assistant is about to do. It is a floor, not a ceiling. But it means the worst realistic outcome of a misunderstood instruction is a badly worded DM or a post scheduled at the wrong hour, both of which are recoverable, rather than a booked revenue entry or a deleted record.
Platform limits are real regardless of what the CRM allows
This is the constraint that surprises people, because it does not come from us and it cannot be raised by upgrading. Every network runs its own sending and posting limits, and they exist to make automated abuse expensive.
The system surfaces what it can: crm_list_social_accounts returns posting limits per account and the crm://social/accounts resource carries the daily post limit. The scheduling tool enforces the per-platform content rules before it accepts a call. The messaging tool documents the WhatsApp free-form window. But no CRM can tell you the exact live sending allowance a network is applying to your account in this hour, because the networks do not publish it that way.
Two habits follow from that. First, treat "the tool accepted it" as different from "the network delivered it", and read back with crm_list_social_posts or crm_social_post_stats rather than assuming. Second, do not use an assistant to work around a limit. Fanning the same message across accounts to get more sends through is the behavior that gets accounts restricted, and it is a category of instruction you should not give and we would rather not help you give.
The related reading here is our bulk messaging best practices guide, which covers pacing on the outbound side in more detail than fits here.
Honest comparison: assistant, scheduler, or automation
An assistant over MCP, a scheduling tool with an AI caption writer, and a no-code automation are three genuinely different products, and at least two of them probably belong in your stack. The useful comparison is not which is best, it is which failure mode you can live with.
A scheduling tool is built around a calendar. It is excellent at the thing it does: a queue, a preview, a grid, per-network previews, and increasingly an AI writer that turns a brief into ten caption variants. Its limitation is not quality, it is scope. It knows about posts. It does not know that the person who replied to Tuesday's post is a contact who churned last quarter, because that is not in its data model and never will be.
A no-code automation is built around a trigger. It is excellent at deterministic, repeat-forever work, it runs while you sleep, and it does the same thing every time, which is a real virtue. Its limitation is that it only runs the branch you drew. Every path has to be anticipated, and social conversations are the canonical example of work where the next step depends on what the last message actually said.
An assistant over MCP is built around judgement. It is good at the messy middle: ranking, summarizing, drafting, planning, noticing that four posts failed for the same reason. Its limitation is the mirror image of the automation's: it needs a turn. Somebody has to ask. It is not a scheduler and it is not a cron job, and a page that told you otherwise would be setting you up to be disappointed.
The table below is the detailed version. It is scored honestly, including the rows where the assistant loses.