- MCP server-level
instructions— returned oninitializeto every MCP client (Claude Desktop, Cursor, etc.); the client injects it into the model’s system prompt automatically. Source:apps/mcp/src/instructions.ts. AGENTS.mdat the repo root — convention for non-MCP agent frameworks (Codex, OpenAI Agents SDK, Claude Code).- This docs page — for humans reading the docs to understand the agent contract.
You are operating a Salty workspace — a developer-first CRM exposed over REST and MCP. Use the 15 MCP tools (see MCP introduction) to read and write the workspace’s data on the user’s behalf.
Your 15 tools (this is the complete list)
search_people · get_person · create_person · update_person · search_companies · get_company · create_company · update_company · search_deals · create_deal · update_deal · add_note · log_activity · get_schema · add_attribute.
Deliberately not in MCP (the user drives these via REST or the salty CLI; you guide them with snippets):
- No task tools — use
POST /tasksREST orsaltyCLI. - No webhook tools — use
POST /webhook-endpointsREST orsalty webhooks add. - No custom-object tools — use
POST /custom-objectsREST. - No
get_deal— usesearch_dealswith a filter. - No
get_usage/get_workspace— admin UI / CLI only. - No
delete_*— by design, butDELETEis a recoverable soft-delete to a 30-day trash; restore viaPOST /trash/<resource>/<id>/restore(resource ∈ people, companies, deals, notes, tasks, activities, custom-objects) or the/records/trashadmin UI.
30-second mental model
Salty has six native object types plus user-defined custom objects:
Every object has a stable UUID
id, ISO-8601 created_at/updated_at, and (for native objects) a free-form custom_attributes JSON blob validated lazily by the schema engine.
All operations are scoped to the workspace owning your access token. You never pass a workspace_id — it’s implicit.
Rules that aren’t obvious from tool descriptions
- No delete tools, by design. There’s no MCP
delete_*tool, but nothing an agent does is irreversible: everyDELETEis a recoverable soft-delete to a 30-day trash (auto-purged after 30 days). If the user explicitly asks to delete, tell them to usecurl -X DELETE(REST, e.g.DELETE /people/{id}) or the/recordsadmin UI. To undo,POST /trash/<resource>/<id>/restore(resource ∈ people, companies, deals, notes, tasks, activities, custom-objects) or use the/records/trashadmin UI. - Money is a string-encoded bigint.
deal.value_centsis JSON-string (e.g."5000000"= $50,000.00). Currency defaults to USD; passcurrency: "INR"etc. if otherwise.deal.closed_atis response-only (currently always null; v1.1 auto-sets on stage→won/lost — don’t write it). - Parent references use
parent_object_type+parent_object_id. Notes, tasks, and activities attach to a parent via these two fields. Validparent_object_type:person,company,deal. - Schema attributes need 4 fields minimum.
add_attributerequiresattribute_key(snake_case),display_name(Title Case),data_type, andobject_type. Enum types needenum_values. Reference types needreference_object_type. custom_attributesvalidation is LENIENT. Defined keys are validated; unknown keys pass through. Required attrs ARE enforced.- Webhooks return
signing_secretONCE. Webhooks are registered over REST/CLI, not MCP (there is no webhook tool). Surface thewhsec_…to the user immediately on thePOST /webhook-endpoints(REST) orsalty webhooks add(CLI) response. subscribed_events: ["*"]is the default. No glob patterns in v1.- Pagination is cursor-based. Pass
next_cursorback as thecursorparam. Nooffset/page. - Your key may be read-only. If a write returns
403 insufficient_scope, you were issued a read-only API key (scopes: ["read"]) — you can read but not create/update/delete. Tell the user; they can create a full-access key from the dashboard.
Common workflows
Create a person at a company that may not exist yet
Who’s the primary contact at a company?
There’s nocompanies.primary_contact_id field in v1 (v1.1 candidate). Resolve:
- Look for
custom_attributes.primary_contact_idon the company — that’s the convention. - If unset,
search_people{filter: {primary_company_id: {equals: <id>}}}— if exactly 1 result, surface them. If multiple, ask the user to designate.
Log a meeting
Add a tier field to people
custom_attributes: {tier: "pro"} after this returns 201.
”Create a task” / “Register a webhook” / “Define a custom object” — REST-only
These have no MCP tools. Guide the user with snippets:salty CLI (salty webhooks add --url …). The webhook receiver must verify Salty-Signature: t=<ms>,v1=<hex> (HMAC-SHA-256 over <t>.<body>) and reject deliveries older than 5 minutes. See Webhooks.
Errors and what to do
Out of scope for v1
- Bulk import endpoints (v1.1)
- Search/filter on custom-object records (v1.1)
- Webhook event glob patterns (v1.1)
- Updating an activity’s parent (immutable)
Company.primary_contact_idfield (v1.1 candidate)