> ## Documentation Index
> Fetch the complete documentation index at: https://trysalty.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP server

> OAuth 2.1 + PKCE. 15 curated tools. Streamable HTTP transport per 2025-06 MCP spec.

[Salty](https://trysalty.com) — the CRM built for AI agents ([trysalty.com](https://trysalty.com)) — exposes an MCP server at `$SALTY_MCP/mcp`. Set the env var to match your deployment:

```bash theme={null}
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**.

<Tip>
  Using **Claude Code**? Skip the manual setup — install the [Salty plugin](/docs/mcp/claude-code-plugin): `/plugin marketplace add ankshvayt/salty-plugin` then `/plugin install salty@salty`.
</Tip>

Built on `@modelcontextprotocol/sdk` 1.x with `WebStandardStreamableHTTPServerTransport` — the [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports) 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.

| #  | Tool               | What it does                           |
| -- | ------------------ | -------------------------------------- |
| 1  | `search_people`    | Filter / paginate people               |
| 2  | `get_person`       | Fetch a person by id                   |
| 3  | `create_person`    | New person                             |
| 4  | `update_person`    | Patch a person                         |
| 5  | `search_companies` | Filter / paginate companies            |
| 6  | `get_company`      | Fetch a company by id                  |
| 7  | `create_company`   | New company                            |
| 8  | `update_company`   | Patch a company                        |
| 9  | `search_deals`     | Filter / paginate deals                |
| 10 | `create_deal`      | New deal                               |
| 11 | `update_deal`      | Patch a deal                           |
| 12 | `add_note`         | Note on a person/company/deal          |
| 13 | `log_activity`     | Activity on a person/company/deal      |
| 14 | `get_schema`       | Describe the workspace schema          |
| 15 | `add_attribute`    | Extend 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 (24-hour lifetime) + a rotating refresh token (180-day).
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](/docs/concepts/authentication#3-oauth-21-pkce-mcp-clients) 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`](https://www.npmjs.com/package/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.

### Option A — OAuth (recommended for end users)

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.

```json theme={null}
{
  "mcpServers": {
    "salty": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.trysalty.com/mcp"
      ]
    }
  }
}
```

### Option B — Static API key (recommended for dev / scripted setups)

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](/docs/concepts/authentication)).

```json theme={null}
{
  "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:

```bash theme={null}
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):

```json theme={null}
{
  "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 cap** — `salty_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.
