# Prelink MCP — Overview

Prelink turns a Google Maps listing or business website into a mobile customer action page: services, reviews, hours, tap-to-call, messaging, directions, and an enquiry form.

## Who this is for

AI agents (Cursor, Claude Desktop, custom MCP clients) acting on behalf of a Prelink account owner — typically agencies or operators managing local-business pages.

## Capabilities

- Create draft pages from Maps / website URLs
- List and inspect owned sites
- Publish / unpublish (plan limits apply)
- Read and update the enquiry inbox
- Check plan usage and entitlements

## First steps

1. Confirm auth: call `whoami` or read `prelink://docs/auth`
2. Prefer reading `prelink://docs/workflows` before multi-step tasks
3. Never invent `siteId` / `enquiryId` / `jobId` — only use values returned by tools

## Response format

Successful tools return JSON text with `"ok": true` (or success fields).
Failures return JSON with `"ok": false` and `"error": "…"` and `isError: true`.

Dates are ISO-8601 strings.

---

# Prelink MCP — Authentication

## API keys

- Create keys in the Prelink app: **API & MCP** (`/app/settings`)
- Format: `plk_…` (shown once at creation)
- Max **5** active keys per account
- **Scopes** (least privilege):
  - `read` — whoami, docs, list/get sites & enquiries, usage, job status
  - `write` — publish, unpublish, update enquiry status
  - `generate` — generate_site (write/generate also imply read)
- **Expiry** — default 90 days; options: never / 30d / 90d / 1y. Expired keys are rejected and soft-revoked.
- Revoke anytime; revoked keys fail auth immediately
- Send as: `Authorization: Bearer plk_…`
- Secrets are stored as SHA-256 hashes only

## Client config (Cursor / Claude)

```json
{
  "mcpServers": {
    "prelink": {
      "url": "https://YOUR_HOST/api/mcp",
      "headers": {
        "Authorization": "Bearer plk_YOUR_KEY"
      }
    }
  }
}
```

Replace `YOUR_HOST` with the Prelink origin (e.g. `prelink.co` or `localhost:3000`).

## Verify

Call `whoami`. You should get account email, plan, and the MCP endpoint.

## Security rules for agents

- Do not print full API keys in logs or user-visible chat unless the user just created one and asked to copy it
- Do not store keys in repo files
- Prefer read-only keys when the task does not need publish/generate
- If auth fails, tell the user to create/revoke keys at `/app/settings`
- If a tool fails with a missing scope, ask the user to mint a key with that scope

---

# Prelink MCP — Tools

## Account

### `whoami`
Verify the API key. Returns account, plan, and MCP URLs.
- **Annotations:** read-only
- **Use when:** connecting for the first time or debugging auth

### `get_usage`
Plan name, published-site count/limit, enquiry allowance, entitlement flags.
- **Annotations:** read-only
- **Use when:** before publish if you might hit plan limits

### `get_docs`
Return a documentation section (`overview` | `auth` | `tools` | `workflows` | `errors` | `limits` | `full`).
- **Annotations:** read-only
- **Use when:** resources are unavailable or you need a refresher mid-task

## Sites

### `list_sites`
List owned sites: id, name, slug, status, public URL, new enquiry count.
- **Annotations:** read-only

### `get_site`
Full site: sections, actions, sources, profile fields, `needsReviewSections`, `editorUrl`.
- **Input:** `siteId`
- **Annotations:** read-only
- **Use when:** after generate, before publish, or when editing guidance is needed

### `generate_site`
Create a **draft** from a Google Maps listing URL or business website URL. Site is owned by the authenticated user immediately (no claim token).
- **Input:** `url` (required), `wait` (optional boolean, default `true`)
- **Behavior:**
  - `wait=true` (default): block until COMPLETED / PARTIAL / FAILED (often 30–120s)
  - `wait=false`: return `jobId` immediately; poll `get_generation_job`
- **Annotations:** open-world (fetches external URLs)
- **Prefer:** Maps listing URLs for local businesses

### `get_generation_job`
Poll job state: PENDING → … → COMPLETED | PARTIAL | FAILED.
- **Input:** `jobId`
- **Annotations:** read-only

### `publish_site`
Publish a draft. Fails on reserved/taken slug or plan published-site limit.
- **Input:** `siteId`
- **Returns:** public `url`
- **Annotations:** idempotent

### `unpublish_site`
Revert to draft; public URL stops working.
- **Input:** `siteId`
- **Annotations:** destructive, idempotent

## Enquiries (inbox)

### `list_enquiries`
List submissions for a site (plan history + monthly visibility rules apply).
- **Input:** `siteId`, optional `status` (NEW | READ | REPLIED | ARCHIVED), optional `limit` (1–100, default 25)
- **Notes:** internal notes only included on Starter+

### `get_enquiry`
One enquiry by id.
- **Input:** `siteId`, `enquiryId`

### `update_enquiry_status`
Set status to NEW | READ | REPLIED | ARCHIVED.
- **Requires:** Starter+ (`enquiryStatusesNotesEnabled`)
- **Input:** `siteId`, `enquiryId`, `status`

## Prompts

### `create_and_publish`
Guided multi-step: generate → review → publish for a given URL.

---

# Prelink MCP — Workflows

## A. Create and publish a page

1. `whoami` — confirm auth + note plan
2. `get_usage` — check `publishedSites` vs `siteLimit`
3. `generate_site` with Maps or website `url` (`wait=true` unless the client may time out)
4. If failed → read `error`, fix URL, retry once
5. `get_site` — summarize name/category; mention `needsReviewSections`
6. `publish_site` — return the public `url` to the user
7. If publish fails on limit → explain plan limit; leave as draft and share `editorUrl`

## B. Async generation (slow clients / timeouts)

1. `generate_site` with `wait=false`
2. Poll `get_generation_job` every few seconds until COMPLETED, PARTIAL, or FAILED
3. Continue from step 5 in workflow A

## C. Triage enquiries for a business

1. `list_sites` — find the site (or ask user for name/slug)
2. `list_enquiries` with `status=NEW`
3. `get_enquiry` for details when needed
4. `update_enquiry_status` → READ or REPLIED after the user handles it
5. If status update is denied → tell user Starter+ is required

## D. Audit an existing account

1. `whoami` + `get_usage`
2. `list_sites`
3. For each published site of interest: `get_site`, `list_enquiries` (limit 10)

## Do / Don't

**Do**
- Use real IDs from tool results
- Prefer Maps URLs for salons, cafés, trades, clinics
- Tell the user the public URL after publish

**Don't**
- Invent profile copy beyond what `get_site` returns unless the user asks to draft edits (edits via MCP are limited — use the web editor for deep content changes)
- Publish without checking usage if the account may be on Free/Starter (1 published site)
- Retry generate in a tight loop on rate-limit errors — honor `retryAfterSec`

---

# Prelink MCP — Errors

All tool failures return JSON like:

```json
{ "ok": false, "error": "Human-readable message" }
```

## Auth

| Symptom | Cause | Agent action |
|--------|--------|--------------|
| 401 / unauthorized | Missing/invalid/revoked key | Ask user to create a key at /app/settings |
| Scope errors | Client misconfigured | Ensure Bearer token is sent |

## Generation

| Error pattern | Action |
|---------------|--------|
| Invalid / unsupported URL | Ask for a Maps listing or http(s) website |
| Content policy / not allowed | Do not retry with bypass tricks; ask for another URL |
| Rate limit + `retryAfterSec` | Wait that many seconds, then retry once |
| FAILED job | Surface `error`; suggest a cleaner Maps URL |

## Publish

| Error pattern | Action |
|---------------|--------|
| Plan allows N published site(s) | Show `get_usage`; offer to unpublish another or keep draft |
| Slug reserved / taken | Tell user to change slug in the web editor, then retry publish |

## Enquiries

| Error pattern | Action |
|---------------|--------|
| Enquiry not found | ID wrong or outside plan history/allowance window |
| Starter+ required | Explain upgrade; don't loop on update_enquiry_status |

## General recovery

1. Re-read the `error` string — it is meant for users
2. Call `get_docs` with `errors` or `workflows` if stuck
3. Prefer one retry with a corrected input over spamming tools

---

# Prelink MCP — Limits & plans

## API / MCP rate limits

- `generate_site`: about **10 requests / minute / user** (also capped per API key) — in-memory blunt force
- `generate_site`: hard cap **50 generations / user / UTC day** — durable (counts GenerationJob rows)
- Other tools: use reasonable pacing; avoid parallel generate storms
- Missing scope → error with `requiredScope`; do not retry with the same key

## Plan entitlements (high level)

Exact numbers come from `get_usage` — always prefer live data over this summary.

| Capability | Free | Starter | Business | Agency |
|------------|------|---------|----------|--------|
| Published sites | 1 | 1 | 3 | 25 |
| Enquiry status/notes | — | ✓ | ✓ | ✓ |
| Enquiry history | short | longer | full | full |
| Custom domain | — | ✓ | ✓ | ✓ |

Beta / grace periods may temporarily grant Business-level access — trust `get_usage`.

## Generation notes

- Generate itself is not plan-gated, but **publish** is
- Generated sites start as **DRAFT**
- Partial generation may still produce a usable draft — check `state` and `get_site`

## Key limits

- Max **5** active API keys per user
- Default key expiry: **90 days** (configurable at creation)
- Prefer scoped keys (e.g. `read` only for inbox triage agents)
