An MCP server for managing interactive novel/game stories with scene navigation, player state tracking, and image loading capabilities.
The server provides 10 tools for a visual novel game engine with basic YAML-backed scene management. All tools are explicitly registered via @mcp.tool() decorator and have descriptions. However, the descriptions are minimal (average ~60 chars) and lack actionable context for LLM selection. Parameter schemas are present with type definitions but lack detailed descriptions explaining constraints, formats, or usage context. Output schemas are not documented. Error handling is minimal, most tools return empty dicts or bare exceptions without recovery guidance. The design follows a simple read/write pattern appropriate to the domain, but falls short of production-grade quality standards.
Record player's choice within their selected story.
Return list of visited scene_ids for the player.
Return scene document (Markdown body + choices).
Return chronological event log for the story.
Return metadata for a given story.
Return global state (scene_id, flags, summary).
Return available stories with minimal metadata.
Descriptions are minimal (30-65 chars) and lack actionable context. LLMs cannot determine WHEN to use each tool or HOW it differs from similar ones. E.g., 'Return metadata for a given story' is vague, it doesn't explain what metadata is included, when to call it vs get_story_state, or what the user will do with the result.
Parameter descriptions are almost entirely absent. The 'story_id' parameter is described as 'The unique identifier for the story' in every tool, but no guidance on format, valid values, or how to obtain one. The 'free_text' param in choose() lacks description of constraints (length, encoding, validation). LLMs cannot infer parameter intent without explicit descriptions.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 53 | 2026-07-28+ | v2 |
| 2026-03-09 | D | 51 | - | v1 |
Return list of available images for the specified story.
Return the image for the player's current story and scene. Resize and compress until data size <= 1MB.
Bind a story to the player and return its opening scene.
Output schemas are not documented anywhere. The source shows tools return dict, list[dict], list[str], or str, but there is no specification of which fields are in each dict, what they mean, or how they chain to downstream tools. E.g., select_story returns {story_id, scene_id, scene}, but 'scene' structure is opaque. get_scene returns the full SCENES dict for a story/scene pair, but the schema of the scene object is not defined.
Error handling is minimal and non-actionable. select_story raises ValueError with a message, but there is no guidance for recovery: 'Unknown story_id or story has no scenes: X' tells the LLM the story is bad, but should it call list_stories()? Also, choose() may silently fail if player_id has no selected story, or if scene_id is invalid. There is no error message or recovery path documented.
The choose() tool accepts optional 'free_text' parameter but its semantics are unclear. Is this a player comment? A narrative branch trigger? The description says 'Optional free-form text input from player' but does not explain what the server does with it (logged? used to generate next scene? ignored?). This ambiguity invites misuse.
Tool naming could be more consistent with Arcade patterns. 'select_story' is good (verb+noun), but 'choose' is vague and lacks a noun (choose what? a choice). Naming should be 'select_choice', 'make_choice', or 'advance_scene' to clarify intent. Similarly, 'load_scene_image' vs 'get_scene_image', inconsistent verb choice with get_scene, get_player_path, etc.
No pagination support for potentially large result sets. list_stories, list_story_images, and get_story_log return unbounded arrays. A story with hundreds of images or a long event log will blow the context window. No limit/offset or cursor-based pagination is offered.
load_scene_image has numeric parameters 'max_width' and 'max_height' with defaults but no bounds specified. Are values in pixels? What are minimum/maximum valid ranges? Can an LLM pass 0 or negative? 999999? The description and schema lack constraints.