{"openapi":"3.1.0","info":{"title":"Pong Score API","version":"0.1.0","description":"A real, stateless, playable 1D-paddle-vs-ball engine simulated tick by tick: real wall bounces, real angle-reflection paddle collisions, a bounded-reaction-speed AI opponent, and real first-to-N scoring. Every tool call takes the full game state and returns the new state; nothing is stored server-side.\n\nEvery operation is read-only or compute-only. The same tools are available over MCP (streamable HTTP) at https://pongscore.connectorbay.com/mcp.","contact":{"name":"Unify Dynamics LLC","email":"hr@unifydynamics.com","url":"https://connectorbay.com/pongscore/docs"},"termsOfService":"https://connectorbay.com/pongscore/terms"},"servers":[{"url":"https://pongscore.connectorbay.com"}],"externalDocs":{"url":"https://connectorbay.com/pongscore/docs"},"security":[{},{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Optional. A Pro API key raises rate limits; no credentials are required."}},"schemas":{"Error":{"type":"object","properties":{"ok":{"const":false},"error":{"type":"object"}}}}},"paths":{"/v1/tools/new_game":{"post":{"operationId":"new_game","summary":"Starts a fresh rally and returns its initial state. Use for \"let's play pong\", \"start a new","description":"Starts a fresh rally and returns its initial state. Use for \"let's play pong\", \"start a new\n    game\", \"play me at pong\".","tags":["tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"width":{"default":40.0,"description":"Court width in units.","maximum":120,"minimum":10,"title":"Width","type":"number"},"height":{"default":20.0,"description":"Court height in units.","maximum":60,"minimum":8,"title":"Height","type":"number"},"paddle_height":{"default":5.0,"description":"Paddle length in units.","maximum":20,"minimum":2,"title":"Paddle Height","type":"number"},"winning_score":{"default":5,"description":"First side to reach this score wins.","maximum":21,"minimum":1,"title":"Winning Score","type":"integer"},"ai_left":{"default":false,"description":"If true, the left paddle is AI-controlled.","title":"Ai Left","type":"boolean"},"ai_right":{"default":true,"description":"If true, the right paddle is AI-controlled (default).","title":"Ai Right","type":"boolean"}},"title":"new_gameArguments","type":"object"}}}},"responses":{"200":{"description":"Tool result. `ok: true` on success; `ok: false` with a structured `error` (provider_unavailable, upstream_error, bad_input) when the tool could not answer.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"422":{"description":"Arguments failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-read-only":true}},"/v1/tools/move_paddle":{"post":{"operationId":"move_paddle","summary":"Sets a paddle's intent for the NEXT step() call — it does not move the paddle immediately,","description":"Sets a paddle's intent for the NEXT step() call — it does not move the paddle immediately,\n    and it is ignored for an AI-controlled paddle. Use for \"move my paddle up\", \"hold position\".","tags":["tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"state":{"additionalProperties":true,"description":"The current game state, exactly as returned by new_game/move_paddle/step.","examples":[{"ball":{"vx":-1.0,"vy":0.4,"x":20.0,"y":10.0},"height":20.0,"left":{"ai":false,"height":5.0,"intent":"stop","score":0,"y":7.5},"paddle_speed":1.0,"right":{"ai":true,"height":5.0,"intent":"stop","score":0,"y":7.5},"status":"in_progress","ticks":0,"width":40.0,"winning_score":5}],"title":"State","type":"object"},"player":{"description":"'left' or 'right'.","title":"Player","type":"string"},"direction":{"description":"'up', 'down', or 'stop'.","title":"Direction","type":"string"}},"required":["state","player","direction"],"title":"move_paddleArguments","type":"object"}}}},"responses":{"200":{"description":"Tool result. `ok: true` on success; `ok: false` with a structured `error` (provider_unavailable, upstream_error, bad_input) when the tool could not answer.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"422":{"description":"Arguments failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-read-only":true}},"/v1/tools/step":{"post":{"operationId":"step","summary":"Advances the simulation exactly one tick: moves paddles (AI paddles compute their own","description":"Advances the simulation exactly one tick: moves paddles (AI paddles compute their own\n    bounded-speed move), moves the ball, and resolves wall bounces, paddle collisions (real\n    angle-reflection), and scoring. Returns the new state plus event\n    (moved/wall_bounce/paddle_bounce/score/game_over) and scorer (set only on a score). Use for\n    \"advance the rally\", \"next tick\", \"let the ball move\".","tags":["tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"state":{"additionalProperties":true,"description":"The current game state, exactly as returned by new_game/move_paddle/step.","examples":[{"ball":{"vx":-1.0,"vy":0.4,"x":20.0,"y":10.0},"height":20.0,"left":{"ai":false,"height":5.0,"intent":"stop","score":0,"y":7.5},"paddle_speed":1.0,"right":{"ai":true,"height":5.0,"intent":"stop","score":0,"y":7.5},"status":"in_progress","ticks":0,"width":40.0,"winning_score":5}],"title":"State","type":"object"}},"required":["state"],"title":"stepArguments","type":"object"}}}},"responses":{"200":{"description":"Tool result. `ok: true` on success; `ok: false` with a structured `error` (provider_unavailable, upstream_error, bad_input) when the tool could not answer.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"422":{"description":"Arguments failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-read-only":true}},"/v1/tools/render":{"post":{"operationId":"render","summary":"A human-readable ASCII court: '|' marks each paddle, 'o' the ball, with the current score","description":"A human-readable ASCII court: '|' marks each paddle, 'o' the ball, with the current score\n    and status below. Use for \"show me the board\", \"what's the score\".","tags":["tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"state":{"additionalProperties":true,"description":"The current game state, exactly as returned by new_game/move_paddle/step.","examples":[{"ball":{"vx":-1.0,"vy":0.4,"x":20.0,"y":10.0},"height":20.0,"left":{"ai":false,"height":5.0,"intent":"stop","score":0,"y":7.5},"paddle_speed":1.0,"right":{"ai":true,"height":5.0,"intent":"stop","score":0,"y":7.5},"status":"in_progress","ticks":0,"width":40.0,"winning_score":5}],"title":"State","type":"object"}},"required":["state"],"title":"renderArguments","type":"object"}}}},"responses":{"200":{"description":"Tool result. `ok: true` on success; `ok: false` with a structured `error` (provider_unavailable, upstream_error, bad_input) when the tool could not answer.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"422":{"description":"Arguments failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-read-only":true}}}}