A CLI that finds all those free games from all those game stores.
And, if you want, posts them to a Chatto channel. MCP server included.
|
|
2 ngày trước cách đây | |
|---|---|---|
| cmd | 2 ngày trước cách đây | |
| internal | 2 ngày trước cách đây | |
| .gitignore | 3 ngày trước cách đây | |
| AGENTS.md | 2 ngày trước cách đây | |
| LICENSE | 3 ngày trước cách đây | |
| README.md | 2 ngày trước cách đây | |
| go.mod | 2 ngày trước cách đây | |
| go.sum | 2 ngày trước cách đây |
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
(a fork of vogler/free-games-claimer)
and 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.
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).
# 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.
cheapo mcp exposes the free games data as an MCP server. Two
transports:
# 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 <addr>, --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:
{"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. /healthz answers unauthenticated with
{"status":"ok","version":...} for monitoring. Example:
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).
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).cheapo list --store gog. GOG has no public giveaway
API; the provider reads the embedded section JSON of the homepage
(<script id="gogcom-store-state">, object with endDate + product).
Exactly one active giveaway; kind: upcoming if the start period lies
in the future.cheapo list --upcoming shows announced
upcoming free promotions (Epic only; Steam ignores the flag).| Store | Feasible without browser/login? | Source |
|---|---|---|
| Epic | ✅ done | public JSON endpoint freeGamesPromotions |
| Steam | ✅ done | search HTML scrape + featuredcategories JSON API |
| GOG | ✅ done | embedded section JSON of the homepage (gogcom-store-state) |
| Prime Gaming | ⚠️ hard | requires Amazon login (session cookies); reference: vogler (Playwright). No public API. |
| AliExpress | ⚠️ hard | login + quirks (exit code != 0 without coins); only points bonuses, hardly any games |
| itch.io | ✅ feasible | https://itch.io/games/free (HTML scrape); no login for detection |
| Humble Bundle | ⚠️ medium | changing promo structure; no stable free-games JSON, scraping required |
| Ubisoft Connect | ❌ | login + client required, no public API |
The package internal/chatto/ is a hand-built ConnectRPC client
(JSON codec) for Chatto (tested against 0.5.0-beta.27+): POST
{base}/api/connect/chatto.api.v1.<Service>/<Method> with a bearer token. The
Chatto Go codebase (github.com/chattocorp/chatto) keeps generated code and handlers under
internal/ — not importable from outside, hence the own client. Message
schemas come from proto/chatto/api/v1/ of the Chatto repo (field numbers
compatible, only the JSON subset is modelled).
CLI commands (credentials deliberately provided by the operator, never fetched automatically):
# .env in the project root (godotenv), never commit it — it is in .gitignore:
CHATTO_TOKEN="<token>" # set by the operator
# CHATTO_BASE_URL=https://chat.chatto.run # optional, is the default
cheapo chatto-rooms # list rooms (client verification)
cheapo chatto-send --room <room_id> # post current free games
cheapo chatto-send --room <room_id> --upcoming # upcoming ones (Epic)
Still open (roadmap): a watch-mode daemon that posts to the chat on new free games (state signature like free-games-notifier); Discord-freestuff-like server-side integration (just an idea).
cmd/cheapo/ Cobra CLI (list, watch, mcp; --store epic|steam|gog|all)
internal/game/ Provider-agnostic game type + provider interfaces
internal/providers/ Store selection (epic|steam|gog|all) for CLI and MCP
internal/ops/ List modes (free/upcoming/deals) — shared by CLI and MCP
internal/epic/ Epic client (freeGamesPromotions, public JSON endpoint)
internal/steam/ Steam client (search scrape + featuredcategories API)
internal/gog/ GOG client (homepage section JSON, gogcom-store-state)
internal/chatto/ Hand-built ConnectRPC client (JSON) for Chatto servers
internal/watch/ Polling/eventing across all providers: reports only new games
internal/mcpserver/ MCP tool (list_offers) over HTTP/stdio
The reference project (vogler/free-games-claimer) automates a real browser with Playwright: login (session cookies, 2FA, captcha handling), detecting the "Free Now" tiles on the store page and clicking through the purchase process. The free games info comes from the same backend JSON that we query directly — details and sources in AGENTS.md.
GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). Copyright (C) 2026 Paul Klumpp. Full text: LICENSE.