← developers

loudly MCP server — connection guide

loudly exposes an MCP server (Model Context Protocol) so an AI agent can drive the daily loop — read the stack, propose and edit cards, render assets, publish, and set up brand kits — bound by exactly the same plan, role, severity, and credit rules as the app and the public API (Rule 11 / PRD §12). It's the fourth thin adapter over the one service layer (lib/services/).

Two ways to authenticate

Mode For How
API key (bearer) Claude Code, Cursor, the Claude API mcp_servers connector, scripts mint an sk_live_… key in Settings → Developers, send it as Authorization: Bearer …
Clerk OAuth the Claude app, ChatGPT, other consumer connector UIs add a custom connector at the MCP URL and sign in — the client discovers Clerk and runs the OAuth flow

Whatever the mode, the request resolves to a non-user mcp actor (it carries no user id), so owner-gated capabilities (team / billing / settings-as-owner) are unreachable by construction, and there is no approval-decision tool.

Why two modes: the Claude app's connector UI accepts OAuth fields only — it has no field for a pasted bearer token — so OAuth is required to reach it. Dev clients and scripts are simplest with a key. (Unkey is not involved; Clerk is the OAuth authorization server.)


Connect from Claude Code (API key)

# mint a key first: Settings → Developers → Generate key (shown once)
claude mcp add --transport http loudly \
  https://<your-domain>/api/mcp/mcp \
  --header "Authorization: Bearer sk_live_YOURKEY" \
  --scope local

Connect from Cursor / other dev MCP clients (API key)

Add an HTTP (Streamable HTTP) MCP server pointing at the endpoint, with a header Authorization: Bearer sk_live_…. Example mcp.json-style entry:

{
  "mcpServers": {
    "loudly": {
      "type": "http",
      "url": "https://<your-domain>/api/mcp/mcp",
      "headers": { "Authorization": "Bearer sk_live_YOURKEY" }
    }
  }
}

Connect from the Claude app / ChatGPT (Clerk OAuth)

  1. In the app, add a custom connector and paste the MCP URL (https://<your-domain>/api/mcp/mcp).
  2. The client fetches /.well-known/oauth-protected-resource, discovers Clerk as the authorization server, and runs OAuth 2.1 (auth-code + PKCE). You'll be redirected to sign in and consent; the app stores and refreshes the token.
  3. The tools appear once connected.

One-time setup (admin):

Connect from a script (raw JSON-RPC)

KEY=sk_live_YOURKEY
curl -s -X POST https://<your-domain>/api/mcp/mcp \
  -H "authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -H "accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'

Responses are Streamable-HTTP framed (event: message\ndata: {…}).


Tools (24)

Each is a thin wrapper over an existing service use-case. Brand-scoped tools take an optional brandId; omit it to use the workspace's default brand.

Read (read scope)whoami · list_brands · get_today_stack · get_calendar · list_cards · get_card · get_performance · get_post_metrics · list_channels · get_credits · get_brand_kit

Write — card lifecycle (write scope)propose_card (AI-draft a brand-grounded card) · edit_card_copy · set_card_channels (channels + optionally what each publishes as: a formats map of per-platform format ids, e.g. { "facebook": "fb-story" } — feed/Story/Reel on FB/IG; Reels need a video creative) · schedule_card · reschedule_card · unschedule_card · dismiss_card (the reason trains the next stack)

Write — generation + publish (write scope)render_card_asset (with no steer and no style it's the idempotent ensure-rendered call, 0 credits on a repeat; a steer and/or a style override is a deliberate paid regen, 1 credit per call) · publish_card (runs the pre-publish severity check first; exactly-once per channel)

Write — brand-kit setup (write scope)create_brand_kit (plan-capped) · update_brand_kit (includes visualStyle — its optional preset picks the image-generation style: one of the eight registry styles — editorial-photo, flat-typographic-poster, flat-illustration, minimal-product, warm-lifestyle, matte-3d, bold-poster, paper-collage — or custom, the derived-from-references contract) · set_product_override · remove_reference_image

Call whoami first to confirm the connection and see the resolved workspace, plan, active brand, and granted scopes.

Scopes + guardrails

Rate limits

Every tool call spends the caller's per-minute budget — the same fixed-window limiter as the REST API: reads 120, writes 30, sensitive (render_card_asset + publish_card) 10, scaled by plan (Growth ×2). An sk_live_ key spends one blended budget across /v1 and MCP; an OAuth connection spends a per-workspace budget. Over the ceiling, a tool returns an error result instead of data:

{ "error": "rate_limited", "retryAfter": 34 }

Back off retryAfter seconds (the window is one minute); there are no X-RateLimit-* headers at the tool layer. The credit ledger and idempotency are the real cost guards — this limiter is the secondary abuse guard.

Troubleshooting

Security