# Scorekeeper agent guide

Create and maintain a generic two-team scoreboard. Obtain the user's title and team names before creation. Names and scores become public through spectator links. Do not create duplicates speculatively.

## Connect

MCP Streamable HTTP: https://www.scorekeeper.co/api/mcp. Configure OAuth in the MCP client. Discovery: https://www.scorekeeper.co/.well-known/oauth-protected-resource/api/mcp. OAuth connects an existing nonanonymous Firebase/Scorekeeper account with scoreboards:read and scoreboards:write scopes. The authorization server supports dynamic registration and PKCE S256. Tool discovery is public; all tool execution needs an account bearer token. MCP tools: create_scoreboard, get_scoreboard, list_scoreboards, update_scoreboard, hand_off_scoreboard.

REST contract: https://www.scorekeeper.co/openapi.json. POST /api/v1/agent-sessions with no body creates a guest session. Store the returned access_token securely and send Authorization: Bearer TOKEN on board requests. A guest token expires in seven days and permits one board. OAuth connects an account for persistent management. Do not put tokens in query strings, public links, chat output or client-side source.

## Create, score, share

POST /api/v1/scoreboards:
```json
{"requestId":"friday-trivia-001","title":"Friday trivia","teams":[{"name":"Foxes"},{"name":"Owls"}]}
```
Read the returned id, teams[].id, revision and links. Initial scores are zero. Return links.spectator to the audience and links.overlay for a browser-source overlay. links.manage is for the account owner only.

GET /api/v1/scoreboards/{id} reads state. GET /api/v1/scoreboards returns {"scoreboards":[...],"nextCursor":null}, only agent-created generic boards owned by this connection's account. Optional limit (default25, maximum100) and cursor query parameters paginate results; pass nextCursor until null. An empty filtered page can still have a next cursor. MCP list_scoreboards accepts the same optional cursor and limit.

PATCH /api/v1/scoreboards/{id}, replacing team ID and revision with the response values:
```json
{"requestId":"friday-point-0001","expectedRevision":"1234567890:123000000","increments":[{"teamId":"team-1","delta":1}]}
```
Use scores:[{teamId,score}] for exact values, or increments:[{teamId,delta}], never both. Optional title or names:[{teamId,name}] rename the board/teams. Updates are atomic; scores and deltas are integers with absolute value at most 999999. Names and title are 1–100 characters. Each team array contains one or two unique, existing team IDs. requestId is 8–128 letters, digits, underscores or hyphens.

## Retry and recover

Persist requestId and the exact body before a mutation. Retry exactly after network uncertainty; the service returns the earlier result. Do not generate another ID to retry a timed-out point. Reusing an ID with different input returns 409 idempotency_conflict. A 409 revision_conflict means another scorer changed the board: GET current state, determine whether the requested point was already applied, and only issue a newly intended mutation with a new requestId and current expectedRevision. Do not blindly replay a stale increment.

401: reconnect OAuth or obtain a new guest session; a new guest token does not recover an old guest board. 403: review scope or board quota. 404: board is absent or not owned by this connection. 429: honor Retry-After, then retry the same intended request. 503: wait and retry; never assume the point was applied. JSON-RPC tool errors have isError:true with error/message in text; expired OAuth also returns mcp/www_authenticate metadata.

## Human handoff

POST /api/v1/scoreboards/{id}/handoff with {"requestId":"friday-handoff-01"}. For OAuth accounts this returns the owner's normal admin URL and does not transfer ownership. For guests it returns a private claim link valid for 15 minutes; signing in and claiming transfers ownership to that account and ends guest control. Keep claim links private. Replayed handoff requests return the same link, even if its expiry has passed; use a new ID only when deliberately requesting a new link. Create the guest handoff before the seven-day session expires.

## Limits and scope

Free boards: 50 total spectator views with branding; account board limit10, guest1. Returned limits are authoritative for the account plan. API authentication is limited to120 requests/minute per session; MCP transport also limits120/minute per request source, and guest creation limits are5/minute,10/hour and30/day per source. Honor 429 Retry-After. API-created boards use the normal public scoreboard display and owner controls. No bracket creation, clock control, deletion, billing or sport-specific rules in this API. Never promise publication in an app directory or automatic recommendation by an agent.
