{
  "openapi": "3.1.1",
  "info": {
    "title": "Elo/Glicko Rating Lab API",
    "version": "1.0.0",
    "summary": "Rate players from game results with Elo, Glicko-1 and Glicko-2; history chart and win odds.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://eloglickoratinglab.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Rate players from game results with Elo, Glicko-1 and Glicko-2; history chart and win odds.",
        "description": "Enter game results (win, draw, loss) grouped into rating periods and see the ratings under Elo (fixed K or FIDE-style K), Glicko-1 and Glicko-2, implemented exactly as in Mark Glickman's papers, with the rating deviation and volatility, a rating history chart and the win probability between any two players.\n\nMethodology:\n- Elo: expected score E = 1 / (1 + 10^((R_opp − R)/400)); after every game R ← R + K(S − E) for both players, in input order. The FIDE-style option uses K = 40 for a player's first 30 games, then 20, and 10 once the player has reached 2400 (the age rule is not modelled).\n- Glicko-1 (Glickman 1999): all games of a period are rated together with each player's pre-period values; RD shrinks with games and grows by c per idle period: RD = min(√(RD² + c²·t), 350).\n- Glicko-2 (Glickman, \"Example of the Glicko-2 system\"): steps 1–8 on the μ/φ scale (÷ 173.7178) with the Illinois algorithm (ε = 0.000001) for the new volatility; players without games only have φ increased to √(φ² + σ²).\n- Win probability: Elo uses E above; Glicko uses E = 1 / (1 + 10^(−g(√(RD_A² + RD_B²))·(R_A − R_B)/400)) with g(RD) = 1 / √(1 + 3q²RD²/π²), q = ln 10 / 400. Draws count as half a win.",
        "tags": [
          "fun"
        ],
        "x-mcp-tool": "elo_glicko_rating_lab",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EloGlickoRatingLabInput"
              },
              "examples": {
                "example1": {
                  "summary": "Glickman's worked example (1500 vs 1400, 1550, 1700)",
                  "value": {
                    "games": [
                      {
                        "period": 1,
                        "player": "Player",
                        "opponent": "A",
                        "score": "1"
                      },
                      {
                        "period": 1,
                        "player": "Player",
                        "opponent": "B",
                        "score": "0"
                      },
                      {
                        "period": 1,
                        "player": "Player",
                        "opponent": "C",
                        "score": "0"
                      }
                    ],
                    "players": [
                      {
                        "name": "Player",
                        "rating": 1500,
                        "rd": 200,
                        "volatility": 0.06
                      },
                      {
                        "name": "A",
                        "rating": 1400,
                        "rd": 30
                      },
                      {
                        "name": "B",
                        "rating": 1550,
                        "rd": 100
                      },
                      {
                        "name": "C",
                        "rating": 1700,
                        "rd": 300
                      }
                    ],
                    "tau": 0.5
                  }
                },
                "example2": {
                  "summary": "Small club league over three periods",
                  "value": {
                    "games": [
                      {
                        "period": 1,
                        "player": "Ana",
                        "opponent": "Ben",
                        "score": "1"
                      },
                      {
                        "period": 1,
                        "player": "Cleo",
                        "opponent": "Dev",
                        "score": "0.5"
                      },
                      {
                        "period": 2,
                        "player": "Ana",
                        "opponent": "Cleo",
                        "score": "0"
                      },
                      {
                        "period": 2,
                        "player": "Ben",
                        "opponent": "Dev",
                        "score": "1"
                      },
                      {
                        "period": 3,
                        "player": "Ana",
                        "opponent": "Dev",
                        "score": "1"
                      },
                      {
                        "period": 3,
                        "player": "Cleo",
                        "opponent": "Ben",
                        "score": "1"
                      }
                    ],
                    "eloK": "fide"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EloGlickoRatingLabResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "EloGlickoRatingLabInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "games": {
            "title": "Games",
            "description": "One row per game. The score is from the first player's point of view. Games with the same period number form one Glicko rating period.",
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "period": {
                  "title": "Period",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 10000,
                  "default": 1
                },
                "player": {
                  "title": "Player",
                  "type": "string",
                  "maxLength": 40
                },
                "opponent": {
                  "title": "Opponent",
                  "type": "string",
                  "maxLength": 40
                },
                "score": {
                  "title": "Result for player",
                  "type": "string",
                  "enum": [
                    "1",
                    "0.5",
                    "0"
                  ],
                  "default": "1"
                }
              },
              "required": [
                "player",
                "opponent",
                "score"
              ]
            }
          },
          "players": {
            "title": "Starting ratings (optional)",
            "description": "Players not listed start at 1500, RD 350, volatility 0.06 (Glickman's defaults for unrated players).",
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "name": {
                  "title": "Name",
                  "type": "string",
                  "maxLength": 40
                },
                "rating": {
                  "title": "Rating",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 4000
                },
                "rd": {
                  "title": "RD",
                  "type": "number",
                  "minimum": 1,
                  "maximum": 350
                },
                "volatility": {
                  "title": "Volatility σ",
                  "type": "number",
                  "minimum": 0.0001,
                  "maximum": 1
                }
              },
              "required": [
                "name"
              ]
            }
          },
          "eloK": {
            "title": "Elo K-factor",
            "type": "string",
            "enum": [
              "10",
              "16",
              "20",
              "24",
              "32",
              "40",
              "fide"
            ],
            "default": "32"
          },
          "tau": {
            "title": "Glicko-2 τ (volatility constraint)",
            "description": "Glickman suggests 0.3–1.2; smaller values change the volatility more slowly.",
            "type": "number",
            "minimum": 0.2,
            "maximum": 1.2,
            "default": 0.5
          },
          "glickoC": {
            "title": "Glicko-1 c (RD growth per idle period)",
            "description": "34.6 lets an RD of 50 grow back to 350 in 100 periods.",
            "type": "number",
            "minimum": 0,
            "maximum": 200,
            "default": 34.6
          },
          "playerA": {
            "title": "Win probability: player A",
            "description": "Default: the two highest-rated players.",
            "type": "string",
            "maxLength": 40
          },
          "playerB": {
            "title": "Win probability: player B",
            "type": "string",
            "maxLength": 40
          },
          "chartSystem": {
            "title": "Chart",
            "type": "string",
            "enum": [
              "glicko2",
              "glicko1",
              "elo"
            ],
            "default": "glicko2"
          }
        },
        "required": [
          "games"
        ]
      },
      "EloGlickoRatingLabResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "players",
              "periods",
              "winProbability"
            ],
            "properties": {
              "periods": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              },
              "players": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "name",
                    "games",
                    "score",
                    "elo",
                    "glicko1",
                    "glicko2"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "games": {
                      "type": "integer"
                    },
                    "score": {
                      "type": "number"
                    },
                    "elo": {
                      "type": "number"
                    },
                    "glicko1": {
                      "type": "object",
                      "required": [
                        "rating",
                        "rd"
                      ],
                      "properties": {
                        "rating": {
                          "type": "number"
                        },
                        "rd": {
                          "type": "number"
                        }
                      }
                    },
                    "glicko2": {
                      "type": "object",
                      "required": [
                        "rating",
                        "rd",
                        "volatility"
                      ],
                      "properties": {
                        "rating": {
                          "type": "number"
                        },
                        "rd": {
                          "type": "number"
                        },
                        "volatility": {
                          "type": "number"
                        }
                      }
                    }
                  }
                }
              },
              "history": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "period",
                    "player",
                    "elo",
                    "glicko1",
                    "glicko2",
                    "rd2"
                  ],
                  "properties": {
                    "period": {
                      "type": "integer"
                    },
                    "player": {
                      "type": "string"
                    },
                    "elo": {
                      "type": "number"
                    },
                    "glicko1": {
                      "type": "number"
                    },
                    "glicko2": {
                      "type": "number"
                    },
                    "rd2": {
                      "type": "number"
                    }
                  }
                }
              },
              "winProbability": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "a": {
                    "type": "string"
                  },
                  "b": {
                    "type": "string"
                  },
                  "elo": {
                    "type": "number"
                  },
                  "glicko1": {
                    "type": "number"
                  },
                  "glicko2": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}