Chapter I.

PotionQuest RPG Engine API Reference

Base URL: https://potionquest.com

Version: 1.0 — 35+ procedural tabletop RPG generators, pure TypeScript, zero AI dependencies.

Source: apps/potionquest/src/lib/rpg-engine/


Table of Contents


Authentication

All endpoints except GET /api/rpg/info require an API key via one of:

X-API-Key: <key>
Authorization: Bearer <key>

The API key is stored as SST secret RpgApiKey in stage potionquest-prod. Logged-in admin users can also authenticate via their potionquest_auth JWT session cookie (used by the admin dashboard at /admin).

Unauthorized response:

{ "error": "Unauthorized. Provide a valid API key via X-API-Key header." }

HTTP 401.


Rate Limiting

  • 60 requests per 60 seconds per IP address
  • Applied before authentication check (prevents brute force)
  • IP extracted from x-forwarded-for or x-real-ip headers, hashed with SHA-256
  • In-memory store, resets on Lambda cold start

Rate limit exceeded response:

{
  "error": "Rate limit exceeded",
  "retryAfterSeconds": 42
}

HTTP 429. Headers: Retry-After: 42, X-RateLimit-Remaining: 0.


Error Responses

All errors follow this shape:

{ "error": "Human-readable error message" }
StatusMeaning
400Bad request — missing or invalid parameters
401Unauthorized — missing or invalid API key
429Rate limit exceeded
500Server error — generator failure

Endpoints

GET /api/rpg/info

Auth: None required.

Returns full API documentation including all generator types, parameter examples, and endpoint descriptions. Useful for programmatic discovery.

curl -s https://potionquest.com/api/rpg/info | python3 -m json.tool

POST /api/rpg/dice

Auth: API key required.

Roll dice using standard tabletop notation.

Request:

{
  "notation": "2d6+3",
  "count": 5
}
FieldTypeRequiredDescription
notationstringYesDice notation: NdS, NdS+M, NdS-M, NdS*M
countnumberNoBatch roll count (max 100, default 1)

Response (single roll):

{
  "notation": "2d6+3",
  "result": {
    "notation": "2d6+3",
    "rolls": [4, 2],
    "modifier": 3,
    "multiplier": 1,
    "total": 9
  }
}

Response (batch roll, count > 1):

{
  "notation": "2d6+3",
  "count": 5,
  "results": [
    { "notation": "2d6+3", "rolls": [4, 2], "modifier": 3, "multiplier": 1, "total": 9 },
    { "notation": "2d6+3", "rolls": [6, 1], "modifier": 3, "multiplier": 1, "total": 10 }
  ]
}

Notation examples: 1d20, 2d6, 1d100, 4d6+0, 1d20+5, 8d6, 2d10-1, 1d6*10


POST /api/rpg/generate

Auth: API key required.

Generate procedural RPG content from 35+ generator types.

Request:

{
  "type": "encounter",
  "params": { "terrain": "forest", "partyLevel": 5, "partySize": 4 },
  "count": 1,
  "format": true
}
FieldTypeRequiredDescription
typestringYesGenerator type (see full list below)
paramsobjectNoGenerator-specific parameters (defaults used if omitted)
countnumberNoNumber of results (max 20, default 1)
formatbooleanNoInclude human-readable formatted string in response

Response (single, count = 1):

{
  "type": "encounter",
  "result": { ... },
  "formatted": "Human-readable text (only if format: true)"
}

Response (batch, count > 1):

{
  "type": "encounter",
  "count": 3,
  "results": [ { ... }, { ... }, { ... } ],
  "formatted": [ "text1", "text2", "text3" ]
}

Invalid type response:

{ "error": "Unknown generator type: 'bad-type'. GET /api/rpg/info for available types." }

HTTP 400.


Generator Reference

Every generator is documented with:

  • Params — all accepted fields with valid values
  • Response — exact JSON shape returned in result
  • Format — whether format: true produces a formatted string

All params are optional unless marked required. When omitted, generators pick random values.


Tier 1: Core DM Tools

encounter

Generate a complete combat encounter with monsters scaled to party level.

Source: src/lib/rpg-engine/encounters/generator.ts

Params:

{
  terrain: "forest" | "mountain" | "swamp" | "desert" | "arctic" | "plains"
         | "jungle" | "coast" | "underwater" | "urban" | "dungeon" | "cave"
         | "ruins" | "sewers",
  partyLevel: number,        // 1-20
  partySize: number,         // typically 3-6
  difficulty: "trivial" | "easy" | "medium" | "hard" | "deadly",  // default: "medium"
  allowBoss: boolean,        // default: false
  minMonsters: number,       // default: 1
  maxMonsters: number        // default: 8
}

Response:

{
  "id": "enc_abc123",
  "name": "Goblin Ambush",
  "description": "A band of goblins lies in wait...",
  "monsters": [
    {
      "monster": {
        "id": "mon_xyz",
        "name": "Goblin Scout",
        "emoji": "👺",
        "level": 1,
        "type": "humanoid",
        "size": "small",
        "stats": { "str": 8, "dex": 14, "int": 10, "con": 10 },
        "ac": 13,
        "hp": 7,
        "attackBonus": 4,
        "damageDice": "1d6",
        "damageBonus": 2,
        "damageType": "piercing",
        "weaponType": "shortbow",
        "xp": 50,
        "gold": "2d6",
        "specialAbilities": ["Nimble Escape"],
        "description": "A wiry goblin with a crude shortbow",
        "behavior": "ambusher"
      },
      "count": 3,
      "position": "flanking"
    }
  ],
  "difficulty": "medium",
  "xpTotal": 150,
  "goldTotal": "6d6",
  "terrain": "forest",
  "ambush": true,
  "lair": false,
  "notes": "The goblins attack from the treeline"
}

Format: No dedicated format function.


npc

Generate a fully-realized non-player character with personality, appearance, background, and roleplaying hooks.

Source: src/lib/rpg-engine/npcs/generator.ts

Params:

{
  race: "human" | "elf" | "dwarf" | "halfling" | "gnome" | "half-elf"
      | "half-orc" | "tiefling" | "dragonborn" | "orc" | "goblin",
  gender: "male" | "female" | "nonbinary",
  socialClass: "peasant" | "common" | "merchant" | "noble" | "outcast",
  occupation: string,              // free-text, or omit for random
  disposition: "friendly" | "neutral" | "unfriendly" | "hostile",
  ageRange: "young" | "adult" | "middle-aged" | "elderly"
}

Response:

{
  "name": "Shaena Gamgee",
  "race": "halfling",
  "gender": "female",
  "personality": {
    "trait": "Cheerful and optimistic, always sees the bright side",
    "quirk": "Cracks knuckles frequently",
    "motivation": "Building a legacy that outlasts them",
    "fear": "Being poor again",
    "secret": "Served a dark power in their youth",
    "voiceHint": "Uses flowery, elaborate vocabulary"
  },
  "appearance": {
    "age": "Adult, in the prime of life",
    "build": "Stocky and solid",
    "distinguishingFeature": "Wears distinctive jewelry",
    "clothing": "Traveling gear, ready for the road",
    "demeanor": "Busy and preoccupied"
  },
  "background": {
    "occupation": "Entertainer (bard, actor, etc.)",
    "origin": "From a distant land",
    "formativeEvent": "Found faith (or lost it)",
    "connection": "Saved an adventurer's life once"
  },
  "oneLiner": "Cheerful halfling entertainer who cracks knuckles frequently",
  "catchphrase": "\"Between you and me...\"",
  "knownFor": "Their incredible memory for names",
  "rumorAboutThem": "They once stole from a noble and got away with it"
}

Format: No dedicated format function.


npc-crowd

Generate a batch of NPCs suitable for populating a tavern or market.

Source: src/lib/rpg-engine/npcs/generator.ts

Params:

{
  size: number,              // crowd size (how many NPCs)
  quality: "squalid" | "poor" | "modest" | "comfortable" | "wealthy" | "aristocratic"
}

Response: Array of NPC objects (same shape as npc above).

Format: No.


combat

Generate a combat setup with initiative order and tactical notes.

Source: src/lib/rpg-engine/encounters/ (uses encounter infrastructure)

Params: Same as encounter.

Response: Same as encounter with additional combat-specific metadata.

Format: No.


loot

Generate a treasure hoard with coins, gems, art objects, and magic items.

Source: src/lib/rpg-engine/loot/treasure.ts

Params:

{
  level: "minor" | "standard" | "major" | "legendary"
}

Response:

{
  "level": "major",
  "coins": {
    "cp": 0,
    "sp": 200,
    "ep": 0,
    "gp": 500,
    "pp": 25
  },
  "gems": [
    {
      "name": "Star Ruby",
      "quality": "exceptional",
      "value": 1000,
      "description": "A deep crimson ruby with a perfect six-rayed star"
    }
  ],
  "art": [
    {
      "name": "Gold-Inlaid Ebony Chest",
      "quality": "masterwork",
      "value": 750,
      "description": "An ornate chest with gold filigree",
      "origin": "Dwarven craftsman"
    }
  ],
  "magicItems": [
    {
      "name": "Flame Tongue Longsword",
      "rarity": "rare",
      "type": "weapon",
      "attunement": true,
      "description": "A longsword wreathed in magical fire",
      "properties": ["Deals extra 2d6 fire damage"],
      "charges": null,
      "value": 5000
    }
  ],
  "totalValue": 8475,
  "description": "A king's ransom in gold, gems, and enchanted steel"
}

rarity values: "common", "uncommon", "rare", "very_rare", "legendary", "artifact"

type values: "weapon", "armor", "potion", "scroll", "ring", "wand", "staff", "rod", "wondrous", "consumable"

Format: Yes — formatted returns a human-readable treasure summary.


shop

Generate a complete shop with owner, inventory, prices, and secrets.

Source: src/lib/rpg-engine/loot/shops.ts

Params:

{
  type: "general_store" | "blacksmith" | "armorer" | "alchemist" | "magic_shop"
      | "temple" | "tavern" | "jeweler" | "fletcher" | "stables"
      | "herbalist" | "bookstore" | "tailor" | "exotic_goods",
  quality: "poor" | "modest" | "comfortable" | "wealthy" | "aristocratic",
  size: "small" | "medium" | "large"
}

Response:

{
  "name": "The Iron Anvil",
  "type": "blacksmith",
  "quality": "comfortable",
  "owner": {
    "name": "Durgan Steelhand",
    "personality": "Gruff but fair dwarf who judges people by their handshake",
    "secret": "Forges weapons for the thieves' guild on the side"
  },
  "description": "A well-kept smithy with a roaring forge...",
  "inventory": [
    {
      "name": "Longsword",
      "category": "weapons",
      "basePrice": 15,
      "currentPrice": 18,
      "quantity": 3,
      "rarity": "common"
    }
  ],
  "roomRate": "1 gp/night",
  "currentEvent": "A sale on surplus shields",
  "weakness": "Low stock of exotic materials"
}

Format: Yes.


Tier 2: World Depth

weather

Generate weather conditions with mechanical effects on gameplay.

Source: src/lib/rpg-engine/world/weather.ts

Params:

{
  climate: "arctic" | "cold" | "temperate" | "warm" | "tropical" | "desert",
  season: "spring" | "summer" | "autumn" | "winter"
}

Response:

{
  "name": "Freezing Rain",
  "description": "A bitter rain that freezes on contact...",
  "icon": "🌧️",
  "temperature": "Below freezing",
  "wind": "Strong gusts from the north",
  "visibility": "Reduced to 100 feet",
  "effects": {
    "travel": "Half speed on roads, quarter speed off-road",
    "combat": "Difficult terrain everywhere",
    "rangedAttacks": "Disadvantage beyond 30 feet",
    "perception": "-5 to Perception checks",
    "stealth": "+5 to Stealth (rain covers sound)",
    "fire": "Open flames extinguished, fire damage halved",
    "special": "CON save DC 12 each hour or gain 1 exhaustion"
  },
  "duration": "2d4 hours",
  "forecast": "Clearing by nightfall"
}

Format: Yes — formats the effects block.


tavern

Generate a complete tavern with staff, menu, rumors, and atmosphere.

Source: src/lib/rpg-engine/world/taverns.ts

Params:

{
  quality: "squalid" | "poor" | "modest" | "comfortable" | "wealthy" | "aristocratic"
}

Response:

{
  "name": "The Dragon and Anchor",
  "type": "fancy",
  "quality": "wealthy",
  "description": "An elegant establishment...",
  "atmosphere": "Elegant decor with private booths",
  "owner": {
    "name": "Rose Goodbarrel",
    "role": "Owner",
    "personality": "Fiercely protective halfling who uses overly formal language",
    "secret": "Served a dark power in their youth"
  },
  "staff": [
    {
      "name": "Fang Bloodfist",
      "role": "Bartender",
      "personality": "Humble half-orc who peppers speech with foreign words"
    }
  ],
  "menu": {
    "drinks": [
      { "name": "Aged Wine", "price": "2 gp" },
      { "name": "Dragon's Breath Whiskey", "price": "3 gp" }
    ],
    "food": [
      { "name": "Venison Steak", "price": "2 gp" }
    ],
    "specialty": {
      "name": "Seven-Course Feast",
      "price": "10 gp",
      "description": "A culinary journey"
    }
  },
  "roomRate": "2 gp/night - Luxury suite with breakfast",
  "rumors": [
    {
      "text": "I heard the Stranger and the Mayor are having an affair.",
      "truth": "true",
      "reality": "It's true and will cause scandal when revealed.",
      "source": "An old timer who's seen it all",
      "type": "local_gossip"
    }
  ],
  "currentEvent": "A storytelling contest",
  "notableFeature": "A stage where bards perform nightly"
}

type values: "dive", "roadhouse", "inn", "tavern", "alehouse", "fancy"

Format: No.


rumor

Generate tavern rumors with truth levels and adventure hooks.

Source: src/lib/rpg-engine/world/rumors.ts

Params:

{
  count: number   // default: 3
}

Response:

{
  "text": "They say there's treasure hidden in the old graveyard.",
  "truth": "half_true",
  "reality": "There was treasure, but someone already took it.",
  "source": "The innkeeper",
  "type": "adventure_hook",
  "hook": "The party could investigate the location."
}

truth values: "true", "mostly_true", "half_true", "mostly_false", "false"

type values: "local_gossip", "adventure_hook", "warning", "legend", "trade_info"

Format: No.


trap

Generate dungeon traps with detection, disarming, and damage.

Source: src/lib/rpg-engine/world/traps.ts

Params:

{
  severity: "harmless" | "dangerous" | "deadly",
  type: "mechanical" | "magical" | "hybrid" | "environmental",
  dungeonLevel: number
}

Response:

{
  "name": "Poison Dart Wall",
  "type": "mechanical",
  "severity": "dangerous",
  "description": "Tiny holes line the corridor walls at chest height...",
  "trigger": "Pressure plate in the center of the hallway",
  "effect": "Darts fire from both walls — 2d4 piercing + DC 13 CON save or poisoned",
  "avoidance": "DC 14 Perception to spot holes, DC 12 Thieves' Tools to jam mechanism",
  "damage": "2d4+poison",
  "saveDC": 13,
  "saveType": "CON",
  "disarmDC": 12
}

Format: Yes.


settlement

Generate a town or city with government, economy, problems, and NPCs.

Source: src/lib/rpg-engine/world/settlements.ts

Params:

{
  size: "thorp" | "hamlet" | "village" | "small_town" | "large_town" | "city" | "metropolis",
  government: "autocracy" | "council" | "democracy" | "magocracy" | "theocracy" | "oligarchy" | "anarchy"
}

Response:

{
  "name": "Thornhaven",
  "size": "village",
  "population": 340,
  "government": "council",
  "leader": { "...NPC object..." },
  "description": "A quiet farming village on the edge of the Thornwood...",
  "notableLocations": ["The Old Mill", "Temple of the Harvest", "Thornhaven Inn"],
  "economy": "Agriculture — wheat and barley",
  "culture": "Insular and suspicious of outsiders",
  "problems": ["Disappearing livestock", "Tax collectors from the Baron"],
  "rumors": [ "...Rumor objects..." ],
  "defenses": "Wooden palisade, volunteer militia of 20",
  "relationships": [
    { "faction": "Baron's Tax Collectors", "attitude": "hostile" }
  ]
}

Format: Yes.


faction

Generate a political or social organization with goals, secrets, and hierarchy.

Source: src/lib/rpg-engine/world/factions.ts

Params:

{
  type: "guild" | "noble_house" | "religious_order" | "criminal_syndicate"
      | "military_order" | "merchant_company" | "secret_society" | "arcane_circle"
      | "political_party" | "tribal_clan" | "revolutionary_movement" | "monster_cult",
  size: "tiny" | "small" | "medium" | "large" | "massive",
  alignment: "lawful" | "neutral" | "chaotic"
}

Response:

{
  "name": "The Obsidian Hand",
  "type": "criminal_syndicate",
  "size": "medium",
  "influence": "regional",
  "alignment": "chaotic",
  "morality": "evil",
  "description": "A ruthless network of thieves and assassins...",
  "symbol": "A black hand clutching a gemstone",
  "headquarters": "The undercity beneath Portmaw",
  "leader": {
    "name": "The Whisper",
    "title": "Shadow Master",
    "personality": "Calculating and patient, never raises their voice",
    "secret": "Is actually the mayor's spouse"
  },
  "goals": ["Control the docks trade", "Eliminate the Merchant's Guild"],
  "methods": ["Blackmail", "Smuggling", "Strategic assassination"],
  "resources": ["Network of informants", "Hidden tunnels", "Corrupt guards"],
  "weakness": "Internal power struggles between lieutenants",
  "secrets": ["The leader's true identity", "A vault beneath the old temple"],
  "relationships": [
    { "faction": "City Watch", "attitude": "corrupt alliance" }
  ],
  "joinRequirements": "Must perform a theft and be vouched by two members",
  "memberBenefits": ["Fence for stolen goods", "Safe houses", "Legal protection"],
  "ranks": ["Finger", "Palm", "Fist", "Shadow", "Shadow Master"]
}

influence values: "local", "regional", "national", "continental", "global"

morality values: "good", "neutral", "evil"

Format: Yes.


religion

Generate a deity with domains, commandments, followers, and enemies.

Source: src/lib/rpg-engine/world/religions.ts

Params:

{
  domain: string,       // e.g. "war", "nature", "death"
  alignment: string     // e.g. "lawful good", "chaotic neutral"
}

Response:

{
  "name": "Veltharion",
  "domains": ["Knowledge", "Magic"],
  "alignment": "Lawful Neutral",
  "symbol": "An open book with a glowing eye",
  "description": "The All-Seeing Sage, keeper of forbidden knowledge...",
  "commandments": [
    "Seek knowledge above all",
    "Never destroy a book",
    "Share wisdom only with the worthy"
  ],
  "priests": "Wear grey robes with silver trim, shave their heads",
  "sacred_animals": ["Owl", "Raven"],
  "enemies": ["Followers of ignorance", "Book burners"],
  "followers": "Scholars, wizards, librarians, and seekers of truth"
}

Format: Yes.


travel

Generate a travel event for overland journeys.

Source: src/lib/rpg-engine/world/travel.ts

Params:

{
  terrain: "forest" | "mountain" | "swamp" | "desert" | "arctic" | "plains"
         | "jungle" | "coast" | "underwater" | "urban" | "dungeon" | "cave"
         | "ruins" | "sewers",
  distance: number,
  difficulty: string
}

Response:

{
  "day": 3,
  "weather": "Overcast with light drizzle",
  "event": "The road forks at a crumbling wayshrine",
  "encounter": "A merchant caravan heading the opposite direction",
  "complication": "Bridge washed out — must ford the river",
  "distance": 24
}

Format: Yes.


Tier 3: OSR Flavor

spell

Generate an OSR-style two-word spell name with magical properties.

Source: src/lib/rpg-engine/osr/spells.ts

Params:

{
  element: "fire" | "ice" | "lightning" | "earth" | "water" | "air" | "shadow"
         | "light" | "death" | "life" | "mind" | "time" | "chaos" | "order"
         | "nature" | "arcane",
  form: "physical" | "ethereal" | "mutation" | "summoning" | "enchantment"
      | "divination" | "illusion" | "transmutation",
  includeSuggestion: boolean   // include a brief spell effect description
}

Response:

{
  "name": "Sleet Vapor",
  "words": ["Sleet", "Vapor"],
  "element": "ice",
  "form": "physical",
  "suggestion": "Creates a freezing mist that obscures vision and chills to the bone"
}

Format: Yes — e.g. "**Sleet Vapor** [physical]"


spellbook

Generate a collection of spells (batch spell generation).

Source: src/lib/rpg-engine/osr/spells.ts

Params:

{
  count: number,                  // default: 6
  element: "...",                 // same as spell
  form: "...",                    // same as spell
  includeSuggestion: boolean
}

Response: Array of spell objects.

Format: Yes — formats as a titled list of spells.


career

Generate a failed career background for character creation (OSR funnel style).

Source: src/lib/rpg-engine/osr/careers.ts

Params:

{
  category: "trade" | "service" | "arcane" | "criminal" | "academic"
          | "military" | "religious" | "artistic" | "bureaucratic" | "bizarre",
  failureReason: "disgraced" | "bankrupt" | "exiled" | "cursed" | "bored"
               | "fired" | "exposed" | "traumatized" | "outlawed" | "surpassed",
  includeBackstory: boolean,
  includeBurden: boolean
}

Response:

{
  "title": "Failed Alchemist (Bankrupt)",
  "profession": "Alchemist",
  "failureReason": "bankrupt",
  "category": "arcane",
  "equipment": ["Cracked alembic", "3 unmarked vials", "Stained notebook"],
  "skill": "Can identify potions by smell",
  "backstory": "Spent their fortune chasing the philosopher's stone...",
  "burden": "Owes 200gp to a dangerous creditor"
}

Format: Yes.


pockets

Search a person's pockets — generate found items with narrative hooks.

Source: src/lib/rpg-engine/osr/pockets.ts

Params:

{
  personType: "commoner" | "soldier" | "noble" | "criminal" | "merchant"
            | "scholar" | "priest" | "mage" | "traveler" | "creature",
  itemCount: number,
  ensureHook: boolean    // guarantee at least one item has a narrative hook
}

Response:

{
  "items": [
    {
      "description": "A locket containing a portrait of someone who looks like the party's patron",
      "category": "mysterious",
      "hook": "Why does this dead bandit carry the mayor's portrait?",
      "value": 5
    },
    {
      "description": "3 copper coins and a button",
      "category": "mundane",
      "value": 0.03
    }
  ],
  "totalValue": 5.03,
  "summary": "A few meager possessions and one unsettling locket"
}

category values: "personal", "mysterious", "valuable", "mundane", "disturbing", "useful", "romantic", "religious"

Format: Yes.


dying-words

Generate last words for a dying enemy or NPC.

Source: src/lib/rpg-engine/osr/dying-words.ts

Params:

{
  enemyType: "beast" | "humanoid" | "undead" | "cultist" | "boss" | "minion" | "innocent",
  category: "blessing" | "curse" | "confession" | "warning" | "defiance" | "mystery"
}

Response:

{
  "text": "You think you've won... but the Crimson Gate is already open...",
  "category": "warning",
  "type": "cultist",
  "tone": "defiant with a knowing smile"
}

Format: Yes.


dungeon-dressing

Generate atmospheric details for dungeon rooms — sensory descriptions that bring rooms to life.

Source: src/lib/rpg-engine/osr/dungeon-dressing.ts

Params:

{
  roomType: "corridor" | "chamber" | "vault" | "crypt" | "throne" | "library"
          | "armory" | "barracks" | "kitchen",
  category: "visual" | "auditory" | "olfactory" | "tactile" | "unsettling"
}

Response:

{
  "roomType": "crypt",
  "details": [
    {
      "category": "visual",
      "description": "Faded frescoes on the walls depict a funeral procession",
      "type": "decoration"
    },
    {
      "category": "olfactory",
      "description": "The air smells of old incense and dry stone",
      "type": "atmosphere"
    }
  ],
  "description": "A somber crypt with faded frescoes and lingering incense..."
}

Format: Yes.


door

Generate a dungeon door with condition, material, clues, and hooks.

Source: src/lib/rpg-engine/osr/doors.ts

Params:

{
  condition: "locked" | "stuck" | "trapped" | "open" | "hidden" | "magical" | "barred" | "broken",
  includeSound: boolean,
  includeHook: boolean
}

Response:

{
  "condition": "locked",
  "material": "Iron-bound oak",
  "feature": "Dwarven runes etched around the frame",
  "clue": "Fresh scratch marks near the lock",
  "sound": "Faint chanting from the other side",
  "hook": "The key was last seen worn by the missing priest"
}

Format: Yes.


monster-motivation

Generate what a monster is doing, wants, and fears — turns combat into roleplay.

Source: src/lib/rpg-engine/osr/monster-motivations.ts

Params:

{
  monsterType: "beast" | "humanoid" | "undead" | "construct" | "aberration"
             | "dragon" | "elemental" | "fey" | "fiend" | "giant" | "plant",
  disposition: "hostile" | "unfriendly" | "neutral" | "friendly" | "allied"
}

Response:

{
  "monsterType": "dragon",
  "disposition": "unfriendly",
  "goal": "Recovering a stolen egg from a nearby village",
  "fear": "That its species is dying out",
  "doingNow": "Circling overhead, watching the party's movements",
  "complication": "The egg is actually in the party's cart, mistaken for a gem"
}

Format: Yes.


found-document

Generate a discoverable document — letters, journals, maps, contracts.

Source: src/lib/rpg-engine/osr/found-documents.ts

Params:

{
  type: "letter" | "journal" | "map" | "contract" | "note" | "inscription" | "record" | "prophecy",
  condition: "fresh" | "aged" | "damaged" | "ancient" | "burned"
}

Response:

{
  "type": "journal",
  "condition": "damaged",
  "title": "Journal of Expedition XVI",
  "content": "Day 14 — The passage narrows. Brenn insists we turn back...",
  "author": "Scholar Aldric Voss",
  "age": "Perhaps 50 years old",
  "value": "Invaluable to the right buyer",
  "hook": "The final entry mentions a sealed door matching one the party just found"
}

Format: Yes.


spell-mishap

Generate a spell gone wrong — wild magic, backfires, and catastrophic failures.

Source: src/lib/rpg-engine/osr/spell-mishaps.ts

Params:

{
  school: "abjuration" | "conjuration" | "divination" | "enchantment"
        | "evocation" | "illusion" | "necromancy" | "transmutation" | "wild_magic",
  severity: "minor" | "major" | "catastrophic"
}

Response:

{
  "description": "The spell rebounds, transforming the caster's hair into living snakes",
  "severity": "major",
  "effect": "Caster gains a snake-hair crown — disadvantage on CHA checks, but can see in darkness",
  "school": "transmutation",
  "saveDC": 15,
  "duration": "Until dispelled or 24 hours"
}

Format: Yes.


Advanced Generators

room-atmosphere

Generate an atmosphere of wrongness for a dungeon room — the feeling that something is off.

Source: src/lib/rpg-engine/generators/room-atmosphere.ts

Params:

{
  severity: "subtle" | "obvious" | "overwhelming",
  category: "spatial" | "temporal" | "biological" | "psychological"
          | "elemental" | "supernatural" | "decay" | "corruption",
  ensureSecret: boolean
}

Response:

{
  "wrongness": "Shadows in this room point toward the center, not away from light sources",
  "category": "spatial",
  "severity": "subtle",
  "sensory": [
    "A faint hum just below the threshold of hearing",
    "The air tastes metallic"
  ],
  "modifier": {
    "name": "Gravity Flux",
    "description": "Gravity shifts slightly toward the room's center",
    "mechanicalEffect": "Movement costs +5 ft when moving away from center",
    "duration": "while_in_room"
  },
  "secret": "The room is built over a buried lodestone enchanted by a mad wizard",
  "investigation": "DC 15 Arcana reveals faint transmutation magic saturating the floor"
}

duration values: "instant", "while_in_room", "lingering", "permanent"

Format: Yes.


boss-encounter

Generate a multi-phase boss fight with lair actions, legendary actions, weaknesses, and monologue.

Source: src/lib/rpg-engine/generators/boss-encounters.ts

Params:

{
  tier: "miniboss" | "major" | "legendary" | "mythic",
  type: "dragon" | "demon" | "undead_lord" | "giant" | "aberration" | "archfey"
      | "elemental_lord" | "construct" | "beast_alpha" | "dark_wizard"
      | "fallen_hero" | "eldritch_horror",
  partyLevel: number,
  includeMonologue: boolean
}

Response:

{
  "name": "Xalrathi the Hollow King",
  "type": "undead_lord",
  "tier": "legendary",
  "description": "A skeletal monarch wreathed in necrotic flame...",
  "phases": [
    {
      "name": "The Hollow Court",
      "trigger": "Combat begins",
      "hpThreshold": 100,
      "description": "Xalrathi fights with cold precision, flanked by spectral courtiers",
      "abilities": ["Necrotic Blast", "Summon Courtier"],
      "behavior": "Stays on throne, directs minions",
      "environmental": "Spectral courtiers block line of sight"
    },
    {
      "name": "The King Rises",
      "trigger": "Below 50% HP",
      "hpThreshold": 50,
      "description": "Xalrathi stands, his crown blazing with dark fire",
      "abilities": ["Crown of Dread", "Life Drain"],
      "behavior": "Aggressive melee, targets healers",
      "environmental": "Throne room cracks, necrotic energy seeps from floor"
    }
  ],
  "lairActions": [
    {
      "name": "Spectral Chains",
      "effect": "Ghostly chains erupt from the floor — DC 15 DEX or restrained",
      "initiative": 20,
      "save": "DEX 15",
      "damage": "2d8 necrotic"
    }
  ],
  "legendaryActions": [
    { "name": "Necrotic Bolt", "cost": 1, "effect": "Ranged spell attack, 3d6 necrotic" },
    { "name": "Raise Fallen", "cost": 2, "effect": "A slain courtier rises with half HP" },
    { "name": "Death's Embrace", "cost": 3, "effect": "All creatures within 15 ft make DC 16 CON save or take 4d8 necrotic and Xalrathi heals for half" }
  ],
  "legendaryResistances": 3,
  "weaknesses": [
    {
      "description": "The crown is the source of his power",
      "discovery": "DC 18 Arcana reveals the crown pulses with each ability used",
      "benefit": "Destroying the crown (AC 20, 30 HP) removes legendary resistances",
      "obviousness": "hinted"
    }
  ],
  "signature": "Crown of the Hollow King — necrotic explosion on death",
  "monologue": "You dare enter my court? I have ruled this kingdom for a thousand years...",
  "deathThroes": "The crown shatters, releasing a wave of freed souls. The throne room begins to collapse.",
  "recommendedLevel": 12
}

Format: Yes.


hazard

Generate environmental hazards — natural disasters, magical anomalies, terrain dangers.

Source: src/lib/rpg-engine/generators/hazards.ts

Params:

{
  type: "natural" | "magical" | "trap" | "environmental",
  severity: "nuisance" | "dangerous" | "deadly",
  terrain: "dungeon" | "forest" | "swamp" | "mountain" | "desert"
         | "arctic" | "underground" | "urban" | "coastal"
}

Response:

{
  "type": "magical",
  "name": "Wild Magic Sinkhole",
  "description": "A patch of ground where reality is thin...",
  "trigger": "Casting any spell within 30 feet",
  "effect": "Roll on Wild Magic Surge table, spell also targets caster",
  "avoidance": "DC 16 Arcana to detect, avoid casting near it",
  "severity": "dangerous",
  "damage": "Varies by surge",
  "saveType": "WIS",
  "saveDC": 14,
  "duration": "Permanent until sealed",
  "terrain": ["dungeon", "underground"]
}

Format: Yes.


secret

Generate hidden passages, secret doors, and concealed caches.

Source: src/lib/rpg-engine/generators/secrets.ts

Params:

{
  type: "door" | "compartment" | "passage" | "cache",
  complexity: "simple" | "moderate" | "complex",
  terrain: "forest" | "mountain" | "swamp" | "desert" | "arctic" | "plains"
         | "jungle" | "coast" | "underwater" | "urban" | "dungeon" | "cave"
         | "ruins" | "sewers"
}

Response:

{
  "type": "passage",
  "name": "The Whispering Crack",
  "description": "A narrow fissure behind a bookshelf, just wide enough to squeeze through",
  "mechanism": "Pull the red book on the third shelf",
  "discoveryDC": 16,
  "contents": "A dusty corridor leading to the old treasury",
  "clues": [
    "Draft of cold air from behind the shelves",
    "Scratch marks on the floor in an arc pattern"
  ]
}

Format: Yes.


riddle

Generate riddles for dungeon doors, sphinx encounters, and puzzle rooms.

Source: src/lib/rpg-engine/generators/riddles.ts

Params:

{
  difficulty: "easy" | "medium" | "hard" | "legendary",
  category: "wordplay" | "object" | "concept" | "nature" | "logic" | "moral",
  includeSource: boolean
}

Response:

{
  "text": "I have cities but no houses, forests but no trees, water but no fish. What am I?",
  "answer": "A map",
  "answerCategory": "object",
  "difficulty": "medium",
  "category": "object",
  "hints": [
    "I can be found on a wall or in a pocket",
    "Explorers love me",
    "I show you where to go but I've never been there"
  ],
  "alternativeAnswers": ["Atlas", "Globe"],
  "source": "Inscribed above an ancient door in Dwarvish"
}

Format: Yes (optionally shows/hides the answer).


quest-hook

Generate adventure hooks with patrons, complications, twists, and rewards.

Source: src/lib/rpg-engine/generators/quest-hooks.ts

Params:

{
  type: "rescue" | "retrieval" | "elimination" | "escort" | "investigation"
      | "defense" | "delivery" | "exploration" | "heist" | "sabotage",
  patronType: "noble" | "merchant" | "priest" | "criminal" | "commoner"
            | "mysterious" | "official" | "scholar",
  stakeLevel: "personal" | "local" | "regional" | "world",
  includeDeadline: boolean
}

Response:

{
  "type": "investigation",
  "hook": "Children in the village have been sleepwalking to the old mill every night",
  "patron": "Elara Thornwood, the village healer",
  "patronType": "commoner",
  "complication": "The mill owner is the mayor's brother",
  "twist": "The children aren't being controlled — they're trying to free something trapped in the mill",
  "stakes": "If the entity is freed without preparation, it will consume the village",
  "stakeLevel": "local",
  "reward": "The healer's entire savings (50gp) and a rare herb that cures any disease",
  "deadline": "The next full moon, in 3 days"
}

Format: Yes.


curse

Generate curses with mechanical effects, triggers, cures, and hidden benefits.

Source: src/lib/rpg-engine/generators/curses.ts

Params:

{
  type: "magical" | "divine" | "demonic" | "fae" | "natural" | "alchemical",
  severity: "minor" | "major" | "legendary",
  origin: "inherited" | "inflicted" | "bargained" | "accidental" | "deserved" | "prophesied",
  includeHiddenBenefit: boolean
}

Response:

{
  "name": "The Midas Blight",
  "type": "fae",
  "severity": "major",
  "origin": "bargained",
  "effect": "Everything the cursed touches turns to gold — including food and living things",
  "mechanicalEffect": "Objects touched become gold (worthless as currency, too heavy). Food turns to gold on contact. Unarmed attacks deal 2d6 bludgeoning as flesh hardens.",
  "trigger": "Skin contact with any organic material",
  "cure": "Return the original bargain — place the golden apple back on the Fae Queen's tree",
  "hiddenBenefit": "Gold-touched items can be used as improvised weapons. The cursed is immune to petrification.",
  "duration": "Until cured"
}

Format: Yes.


hireling

Generate hirelings for hire — porters, guides, guards, with loyalty, skills, and deal-breakers.

Source: src/lib/rpg-engine/generators/hirelings.ts

Params:

{
  type: "porter" | "torchbearer" | "guard" | "guide" | "healer" | "sage"
      | "scout" | "cook" | "animal-handler" | "translator" | "locksmith" | "entertainer",
  minLoyalty: number,        // 2-20
  includeBackground: boolean,
  includeSecret: boolean
}

Response:

{
  "name": "Pip Farthing",
  "type": "torchbearer",
  "description": "A wiry teenager with burn-scarred hands and an eager grin",
  "loyalty": 14,
  "loyaltyLevel": "loyal",
  "morale": "confident",
  "skill": "Can keep a torch lit in any weather",
  "quirk": "Names every torch and mourns when they burn out",
  "dealBreaker": "Will not enter water deeper than their knees",
  "payment": {
    "dailyRate": 1,
    "bonusTrigger": "Surviving a combat encounter",
    "nonMonetary": "A good meal and a warm place to sleep"
  },
  "specialAbility": "Can juggle lit torches as a distraction",
  "background": "Orphan who grew up in the lamplighter's guild",
  "secret": "Is actually the missing heir of a minor noble house"
}

loyaltyLevel values: "disloyal", "reluctant", "steady", "loyal", "devoted"

morale values: "broken", "shaken", "nervous", "steady", "confident", "inspired"

Format: Yes.


prophecy

Generate cryptic prophecies with hidden meanings and plot twists.

Source: src/lib/rpg-engine/generators/prophecies.ts

Params:

{
  scope: "personal" | "local" | "world" | "cosmic",
  tone: "hopeful" | "ominous" | "neutral" | "cryptic",
  includeSource: boolean
}

Response:

{
  "scope": "world",
  "text": "When the three moons align and the silver tower sings, the Sleeper beneath the sea shall wake, and the world shall know a second dawn — or an endless night.",
  "subject": "The return of an ancient god",
  "condition": "The three moons must align (happens every 1,000 years — next occurrence in 3 months)",
  "interpretation": "Most scholars believe it refers to a literal awakening of something beneath the ocean",
  "twist": "The 'Sleeper' is not a god but a weapon, and the 'second dawn' is its detonation",
  "tone": "ominous",
  "source": "The Black Codex, written by the last Oracle of Vel'Thuum"
}

Format: Yes.


nightmare

Generate character nightmares — prophetic, traumatic, or just deeply strange.

Source: src/lib/rpg-engine/generators/nightmares.ts

Params:

{
  type: "prophetic" | "traumatic" | "strange" | "warning" | "memory",
  character: string    // character name/description for personalization
}

Response:

{
  "type": "prophetic",
  "description": "You stand in a field of black wheat under a red sky. Each stalk whispers a name you've forgotten. At the center, a door stands alone — your hand reaches for the handle but your reflection in the brass knob is not your own.",
  "mood": "Dread mixed with compulsion",
  "imagery": [
    "Black wheat whispering names",
    "A red sky without a sun",
    "A door standing alone in a field",
    "A wrong reflection"
  ],
  "emotionalImpact": "The dreamer wakes with the certainty that the door is real and waiting",
  "wakeEffects": "DC 12 WIS save or gain no benefit from this rest",
  "meaning": "The door represents a choice the character will face — the wrong reflection is who they'll become if they choose wrong"
}

Format: Yes.


scar

Generate battle scars with narrative weight — physical, emotional, and supernatural.

Source: src/lib/rpg-engine/generators/scars.ts

Params:

{
  type: "physical" | "emotional" | "supernatural",
  visibility: "hidden" | "subtle" | "noticeable" | "prominent" | "disfiguring",
  damageType: "slashing" | "piercing" | "bludgeoning" | "fire" | "cold" | "acid"
            | "lightning" | "necrotic" | "radiant" | "psychic" | "poison"
}

Response:

{
  "type": "supernatural",
  "name": "The Witch's Kiss",
  "description": "A lip-shaped burn mark on the back of the hand that glows faintly blue in moonlight",
  "origin": "Touched by a hag's dying curse",
  "location": "Back of the right hand",
  "visibility": "subtle",
  "effect": "Advantage on saves against fey magic, but fey creatures can always sense you within 60 ft",
  "roleplayNote": "The scar tingles when fey creatures are nearby",
  "damageType": "necrotic",
  "healable": false,
  "healingMethod": "Only by the hag who placed it (she's dead)"
}

Format: Yes.


reputation

Generate reputation effects — fame, infamy, and the consequences of being known.

Source: src/lib/rpg-engine/generators/reputation.ts

Params:

{
  type: "fame" | "infamy" | "mixed",
  region: string    // e.g. "the Northern Reaches"
}

Response:

{
  "type": "fame",
  "title": "The Dragon Slayer of Thornhaven",
  "description": "Word of the dragon's defeat has spread across the region",
  "level": 4,
  "consequences": [
    "Free drinks in most taverns",
    "Nobles seek you out for quests",
    "Aspiring adventurers follow you around"
  ],
  "howToIncreaseIt": "Defeat another significant threat publicly",
  "howToDecreaseit": "Fail publicly, or be absent when needed",
  "inRegion": "the Northern Reaches"
}

level: 1 (barely known) to 5 (legendary)

Format: Yes.


death-legacy

Generate what happens when a character dies — last words, physical legacy, supernatural effects, and how they're remembered.

Source: src/lib/rpg-engine/generators/death-legacy.ts

Params:

{
  characterClass: "warrior" | "paladin" | "ranger" | "rogue" | "wizard" | "cleric"
                | "druid" | "bard" | "barbarian" | "monk" | "warlock" | "sorcerer",
  causeOfDeath: "combat" | "sacrifice" | "curse" | "old-age" | "poison"
              | "magic" | "betrayal" | "heroic"
}

Response:

{
  "lastWords": {
    "text": "Tell them... I held the line.",
    "type": "hope",
    "finalBreath": true
  },
  "physical": {
    "items": ["Dented shield (now legendary)", "Blood-stained journal", "A ring meant for someone back home"],
    "restingPlace": "Buried on the hilltop where they fell, facing the enemy's land",
    "remainsCondition": "Peaceful, as if sleeping"
  },
  "memory": {
    "description": "The soldiers who survived tell the story every year on the anniversary",
    "rememberedBy": "The entire regiment and the village they saved",
    "duration": "Generations — a holiday is named after them"
  },
  "supernatural": {
    "description": "On foggy mornings, a spectral figure stands on the hilltop in a guardian's pose",
    "magnitude": "noticeable",
    "permanent": true
  },
  "legacy": {
    "type": "spiritual",
    "description": "Their courage inspires a new order of shield-bearers",
    "manifestation": "The shield glows when wielded by someone defending the innocent",
    "implications": "The shield becomes a relic — future quests may revolve around it"
  }
}

supernatural.magnitude values: "subtle", "noticeable", "dramatic", "world-changing"

legacy.type values: "physical", "spiritual", "social", "magical", "prophetic"

Format: Yes.


legendary-affix

Generate Diablo-style item affixes — prefixes and suffixes for magical items.

Source: src/lib/rpg-engine/generators/legendary-affixes.ts

Params:

{
  itemType: "weapon" | "armor" | "accessory" | "any",
  powerLevel: "common" | "uncommon" | "rare" | "epic" | "legendary",
  element: "fire" | "ice" | "lightning" | "poison" | "holy" | "shadow"
         | "arcane" | "nature" | "psychic" | "force",
  stat: "strength" | "dexterity" | "constitution" | "intelligence"
      | "wisdom" | "charisma" | "speed" | "armor" | "health",
  prefixOnly: boolean,
  suffixOnly: boolean,
  forceDouble: boolean    // guarantee both prefix and suffix
}

Response:

{
  "prefix": {
    "name": "Blazing",
    "effect": "Wreathed in magical fire that never extinguishes",
    "mechanicalBonus": "+1d6 fire damage",
    "element": "fire",
    "powerLevel": "rare",
    "itemTypes": ["weapon"]
  },
  "suffix": {
    "name": "of the Titan",
    "effect": "Imbued with the strength of giants",
    "mechanicalBonus": "+2 STR, advantage on Athletics",
    "stat": "strength",
    "powerLevel": "rare",
    "itemTypes": ["weapon", "armor", "accessory"]
  },
  "combinedPowerLevel": "rare",
  "hasSynergy": true,
  "synergyEffect": "Fire damage scales with STR modifier",
  "affixCount": 2
}

Usage example: Apply to a longsword to get Blazing Longsword of the Titan — +1d6 fire damage, +2 STR, fire damage scales with STR modifier.

Format: Yes.


Quick Reference: All Generator Types

TypeTierParams?Format?
encounter1Yes — terrain, partyLevel, partySize, difficulty, allowBossNo
npc1Yes — race, gender, socialClass, occupation, disposition, ageRangeNo
npc-crowd1Yes — size, qualityNo
combat1Yes — same as encounterNo
loot1Yes — levelYes
shop1Yes — type, quality, sizeYes
weather2Yes — climate, seasonYes
tavern2Yes — qualityNo
rumor2Yes — countNo
trap2Yes — severity, type, dungeonLevelYes
settlement2Yes — size, governmentYes
faction2Yes — type, size, alignmentYes
religion2Yes — domain, alignmentYes
travel2Yes — terrain, distance, difficultyYes
spell3Yes — element, form, includeSuggestionYes
spellbook3Yes — count, element, formYes
career3Yes — category, failureReason, includeBackstory, includeBurdenYes
pockets3Yes — personType, itemCount, ensureHookYes
dying-words3Yes — enemyType, categoryYes
dungeon-dressing3Yes — roomType, categoryYes
door3Yes — condition, includeSound, includeHookYes
monster-motivation3Yes — monsterType, dispositionYes
found-document3Yes — type, conditionYes
spell-mishap3Yes — school, severityYes
room-atmosphereAdvYes — severity, category, ensureSecretYes
boss-encounterAdvYes — tier, type, partyLevel, includeMonologueYes
hazardAdvYes — type, severity, terrainYes
secretAdvYes — type, complexity, terrainYes
riddleAdvYes — difficulty, category, includeSourceYes
quest-hookAdvYes — type, patronType, stakeLevel, includeDeadlineYes
curseAdvYes — type, severity, origin, includeHiddenBenefitYes
hirelingAdvYes — type, minLoyalty, includeBackground, includeSecretYes
prophecyAdvYes — scope, tone, includeSourceYes
nightmareAdvYes — type, characterYes
scarAdvYes — type, visibility, damageTypeYes
reputationAdvYes — type, regionYes
death-legacyAdvYes — characterClass, causeOfDeathYes
legendary-affixAdvYes — itemType, powerLevel, element, stat, prefixOnly, suffixOnly, forceDoubleYes

MUD & AI Player

A set of experimental CLI harnesses that chain the RPG Engine generators into full playable experiences. All three live under src/lib/rpg-engine/ and are not part of the HTTP API — they are run as local Node scripts (npx tsx …) against the same generator library that powers the endpoints above.

MUD Overview

File: src/lib/rpg-engine/mud/index.ts

A text adventure that stitches together the OSR and advanced generators — doors, room atmospheres, monsters with motivations, secrets, riddles, hazards, loot with legendary affixes, found documents, curses — into a dungeon crawl. A local Ollama LLM (default model deepseek-r1:latest at http://localhost:11434) narrates the scene; the generators supply the structured content. Works entirely offline once the model is pulled.

Procedural elements wired into the MUD loop:

  • door, room-atmosphere, hazard, secret, riddle, monster-motivation
  • career (for the player character), pockets, dying-words
  • quest-hook, curse, hireling, legendary-affix, nightmare, prophecy, found-document, spell-mishap
npx tsx apps/potionquest/src/lib/rpg-engine/mud/index.ts
# or
./rpg-mud.sh

Playing the MUD

The MUD is a REPL — type a command at the prompt, the narrator responds, the room state updates.

Supported commands (free-form; the parser matches on verbs):

VerbEffect
lookRe-describe the current room, including atmosphere and visible features
searchAttempt to uncover the room's secret (if any)
openInteract with the room's door (condition, locks, traps)
fightEngage the current monster; resolves HP/motivation/dying-words on defeat
talkAttempt a social resolution with the monster (motivation-driven)
readRead any found-document in the room
restRecover HP; risks a nightmare roll
inventoryList carried loot (may include legendary affixes)
quitEnd the session

Environment:

  • OLLAMA_URL — defaults to http://localhost:11434
  • OLLAMA_MODEL — defaults to deepseek-r1:latest

AI Player

Files: src/lib/rpg-engine/mud/ai-player.ts, src/lib/rpg-engine/mud/claude-plays.ts

Autonomous agents that drive the MUD loop without a human at the keyboard. The decision-making agent inspects the current GameState (HP, gold, inventory, room contents, active monster) and picks an action; the engine resolves it with the same generators the interactive MUD uses; a transcript is emitted to stdout.

  • ai-player.ts — generic autonomous runner: rolls up a character, walks the dungeon, makes combat/search/talk decisions until death or victory
  • claude-plays.ts — a scripted strategic playthrough of "The Hollow Throne" adventure, suitable for recording canonical game logs
npx tsx apps/potionquest/src/lib/rpg-engine/mud/ai-player.ts
npx tsx apps/potionquest/src/lib/rpg-engine/mud/claude-plays.ts

MCP Server

File: src/lib/rpg-engine/mcp-server/index.ts Config: apps/potionquest/.mcp.json (registers the server as potionquest-rpg)

A Model Context Protocol server that exposes the MUD to Claude (or any MCP client) as callable tools. Claude can then reason about the state turn-by-turn instead of the game engine picking a best-guess action — the engine provides the world, Claude provides the player.

Exposed tools:

ToolPurpose
rpg_start_gameBegin a new run, optionally with a player name
rpg_get_stateReturn the current room, HP, inventory, active monster, and visible features
rpg_actionTake an action — look, search, open, fight, talk, read, rest
rpg_get_optionsEnumerate valid actions for the current state

Transport is stdio, launched by the MCP client:

{
  "mcpServers": {
    "potionquest-rpg": {
      "type": "stdio",
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/apps/potionquest/src/lib/rpg-engine/mcp-server/index.ts"]
    }
  }
}