Skip to content

Add Turnstile to login and run MCP on the stateless platform kit - #244

Merged
clbotdev merged 2 commits into
mainfrom
feat/login-turnstile-mcp-kit
Nov 1, 2026
Merged

clbotdev merged 2 commits into
mainfrom
feat/login-turnstile-mcp-kit

Conversation

@clbotdev

@clbotdev clbotdev commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Cloudflare Turnstile on login

  • The API's hosted login page (dashboard login and MCP/OAuth /oauth/login) shows a Turnstile widget when TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY are set. Off when neither is set; setting only one stops the API from starting.
  • Better Auth's captcha plugin checks the token on /email-otp/send-verification-otp, the request that emails a sign-in code. Tokens must be issued for the API's host (PUBLIC_API_URL); Cloudflare's testing secrets skip that check because they always report example.com.
  • Needs @codelitdev/oauth-server-kit 0.8.0. The web app is unchanged: login stays in the API.

MCP on the stateless platform kit

  • Upgrades the CodeLit platform packages to 0.8.0 (mcp-server-kit, platform, billing, platform-conformance, observability) and serves MCP through createMcpServerKit, the same way SendLit does. Drops @modelcontextprotocol/sdk 1.x and the session map.
  • Sign-in still goes through resolveAuth. Tools are definitions with a risk: read tools need data:read, the rest data:write (API keys carry both).
  • Behaviour changes for MCP clients:
    • No sessions: no mcp-session-id, GET/DELETE return 405. 2025-era clients still initialize in stateless mode.
    • delete_media and seal_media need confirm: true.
    • update_media_settings is now a write (idempotent), not destructive.
    • Errors are platform errors: { code, message, details }.
    • X-API-Key works alongside x-medialit-apikey; both at once is credential_ambiguous.
  • The MCP route parses its own JSON body, so malformed JSON gets a JSON-RPC 400 instead of a 500.
  • Docs: frontlit/docs/for-agents/mcp-server.md documents confirm. The integration MCP client is now stateless.

Testing

  • API: typecheck, lint, 165 tests, conformance and billing drift check all pass. New tests: src/__tests__/login-captcha.test.ts and src/mcp/__tests__/routes.test.ts (real app, real API key, official MCP client).
  • run-with-local-stack.sh (REST, MCP and CLI integration suites) passes against a fresh stack.
  • Manual end to end on a local stack with Cloudflare's test keys, in a real browser:
    • Dashboard login through the widget and an emailed code works. No token gives 400; the always-fail secret gives 403 with "Captcha verification failed" on the page and no email.
    • MCP over OAuth: discovery, client registration, PKCE login through Turnstile, consent, token, then tools with the official client. A data:read token is refused write tools with insufficient_scope.

Not tested: the Turnstile hostname check with real keys; it needs a staging host.

Deploy notes

  • To turn Turnstile on, create a widget for the API's public hostname and set TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY on the API.
  • platform-cli doctor already fails on main (workflow hash and next mismatch). This PR doesn't change that; platform-cli sync can follow separately.

Login: the hosted login page shows a Cloudflare Turnstile widget when
TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY are set, and Better Auth's
captcha plugin verifies the token on the email-OTP send request. Tokens must
be issued for the API's host; Cloudflare's testing secrets skip that check.
Setting only one key stops the API from starting.

MCP: upgrade the CodeLit platform packages to 0.8.0 and serve MCP through
createMcpServerKit. Requests are stateless (no sessions; 2025-era clients
still work), tools are data with a risk level that sets the required scope,
destructive tools (delete_media, seal_media) need confirm: true, and tool
errors are platform errors. x-medialit-apikey still works and is mapped to
the kit's X-API-Key. The MCP route parses its own JSON so bad bodies get a
JSON-RPC error instead of a 500.
…port

CodeQL flags an unanchored regex used on a URL, even in a test.
@clbotdev
clbotdev merged commit 3cf880a into main Nov 1, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants