Skip to main content
URL: https://mcp.chatsailer.com/mcp/crm Read and manage a Sailer workspace. Record tools take a resource argument so they can grow without eating the client’s tool budget. Today the only value is "contact". Campaigns have their own tools. Organizations, deals, and messages are not tools yet. Every workspace defines its own custom fields. The JSON Schema a client caches does not include them — clients cache by server, not by tenant. That is why describe_schema exists, and why you call it first.
First connection is read-only. Write tools will not even appear until you reconnect with contacts:write.

Orient

whoami

No arguments, no scope. Which workspace this connection is bound to, who authorized it, oauth vs api_token, whether it reads company-wide, and the scopes you actually have.
Which Sailer workspace am I connected to?
A Sailer account can belong to several workspaces. The connection is bound to exactly one, chosen at consent. No tool argument can change it.

describe_schema

Scope: contacts:read. Optional resource ("contact"); omit it to describe everything this server can touch. Returns built-in and custom fields, which are required or read-only, select options, and which keys are filterable, sortable, or expandable.
What custom fields does this workspace have?
Use the key it returns inside custom_fields on create and update. Guessing from another workspace — or from these docs — is how you get an unknown-field error.

Read contacts

Ids look like con_8f3a…. That prefix is the resource; a deal id sent here is a 400, not a mysterious 404.

search_records

Scope: contacts:read. resource defaults to "contact". If has_more is true, keep paging. Do not count a page and call it the population — that is what count_records is for. Cursors are opaque, same contract as REST pagination.
Find contacts created in the last 7 days.

get_record

Scope: contacts:read. resource, id, optional expand.
Show me contact con_…
Missing, deleted, or outside what you can see: not found. Same next step in all three cases — search again.

count_records

Scope: contacts:read. Same filter / q as search, no paging.
How many contacts are in this workspace?

Write contacts

Scope on every tool here: contacts:write. Call describe_schema first. Unknown or read-only custom-field keys are rejected, not ignored. first_name and phone are required on create. Phone is unique per workspace and stored as E.164.

create_record

New row. Prefer upsert_contact if the person may already exist — a duplicate is not automatically reversible.
Create a contact named Ana with phone +5511999999999.

update_record

Destructive: it overwrites what you send. Omit a field to leave it; send null to clear it. Read first if you meant to append.
Set Ana’s email to ana@example.com.

upsert_contact

Match on phone, after canonicalization — (11) 98765-4321 and +55 11 98765-4321 are the same person. Default on_match is update; ignore returns the existing row untouched. The result tells you which branch ran (created vs matched_existing). The record alone does not.
Add or update the contact with this phone number.

Campaigns

list_campaigns

Scope: campaigns:read. Newest first. Optional status, limit 1–100 (default 25). Use this to get a campaign_id.
List this workspace’s campaigns.

get_campaign_analytics

Scope: campaigns:read. One campaign, one metric family. Optional window_start / window_end (UTC). Defaults to the last 30 days. These families are not interchangeable. big_numbers and daily_metrics use a strict “answered” (a qualifying inbound, classified as a reply). status_funnel uses the lifecycle status, which is usually a larger number. Rates on big_numbers are 0–100, not fractions. Every response carries _meta.caveats. Read them before comparing two numbers. A difference between two families is not a change over time.
Show me campaign performance. Read the caveats before you summarize.