{"openapi":"3.1.0","info":{"title":"Tic-Tac-Toe API","version":"0.1.0","description":"A real, stateless, playable tic-tac-toe engine with an unbeatable minimax AI opponent (random/easy/optimal difficulty). 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://tictactoe.connectorbay.com/mcp.","contact":{"name":"Unify Dynamics LLC","email":"hr@unifydynamics.com","url":"https://connectorbay.com/tictactoe/docs"},"termsOfService":"https://connectorbay.com/tictactoe/terms"},"servers":[{"url":"https://tictactoe.connectorbay.com"}],"externalDocs":{"url":"https://connectorbay.com/tictactoe/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 tic-tac-toe game and returns its initial state. Use for \"let's play","description":"Starts a fresh tic-tac-toe game and returns its initial state. Use for \"let's play\n    tic-tac-toe\", \"start a new game\".","tags":["tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"first_player":{"default":"X","description":"Which symbol moves first.","examples":["X","O"],"title":"First Player","type":"string"}},"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/make_move":{"post":{"operationId":"make_move","summary":"Plays one move and returns the resulting state. status is invalid_move (illegal move — state","description":"Plays one move and returns the resulting state. status is invalid_move (illegal move — state\n    is unchanged), in_progress, win (winner + winning_line are set), or draw. Use for \"play cell 4\",\n    \"I'll take the center\".","tags":["tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"state":{"additionalProperties":true,"description":"The current game state, exactly as returned by new_game/make_move.","examples":[{"board":["X",null,"O",null,"X",null,null,null,"O"],"current_player":"X"}],"title":"State","type":"object"},"position":{"description":"Cell to play, 0-8 (row = position // 3, col = position % 3; see render() for the numbered layout).","maximum":8,"minimum":0,"title":"Position","type":"integer"},"player":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"'X' or 'O'. Optional — defaults to the state's current_player; supplying it just double-checks whose turn you think it is.","title":"Player"}},"required":["state","position"],"title":"make_moveArguments","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/get_valid_moves":{"post":{"operationId":"get_valid_moves","summary":"Lists the still-open cell indices (0-8); empty once the game has ended. Use for \"what moves","description":"Lists the still-open cell indices (0-8); empty once the game has ended. Use for \"what moves\n    are open\", \"is the center taken\".","tags":["tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"state":{"additionalProperties":true,"description":"The current game state, exactly as returned by new_game/make_move.","examples":[{"board":["X",null,"O",null,"X",null,null,null,"O"],"current_player":"X"}],"title":"State","type":"object"}},"required":["state"],"title":"get_valid_movesArguments","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 board. Empty cells are labeled with their own index (0-8) so a","description":"A human-readable ASCII board. Empty cells are labeled with their own index (0-8) so a\n    player's chosen cell maps directly to `make_move`'s position argument. Use for \"show me the\n    board\", \"what does it look like now\".","tags":["tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"state":{"additionalProperties":true,"description":"The current game state, exactly as returned by new_game/make_move.","examples":[{"board":["X",null,"O",null,"X",null,null,null,"O"],"current_player":"X"}],"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}},"/v1/tools/ai_move":{"post":{"operationId":"ai_move","summary":"Chooses the AI's next move for whichever symbol is currently to move, WITHOUT applying it —","description":"Chooses the AI's next move for whichever symbol is currently to move, WITHOUT applying it —\n    call make_move with the returned position to actually play it. Use for \"make the computer's\n    move\", \"play as the AI\", \"what would the AI do here\".","tags":["tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"state":{"additionalProperties":true,"description":"The current game state, exactly as returned by new_game/make_move.","examples":[{"board":["X",null,"O",null,"X",null,null,null,"O"],"current_player":"X"}],"title":"State","type":"object"},"difficulty":{"default":"optimal","description":"random, easy, or optimal (default). optimal is genuinely unbeatable.","title":"Difficulty","type":"string"}},"required":["state"],"title":"ai_moveArguments","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}}}}