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

# Protocol reference

> Technical reference for the Lawtte MCP server — endpoint, transport, OAuth 2.1 discovery and authorization, API-key auth, tool schemas, and error responses for any MCP client.

The protocol-level detail for connecting any MCP client to Lawtte. For what the
server is and who can use it, start at [the Lawtte MCP server](/mcp/overview); to
get it working in Claude without any of this, use
[connect Claude](/mcp/connect-claude).

## Endpoint

|                  |                                                                                                     |
| ---------------- | --------------------------------------------------------------------------------------------------- |
| **Server URL**   | `https://www.lawtte.ai/api/mcp`                                                                     |
| **Transport**    | Streamable HTTP (`GET` and `POST` on the same URL)                                                  |
| **Capabilities** | `tools` only — no resources, no prompts, no sampling                                                |
| **Auth**         | OAuth 2.1 (preferred) or a Lawtte API key                                                           |
| **CORS**         | Open, with `authorization`, `content-type`, `mcp-protocol-version` allowed; `OPTIONS` returns `204` |

<Note>
  Access is limited to **Lawtte Studio** accounts. A principal that resolves to any
  other kind of firm authenticates fine and still gets `401` — the server exposes
  nothing to it.
</Note>

## Discovery

The server implements OAuth 2.0 Protected Resource Metadata (RFC 9728). An
unauthenticated request returns `401` with the pointer in `WWW-Authenticate`:

```bash theme={null}
curl -i -X POST https://www.lawtte.ai/api/mcp \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize"}'
```

```http theme={null}
HTTP/2 401
www-authenticate: Bearer resource_metadata="https://www.lawtte.ai/.well-known/oauth-protected-resource/api/mcp"
```

That document names the authorization server:

```json theme={null}
{
  "resource": "https://www.lawtte.ai/api/mcp",
  "authorization_servers": ["https://<project>.supabase.co/auth/v1"],
  "bearer_methods_supported": ["header"]
}
```

<Warning>
  Follow the discovery chain rather than hard-coding the authorization server. It is
  an implementation detail and can move; the resource metadata URL will not.
</Warning>

## OAuth 2.1

The authorization server publishes standard metadata at
`/.well-known/oauth-authorization-server` and supports:

| Feature                     | Value                                                   |
| --------------------------- | ------------------------------------------------------- |
| Grant types                 | `authorization_code`, `refresh_token`                   |
| PKCE                        | `S256` (use it — `plain` is advertised but don't)       |
| Response types              | `code`                                                  |
| Scopes                      | `openid`, `profile`, `email`, `phone`, `offline_access` |
| Dynamic client registration | Supported (RFC 7591)                                    |

Because registration is dynamic, a client needs no pre-issued credentials. The flow:

<Steps>
  <Step title="Register">
    `POST` to the `registration_endpoint` from the authorization server metadata.
    You get a `client_id` back.
  </Step>

  <Step title="Authorize">
    Send the user to the `authorization_endpoint` with PKCE. Request
    `offline_access` if you want a refresh token.
  </Step>

  <Step title="The user picks a receptionist">
    Lawtte shows its own consent screen: the user signs in, chooses **one** Studio
    calling agent, and approves the call tools. That choice is stored as a grant
    against your `client_id` and their user — it's what the server later resolves a
    token to.
  </Step>

  <Step title="Exchange and call">
    Swap the code at the `token_endpoint` and send the access token as
    `Authorization: Bearer <token>` on every MCP request.
  </Step>
</Steps>

<Info>
  One grant is one receptionist. A token never spans two Studio accounts, and a user
  who wants to switch re-approves. Grants are revocable at any time from
  **Dashboard → Connect**, and revocation takes effect immediately.
</Info>

## API-key authentication

For clients that can't do OAuth — a local `mcp-remote` setup, a script, CI — the
server also accepts a Lawtte API key as a bearer token:

```http theme={null}
Authorization: Bearer lawtte_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Keys are created by the Studio account owner in **Dashboard → Connect**, shown once,
and stored only as a SHA-256 hash. A key is bound to one firm and carries the same
access as an OAuth grant for that firm.

<Warning>
  A key can place real phone calls. Keep it in a file or secret store, never in a URL
  or a command argument — arguments are visible to every process on the machine. See
  [Claude Desktop](/mcp/claude-desktop) for the header-file pattern.
</Warning>

## Tools

Three tools, described in plain language in [connector tools](/mcp/tools). The
schemas, for a client that needs them:

<CodeGroup>
  ```json place_call theme={null}
  {
    "name": { "type": "string", "minLength": 1, "maxLength": 120 },
    "phone": { "type": "string", "minLength": 7, "maxLength": 32 },
    "objective": { "type": "string", "minLength": 1, "maxLength": 1000 },
    "context": { "type": "string", "maxLength": 4000 },
    "dry_run": { "type": "boolean" }
  }
  ```

  ```json get_call_result theme={null}
  {
    "call_id": { "type": "string", "format": "uuid" }
  }
  ```

  ```json list_calls theme={null}
  {
    "since": { "type": "string", "format": "date-time" },
    "status": {
      "enum": ["blocked", "dialing", "in_progress", "completed", "failed"]
    }
  }
  ```
</CodeGroup>

`place_call` carries `destructiveHint: true`; the other two carry
`readOnlyHint: true`. A client that surfaces those hints should require confirmation
before `place_call` — it dials a real person.

Results come back as a text content block holding JSON. Trailing whitespace and key
order aren't guaranteed; parse it, don't pattern-match it.

## Errors

| Status | Meaning                                                                            | What a client should do                                        |
| ------ | ---------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `401`  | No token, an invalid token, a revoked grant, or a firm that isn't a Studio account | Re-run discovery and re-authorize. Don't retry the same token. |
| `503`  | The credential store is unreachable; the server fails closed rather than dialing   | Retry with backoff. No call was placed.                        |

Business-rule refusals are **not** HTTP errors. A call blocked by the Do Not Call
list, the daily cap, the two-hour repeat rule, or calling hours returns a normal
tool result with the reason in it — surface that text to the user rather than
treating it as a failure. See [limits and safety](/mcp/limits-and-safety).

## Connecting a client

<AccordionGroup>
  <Accordion title="MCP Inspector" icon="magnifying-glass">
    Point it at `https://www.lawtte.ai/api/mcp` with an `Authorization: Bearer <key>`
    header, or let it run the OAuth flow. Good for confirming the tool list and
    running a `dry_run` before you write any code.
  </Accordion>

  <Accordion title="mcp-remote (stdio clients)" icon="terminal">
    For clients that only speak stdio:

    ```bash theme={null}
    npx mcp-remote https://www.lawtte.ai/api/mcp \
      --header-file /absolute/path/to/header-file \
      --transport http-only
    ```

    The header file holds one line: `Authorization: Bearer lawtte_sk_…`
  </Accordion>

  <Accordion title="Your own client" icon="code">
    Any MCP SDK works. Connect over streamable HTTP to the server URL, attach the
    bearer token, and call `tools/list` to confirm you see `place_call`,
    `get_call_result`, and `list_calls`. If the list is empty, the principal resolved
    to something that isn't a configured Studio account.
  </Accordion>
</AccordionGroup>

## Before you go to production

<Check>
  Run `place_call` with `dry_run: true` first. It performs every check — number
  validation, Do Not Call, quota, calling hours — and dials nothing, so you can prove
  your integration end to end without calling anyone.
</Check>

Then read [limits and safety](/mcp/limits-and-safety). The caps aren't advisory:
they're enforced server-side before dialing and they fail closed.


## Related topics

- [Claude Desktop (macOS)](/mcp/claude-desktop.md)
- [The Lawtte MCP server](/mcp/overview.md)
- [Connector tools](/mcp/tools.md)
- [API overview](/api-reference/introduction.md)
- [Limits and safety](/mcp/limits-and-safety.md)
