apps/mcp
The five-on-it MCP server (TECHNICAL_SPECS §9.7): the tools, resources and prompts with which Claude, in Claude Code, makes games for Five On It. It is a thin adapter over the platform API with no rights of its own: every tool is a call or two to the API as the signed-in creator, under that account's quotas; over HTTP, Claude Code signs the creator in with OAuth, the API being the authorization server. How a creator uses it is in Creating games.
| File | What |
|---|---|
server.ts | createMcpServer: the server's instructions, its tools, resources and prompts, over one API caller. |
lib.ts | The package's entry (@foi/mcp): createMcpServer, createApi, and the LOOP and RULES the prompts hand Claude, which the platform's own model (apps/creator) reads too, running the tools in its process. |
tools.ts | The tools: catalogue_search (with a limit), catalogue_get, levels_list, level_get, draft_create, draft_get, draft_list, level_spec_set, game_set, mission_upsert and mission_remove (one call each, the API merging the mission's places and zones), draft_build (the API's long poll, GET /builds/:id?wait=, with the build's preview), draft_playtest, draft_publish (with the game's page, preview and warnings), room_open, games_search (searched and sorted by the API, with ratings), game_remix. Every write takes an optional ifRevision: the API refuses one meant for a revision the draft has left (the studio or the chat changed it), and the tool says to read it again. |
resources.ts | Everything read from the API, as a script or the creator chat reads it: the JSON Schemas (foi://schemas/game, mission, level-spec, from GET /schemas; one template's alone, foi://schemas/level-spec/{template}), the guides (foi://guide/missions, tiles, city-spec, tone, from GET /guides/:name; one section, foi://guide/{name}/{section}) and the bundled games (foi://examples/{level}, from GET /levels/:name/game). |
prompts.ts | The prompts Claude Code offers as slash commands: create_game, add_mission, fix_errors. |
format.ts | How tools answer: short Markdown, the validator's issues one per line with their path, the API's refusals with a hint, play links, the links to a game's page and to previews in the web client. |
api.ts | createApi: the platform API as one caller, with a forwarded token or an e-mail and password it signs in with (again after a 401). |
config.ts | loadConfig: FOI_API_URL, FOI_WEB_URL, FOI_MCP_PORT, FOI_PUBLIC_URL, FOI_AUTH_URL, FOI_API_TOKEN, FOI_EMAIL and FOI_PASSWORD. |
http.ts | The Streamable HTTP transport, POST /mcp: stateless, a server per request acting as the bearer of its Authorization header; OAuth's protected resource (its metadata naming the API, a 401 with WWW-Authenticate for a creator's tool without a token or a token the API refuses). |
stdio.ts | The process over stdio, for local development (the repository's .mcp.json registers it). |
index.ts | The process over HTTP on FOI_MCP_PORT (8091), for the Compose stack. |
Configuration
Each is in the root .env.example with its comment, but FOI_PUBLIC_URL, which the Compose stack sets too: deploy/.env.example.
| Variable | Default | What |
|---|---|---|
FOI_API_URL | http://localhost:8080 | The platform API. |
FOI_WEB_URL | http://localhost:5173 | The web client: a room's join URL becomes the play link ${FOI_WEB_URL}/?server=<the join URL, encoded>, a game's page is ${FOI_WEB_URL}/?game=<id> and a preview ${FOI_WEB_URL}/api/previews/<hash>.png (the web client's origin serves the API under /api). |
FOI_MCP_PORT | 8091 | The HTTP transport's port. |
FOI_PUBLIC_URL | each request's address | HTTP: where clients reach this server; <it>/mcp is the resource OAuth tokens are for, <it>/.well-known/oauth-protected-resource/mcp its metadata (the stack's: its public address). |
FOI_AUTH_URL | FOI_API_URL | HTTP: the OAuth authorization server the metadata names, the API's issuer (the stack's: its public address, where the API's /oauth/… is routed). |
FOI_API_TOKEN | stdio: the session token to call the API with. | |
FOI_EMAIL, FOI_PASSWORD | stdio, without a token: the creator to sign in as (POST /auth/login; the development accounts are made on sight). |
Transports and signing in
- stdio (
npm run -w apps/mcp stdio, or Claude Code starting it from.mcp.json) calls the API as the creator its environment names. It signs in on the first call, not at start, so it may start before the API does; a call while the API is down says so and how to start it. - Streamable HTTP (
npm run dev:mcp, port 8091) is stateless: each request gets its own server whose API calls carry that request'sAuthorization: Bearerheader, so the MCP server holds no session and no secret. The bearer is an OAuth access token: the server is OAuth's protected resource (RFC 9728), the platform API its authorization server (apps/api/src/oauth.ts, TECHNICAL_SPECS §9.6).GET /.well-known/oauth-protected-resource/mcp(and/.well-known/oauth-protected-resource) names the resource (<FOI_PUBLIC_URL>/mcp) and the API's issuer (FOI_AUTH_URL). A request without a token acts anonymously (the handshake, the lists, the resources, the prompts and the reading tools,OPEN_TOOLS: the catalogue, the levels and the public games); a call of any other tool without one is answered 401 withWWW-Authenticate: Bearer resource_metadata="…", from which Claude Code finds the API, registers, opens its sign-in page and calls again with the token. A token the API refuses (GET /meanswers 401: expired, revoked) is answered 401 witherror="invalid_token"whatever the request, so the client refreshes it or signs in again. A session fromPOST /auth/loginsent with--headerworks the same way (see the guide).
mcp.integration.test.ts runs the whole loop against the platform API in the same process (buildApp with memory adapters and a fake fleet) over the SDK's in-memory transport (sign in, a draft on test-mission-basics, the bundled game set, a build with an error and its fix and then its preview, a playtest, publishing with the game's page, a room, another creator's remix), the HTTP transport's bearer forwarding over a real socket (anonymous reads open, a creator's tool a 401), the protected resource metadata and the 401s' WWW-Authenticate, and OAuth end to end with the SDK's own client and a headless creator: discovery from the 401, registration, the sign-in page and Allow, the code with PKCE, the tools as the creator, a refresh when the API refuses the access token, and a revocation that sends the creator to sign in again. mcp.test.ts covers, against a stub of the API, the tools of the catalogue and the level specs, builds that report generated places, fail or run long, the games' search with ratings, remixes and preview links, the API's refusals, the client's sign-in and the configuration.