This is a submission for the Sanity Challenge, Path One: Ship an Agent That Queries Real Content
What I Built
Rulebook Oracle — a chat agent that answers tabletop-TTRPG rules questions only from a Sanity Context Knowledge Base, exposed over a Context MCP endpoint, with a minimal Astro chat UI running on the Vercel AI SDK.
It's a "no-flipping-the-rulebook" companion for a homebrew d6 fantasy RPG ("Steeldusk"). I wrote and seeded the rulebook into Sanity: 37 rule sections, 10 errata, 10 FAQs. The interesting part is what happens when content contradicts itself: when newer errata overrides the core rulebook, the agent surfaces both claims side by side, cites every source, and the UI stamps the answer with an Override badge. You always see what is actually in effect, and why.
None of that works by keyword search alone — the agent reasons across related entries (rule + errata + FAQ that reference each other) and only answers from what it retrieved. Ask it "Can I attack twice if I wield two short swords?" and it explains that the 2025-03-01 errata replaced the original two-attack rule with advantage + the Ambidextrous talent, citing both the rule and the errata.
Demo
Try it live: https://rulebook-oracle.netlify.app — open the chat and try:
- "Can I attack twice if I wield two short swords?" → an Override answer citing the errata
- "What happens when my Health hits 0?" → Wounds & Dying, with the death-saves override
- "Does advantage from high ground stack with advantage from a spell?" → a rule + FAQ synergy (advantage never stacks)
- "Is a natural 6 always a hit?" → no — a natural 6 counts as two successes, not an automatic hit
Code
GitHub: Karllouise-code/sanity-rulebook-oracle
Monorepo layout: sanity/ (schemas + idempotent seed runner) and astro-app/ (Astro SSR chat + @ai-sdk/mcp agent loop, deployed to Netlify). The README walks the whole chain: Sanity → Context → MCP → app.
How I Used Sanity
-
Structured content: three document types —
ruleSection,errataItem,faqItem. The errata schema captureschangeType(clarify/override) andeffectiveDate— that's what makes "who wins when sources conflict?" decidable by data, not by guesswork. -
Seed: a transactional,
createOrReplace-based script upserts the 57 documents into theproductiondataset (idempotent). -
Sanity Context → Knowledge Base: the KB indexes the three document types and follows the
ruleRefreferences, so errata and FAQs stay linked to the rule section they change. Sanity consolidated the 57 docs into 13 topical entries. -
Context MCP endpoint: the agent connects to the
rulebook-oracleMCP endpoint (Knowledge Base mode) over HTTP with a Bearer org token. It discoversinitial_context(the KB outline),knowledge_base_search(content-aware, ranked), andknowledge_base_read, so it can search, pick relevant entries, and read them — no raw GROQ from the app. How I Used Sanity — point 5, search-first rather than outline scoring: -
The agent loop (
astro-app/src/lib/rules-agent.ts): picks the relevant entry paths viaknowledge_base_search(falling back to an outline-score heuristic), reads them, and completes the answer only from those entries.changeType: 'override'becomes the red Override badge. An optional Gemini/OpenAI key summarizes the retrieved entries into natural prose with the citations preserved; otherwise answers are composed deterministically.
Sanity Project Details
-
Project ID:
g2fvri91 -
Dataset:
production -
Knowledge Base: Rulebook Oracle KB (
kblX52Exu3S8) -
Context MCP endpoint:
rulebook-oracle(orgoeiiciaw8, Knowledge Base mode) - Dashboard: https://www.sanity.io/manage/project/g2fvri91
Top comments (0)