API & MCP reference
Everything the Studio can do, your tools can do. The Stories API is a small, stable surface — and the ScryMaster MCP connector wraps it as safe buttons for an AI assistant. This page is the human guide; the machine-readable truth is always /api/v1/openapi.json and the live vocabulary at /api/v1/actions.
Authentication — two front doors, one set of rules
- In the app: your signed-in session (cookies) — this is what the Studio uses.
- Programmatic: a personal access token. Mint one in the Studio → ⚙ Settings → Integrations → API tokens (it starts with
scry_) and send it asAuthorization: Bearer scry_…. Tokens are yours to revoke any time; treat them like passwords.
Both doors call the same server functions with the same ownership checks, save-conflict protection, publish gates, and the official-content delete guard — an assistant can never reach a code path you couldn’t.
The document schema — author from the spec, not from a sample
The request/response bodies are fully specified in /api/v1/openapi.json under components.schemas. The top-level AdventureDoc schema (and every sub-shape) is derived from the engine’s closed source-of-truth types, so it can never drift from what the validator accepts:
- Required fields —
AdventureDocrequiresformatVersion,meta(which requiresid+title),start,nodes, anddeps. - Node grammar per kind —
AdvNodeis aoneOfkeyed on thekinddiscriminator: agateusesbranches+else, achoiceuseschoices, askillCheckusescheck+tiers, acombatusesencounter+onVictory/onDefeat, and so on. - The closed action vocabulary —
Actionis aoneOfdiscriminated bytype; each verb carries its exact param shape, and deprecated verbs are flagged. This mirrors the live list at /api/v1/actions. - The Condition AST —
Conditionenumerates every predicate form (flag,var,hasItem,relationship,all/any/not…). - The
{{ binding }}grammar — prose fields interpolate run state; see bindings & the data tree for the token/expression syntax.
So save_adventure’s request body and get_adventure’s response are both concrete schemas you can validate against — a fresh account never has to reverse-engineer the doc shape from an existing adventure.
The safety contract
- No clobbering. Reading a draft returns a
draftHash; send it back asbaseHashwhen saving or publishing. If the draft changed in between (another tab, another tool), the write is rejected with409— re-read, re-apply, re-save. - Publish is gated. Validation + IP denylist run server-side; a publish that would discard content players already have is blocked until explicitly confirmed.
{ dry: true }returns the full verdict with no write. - Players never break. Published versions are immutable and runs pin the version they started on — a new release is adoption, never migration.
- Authorship is recorded. Every create/save/restore/delete/publish lands in the story’s provenance ledger (visible in the publish center’s “Recent activity”) labeled by surface: Studio, MCP, or system.
Core endpoints
GET/POST /api/v1/adventures — list / create draftsGET/PATCH/DELETE /api/v1/adventures/:id — read / save ({doc|node|rule|listing, baseHash}) / deletePOST …/:id/validate · POST …/:id/simulate — the completeness + playability reportsGET …/:id/health · GET …/:id/world — the Campaign Health scorecard + derived World Model (steer toward a good story)POST …/:id/publish — publish (or {dry:true} for the verdict)GET …/:id/versions · POST …/:id/restore — history + restore-into-draftGET …/:id/activity — the authorship ledger (owner-only)GET /api/v1/actions · GET /api/v1/openapi.json — vocabulary + spec (public)GET /api/v1/store · GET /api/v1/adventures/published/:id — the player-facing surface (public)
The MCP connector
Remote (one URL): add https://scrymaster.com/api/mcp as a connector — claude mcp add --transport http scrymaster https://scrymaster.com/api/mcp. Authenticate via the OAuth sign-in it offers (approve once in the browser), or send your scry_… token as a bearer header. Stdio (npm): claude mcp add scrymaster --env SCRYMASTER_TOKEN=scry_… --env SCRYMASTER_API_URL=https://scrymaster.com -- npx -y @scrymaster/mcp, or the equivalent { "command": "npx", "args": ["-y", "@scrymaster/mcp"] } in any MCP client. No MCP client at all? Every tool below maps 1:1 to a bearer-authed REST call. Always use the apex host — never www (it 308-redirects and some agents won’t follow it).
- Grounding:
get_vocabulary(works before auth — always call it first; now also lists the wizard/module recipes). - Drafts:
list_adventures,get_adventure,create_adventure,save_adventure,delete_adventure,update_listing. - Quality:
validate_adventure,simulate_adventure,get_health(scorecard),get_world(cast/quests/factions — check they read back). - Releases:
dry_publish,publish_adventure,list_versions,restore_version. - Art & narration:
upload_image,set_scene_background,set_choice_image, plus the narration enrichment suite.
The authoring contract a well-behaved assistant follows: get_vocabulary → get_adventure → edit → save_adventure(baseHash) → validate_adventure → simulate_adventure → get_health → get_world → dry_publish → publish_adventure. Studio panels (Graph, World, Database, State…) are views over the same document — an assistant edits the document itself, at full parity, and reads get_health/get_world to steer toward a good story, not just a valid one.
Troubleshooting
- 401 / “invalid or revoked token” — the token is wrong, revoked, or missing the
scry_prefix. Mint a fresh one in ⚙ Settings. - 409 on save/publish — the draft changed since you read it. Call
get_adventureagain, re-apply your edit to the fresh doc, and save with the new hash. Never retry the same call blindly. - 422 on publish — the response includes the validation report, denylist hits, or the data-loss report; fix what’s listed (or, for intentional content removal, review the loss report with the story owner and pass
confirmDataLoss). - 403 deleting — official content (the tutorial) can never be deleted.
- Vocabulary unreachable — don’t author from memory; fix connectivity first. The engine rejects unknown verbs.
Next: Build with your AI →
