steam-games-mcp
AGENTS.md4 starsChanged 28 days agoMITView on GitHub
Single source of truth for working on this repository — for any model or agent. CLAUDE.md only references this file (@AGENTS.md); keep all shared guidance here, not in CLAUDE.md. (For end-user/runtime docs, see README.md.) A TypeScript MCP server for Steam. Hybrid backend, mirroring the mal-mcp/tmdb-mcp pattern: store/game reads go through the Steam Storefront API (store.steampowered.com/api/) which needs *no key*, while player data (profiles, libraries, achievements, friends) uses the *official Steam Web API* (api.steampowered.com) which needs a *free key**. Design rationale…
- Reads credentials
We split this AGENTS.md into the rules it gives an agent
5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 100
Keep clients fetch+cache only.
- Very specific: line 117
Use describe()/test() nesting in src/__tests__/ whenever 2+ tests share a subject.
- Very specific: line 119
Write tool descriptions and per-field .describe() text for the calling model: explain when to use a tool and what each parameter means.
- Distinctive: line 133
Keep dependencies minimal.
- Very specific: line 165
Update CHANGELOG.md (Unreleased section) — see the changelog-style skill for entry style.
≈ “Common” is an estimate: at least 1 in 5 of the 36,368 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AGENTS.md
Single source of truth for working on this repository — for any model or
agent. CLAUDE.md only references this file (@AGENTS.md); keep all shared
guidance here, not in CLAUDE.md. (For end-user/runtime docs, see README.md.)
Project shape
A TypeScript MCP server for Steam. Hybrid backend, mirroring the mal-mcp/tmdb-mcp
pattern: store/game reads go through the Steam Storefront API
(store.steampowered.com/api/*) which needs no key, while player data
(profiles, libraries, achievements, friends) uses the official Steam Web API
(api.steampowered.com) which needs a free key. Design rationale (why two
clients, the keyless caveat, why no SteamDB/price-history, API references,
template reuse) lives in docs/architecture.md.
src/
index.ts # bin entry — calls start()
server.ts # buildServer() + start(); registers everything
config.ts # env → validated Config (zod)
version.ts # VERSION/USER_AGENT, kept in sync with package.json by a test
format/ # raw Steam payloads → trimmed, agent-facing shapes: storefront.ts,
# web.ts + webAchievements.ts (official Web API: player data /
# achievement lists), store.ts (keyless store-service summarizers:
# GetItems/Query/wishlist/recommendations) over storeCard.ts (their
# shared StoreItem/tag/compat/card/filter toolkit), shared.ts (helpers),
# charts.ts (keyless chart services: top sellers / most played /
# live concurrent players, for get_charts).
# Each shaper has a co-located *.schemas.ts (schema-first: the shaper
# builds its return value via `schema.parse({...})`, see Conventions);
# storeCard.ts shares store.schemas.ts with store.ts
lib/ # GENERIC carcass: http, rateLimit, cache, concurrency, errors,
# logger, result
clients/ # storefront.ts (keyless store; takes storeService as a
# PurchasePriceSource, because Storefront prices are wrong —
# see docs/architecture.md), web.ts (official Web API; key
# optional; builds storeService.ts, exposed via `.store` for
# tools/webStore.ts; also the one place that talks to
# steamcommunity.com, to resolve a vanity STEAM_ID without a
# key), storeService.ts (modern store-browse/
# query/wishlist-sorted card services)
tools/ # storefront.ts, webStore.ts (keyless-capable Web API tools),
# webPlayer.ts (key-gated player tools), webShared.ts (steamid
# schema, steamIdTool helper and requireKey, shared by the two),
# common.ts (shared param schemas, toCompatFilters + reply
# wrapper across storefront+web), guard.ts, prompts.ts (MCP
# Prompts)
__tests__/ # node:test (*.test.ts) + helpers.ts
scripts/ # build-tests.mjs, run-tests.mjs, sync-version.mjs (+ its
# sync-version.d.mts declaration) (generic), check-api.mjs
# (domain), preversion-check.mjs (CHANGELOG gate)
.agents/skills/ # reusable agent workflows for this repo (e.g. live-audit/) —
# plain Markdown with a YAML frontmatter name/description,
# not tied to any one tool's orchestration features. Same
# skill name/layout as this project's sibling MCP servers
# (tmdb-mcp, mal-mcp, anilist-mcp-server) — sync
# improvements both ways rather than letting them drift.
# `.claude/skills` is a symlink here (Codex CLI/Gemini CLI
# read `.agents/skills` directly, Claude Code the symlink)Commands
npm run build # tsc --noEmit + tsup → dist/index.js (single ESM bundle)
npm test # build tests with esbuild, run with node:test
npm run test:coverage # same, with coverage (gate: ~80%)
npm run lint # eslint
npm run format # prettier --write
npm run check:api # live upstream health-check (Storefront keyless; Web key checks skipped without STEAM_API_KEY)
npm run inspector # run under the MCP InspectorConventions
- Docs and in-code text are English (README, docs, comments, tool descriptions, error messages) — and so is everything posted to a public/shared surface (commit messages, GitHub issue/PR comments and bodies), regardless of what language the working conversation happens to be in.
- Runtime floor is Node ≥ 20.11 (global
fetch, stablenode:test,context.mock.timersused throughout__tests__/,AbortSignal.any()inlib/http.ts, andimport.meta.dirnamein thescripts/*.mjshelpers — need 20.4+/20.3+/20.11+ respectively, so>=20alone would understate the real requirement); tsup targetsnode20(esbuild's Node targets are major-version-only, so this doesn't need to track the patch floor)..nvmrcpins the maintainer's local dev version (currently22) for convenience, not the supported floor — CI'snode: [20, 22, 24]matrix is what actually enforces>=20.11. Note@types/nodetracks current Node, not the floor, sotscwill happily accept an API that only exists past 20.11; the CI node-20 job is the only thing that catches it. - Never write to stdout — it is the MCP protocol channel. Use the logger,
which writes to stderr only and redacts credentials (the Web API key
travels as a
keyquery param). There is no MCPloggingcapability and nonotifications/messagemirroring (removed per SEP-2577 / protocol 2026-07-28 — seesrc/server.ts's comment onserveStdio). To add a log destination, pass asinktocreateLoggerrather than callingconsole.*. - Tool failures return
{ isError: true }results (viaguard()/result.ts), never thrown — the agent should get an actionable message. - Keep clients fetch+cache only; all raw→agent-facing shaping lives in
src/format/(storefront.ts/web.ts/webAchievements.ts/store.tsover thestoreCard.tstoolkit, generic helpers inshared.ts). Every exported summarizer builds its return value via its co-located schema'sschema.parse({...})(schema-first: the schema is the single source of truth, so the shaper and itsoutputSchemacan't drift apart) —.parse()calls and schema imports belong only informat/, never inclients/. Trim responses for token efficiency (cap big lists like a player's library, a game's achievements, or a friend list). - Never use
z.date()/z.bigint()/z.nan()/.transform()/z.map()/z.set()in a tool'sinputSchema/outputSchema. The SDK converts every registered schema via zod's nativez.toJSONSchema()with its defaultunrepresentable: "throw"(seenode_modules/@modelcontextprotocol/server/ dist/src-*.mjs'sstandardSchemaToJsonSchema) — one of these types reaching a tool schema throws at registration time, not a degraded-but-working JSON Schema. Dates from Steam already arrive as display strings (e.g.release_date), so plainz.string()is correct there anyway, not a workaround. - Use
describe()/test()nesting insrc/__tests__/whenever 2+ tests share a subject; a flat list oftest()calls is fine for single-subject files. - Write tool
descriptions and per-field.describe()text for the calling model: explain when to use a tool and what each parameter means. Check new or edited descriptions against thetool-description-checkskill (Glama's TDQS rubric) before committing. - Name a field for what it actually accepts, not a generic ID suffix —
e.g.
steamid(a 17-digit SteamID64 only) rather than a genericuser_id/id, with the vanity/custom-profile-name case handled by its own separatevanityfield (resolved to asteamidviaresolve_vanity_url), andget_game'sappid/namekept as two distinct optional fields instead of one overloadedid. Keep the same field name for the same concept across every tool that takes it — grep sibling tools before naming a new field for an existing concept (steamidis one shared schema exported fromtools/webShared.ts, not redeclared per tool). - Keep dependencies minimal. New deps need a clear justification (supply-chain).
- Never commit secrets. The key comes from env vars / OS keychain only.
- Cross-platform: macOS, Linux and Windows. Avoid POSIX-only shell in npm scripts (use the Node helper scripts).
- Commits: author/committer
Grinv <4070730+Grinv@users.noreply.github.com>; do not add aCo-Authored-Bytrailer. - Work directly on
main— single-developer repo, so no feature branches (a forgotten branch silently drifts: one sat unmerged for a month and needed a conflicted rebase to land). Commit straight tomain; only branch if the maintainer explicitly asks for one. - CodeQL (
.github/workflows/codeql.yml) scansjavascript-typescripton push/PR to main plus a weekly cron — no local equivalent command; findings surface under the repo's Security → Code scanning tab.
Testing the live/published server
For a full audit of the currently published (or just-fixed) package —
build/test/lint plus hammering the live MCP tools with edge cases,
cross-checked against source — follow
.agents/skills/live-audit/SKILL.md.
It covers the keyless-vs-key-gated tool split, SteamID64/appid edge cases,
and known bug classes found in past passes worth checking don't recur. For a
diff that touches error-handling or partial-failure resilience specifically (a new
Promise.allSettled/try-catch), also run /code-review (or an equivalent
static-reasoning pass) over it — live-testing structurally can't trigger one
specific sub-request's failure on cue, so bugs in that exact path (an
unsanitized error message, an outer Promise.all quietly defeating the fix)
need the code read, not called.
Before opening a PR
Run npm run build && npm test && npm run lint && npm run format:check.
Update CHANGELOG.md (Unreleased section) — see the changelog-style skill for
entry style.
Releasing
package.json is the single source of truth for the version; npm version
bumps + syncs every derived file + tags the release. See the release skill
for the full steps (including the preversion gate on CHANGELOG.md and
tool descriptions) and MCP Registry details.
Notes
Personal/scratch notes (not load-bearing) live in docs/notes.md.
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 28,693 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository8 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- changelog-styleSkill
- docs-consistency-checkSkill
- live-auditSkill
- prompt-checkSkill
- releaseSkill
- self-learningSkill
- tool-description-checkSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

