| name | working-on-plugins |
| description | Game plugin and adapter section design for Savecraft. Use when creating new plugins, designing GameState sections, adding sections to existing parsers, or working on section output in plugins/ or worker/src/adapters/. Triggers on section layout, progressive disclosure, GameState output, plugin sections, adapter sections, section sizing, or overview design. |
Working on Plugins: Section Design
Both WASM plugins (plugins/*/parser/) and API adapters (worker/src/adapters/) produce the same GameState shape: identity, summary, and sections. This skill covers how to design sections so they work well with the MCP tools and AI consumers.
For the ndjson contract, WASM runtime, and plugin build system, see docs/plugins.md. For API adapter architecture, see docs/adapters.md.
Overview Section (Required)
get_save embeds one section's data as the overview in its response. It picks the first match from this list:
character_overview → player_summary → overview → summary
Every plugin MUST produce a section matching one of these names. If none match, get_save falls back to the first section alphabetically — which may be a 200KB deck list or item dump.
The overview section is the AI's first look at the save. It should contain enough context to answer "what is this character/save?" and guide the AI to the right detailed sections.
What belongs in the overview
- Identity: name, class, level, rank — whatever defines "who"
- Key stats: currency, progression milestones, win/loss record
- Index of available sections with pointers — deck names (not card lists), character names (not inventories), match list (not turn-by-turn logs)
- Anything that helps the AI decide which section to fetch next
What does NOT belong in the overview
- Full item lists, card lists, or inventories
- Turn-by-turn logs, match replays, or event histories
- Large nested objects that the AI won't use to route
Target: overview section < 15KB. A typical overview is 2-10KB.
Section Size Limits
| Limit | Value | Enforced by |
|---|
| Hard max per section | 80KB | SECTION_SIZE_LIMIT in worker/src/mcp/tools.ts — get_section rejects sections over this |
| Overview target | < 15KB | Convention — keeps get_save response usable |
| Individual item section | < 10KB typical | Convention — per-deck, per-character sections |
If a section could exceed 80KB with realistic data, it MUST be split.
Progressive Disclosure Pattern
When a game has a large collection (decks, characters, items, matches), split into per-item sections with an index in the overview.
Pattern
overview section:
decks: [{name: "Slivers", format: "Brawl", section: "deck:Slivers"}, ...]
deck:Slivers section: (full card list, ~5KB)
deck:Control section: (full card list, ~3KB)
The AI sees the index in the overview and fetches individual items as needed. This is how D2R shared stash (overview + tab1, tab2, ...) and MTGA (player_summary + deck:*, game:*) work.
When to split
Split when any of these are true:
- Collection has variable size controlled by the player (decks, blueprints, characters)
- A single collection item can be > 5KB (full card lists, turn-by-turn logs)
- Total collection could exceed 80KB (80 decks × 5KB = 400KB)
Section naming for per-item sections
Use prefix:human_readable_name — the AI requests sections by name, so readability matters.
deck:[HB] Slivers (MTGA deck)
game:7a0be838-0033-4e16 (MTGA game log by matchId)
tab1, tab2, tab3 (D2R stash tabs)
character:Warrior (per-character sections)
Stale section cleanup
The daemon sends allSectionNames in every PushSave message. The worker deletes any sections NOT in this list. Dynamic section names (deck:, game:) work correctly — the parser includes them all in the output, and the worker cleans up sections that no longer exist.
Section Descriptions
Section descriptions are the AI's guide for when to fetch a section. Write them as directives, not documentation.
Good (tells AI when/why to fetch):
"Deck list for [HB] Slivers (Brawl) — main deck, sideboard, and command zone cards"
"Turn-by-turn game log for match abc123 — use to analyze play sequencing and identify misplays"
"Aggregated character stats: resistances, magic find, FCR/FHR breakpoints — use to evaluate gear upgrades"
Bad (explains what it is, not when to use it):
"Contains the player's deck data"
"Game log information"
"Character statistics"
Existing Section Patterns by Game
| Game | Overview Section | Per-Item Sections | Reference |
|---|
| D2R Character | character_overview | None (sections are small) | plugins/d2r/parser/main.go |
| D2R Stash | overview | tab1, tab2, ... | plugins/d2r/parser/main.go:545 |
| MTGA | player_summary | deck:*, game:* | plugins/magic/parser/main.go |
| Clair Obscur | overview | character:* | plugins/clair-obscur/parser/ |
| WoW | character_overview | None (API data is bounded) | worker/src/adapters/wow/ |
| SDV | player_summary | None (data is naturally bounded) | plugins/sdv/parser/main.go |
Attribution (Required)
Every plugin must declare an [attribution] section in plugin.toml listing the third-party IP sources whose data it uses. The build pipeline reads this to embed legal disclaimers in views.
[attribution]
sources = ["wotc", "scryfall", "17lands"]
Valid source keys are defined in views/src/attributions.ts: wotc, scryfall, 17lands, blizzard, raiderio, ludeon, concernedape, kepler. To add a new source, add it to the SOURCES record in that file first.
The build fails if [attribution] is missing or uses an unknown key.
Checklist for New Plugins