# cheapo A CLI that finds all those free games from all those game stores. And, if you want, posts them to a Chatto channel. Free games CLI in Go — lists current free game offers (Epic Games Store, Steam) and can use `watch` to wait for **new** free games. Inspired by / reference: [free-games-claimer](https://github.com/DoSpamu/free-games-claimer) (a fork of [vogler/free-games-claimer](https://github.com/vogler/free-games-claimer)) and [free-games-notifier](https://github.com/jaixmario/free-games-notifier). Unlike the reference projects, we need **no browser and no login**: we read the public store endpoints directly (details: `AGENTS.md`). Claiming the games is not part of this tool — it only detects and reports them. ## Install / build ```bash go build -o bin/cheapo ./cmd/cheapo ``` Prerequisites: Go ≥ 1.27. For linting: `gopls` (`go install golang.org/x/tools/gopls@latest`), formatting with `gofmt` (details: `AGENTS.md`). ## Usage ```bash # Games free right now (Germany, German) cheapo list # As JSON (e.g. for further processing / bot) cheapo list --json # Other region / locale cheapo list --country AT --locale en-US # Wait for new free games (polling every 15 min, baseline = current state) cheapo watch # Different interval, single check cheapo watch --interval 5m cheapo watch --once # Other stores / upcoming free games (preview, Epic only) cheapo list --store steam cheapo list --store gog cheapo list --store all cheapo list --upcoming ``` Example output: ``` 🎮 [epic] Monument Valley By: Netflix, Inc. Instead of: 6.99 € Free until: 02.11.2026 17:00 (CET) https://store.epicgames.com/en-US/p/monument-valley-1d99d3 ``` `cheapo watch` is suitable as a long runner for a systemd service or cron/timer; every newly detected game is reported on stderr as soon as a poll finds it. ## MCP server `cheapo mcp` exposes the free games data as an **MCP server**. Two transports: ```bash # HTTP (Streamable HTTP) — the mode for remote clients, agents, monitoring cheapo mcp --http 127.0.0.1:8080 # stdio — only for clients that start the binary themselves cheapo mcp ``` Flags (HTTP): `--http `, `--path` (default `/mcp`), `--token` or `$CHEAPO_MCP_TOKEN` (bearer auth), `--stateless` (for proxies/load balancers), `--session-timeout` (default 30m). Region/locale/stores come from the global flags `--country`/`--locale`/`--store` and apply as defaults for the tool calls. **Tools:** | Tool | Purpose | |------|---------| | `list_offers` | Mirrors `cheapo list`: `mode` = `free` (default) / `upcoming` / `deals`, plus `store`, `country`, `locale` | Example call: ```json {"name": "list_offers", "arguments": {"mode": "deals", "store": "steam"}} ``` The server is **stateless**: every call returns the complete current state. What counts as "new" is decided by the client — it is the only one that knows what it has already seen. This means no server memory, no state file, and no surprises after a restart. Responses are structured (MCP `structuredContent`) with `title`, `store`, `kind`, `seller`, `url`, `original_price`, `starts`, `ends` (RFC 3339, absent when the date is unknown), `id`. Unreachable stores land in `warnings` instead of failing the call. **Auth & operation**: without `--token` the endpoint is open — in that case only bind to loopback. The token comes via `--token`, `$CHEAPO_MCP_TOKEN` or a `.env` file (`CHEAPO_MCP_TOKEN=...`, same mechanics as Chatto — existing environment variables take precedence). `/healthz` answers unauthenticated with `{"status":"ok","version":...}` for monitoring. Example: ```bash curl http://127.0.0.1:8080/healthz # MCP client (streamable HTTP): http://127.0.0.1:8080/mcp ``` **Limitations:** A long runner that posts on new free games remains `cheapo watch` or the Chatto daemon (below), not the MCP server — it only answers requests (`list_offers` covers `list`; `watch` has no equivalent). ## More platforms - **Steam** ✅ done: `cheapo list --store steam` or `--store all`. Free-to-keep via HTML scrape of `store.steampowered.com/search/?maxprice=free&specials=1`, free weekend via `store.steampowered.com/api/featuredcategories/` (details: `AGENTS.md`). With `--store` you choose `epic`, `steam`, `gog` or `all` (default). - **GOG** ✅ done: `cheapo list --store gog`. GOG has no public giveaway API; the provider reads the embedded section JSON of the homepage (`