servers / onsa

Onsa MCP server

communitystreamable_httpremotewrite capablehealthy

Find scored B2B leads, read campaign replies and send approved LinkedIn outreach.


01Tools · 13

How to read this: tool names here are observed from a live tools/list handshake. The Risk label is a heuristic inferred from the tool name (write/destructive verbs), not from executing the tool — a conservative guess, not a verified capability. We never escalate risk from a description. Found one that's wrong? Tell us — we fix on report.

ToolRiskSide effectsApproval
continue_campaign
Say something to the agent inside an EXISTING campaign, and get a jobId to poll with fetch_leads. This is how you grow or steer a cohort instead of forking a new one: "find 5 more like these", "look at Singapore and the Gulf instead of US institutions", "focus on funds over $5bn AuM". Prefer this over find_leads whenever the user means "more of this" or "not quite, adjust" — find_leads always creates a SEPARATE campaign with its own ICP, which splits the funnel and makes the two incomparable. The agent sees the campaign's existing leads and ICP, so you can refer to them. It cannot answer you back through this API, so give instructions, not questions. Two limits worth telling the user about: new leads count against the prospect quota, and de-duplication is per workspace — asking for 5 more can yield fewer if the agent rediscovers people already in this workspace. This does NOT remove or skip leads; saying "drop the bad ones" will not take them out of the cohort or out of the outreach queue.
readfalseunknown
fetch_leads
Get the status/results of a find_leads job by jobId. "pending" means the search is still running (leads may already hold a partial list); "completed" means the agent delivered a batch of leads — it is NOT a guarantee that no more will arrive, so if total is still rising keep polling. "stalled" means no leads arrived for ~15 minutes — the agent asked a question, errored, or never delivered; STOP polling, read `agentMessage` for the reason, relay it to the user and re-run find_leads with the answer folded into the query. Also returns total (leads in this response), campaignId (pass it to get_campaign / get_campaign_leads / get_campaign_stats) and campaignUrl — a deep link straight into the prospects tab for this search in Onsa. `agentMessage` is agent text from this campaign's chat, filtered to what was said AFTER this job started — so it is never a reply that predates your request, but on a campaign with several searches it CAN be a late message from an earlier one. A null value does NOT mean the agent is silent: artifact-only messages carry no text, and an agent that ERRORED writes nothing here at all. Treat null as "no news", apply the 15-minute rule, and never read it as proof it is still working. If it is asking for something and no leads are arriving, STOP POLLING, relay that question to the user, and start a new find_leads with the answer folded into the query; the agent cannot be replied to through this API. Each lead carries name, companyName, linkedInUrl, position, headline, location, industry, companyUrl, email (often null), and — most usefully — score (1–5) and scoreExplanation, the reasoning for why this person matches the ICP.
readfalseunknown
find_leads
Start a live B2B lead search with Onsa's AI agent — finds real people (with LinkedIn profiles) matched against your ICP. Pass a natural-language brief (titles, company type, geography, e.g. 'find 5 fintech founders in NYC'). Returns a jobId IMMEDIATELY; the search usually takes 3–10 minutes, so then poll fetch_leads with that jobId until status is "completed". `limit` is a target the agent aims at, not a cap — expect it to return more, and note that every lead it finds counts against the workspace's prospect quota.
readfalseunknown
get_campaign
Get one campaign's details: its ICP (the ideal-customer-profile the agent derived and scores leads against), its outreach template, and its settings. The ICP is returned exactly as stored, which is snake_case: `perfect_lead` and `reachable_market` are one-line summaries, while `company` and `person` hold the rules that actually drive scoring — each is an object with `critical` and `preferential` rule lists. `product` and `owner` describe the seller. Read `company.critical` / `person.critical` before proposing a refinement; the two summary strings are not the criteria. Do not assume any key is present. Also read outreachTemplate to see how much personalization the messages allow: a template whose only placeholders are [FIRST_NAME] / [COMPANY_NAME] produces near-identical mail-merge copy for every lead.
readfalseunknown
get_campaign_leads
Read the leads of ANY campaign by campaignId, with the same fields as fetch_leads — including score and scoreExplanation. Use this for a campaign you did not start in this session (fetch_leads only works with a jobId from your own find_leads call), and pass leadIds to resolve the reply buckets from get_campaign_stats back into actual people.
readfalseunknown
get_campaign_stats
Get the outreach funnel for one campaign: invites sent, invites accepted, messages sent, and replies split into positive / negative / other by sentiment. If the user asks to analyse "LinkedIn reactions" they almost certainly mean the REPLIES people sent — use list_replies for the actual text; this tool only counts them. (Onsa stores no post likes or emoji reactions at all.) LinkedIn and email are merged, as in Onsa's Overview page. These count LEADS, not actions — a lead invited twice counts once — so where a lead was re-invited these read slightly lower than the Overview widget, which counts actions. Say so if the user compares the two. IMPORTANT: invitesSent can EXCEED leadsTotal, and that never means anyone was invited twice — the counters are already de-duplicated by lead. It means a lead who was contacted has since been skipped or deleted: action rows survive that, leadsTotal does not count it. Do not report such a gap as a double invite. Use it to judge which ICP cohort actually earned replies. `rates` already gives you acceptancePct, replyPct, positivePct and negativePct over the correct denominators — use them rather than doing the arithmetic yourself, and never divide by leadsTotal, which counts every prospect in the cohort including those never contacted. RANK COHORTS ON positivePct, NEVER ON replyPct: a rejection is a reply, so a cohort that provokes people scores high on replyPct while producing nothing. A high replyPct next to a high negativePct means the copy is annoying its audience — say that, do not call it engagement. IMPORTANT LIMIT ON ALL FOUR RATES: they count only replies Onsa has SCORED, so an unscored or still-in-window reply is in none of them and list_replies can legitimately show more replies than these rates imply — quote them as "of the replies Onsa scored", and use list_replies when the question is how many people actually answered. A rate is null when its denominator is 0, meaning nothing was sent, so no rate exists; that is different from the COUNTS above, which are genuinely 0 in that case. Two further caveats worth stating when you report these: the *Scheduled counts are everything queued regardless of date, and sentiment is evaluated once per lead ever, not once per reply. A campaign whose invites were never sent reads as all zeros, which means "not tried yet", not "failed". Counts alone cannot tell the user what to DO: follow this with list_next_steps for the ranked to-do list and list_replies for the words.
readfalseunknown
get_lead_memo
The research memo Onsa's agent wrote about one lead — usually far richer than scoreExplanation: role history, company size and stage, what they have said publicly, what the angle on them is. THIS IS THE RAW MATERIAL FOR GOOD OUTREACH. Read it before calling rewrite_outreach, and build the message around one specific, checkable fact from it rather than a generic opener. MOST LEADS HAVE NO MEMO — Onsa writes one only for prospects it has researched, so memo: null is the common case, not an error. When it is null, fall back to scoreExplanation and do not invent details.
readfalseunknown
list_campaigns
List the campaigns (past lead searches) in this workspace that the user takes part in, newest first. Returns the newest `limit` of them (default 50) — compare `returned` against `total` to see whether older campaigns were omitted. Use it to see which ICP cohorts have already been tried before proposing a new one. Each entry has id, title, leadsTotal, hasIcp, tags, createdAt and updatedAt. Pass an id to get_campaign for its ICP, get_campaign_leads for its people, get_campaign_stats for its outreach funnel, list_replies for what prospects wrote back, or list_next_steps for what the campaign still needs a human to do — that last one is the answer when the user asks for recommendations rather than numbers. Note that searches started over MCP may share a generic title, so use the ICP and the dates to tell cohorts apart, not the title alone. A campaign row exists from the moment a search starts, so the NEWEST entry is often still empty (leadsTotal 0) — pick what to analyse by leadsTotal, never by position in the list.
readfalseunknown
list_next_steps
What this campaign still needs from a human, as a ranked to-do list: people who REPLIED, people who accepted an invite but were never messaged, drafts waiting for approval, leads found but never contacted, and setup that is missing. CALL THIS WHENEVER THE USER ASKS FOR RECOMMENDATIONS, ADVICE, NEXT STEPS, OR WHAT TO DO — and alongside get_campaign_stats whenever they ask how a campaign is PERFORMING or what is happening with it, because the funnel gives you numbers and this gives you the actions behind them. It is the question "what should I do about this campaign today". Use it before suggesting new searches — an unworked cohort is almost always worth more than another one. For replies specifically, list_replies is the better source: its `awaitingOurReply` compares timestamps, while the `replied` bucket here is only leads whose sentiment was scored and does not know whether you have since answered. Pass any leadIds to get_campaign_leads to see who the people actually are. Honest limits, which you should pass on rather than overstate: Onsa does not record whether you have ALREADY replied, or whether you contacted someone outside Onsa; "replied" only includes replies whose sentiment was scored, so it is a floor; a withdrawn or unreachable invite leaves no trace, so some "never contacted" leads may already have been tried. Sentiment is judged once per lead, not per message. Counts are complete; leadIds are capped at 200.
readfalseunknown
list_pending_outreach
List outreach messages the agent has drafted that are waiting for a human to approve — the "a message for X is ready" queue. Each entry has the draft text plus the lead it is for and why that lead scored as it did. Use it to review drafts in bulk, or to spot copy that reads like a mail-merge before it goes out. Omit campaignId for the whole workspace. This tool does not send anything itself — improve copy with rewrite_outreach (read get_lead_memo first), then queue it with send_outreach once the user has approved that exact text for that exact person.
readfalseunknown
list_replies
THE ACTUAL WORDS people replied with, for every lead in this campaign that answered. get_campaign_stats tells you HOW MANY replied and how Onsa labelled them; this tells you WHAT THEY SAID, which is the only way to work out why a cohort is landing or annoying people. Use it before you draw any conclusion about an ICP from response counts. Each entry carries the outbound message we sent and their reply, so you can read the exchange. `sentiment` is Onsa's own label and is written ONCE per lead, on their first reply — later replies never change it, and a reply Cogent has not scored yet comes back with sentiment: null, which means UNSCORED, not neutral. Those unscored replies are missing from get_campaign_stats entirely, so this tool can show more replies than the funnel counts. Trust the text over the label. THREE DERIVED FIELDS, so you do not have to work them out. `awaitingOurReply`: they spoke last and no sent message followed — this is how you find a reply nobody ever answered, and it is usually the most useful thing here. It is STRUCTURAL ONLY: a flat "no thanks" reads true as well, so check `sentiment` and the text before you suggest following anyone up. Onsa also sees only what Onsa sent, so it cannot know the user replied by hand in LinkedIn: say "no reply recorded here", never "you ignored them". `daysSinceLastReply` is elapsed whole days, NOT time-unanswered — it is filled in even where we did answer, so only say "unanswered for N days" when `awaitingOurReply` is also true. `looksLikeBroadcast`: their most recent message reached another profile in this campaign word for word, so it is a mass DM rather than an answer — do not count it as engagement. Treat all three as a FLOOR, never a verdict: a blast only one lead received is indistinguishable from a real reply, and two people who send the same long template will both be flagged. Read the text before you trust any of them. Top-level `awaitingOurReplyCount` spans the whole campaign rather than this page, and leaves out broadcasts and replies scored negative; raise `limit` if it exceeds `returned`.
readfalseunknown
rewrite_outreach
Replace the text of an outreach draft that is waiting for approval. This is how you fix copy that reads like a mail-merge: get the draft and the lead's scoreExplanation from list_pending_outreach, then write something that names a specific, checkable fact about that person. Do not invent facts — use only what the lead data gives you. The rewritten draft stays in the approval queue; it is not sent by this tool.
readfalseunknown
send_outreach
Queue an approved outreach draft for sending to a real person on LinkedIn. THIS REACHES A HUMAN. Show the user the exact message AND the recipient, and get their explicit go-ahead for that person, every time — even if they said "send them all". You must pass back both `confirmText` (character-for-character, not re-typed) and `confirmName` (the recipient's name), because these drafts are often near-identical between people and matching the body alone does not prove you meant this one. A mismatch refuses without telling you the real text: re-read it with list_pending_outreach. This tool ALSO puts a confirmation dialog in front of the person at the keyboard, quoting the draft as stored — you cannot answer it, and nothing is sent until they do. If they decline or dismiss it, STOP: do not call this again for that lead, ask what they want changed. Note this QUEUES the message — Onsa sends it on its own schedule, subject to daily limits — so report it as queued, not delivered.
writetrueunknown

02Install & source
https://api.onsa.ai/api/mcp
remote_url

03Access granted
Social selling & outreach · write

The access this server can exercise, inferred from its verified tools — not a declared OAuth scope.


05Provenance & freshness
sourcesOfficial MCP Registry [p1]
last_checked2026-09-08 06:31Z
next_check2026-09-10 06:31Z
cadenceevery 48h
verifiedtools_list:passed handshake:passed metadata:passed
index_statusindex5 unique facts >= 5

06Badge

Add the “as seen on MCPExplorer” badge to your README. Onsa MCP — as seen on mcpexplorer.com

[![Onsa MCP — as seen on mcpexplorer.com](https://mcpexplorer.com/badge/onsa.svg)](https://mcpexplorer.com/servers/onsa)

Next step

This is one server. A loadout combines the right servers, governance, and proven plays for a whole job — assembled deliberately, not tool-dumped.

Explore loadouts →