steam-backlog-enforcer/steam_backlog_enforcer/_mcp_query.py
Krzysztof kuhy Rudnicki 7323c896f6
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (push) Waiting to run
Tests / test (push) Waiting to run
Import split-out names from the modules that own them; drop the four lazy facades
hltb, _cmd_done, _whitelist and _actions each kept a module __getattr__
that served names moved out in the 250-line split, via importlib to
dodge the resulting import cycle. pylint cannot see through that, and
graded every such import as no-name-in-module (26 of them). The 32
import statements now name the real module; with nothing left going
through the facades, they are gone.

Also the pylint hook on 4.0.8 with the source-only scope config, the
three implicit-booleaness disables the reference repos carry, the
http.server do_GET admission, and the third-party pins the hook was
missing (aiohttp, websockets, howlongtobeatpy). Gate still 8.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHCbkJWXHjtTUaqvnHRAqA
2026-09-12 20:22:44 +02:00

98 lines
3.4 KiB
Python

"""Read-only MCP tools: dataset, status, stats and the backlog list.
Separated from the mutating tools in
:mod:`steam_backlog_enforcer._mcp_actions` so that "what can this server
change?" is answerable by looking at one file.
"""
from __future__ import annotations
from typing import Any
from steam_backlog_enforcer._manual_pick_lifecycle import status_payload
from steam_backlog_enforcer._mcp_server import (
_DEFAULT_LIST_LIMIT,
_backlog_sort_key,
mcp,
)
from steam_backlog_enforcer._snapshot import load_snapshot
from steam_backlog_enforcer._web_dataset import build_web_dataset, dataset_to_payload
from steam_backlog_enforcer.config import State
from steam_backlog_enforcer.steam_api import GameInfo
@mcp.tool()
def get_dataset() -> dict[str, Any]:
"""Return the full, secrets-free backlog dataset.
Includes every incomplete candidate game with HowLongToBeat times,
ProtonDB tiers, pace-vs-HLTB calibration, and the default CLI thresholds —
the same projection the local web UI consumes. Reads on-disk caches only
(no network, no Steam API key).
"""
return dataset_to_payload(build_web_dataset(State.load()))
@mcp.tool()
def get_status() -> dict[str, Any]:
"""Return the current enforcer status.
Reports the assigned game, finished count, whether the Steam store is
blocked, installed-game count, any active total block (with days remaining),
and whether a manual-pick lock is in force.
"""
return status_payload(State.load())
@mcp.tool()
def get_stats() -> dict[str, Any]:
"""Return backlog completion-time estimates.
A focused subset of the dataset: the default qualifying-games summary
(rush / leisure / worst-case totals) and the player's measured pace versus
HowLongToBeat.
"""
payload = dataset_to_payload(build_web_dataset(State.load()))
return {
"default_summary": payload["default_summary"],
"pace_vs_hltb": payload["pace_vs_hltb"],
}
@mcp.tool()
def list_backlog(limit: int = _DEFAULT_LIST_LIMIT) -> dict[str, Any]:
"""List incomplete games, shortest-completionist-first, capped at ``limit``.
Args:
limit: Maximum number of games to return (non-positive returns none).
"""
snapshot = load_snapshot()
if snapshot is None:
return {
"total": 0,
"returned": 0,
"games": [],
"note": "No snapshot found. Run 'scan' first.",
}
games = [GameInfo.from_snapshot(entry) for entry in snapshot]
incomplete = sorted((g for g in games if not g.is_complete), key=_backlog_sort_key)
capped = incomplete[: max(0, limit)]
return {
"total": len(incomplete),
"returned": len(capped),
"games": [
{
"app_id": g.app_id,
"name": g.name,
"completion_pct": round(g.completion_pct, 1),
"completionist_hours": g.completionist_hours,
}
for g in capped
],
}
# ──────────────────────────────────────────────────────────────
# Gated write tools (preview unless confirm=True; NEVER allowlist these)
# ──────────────────────────────────────────────────────────────