Auth & security
The one rule that matters
Section titled “The one rule that matters”Every tool call authenticates and acts as the calling user’s own Supabase JWT — never a service-role key. This is a hard architectural rule shared across every MCP server DevFellowship runs (see the DFL MCP fleet docs), not something specific to this server.
What happens on each request
Section titled “What happens on each request”- Extract the bearer token from
Authorization: Bearer <jwt>. - Verify it against the Supabase project’s public JWKS endpoint
(
SUPABASE_URL/auth/v1/.well-known/jwks.json) — this project signs tokens with ES256 (asymmetric keys), not the older HS256 shared-secret scheme. Verification happens locally against the cached public key; no per-call round trip to Supabase Auth. - Resolve the caller’s org. The server looks up the caller’s own
public.usersrow (org_id,role,active) — the same lookup the staff UI’suseCurrentStaffUserhook makes. No row means “not a registered staff user,” and every tool call is refused. - Build a per-request Supabase client from the project’s
publishable (anon) key plus the caller’s raw JWT as the
Authorizationheader. This is the client every tool uses. PostgREST enforces the sameorg_isolationRLS policy that gates the staff UI’s own reads and writes — a tool cannot return or modify a row outside the caller’s organization, because Postgres itself refuses to show it the row. - Role checks beyond RLS. A few actions have a role gate the schema
itself doesn’t express (e.g. only an
ownermay invite or update staff) — these mirror the same checks the staff UI’s own React components make, reproduced server-side, plus (forinvite_staff) re-checked again inside the Edge Function itself as the real enforcement point.
Sessions
Section titled “Sessions”One MCP session (one mcp-session-id) is bound to the JWT presented on
its initialize call. A session ID is only honored for the identity that
opened it — if it appears from a different (even validly authenticated)
caller, the server treats the request as if no session ID was given at
all, forcing a fresh handshake.
Getting a token
Section titled “Getting a token”There’s no separate credential to request. Any real Marques Boxing staff login (the same one used for the web app) mints a usable token via Supabase Auth’s normal password grant — see Getting started.