Footics MCP is a remote MCP server that exposes a single Footics user's own data to whichever AI assistant they connect. This document describes what the server can and cannot do, and how it is hardened.
- Every
/mcprequest requires a valid bearer token (MCP_REQUIRE_AUTH=true). No token returns401with the OAuthWWW-Authenticatechallenge and resource metadata (RFC 9728). - Tokens are Supabase access tokens — issued either by the interactive OAuth 2.1
flow ("Sign in with Footics") or as a session token for testing. They are
verified one of two ways (
MCP_TOKEN_VERIFY):getuserresolves the user against Supabase,jwksverifies the signature locally against the project's JWKS. - The verified token's subject is the only identity the tools act as. There is no way to act on behalf of another user.
| Capability | Scope |
|---|---|
| Read matches, standings, leaderboards, groups, jokers | Game data visible to the user |
| Read predictions | Only the caller's own — never another player's pick on an upcoming match |
Write a prediction (submit_prediction) |
Only the caller's own, and only when MCP_ENABLE_WRITES=true |
Writes reuse the main app's guardrails: the match must not have kicked off, scores are bounded (0–20), and the per-phase joker quota is enforced.
- Act as, or read the private predictions of, another user.
- Reach anything beyond the database-backed tools registered in
lib/tools.ts. - Run while the kill switch is off:
MCP_ENABLED=falsemakes/mcpreturn503before any token check or database call.
- Auth required, writes off by default. A fresh deployment is read-only.
- Rate limiting: a per-user tool-call limit (
MCP_RATE_LIMIT_PER_MIN, default 30), in-memory and per-instance. - Kill switch:
MCP_ENABLED=falsecuts all database traffic with a single environment change.
- Rate limiting is per-instance. Across several serverless instances the
effective limit is higher. For a hard global limit, back
lib/rate-limit.tswith a shared store (e.g. Upstash Redis). getuserverification costs one network round-trip per request.jwksavoids it but requires the Supabase project to use asymmetric signing keys.- The server trusts the database's row scoping. Tools always filter by the authenticated user id; row-level security on the shared database is recommended as defense in depth.
For non-sensitive issues, open a GitHub issue.
For sensitive vulnerabilities — anything that could let someone read or write data they should not — email tom@tomca.be privately first, or open a private security advisory on GitHub. Coordinated disclosure is appreciated; acknowledgment within 72 hours.