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

# Making the MCP discoverable to agents

> How Soundcheck's MCP server is published and positioned so AI agents find and choose it for live-event tasks.

This is the operator playbook for **agent discoverability** — making AI agents find the Soundcheck MCP and pick it for "staff / run a live event" tasks. It complements the end-user setup in [the MCP server doc](./mcp-server).

The goal: be the registry-verified, well-described **front door** for any agent asked how to staff or run a live event. The wedge is that the MCP ecosystem has events *ticketing-discovery* wrappers but **no crew/staffing server** — Soundcheck owns staffing.

<Info>
  Phase 0 (this doc) makes the **existing OAuth server** maximally discoverable and well-described, and publishes listing/docs assets. It adds **no new tools**. The anonymous public-read tier (Phase 1) and public gig search (Phase 2) are a separate, planned effort — see [Roadmap](#roadmap).
</Info>

## What the server advertises (in code)

Set in `mcp/internal/mcpserver/server.go` and `mcp/cmd/server/main.go`:

* **`Implementation.Title`** = "Soundcheck — Live Event Staffing", **`WebsiteURL`** (from `MCP_WEBSITE_URL`, default `https://soundchecklive.io`), and **`Version`**.
* **Server `Instructions`** — a one-paragraph "what Soundcheck is + when to use it + how to treat consequential tools." This is a primary signal the model uses to route a task here, so keep it crisp and current.
* **Protected Resource Metadata** (`/.well-known/oauth-protected-resource`, RFC 9728): `resource`, `authorization_servers` (Clerk), `bearer_methods_supported`, `resource_name`, and — when the env vars are set — `resource_documentation` (`MCP_DOCS_URL`) and `resource_policy_uri` (`MCP_POLICY_URL`).

Tool **names and descriptions** are the other selection signal: they are namespaced and action-first, and the consequential ones state "preview/confirm first."

## 1. Publish to the official MCP Registry

The [official MCP Registry](https://registry.modelcontextprotocol.io) (launched Sept 2025) is the canonical index that downstream catalogs federate from. It hosts **metadata only**, and is still **preview** (no durability guarantees) — list there, but don't depend on it as the sole channel.

* The listing lives in `mcp/server.json` in the repo (schema `2025-12-11`). It uses a `remotes` entry (`streamable-http`) — not `packages` — because this is a hosted server.
* **Before publishing:**
  * Verify the reverse-DNS namespace `io.soundchecklive/soundcheck` (DNS TXT on `soundchecklive.io`, or the `soundchecklive` GitHub org).
  * Confirm `remotes[0].url` is the **production** endpoint (`https://mcp.soundchecklive.io/mcp` — adjust if the deployed prod hostname differs; dev is `mcp.dev.soundchecklive.io`).
  * Keep `version` in lockstep with `mcpserver.Version`.
* Publish with the `mcp-publisher` CLI (`mcp-publisher login` → `mcp-publisher publish`).

## 2. Submit to client connector directories

* **Anthropic Connectors Directory** (manual review, \~2 weeks). Requirements: a production remote server, **per-tool annotations** (already set — read tools `readOnlyHint`, writes `destructiveHint`/`idempotentHint`), a **privacy policy URL** (set `MCP_POLICY_URL`), **≥3 usage examples**, and a logo/favicon. See Anthropic's connector submission docs.
* **ChatGPT / Cursor** discover by HTTPS URL (developer mode / Settings → MCP). No submission needed; the registry + docs cover them.
* Mirror to **PulseMCP**, **Smithery**, **mcp.so**, and the **GitHub MCP Registry** (most federate from the official registry once listed).

## 3. `llms.txt`

`web/public/llms.txt` in the repo is served at the site root (`/llms.txt`). It gives LLM traffic a curated, link-rich summary of Soundcheck and the MCP. Treat it as one signal (registry presence + tool quality matter more), and keep its links pointing at real docs pages.

## 4. `.well-known/mcp` (later, optional)

A `.well-known/mcp` "advertise from your domain" convention exists only as competing, unmerged proposals (SEP-1649, SEP-1960) as of early 2026 — **not standardized**. We already serve the required `/.well-known/oauth-protected-resource`. Revisit `.well-known/mcp` once the spec settles; don't depend on it.

## Roadmap

A **tiered** MCP is the target shape:

| Tier                 | Auth         | Purpose                                                                                                                                                                                   |
| -------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 — Public discovery | none         | the agent front door: `search_public_gigs`, `get_public_org`, `list_positions`, intake (`request_booking`/`request_sponsorship`), and authority **prompts** (`how_to_staff_a_live_event`) |
| 2 — Member           | OAuth        | personal reads + quick actions (today's `my_agenda`, availability, accept/decline)                                                                                                        |
| 3 — Coordinator      | OAuth + role | run gigs (today's create/staff/call-list/messaging/import)                                                                                                                                |

* **Phase 1 — DONE.** Tier 1 ships as a **separate, unauthenticated public MCP endpoint at `/public/mcp`** (a distinct `mcp.Server` instance registered with only public tools, mounted with no auth middleware — isolated by construction; it calls public API routes with no bearer token, so it cannot leak member data). Tools: `get_public_org`, `list_positions`, `request_booking` (→ `POST /lead-requests`), `request_sponsorship` (→ `POST /sponsor-requests`). Prompts: `how_to_staff_a_live_event`, `plan_event_crew`. e2e tests assert it is reachable without auth, exposes no member tools, and forwards no `Authorization` header.
  * **Per-source rate limiting — DONE (app-level).** `/public/mcp` is wrapped by a per-client-IP token-bucket limiter in the MCP server (`mcp/internal/ratelimit`), configured via `PUBLIC_RATELIMIT_RPS` / `PUBLIC_RATELIMIT_BURST` / `PUBLIC_RATELIMIT_TRUSTED_PROXY_COUNT` (the Azure App Service module sets the trusted-proxy count to 1 so the limiter keys on the real client IP — the proxy-written rightmost `X-Forwarded-For` hop — not the App Service front end, and a forged `X-Forwarded-For`/`X-Real-IP` can't rotate the key; missing/short XFF fails closed to `RemoteAddr`). Idle buckets are TTL-evicted so the map can't grow unbounded. The downstream API's per-IP limiter can't help here at all — all `/public/mcp` traffic reaches it from the MCP's single egress IP — which is why the limit lives in front of `/public/mcp`.
    * **Keying is per-source IP only** (not per-tool). All four public tools share one bucket per IP, because MCP calls are all `POST /public/mcp` with the tool name in the JSON-RPC body, not the URL path. Stricter limits for the intake tools (`request_booking`/`request_sponsorship` — anonymous, no captcha/dedup, email unverified, risk ≈ the existing public web forms) are an **open follow-up**: enforce in those tool handlers, by parsing the JSON-RPC method in the middleware, or at the edge.
    * **Single-instance assumption.** The bucket map is process-local, so the effective limit is `RPS × instance_count`. It is exact at one instance (dev today: `default_capacity=1`, autoscale off). Before enabling autoscale or running MCP multi-instance, either pin it to a single instance, lower `RPS`/`BURST` to tolerate the fan-out, or move enforcement to the edge (below). **Tune `RPS`/`BURST` per environment before a wide launch.**
  * **Durable edge option (follow-up):** route `mcp.soundchecklive.io` through the GCP ALB (new serverless/FQDN NEG + url\_map host rule + its own Cloud Armor policy), adding an OWASP WAF + edge rate-limiting in front of the app-level limiter. If you do this, bump `PUBLIC_RATELIMIT_TRUSTED_PROXY_COUNT` to 2 (ALB + App Service front end).
  * The Registry `remotes` entry and `llms.txt` now point at `/public/mcp` (the anonymous front door) so agents reach it without an OAuth round-trip; the member `/mcp` is for signed-in users.
* **Phase 2** adds public gig/event search. This is blocked on an **API change**: `GET /storefront/events` currently returns full rosters/assignments/invitations to anonymous callers — it needs a public-safe event projection and an `isPublic`/org-consent flag before any agent exposure.
