Skip to main content
Salty exposes an MCP server at $SALTY_MCP/mcp. Set the env var to match your deployment:
export SALTY_MCP=https://mcp.trysalty.com
Any MCP-compliant client — Claude.ai web, ChatGPT, Cursor, Windsurf, Claude Desktop — can connect, authorize via OAuth, and call exactly 15 tools. Built on @modelcontextprotocol/sdk 1.x with WebStandardStreamableHTTPServerTransport — the Streamable HTTP transport from the 2025-06 MCP spec. Single endpoint (POST /mcp), stateless mode, plain JSON responses.

The 15 tools

Tight by design — more tools = bigger context window = worse agent performance.
#ToolWhat it does
1search_peopleFilter / paginate people
2get_personFetch a person by id
3create_personNew person
4update_personPatch a person
5search_companiesFilter / paginate companies
6get_companyFetch a company by id
7create_companyNew company
8update_companyPatch a company
9search_dealsFilter / paginate deals
10create_dealNew deal
11update_dealPatch a deal
12add_noteNote on a person/company/deal
13log_activityActivity on a person/company/deal
14get_schemaDescribe the workspace schema
15add_attributeExtend the schema with a new attribute
Notably absent: delete_* tools. Deletion is destructive — agents go through the REST API or CLI where a human can review. This is a deliberate safety choice. Every tool description ends with Returns errors with code and message; respect rate limit headers. Input schemas avoid oneOf/anyOf at the top level (breaks Claude’s MCP client).

How clients connect

You don’t configure anything per-client beyond pointing it at the MCP URL. The discovery + OAuth flow is automatic:
  1. The client POSTs /mcp with no auth → server returns 401 + WWW-Authenticate: Bearer resource_metadata=….
  2. The client GETs the well-known protected-resource metadata to discover the auth server (it’s the same host as $SALTY_API).
  3. The client GETs /.well-known/oauth-authorization-server to discover the token + registration endpoints.
  4. The client POSTs /oauth/register (RFC 7591 dynamic client registration) → gets a client_id.
  5. The client redirects the user’s browser to /oauth/authorize?…&code_challenge=… → user lands on Salty’s consent page → clicks Approve → browser returns to client with ?code=….
  6. The client POSTs /oauth/token with code + code_verifier → gets salty_oat_… access token (90-day lifetime) + refresh token.
  7. The client retries POST /mcp with Authorization: Bearer salty_oat_… → server forwards the bearer to the Salty API and returns the tool result.
See Concepts → Authentication for the full OAuth surface.

Adding Salty to Claude.ai (web)

Settings → Connectors → Add custom connector. URL: $SALTY_MCP/mcp. Click Connect; the OAuth flow above runs in a popup. Done.

Adding Salty to Claude Desktop / Cursor

These clients only speak stdio MCP, so we bridge through mcp-remote — a stdio↔HTTP shim that forwards every JSON-RPC call to $SALTY_MCP/mcp. Two ways to authenticate. Pick the one that matches your use case. The client runs the full PKCE dance on first connect. mcp-remote opens a browser, you approve at /oauth/consent, the salty_oat_… token is cached in ~/.mcp-auth. Subsequent launches are silent.
{
  "mcpServers": {
    "salty": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.trysalty.com/mcp"
      ]
    }
  }
}
Skip the browser flow by passing a long-lived sk_live_… key via --header. The MCP server forwards any bearer to the Salty API, which accepts all three kinds — sk_live_…, salty_oat_…, and Supabase JWTs (see Authentication).
{
  "mcpServers": {
    "salty": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.trysalty.com/mcp",
        "--header",
        "Authorization:Bearer ${SALTY_API_KEY}"
      ],
      "env": {
        "SALTY_API_KEY": "sk_live_..."
      }
    }
  }
}
Why pick this:
  • No ~/.mcp-auth cache to invalidate if you reset local DB or rotate OAuth client rows.
  • One value to swap (env.SALTY_API_KEY) — useful if a dev workspace gets reseeded.
  • Works in non-interactive contexts where there’s no browser to open.
Mint a key from the admin UI’s API keys page or POST /api-keys. Fully quit the client (⌘Q on macOS) after editing the config — most MCP clients only re-read on cold start.

Sample tool call

After the client is authorized, calls look like JSON-RPC 2.0:
TOKEN=salty_oat_…
curl -sX POST $SALTY_MCP/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "create_person",
      "arguments": { "email": "jane@acme.com", "first_name": "Jane" }
    }
  }'
Response (truncated):
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [{
      "type": "text",
      "text": "{\"id\":\"\",\"email\":\"jane@acme.com\",\"first_name\":\"Jane\",\"last_name\":null,…}"
    }]
  }
}

What MCP inherits from the REST API

Every tool forwards to the corresponding REST endpoint with the user’s bearer token, so MCP gets:
  • RLS — agents can only touch their own workspace
  • Rate limits — per-plan sustained + burst
  • Usage capsalty_oat_… tokens count as agent traffic
  • Idempotency — pass Idempotency-Key in the tool’s HTTP layer
  • Audit log — every call lands in api_call_log
No special MCP-only code paths to maintain. The 15 tools are a thin shim over the same surface that drives curl and the SDK.