# Beacon MCP

This document covers the **currently implemented** Beacon MCP server. It is a companion to [`api.md`](./api.md) — the HTTP API and the MCP server share the same auth model, tenancy boundary, and underlying actions.

For the machine-readable HTTP contract, use `GET /openapi.yaml` on your Beacon install.

## Endpoint

```
POST /api/mcp
```

The MCP server is mounted at `/api/mcp` and uses streamable HTTP transport.

## Authentication

MCP uses the same tenant API key authentication as the HTTP API. Any of these will work:

- `X-API-Key: beacon_...` (preferred)
- `Authorization: Bearer beacon_...`

Test mode is enabled by appending `_test` to a valid API key. In test mode the server bypasses license validation and simulates Twilio delivery.

See [`api.md`](./api.md#authentication) for details.

## Connecting a client

### Claude Code

```bash
claude mcp add beacon http://localhost:8000/api/mcp \
  --transport http \
  --scope user \
  --header "X-API-Key: beacon_YOUR_KEY_HERE_test"
```

### Other MCP clients

Point any streamable-HTTP MCP client at `https://<your-host>/api/mcp` and supply the API key via `X-API-Key` or `Authorization: Bearer`.

---

## Tools

Tools are tenant-scoped to the team that owns the API key. They cover messaging, contacts, threads, business numbers, OTP, and a layer of agent-native helpers that bundle multiple primitives into single calls.

### Messaging

| Tool         | Description                                     | HTTP equivalent      |
|--------------|-------------------------------------------------|----------------------|
| `send_text`  | Send an SMS, creating or appending to a thread. | `POST /text`         |
| `send_otp`   | Generate and send an OTP.                       | `POST /otp/generate` |
| `verify_otp` | Verify a submitted OTP code.                    | `POST /otp/verify`   |

### Contacts

| Tool                          | Description                                    | HTTP equivalent                                 |
|-------------------------------|------------------------------------------------|-------------------------------------------------|
| `list_contacts`               | Search, filter, and paginate contacts.         | `GET /contacts`                                 |
| `get_contact`                 | Fetch a contact by ID.                         | `GET /contacts/{id}`                            |
| `find_contact_by_phone`       | Look up a contact by phone number.             | `GET /contacts/by-phone/{phone}`                |
| `create_contact`              | Create a contact.                              | `POST /contacts`                                |
| `update_contact`              | Update a contact.                              | `PATCH /contacts/{id}`                          |
| `upsert_contact`              | Create-or-update by ID, external ID, or phone. | `POST /contacts/upsert`                         |
| `add_contact_phone_number`    | Attach a phone to a contact.                   | `POST /contacts/{id}/phone-numbers`             |
| `update_contact_phone_number` | Update a contact phone.                        | `PATCH /contacts/{id}/phone-numbers/{phoneId}`  |
| `remove_contact_phone_number` | Remove a contact phone.                        | `DELETE /contacts/{id}/phone-numbers/{phoneId}` |
| `find_duplicate_contacts`     | List duplicate-contact candidates.             | `GET /contacts/duplicates`                      |
| `merge_contacts`              | Merge two contacts.                            | `POST /contacts/merge`                          |

### Threads

| Tool                  | Description                                      | HTTP equivalent                       |
|-----------------------|--------------------------------------------------|---------------------------------------|
| `list_threads`        | List threads sorted by last activity.            | `GET /threads`                        |
| `get_thread`          | Fetch a thread summary.                          | `GET /threads/{id}`                   |
| `get_thread_messages` | Paginate messages within a thread.               | `GET /threads/{id}/messages`          |
| `check_replies`       | Poll for new inbound messages since a timestamp. | `GET /threads/{id}/replies?since=...` |

### Business Numbers

| Tool                           | Description                          | HTTP equivalent                            |
|--------------------------------|--------------------------------------|--------------------------------------------|
| `list_business_numbers`        | List the team's business numbers.    | `GET /business-numbers`                    |
| `get_business_number`          | Fetch a business number.             | `GET /business-numbers/{id}`               |
| `create_business_number`       | Register a business number.          | `POST /business-numbers`                   |
| `update_business_number`       | Update a business number.            | `PATCH /business-numbers/{id}`             |
| `make_primary_business_number` | Mark a number as the team's primary. | `POST /business-numbers/{id}/make-primary` |
| `delete_business_number`       | Archive a business number.           | `DELETE /business-numbers/{id}`            |

### Agent-native helpers

These tools wrap multiple primitives into a single call, so an agent does not have to chain three or four lookups. They are the most useful tools for LLM workflows.

| Tool                        | Description                                                                                                                                                                     |
|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `search_people_and_threads` | One query across contacts and threads. Use when the agent does not know where to start.                                                                                         |
| `get_contact_context`       | Returns a contact plus its phones, recent threads, and recent messages.                                                                                                         |
| `resolve_phone_to_context`  | Given a phone, returns the matched contact, thread, opt-out status, and recent activity.                                                                                        |
| `summarize_thread`          | Computed summary of a thread with the latest inbound and outbound context.                                                                                                      |
| `prepare_outreach_message`  | Composes a send-safe draft from a fixed template plus contact or thread history (not model-generated). Review before sending. Does not send.                                                                                                   |
| `send_followup_to_contact`  | Resolves the right thread or phone for a contact and sends a follow-up. Instruction-based drafts are templated from the instruction text (not model-generated) — review the reported draft. Honors explicit sender or phone context to avoid cross-thread leakage. |

---

## Resources

Resources are stable, read-mostly views an agent can pull repeatedly without re-querying tools.

### Entity resources

Tenant-scoped snapshots of live records, resolved from the API key just like the tools.

| URI                      | Description                                                                       |
|--------------------------|-----------------------------------------------------------------------------------|
| `contact://{id}`         | Canonical contact snapshot.                                                       |
| `thread://{id}`          | Canonical thread snapshot.                                                        |
| `thread://{id}/messages` | Recent transcript (latest 50). Pass `?full_history=true` for the full transcript. |
| `business-number://{id}` | Business number metadata.                                                         |
| `team://current`         | The current tenant team and its business numbers.                                 |

Read any resource by its URI:

```json
{ "method": "resources/read", "params": { "uri": "thread://01J9.../messages?full_history=true" } }
```

### Schema resources

Machine-readable JSON Schemas for Beacon's core entities, sourced from the OpenAPI spec at `GET /openapi.yaml`. They are **public reference data — not tenant-scoped** — so an agent can introspect Beacon's shapes before it makes a single tool call. Served as `application/json` and enumerated in `resources/list`.

| URI                               | Description                                                                                       |
|-----------------------------------|---------------------------------------------------------------------------------------------------|
| `beacon://schema/message`         | The message envelope, the direction enum, and the `send_text` request/response shapes.            |
| `beacon://schema/thread`          | The thread summary entity, the cursor-paginated thread list response, and the pagination cursor.  |
| `beacon://schema/contact`         | Contact detail (with phones and notes), the contact summary (with tags), and the phone/note shapes. |
| `beacon://schema/business-number` | Business number detail and summary, plus the number type and status enums.                        |
| `beacon://schema/error`           | The error envelope, the license error envelope, and the catalog of error codes with messages.     |

Example invocation:

```json
{ "method": "resources/read", "params": { "uri": "beacon://schema/message" } }
```

### Guide resources

Agent playbooks written in Markdown (`text/markdown`). Each is a short how-to an agent should read before attempting the matching workflow. Like the schema resources, these are public reference data — not tenant-scoped.

| URI                                      | Description                                                                          |
|------------------------------------------|--------------------------------------------------------------------------------------|
| `beacon://guides/sending-first-message`  | Start a new outbound conversation with `send_text`; resolve the phone first to avoid duplicate threads. |
| `beacon://guides/handling-replies`       | Poll inbound replies with `check_replies` and reply on an existing thread.           |
| `beacon://guides/managing-contacts`      | Find, create, upsert, deduplicate, and merge contacts.                               |
| `beacon://guides/opt-out-and-compliance` | Opt-out keywords, what an opt-out blocks, and checking status before outreach.       |
| `beacon://guides/otp-flow`               | Generate and verify one-time passwords with `send_otp` / `verify_otp`.               |
| `beacon://guides/error-handling`         | Interpret tool errors and pick the right recovery action.                            |

Example invocation:

```json
{ "method": "resources/read", "params": { "uri": "beacon://guides/sending-first-message" } }
```

---

## Prompts

Prompts encode reusable agent workflows. They are not CRUD wrappers — they are operator playbooks the agent can replay.

| Prompt                     | Description                                                                                                   |
|----------------------------|---------------------------------------------------------------------------------------------------------------|
| `draft_follow_up`          | Given a contact or thread, draft a follow-up message.                                                         |
| `summarize_conversation`   | Summarize a thread for handoff or CRM notes.                                                                  |
| `merge_contact_decision`   | Review two contacts and recommend a merge strategy.                                                          |
| `reply_triage`             | Classify the latest inbound reply, recommend urgency, and suggest next handling steps.                       |
| `thread_reply_suggestions` | Draft 2–3 distinct, tone-tagged, ready-to-send reply options for the latest inbound message on a thread.     |
| `support_handoff_summary`  | Build a structured handoff packet (situation, customer, history, open questions, next action) for a thread.  |
| `contact_enrichment_notes` | Propose notes, tags, and field updates for a contact from its recent conversation history.                   |
| `outreach_plan`            | Turn an audience and goal into a structured outreach plan with sequencing and ready-to-adapt message variants. |
| `otp_assistant`            | Walk an agent through the `send_otp` / `verify_otp` flow, including resend and lockout edge cases.           |

### Invoking prompts

Prompts are fetched with `prompts/get`, passing arguments by name. Required arguments must be supplied; optional ones can be omitted to take the default.

`thread_reply_suggestions` — `threadId` (required, UUID), `tone` (optional):

```json
{ "method": "prompts/get", "params": { "name": "thread_reply_suggestions", "arguments": { "threadId": "01J9...", "tone": "warm" } } }
```

`support_handoff_summary` — `threadId` (required, UUID), `audience` (optional):

```json
{ "method": "prompts/get", "params": { "name": "support_handoff_summary", "arguments": { "threadId": "01J9...", "audience": "the billing team" } } }
```

`contact_enrichment_notes` — `contactId` (required, UUID), `window` (optional, 1–10 recent threads, default 3):

```json
{ "method": "prompts/get", "params": { "name": "contact_enrichment_notes", "arguments": { "contactId": "01J9...", "window": 5 } } }
```

`outreach_plan` — `audience` (required), `goal` (required), `count` (optional, 1–5 variants, default 3), `tone` (optional):

```json
{ "method": "prompts/get", "params": { "name": "outreach_plan", "arguments": { "audience": "stalled trial users", "goal": "book a demo", "count": 3, "tone": "direct" } } }
```

`otp_assistant` — `phase` (optional: `generate`, `verify`, or `troubleshoot`; omit to cover the full flow):

```json
{ "method": "prompts/get", "params": { "name": "otp_assistant", "arguments": { "phase": "verify" } } }
```

---

## Tenancy and safety

- Every tool resolves the tenant from the API key. There is no `team_id` parameter on any tool.
- Sends are blocked for opted-out contacts (STOP / STOPALL / UNSUBSCRIBE / CANCEL / END / QUIT).
- Sends are subject to the same token-bucket rate limit as the HTTP API.
- Idempotency keys supplied to write tools follow the same 24h dedupe window as the HTTP API.

## Not yet implemented

These are intentionally still out of scope:

- Quota- and billing-aware MCP capabilities (SaaS tier)
