Frontend State Management
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
Frontend state management in the MCP Games ecosystem is handled primarily within the cyoa-engine application, leveraging a combination of Zustand for client-side state orchestration and TanStack Query for asynchronous data fetching. This system ensures real-time narrative updates, interactive choice selection, and persistent game progress across the Choose Your Own Adventure (CYOA) experience.
The architecture separates the core game logic—encapsulated in the story-engine package—from the UI state. While the frontend manages the user interface and connection status, the underlying story progression is driven by a deterministic state machine that tracks variables, scene transitions, and user history.
Sources: apps/cyoa-engine/README.md, apps/cyoa-engine/package.json:16-18, packages/story-engine/src/index.ts:1-15
Core State Architecture
The frontend state is divided into three primary domains: local UI state, story engine state, and external context state (MCP connections).
State Management Technologies
| Tool | Purpose | Implementation File |
|---|---|---|
| Zustand | Global client-side state for the UI, profile, and active game session. | apps/cyoa-engine/package.json |
| TanStack Query | Managing asynchronous requests to the mcp-connector and narrative-ai. | apps/cyoa-engine/package.json |
| StoryEngine | Logic-heavy state transitions, variable manipulation, and progress calculation. | packages/story-engine/src/index.ts |
Sources: apps/cyoa-engine/package.json:16-18, apps/cyoa-engine/README.md:14-19
Story State Structure
The StoryState interface defines the minimum required data to represent a game session at any point in time.
export interface StoryState {
storyId: string;
userId: string;
currentSceneId: string;
variables: StoryVariables;
choiceHistory: string[];
timestamp: string;
}
Sources: packages/story-engine/src/index.ts:18-25
Data Flow and Transitions
The frontend initiates state transitions when a user interacts with a Choice component. This triggers the StoryEngine, which validates requirements and applies variable effects before returning the updated state.
Choice Processing Flow
This diagram illustrates the flow from a user interaction to the updated UI state.
The engine ensures that choices are only "available" if the current StoryVariables meet the criteria defined in choice.requirements.variables.
Sources: packages/story-engine/src/index.ts:49-76, packages/story-engine/src/index.ts:140-155
Variable Tracking and Effects
The state management system includes a dedicated mechanism for tracking "Story Variables"—key-value pairs that influence the narrative. When a choice is made, VariableEffect objects modify this state using specific operations.
Variable Operations
| Operation | Description | Logic Implementation |
|---|---|---|
set | Replaces the variable value entirely. | newVariables[effect.variable] = effect.value |
increment | Adds a numeric value to an existing variable. | `(Number(var) |
decrement | Subtracts a numeric value from an existing variable. | `(Number(var) |
append | Adds an item to a variable array. | (newVariables[var] as unknown[]).push(val) |
Sources: packages/shared-types/src/index.ts:79-84, packages/story-engine/src/index.ts:162-187
Progress and Persistence
Frontend state can be serialized to the GameProgress format for persistence in the user's profile.
Persistence Lifecycle
The sequence below shows how the frontend state is transitioned into a persistent save state.
The toGameProgress method maps the internal StoryState to the GameProgress interface defined in the shared types package, which includes the choices history and the final timestamp.
Sources: packages/story-engine/src/index.ts:117-133, packages/shared-types/src/index.ts:120-126
Conclusion
Frontend state management in this monorepo is a multi-layered system designed for consistency and extensibility. By leveraging Zustand for UI reactivity and a decoupled StoryEngine for narrative logic, the system maintains a clear separation between how the story is displayed and how its underlying data evolves. This architecture supports complex branching narratives while ensuring that user progress and MCP-derived context remain synchronized across the application.