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.
What the server advertises (in code)
Set inmcp/internal/mcpserver/server.go and mcp/cmd/server/main.go:
Implementation.Title= “Soundcheck — Live Event Staffing”,WebsiteURL(fromMCP_WEBSITE_URL, defaulthttps://soundchecklive.io), andVersion.- 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) andresource_policy_uri(MCP_POLICY_URL).
1. Publish to the official MCP Registry
The official MCP Registry (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.jsonin the repo (schema2025-12-11). It uses aremotesentry (streamable-http) — notpackages— because this is a hosted server. - Before publishing:
- Verify the reverse-DNS namespace
io.soundchecklive/soundcheck(DNS TXT onsoundchecklive.io, or thesoundcheckliveGitHub org). - Confirm
remotes[0].urlis the production endpoint (https://mcp.soundchecklive.io/mcp— adjust if the deployed prod hostname differs; dev ismcp.dev.soundchecklive.io). - Keep
versionin lockstep withmcpserver.Version.
- Verify the reverse-DNS namespace
- Publish with the
mcp-publisherCLI (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, writesdestructiveHint/idempotentHint), a privacy policy URL (setMCP_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:- Phase 1 — DONE. Tier 1 ships as a separate, unauthenticated public MCP endpoint at
/public/mcp(a distinctmcp.Serverinstance 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 noAuthorizationheader.- Per-source rate limiting — DONE (app-level).
/public/mcpis wrapped by a per-client-IP token-bucket limiter in the MCP server (mcp/internal/ratelimit), configured viaPUBLIC_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 rightmostX-Forwarded-Forhop — not the App Service front end, and a forgedX-Forwarded-For/X-Real-IPcan’t rotate the key; missing/short XFF fails closed toRemoteAddr). 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/mcptraffic 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/mcpwith 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, lowerRPS/BURSTto tolerate the fan-out, or move enforcement to the edge (below). TuneRPS/BURSTper environment before a wide launch.
- Keying is per-source IP only (not per-tool). All four public tools share one bucket per IP, because MCP calls are all
- Durable edge option (follow-up): route
mcp.soundchecklive.iothrough 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, bumpPUBLIC_RATELIMIT_TRUSTED_PROXY_COUNTto 2 (ALB + App Service front end). - The Registry
remotesentry andllms.txtnow point at/public/mcp(the anonymous front door) so agents reach it without an OAuth round-trip; the member/mcpis for signed-in users.
- Per-source rate limiting — DONE (app-level).
- Phase 2 adds public gig/event search. This is blocked on an API change:
GET /storefront/eventscurrently returns full rosters/assignments/invitations to anonymous callers — it needs a public-safe event projection and anisPublic/org-consent flag before any agent exposure.