expected_stats_batch
Retrieve expected batting statistics (xBA, xSLG, xwOBA) for multiple players in a single request to analyze lineup or rotation performance.
Instructions
Expected stats (xBA, xSLG, xwOBA vs actual) for multiple batters and/or pitchers in one call.
Use this when the user asks for a lineup, rotation, "team starters", or any named group (e.g. Yankees 1–9 and five starters). Pass comma-separated names. The MCP does not fetch MLB rosters automatically — use current player names from context or a quick web lookup, then list them here.
Args:
year: Season year (e.g. 2026). If Savant has not published that season’s leaderboard
yet (common before Opening Day or in early April), the table may be empty — use
the prior year for full-season expected stats, or lower min_plate_appearances
once enough games are played.
batters: Comma-separated hitter names, e.g.
"Aaron Judge, Juan Soto, Giancarlo Stanton, Anthony Volpe, ..."
pitchers: Comma-separated pitcher names, e.g.
"Gerrit Cole, Carlos Rodon, Marcus Stroman, Clarke Schmidt, Luis Gil"
min_plate_appearances: Minimum PA to qualify (default 50).
Provide at least one of batters or pitchers. Semicolons and newlines also separate names.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| year | Yes | ||
| batters | No | ||
| pitchers | No | ||
| min_plate_appearances | No |
Implementation Reference
- src/statcast_mcp/server.py:802-890 (handler)The implementation of the 'expected_stats_batch' tool, which fetches and filters expected statistics for multiple batters and pitchers.
def expected_stats_batch( year: int, batters: str | None = None, pitchers: str | None = None, min_plate_appearances: int = 50, ) -> str: """Expected stats (xBA, xSLG, xwOBA vs actual) for *multiple* batters and/or pitchers in one call. Use this when the user asks for a lineup, rotation, "team starters", or any named group (e.g. Yankees 1–9 and five starters). Pass comma-separated names. The MCP does not fetch MLB rosters automatically — use current player names from context or a quick web lookup, then list them here. Args: year: Season year (e.g. 2026). If Savant has not published that season’s leaderboard yet (common before Opening Day or in early April), the table may be empty — use the prior year for full-season expected stats, or lower ``min_plate_appearances`` once enough games are played. batters: Comma-separated hitter names, e.g. "Aaron Judge, Juan Soto, Giancarlo Stanton, Anthony Volpe, ..." pitchers: Comma-separated pitcher names, e.g. "Gerrit Cole, Carlos Rodon, Marcus Stroman, Clarke Schmidt, Luis Gil" min_plate_appearances: Minimum PA to qualify (default 50). Provide at least one of batters or pitchers. Semicolons and newlines also separate names. """ from pybaseball import ( statcast_batter_expected_stats as _bat_exp, statcast_pitcher_expected_stats as _pit_exp, ) batter_names = _parse_player_name_list(batters or "") pitcher_names = _parse_player_name_list(pitchers or "") if not batter_names and not pitcher_names: return ( "Provide at least one of batters or pitchers as a comma-separated list " '(e.g. batters="Aaron Judge, Juan Soto").' ) sections: list[str] = [] def _empty_leaderboard_note(role: str, min_pa: int) -> str: return ( f"**No Statcast data for {year} yet** — the `{role}` expected-stats leaderboard " f"returned zero rows at `min_plate_appearances={min_pa}`. " "That usually means the regular season has not produced enough qualified players " "in Savant’s feed yet, or the year’s table is not published. " f"Use **`year={year - 1}`** for a complete prior-season leaderboard, or retry " "after more games with a **lower** `min_plate_appearances`.\n\n" ) if batter_names: try: df = _bat_exp(year, minPA=min_plate_appearances) except Exception as e: return f"Error fetching batter expected stats: {e}" if df is None or getattr(df, "empty", True): sections.append(f"### Batters (expected stats, {year})\n\n") sections.append(_empty_leaderboard_note("batter", min_plate_appearances)) else: merged, errs = _batch_filter_players(df, batter_names) sections.append(f"### Batters (expected stats, {year})\n\n") if merged.empty: sections.append("No batter rows matched.\n\n") else: sections.append(_fmt(merged, max_rows=len(merged))) if errs: sections.append("\n**Notes:** " + " | ".join(errs)) if pitcher_names: try: df = _pit_exp(year, minPA=min_plate_appearances) except Exception as e: return f"Error fetching pitcher expected stats: {e}" if df is None or getattr(df, "empty", True): sections.append(f"\n\n### Pitchers (expected stats allowed, {year})\n\n") sections.append(_empty_leaderboard_note("pitcher", min_plate_appearances)) else: merged, errs = _batch_filter_players(df, pitcher_names) sections.append(f"\n\n### Pitchers (expected stats allowed, {year})\n\n") if merged.empty: sections.append("No pitcher rows matched.\n\n") else: sections.append(_fmt(merged, max_rows=len(merged))) if errs: sections.append("\n**Notes:** " + " | ".join(errs)) return "".join(sections).strip() - src/statcast_mcp/server.py:801-802 (registration)The registration of the 'expected_stats_batch' tool using the @mcp.tool() decorator.
@mcp.tool() def expected_stats_batch(