ScryMaster Docs

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 as Authorization: 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 fieldsAdventureDoc requires formatVersion, meta (which requires id + title), start, nodes, and deps.
  • Node grammar per kindAdvNode is a oneOf keyed on the kind discriminator: a gate uses branches + else, a choice uses choices, a skillCheck uses check + tiers, a combat uses encounter + onVictory/onDefeat, and so on.
  • The closed action vocabularyAction is a oneOf discriminated by type; each verb carries its exact param shape, and deprecated verbs are flagged. This mirrors the live list at /api/v1/actions.
  • The Condition ASTCondition enumerates 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 as baseHash when saving or publishing. If the draft changed in between (another tab, another tool), the write is rejected with 409 — 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 drafts
GET/PATCH/DELETE /api/v1/adventures/:id — read / save ({doc|node|rule|listing, baseHash}) / delete
POST …/:id/validate · POST …/:id/simulate — the completeness + playability reports
GET …/: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-draft
GET …/: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_adventure again, 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 →