Recap Raven

official

Discord D&D note taker: search and cite your TTRPG session recaps, quests, NPCs and transcripts from any AI. Requires a Recap Raven API key.

Hosted MCP Server

npx add-mcp 'https://mcp.recapraven.com/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

For custom AI workflows

Your campaign tools, in a compatible AI client.

Read your recaps and campaign data through MCP. Existing API-key access works with compatible clients. Sign in from an AI app that supports a public client identity; check your client’s requirements below before connecting.

What is MCP?

MCP lets compatible AI apps call Recap Raven tools directly. Instead of copying recaps into another AI, your AI can ask Recap Raven for sessions, transcript windows, hooks, entities, quotes, and campaign answers.

Get started

Raven’s remote MCP endpoint is https://mcp.recapraven.com/mcp . Choose a client for setup instructions. Premium includes deterministic reads; Searchable Memory is required for memory search and AI answers.


MCP endpoint

https://mcp.recapraven.com/mcp

ChatGPT setup

  1. On ChatGPT’s website, open Plugins, select +, then Add custom MCP server. Enter “Recap Raven” and the server URL below.
  2. When Raven sign-in is enabled, choose OAuth and the published client identity (CIMD) option with public-client authentication. No client secret or Raven API key is needed. Automatic client registration is not supported by Raven.
  3. Create the plugin, install it, then sign in to Raven and review the app identity and requested access. Only approve a connection you started.
  4. In a conversation, select Recap Raven with @ and ask: “Use Recap Raven to list my campaigns.”
  5. Read and write tools depend on your permissions and confirmations. If setup is missing, check workspace restrictions and the official ChatGPT setup guide for your app.

ChatGPT remote server URL

https://mcp.recapraven.com/mcp

Consent and connection help

A Raven sign-in connection will ask for permission to access your campaigns, transcripts and GM-only information. Tool results go to your chosen AI provider, and paid tools spend your existing Ask credits.

If you approve background access, the app can refresh its connection for up to 30 days, even when you are signed out of Raven. After expiry or revocation, reconnect and approve again. In Account settings, review approved AI connections and disconnect one connection or use Disconnect all AI connections. API keys are managed separately.

Empty results? Check that you signed in to the Raven account that owns your campaigns. A plan change can remove tool access; AI answers also need available credits. If consent expires or fails, restart the connection from the client.

Missing menus or an unsupported workspace cannot be fixed by changing your Raven key. Check the linked vendor requirements or contact our Discord community with the app name, version and error message. Do not send tokens or keys.

Available on Premium and Searchable Memory

Premium includes API keys and access to your available recaps, transcripts and manual World data. Searchable Memory is required for memory tools, campaign search and AI answers. Semantic search and AI answers spend your existing Ask credits.

MCP availability by plan

Free tierUnavailable
PremiumAPI keys, available recaps, transcripts and manual World data. Memory tools are not included.
Plans with memoryDeterministic tools at no extra cost; search_corpus uses low chat credits per search, and cited ask tools spend Ask credits.
API keyCreate one personal API key from API Keys. Revoke any time; MCP access ends when the account returns to Free.

Fair use & rate limits

The MCP server is a shared, metered surface. To keep it sustainable (and stop a runaway agent loop from burning a whole licence in minutes), every API key is rate-limited in addition to your Ask allowance.

MCP rate limits

Server fair-usePer-API-key request ceiling on the MCP server itself (all tool calls combined) — prevents a single key from flooding the endpoint. Responses over the limit return 429 with Retry-After.
Premium · all plans / Memory · 5 sessions15 requests/minute and 150 requests/hour.
Memory · 10 sessions30 requests/minute and 450 requests/hour.
Memory · 20 sessions60 requests/minute and 1200 requests/hour.
Free toolsRate-limited per API key. Deterministic reads and keyword searches (search_transcript, search_facts) cost no credits. Semantic search_corpus is not included here; it uses low chat credits per completed search.
Chat-credit toolssearch_corpus (low chat credits) and cited ask tools are bounded by both your Ask allowance and a per-key rate limit. The usage gate stops spend when the allowance is exhausted; the rate limit stops a burst from draining it instantly. No separate MCP quota — your agent spends the same per-licence chat credits it uses in the app.

Included at no extra cost

Deterministic read tools — sessions, entities and the ledger, hooks, mechanics, engagement, transcript and beats search, recaps, lore and lexicon. They return stored campaign data with no AI inference, so available tools cost no credits. Memory tools still require Searchable Memory. Semantic search_corpus is not included in this list; it uses low chat credits. Expand any tool for example input, example output and field reference.

list_campaigns No extra tool charge

Discovery tool: list the campaigns available to your MCP key so other tools can use the right campaign_id.

Example input

{}

Example output

{
  "campaigns": [
    {
      "campaign_id": "a1b2c3d4-…",
      "name": "The White Moose",
      "module": "Dolmenwood"
    }
  ]
}

Field descriptions

FieldTypeDescription
campaignsarrayCampaigns owned by your account.
campaigns[].campaign_idstring (uuid)Use this id with campaign-scoped MCP tools.
campaigns[].namestringCampaign name.
campaigns[].modulestring | nullOptional module/system label.

list_sessions No extra tool charge

Discovery tool: list ready sessions with their session_number and title, so an agent can map "session 12" to the right recap_id.

Example input

{
  "campaign_id": "a1b2c3d4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid) | omittedOptional campaign filter.

Example output

{
  "recaps": [
    {
      "recap_id": "r1e2c3a4-…",
      "campaign_id": "a1b2c3d4-…",
      "session_number": 12,
      "title": "The Choosing",
      "status": "ready"
    }
  ]
}

Field descriptions

FieldTypeDescription
recapsarrayReady sessions visible to your account.
recaps[].recap_idstring (uuid)Use this id with get_recap, get_transcript_window and get_mechanics_events.
recaps[].campaign_idstring (uuid) | nullCampaign that owns the session.
recaps[].session_numberinteger | nullThe GM’s own 1-based session numbering (DM-editable), when set.
recaps[].titlestring | nullSession title.
recaps[].statusstringOnly ready sessions are listed.

get_engagement No extra tool charge

The campaign engagement dashboard: per-speaker participation trend and the cached cross-session synthesis (summary + key-events timeline).

Example input

{
  "campaign_id": "a1b2c3d4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to read. Must be owned by your account.

Example output

{
  "sessions": [
    {
      "recap_id": "…",
      "session_ref": "S01AB",
      "session_at": "2026-06-20T19:00:00Z"
    }
  ],
  "players": [
    {
      "speaker": "Mira",
      "points": [
        {
          "session_ref": "S01AB",
          "participation_pct": 28.4,
          "turn_count": 42,
          "talk_time_seconds": 612,
          "word_count": 1840
        }
      ]
    }
  ]
}

Field descriptions

FieldTypeDescription
sessionsarrayOne entry per ready session in the campaign.
sessions[].recap_idstring (uuid)The recap/session id.
sessions[].session_refstringShort human reference, e.g. S01AB.
sessions[].session_atstring (ISO 8601)When the session started (UTC).
playersarrayOne entry per recognised speaker.
players[].speakerstringSpeaker display name.
players[].pointsarrayPer-session metrics for this speaker.
players[].points[].participation_pctnumberShare of conversation, 0–100.
players[].points[].turn_countintegerNumber of speaking turns taken.
players[].points[].talk_time_secondsintegerTotal seconds speaking.
players[].points[].word_countintegerTotal words spoken.

get_credit_balance No extra tool charge

Your Ask allowance as percentage used, plus a recent-activity feed. Raw allowance counts are not exposed to MCP clients.

Example input

{}

Example output

{
  "usage": {
    "percent_used": 29
  },
  "recent_activity": [
    {
      "type": "recap_ready",
      "label": "Session S01AB recap is ready",
      "date": "2026-06-21T08:02:00Z",
      "recap_id": "…"
    },
    {
      "type": "credit",
      "label": "Asked your campaign",
      "date": "2026-06-21T08:05:00Z"
    }
  ]
}

Field descriptions

FieldTypeDescription
usage.percent_usedintegerWhole-number Ask usage from 0 to 100.
recent_activityarrayNewest-first feed (session-recorded, recap-ready, credit-ledger events).
recent_activity[].typestringEvent kind.
values: recap_ready | session_recorded | credit
recent_activity[].labelstringHuman-readable summary.
recent_activity[].datestring (ISO 8601)When the event occurred.
recent_activity[].recap_idstring (uuid) | nullPresent for recap/session-related events.

get_memory_overview No extra tool charge

Richest GM situational-awareness blob: distribution, engagement trend + overall score, per-session participation, pillars of play.

Example input

{
  "campaign_id": "a1b2c3d4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to read.

Example output

{
  "distribution": [
    {
      "label": "Roleplay",
      "pct": 58
    }
  ],
  "engagement": {
    "overall": {
      "score": 74,
      "label": "Playful / tense"
    }
  },
  "pillars": [
    "The green-eyed stranger",
    "The old inn",
    "The white moose hunt"
  ]
}

Field descriptions

FieldTypeDescription
distributionarrayShare of play by category.
distribution[].labelstringCategory name.
distribution[].pctnumberShare, 0–100.
engagement.overall.scorenumber | nullComposite engagement score, 0–100.
engagement.overall.labelstring | nullQualitative label, e.g. "Playful / tense".
pillarsstring[] | nullRecurring themes / pillars of play.

get_quest_log No extra tool charge

The player-visible quest log. GM additions and edits are shared with the party through Discord /questlog. Keep GM secrets out.

Example input

{
  "campaign_id": "a1b2c3d4-…",
  "page": 1
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)Your campaign to read.
pageintegerOptional, starting at 1. Ten quests per page.

Example output

{
  "quests": [
    {
      "id": "d4e5f6a7-…",
      "title": "Find the missing courier",
      "objective": "Follow the trail from the bridge.",
      "status": "active",
      "progress": "The party found a torn satchel."
    }
  ],
  "page": 1,
  "has_more": false
}

Field descriptions

FieldTypeDescription
questsarrayActive/stale first, then completed/abandoned. Internal editing metadata and source links are excluded.
quests[].idstring (uuid)Quest identifier.
quests[].titlestringQuest title.
quests[].objectivestringThe party’s objective.
quests[].statusstringactive, stale, completed or abandoned.
quests[].progressstringLatest saved progress.
pageintegerCurrent page.
has_morebooleanRequest the next page when true.

get_open_hooks No extra tool charge

Unresolved narrative hooks — what to prep next session.

Example input

{
  "campaign_id": "a1b2c3d4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to read.

Example output

{
  "hooks": [
    {
      "text": "Nobody bypasses the Choosing.",
      "session_ref": "S01AB",
      "session_date": "2026-06-20"
    }
  ]
}

Field descriptions

FieldTypeDescription
hooksarrayCurrently open narrative hooks.
hooks[].textstringThe hook description.
hooks[].session_refstringSession where the hook was introduced.
hooks[].session_datestring (YYYY-MM-DD)Date of that session.

get_previously_on No extra tool charge

Previously-on context for the campaign, built from owner-scoped memory.

Example input

{
  "campaign_id": "a1b2c3d4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to read.

Example output

{
  "sections": [
    {
      "title": "Last time",
      "bullets": [
        "The party followed the white moose into the mist."
      ]
    }
  ]
}

Field descriptions

FieldTypeDescription
sectionsarrayCampaign memory sections for recap/context.
sections[].titlestringSection heading.
sections[].bulletsstring[]Concise memory bullets.

get_next_session_prep_pack No extra tool charge

GM-only next-session prep advisory from Recap Raven’s prep cache. It may generate once, then stays cached until the next recap is ingested. Returns credit_cost: 0.

Example input

{
  "campaign_id": "a1b2c3d4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to prep. Must be owned by your account.

Example output

{
  "likely_directions": [
    {
      "title": "Return to the inn",
      "detail": "The unresolved clue points back to the old inn."
    }
  ],
  "threads_to_watch": [
    "Who opened the moonstone gate?"
  ],
  "prep_suggestions": [
    "Review the innkeeper and the gate password."
  ],
  "credit_cost": 0,
  "cache_policy": "cached_until_next_recap_ingested"
}

Field descriptions

FieldTypeDescription
likely_directionsarrayGM-facing likely next directions.
threads_to_watchstring[]Threads the GM may want to keep in mind.
prep_suggestionsstring[]Concrete prep prompts for the GM.
credit_costnumberAlways 0 for this MCP tool.
cache_policystringcached_until_next_recap_ingested.

get_chronicle No extra tool charge

Scenes-per-session map — the structured campaign chronicle.

Example input

{
  "campaign_id": "a1b2c3d4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to read.

Example output

{
  "sessions": [
    {
      "recap_id": "…",
      "session_ref": "S01AB",
      "scenes": [
        "The market",
        "The hunt",
        "The banshee fight"
      ]
    }
  ]
}

Field descriptions

FieldTypeDescription
sessionsarrayOne entry per session.
sessions[].recap_idstring (uuid)The recap/session id.
sessions[].scenesstring[]Ordered scene labels for that session.

get_entity No extra tool charge

The player-visible entity ledger derived from recorded sessions; private uploaded lore is excluded. With no name it returns the roster. With a name or alias it returns the matching card, interaction history, transcript turn links, relations, or ambiguity candidates.

Example input

{
  "campaign_id": "a1b2c3d4-…",
  "name": "Captain Vale"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to read.
namestring | omittedEntity name/alias to resolve to a single card. Omit for the roster.

Example output

{
  "entity": {
    "id": "…",
    "canonical_name": "Captain Vale",
    "kind": "npc",
    "aliases": [
      "Vale"
    ],
    "search_aliases": [],
    "interactions": [
      {
        "recap_id": "…",
        "summary": "Vale warned the party.",
        "turn_start": 12,
        "turn_end": 16
      }
    ],
    "relations": [
      {
        "subject": "Captain Vale",
        "predicate": "located_in",
        "object": "Saltmoor Harbour",
        "recap_id": "…",
        "turn_start": 41,
        "turn_end": 44,
        "visibility": "player_known",
        "session_date": "2026-05-01T00:00:00Z",
        "is_current": true,
        "predicate_class": "stateful"
      }
    ]
  }
}

Field descriptions

FieldTypeDescription
entitiesarray (roster mode)When no name is given: {canonical_name, kind, aliases, search_aliases} for each entity.
entityobject | null (card mode)When a name is given: the resolved entity card, or null if the name matches nothing.
candidatesarrayPossible entity cards when an alias is ambiguous; empty when resolution is unique.
entity.idstring (uuid)Stable campaign entity id.
entity.canonical_namestringCanonical entity name.
entity.kindstringEntity kind.
values: pc | npc | creature | place | item | faction | deity | other
entity.aliasesstring[]Display aliases.
entity.search_aliasesstring[]Retrieval-only aliases.
entity.interactionsarrayGrounded history entries with recap and transcript-turn links.
entity.interactions[].recap_idstring (uuid)Session containing the interaction.
entity.interactions[].turn_startinteger | nullFirst transcript turn for the evidence link.
entity.interactions[].turn_endinteger | nullLast transcript turn for the evidence link.
entity.interactions[].identity_matchstringconfirmed or suggested; suggested links are possible alias matches, not canonical identity claims.
values: confirmed | suggested
entity.relationsarrayRelations graph (oldest → newest).
entity.relations[].predicatestringe.g. located_in, ally_of, owns.
entity.relations[].recap_idstring (uuid)Session that evidences the relation.
entity.relations[].visibilitystringGM-visibility of the edge. Legacy pre-witness data can carry dm_only edges — do not surface to players.
values: player_known | dm_only
entity.relations[].is_currentbooleanThe live leg of a stateful predicate.

get_mechanics_events No extra tool charge

Turn-anchored mechanics events (attacks, spells, damage, rests, conditions) for one session — the same data as the mechanics tab.

Example input

{
  "recap_id": "…"
}

Field descriptions

FieldTypeDescription
recap_idstring (uuid)The session to read. Use list_sessions to discover ids.

Example output

{
  "recap_id": "…",
  "mechanics": [
    {
      "action": "attack",
      "actor": "Mira",
      "target": "Goblin",
      "damage_amount": 7,
      "damage_type": "slashing",
      "evidence_turn_start": 41,
      "evidence_turn_end": 41
    }
  ]
}

Field descriptions

FieldTypeDescription
recap_idstring (uuid)The session these events belong to.
mechanicsarrayTurn-anchored mechanics events.
mechanics[].actionstringEvent kind.
values: attack | spell_cast | short_rest | long_rest | consumable_used | condition_applied | ability_used | form_changed
mechanics[].actorstringWho acted.
mechanics[].evidence_turn_startintegerFirst turn evidencing the event.
mechanics[].evidence_turn_endintegerLast turn evidencing the event.

get_lore_documents No extra tool charge

The list of lore documents for the campaign.

Example input

{
  "campaign_id": "a1b2c3d4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to read.

Example output

{
  "documents": [
    {
      "id": "…",
      "title": "The Choosing",
      "updated_at": "2026-06-20"
    }
  ]
}

Field descriptions

FieldTypeDescription
documentsarrayLore documents.
documents[].idstring (uuid)Document identifier.
documents[].titlestringDocument title.

get_lore_document No extra tool charge

A single lore document with its full body.

Example input

{
  "campaign_id": "a1b2c3d4-…",
  "doc_id": "…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to read.
doc_idstring (uuid)The lore document to fetch.

Example output

{
  "document": {
    "id": "…",
    "title": "The Choosing",
    "body": "…"
  }
}

Field descriptions

FieldTypeDescription
document.idstring (uuid)Document identifier.
document.titlestringDocument title.
document.bodystringFull document body (markdown).

get_lexicon No extra tool charge

Lexicon entries (canonical names, aliases, type, confidence).

Example input

{
  "campaign_id": "a1b2c3d4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to read.

Example output

{
  "entries": [
    {
      "id": "…",
      "canonical": "Sol’Thun",
      "type": "npc",
      "aliases": [
        "the archmage"
      ],
      "confidence": 0.92,
      "enabled": true
    }
  ]
}

Field descriptions

FieldTypeDescription
entriesarrayLexicon entries.
entries[].canonicalstringCanonical name.
entries[].typestringEntity kind.
values: npc | place | item | creature | other
entries[].aliasesstring[]Alternate names / aliases.
entries[].confidencenumberExtraction confidence, 0–1.
entries[].enabledbooleanWhether this entry is active.

get_transcript_window No extra tool charge

A positional slice of a session’s stored turns (no content search — just from/to turn indices).

Example input

{
  "recap_id": "…",
  "from": 0,
  "to": 20
}

Field descriptions

FieldTypeDescription
recap_idstring (uuid)The recap/session to read. Use list_sessions to discover ids.
fromintegerStart turn index (inclusive, default 0).
tointegerEnd turn index. Omit for a 50-turn window from the start index.

Example output

{
  "recap_id": "…",
  "total_turns": 184,
  "from": 0,
  "to": 20,
  "turns": [
    {
      "index": 0,
      "t": "~00:01",
      "speaker": "GM",
      "text": "…"
    }
  ]
}

Field descriptions

FieldTypeDescription
total_turnsintegerTotal turns in the session.
fromintegerStart index returned.
tointegerEnd index returned.
turnsarrayTurns in the requested window.
turns[].indexintegerPositional index in the session.
turns[].tstringRelative timestamp, e.g. ~00:01.
turns[].speakerstringSpeaker display name.
turns[].textstringThe spoken text.

get_recap No extra tool charge

The player-safe recap HTML for a ready recap. The artifact type is server-owned; there is no type input.

Example input

{
  "recap_id": "…"
}

Field descriptions

FieldTypeDescription
recap_idstring (uuid)The recap/session whose player-safe recap should be fetched.

Example output

{
  "recap": {
    "recap_id": "…",
    "content_type": "player_recap_html",
    "html": "<!doctype html>…",
    "truncated": false
  }
}

Field descriptions

FieldTypeDescription
recap.recap_idstring (uuid)The recap/session id.
recap.content_typestringAlways player_recap_html.
values: player_recap_html
recap.htmlstringThe player-safe recap HTML.
recap.truncatedbooleanTrue when the MCP response cap trimmed the HTML.

search_transcript No extra tool charge

Exact keyword (full-text) search over your campaign transcript turns. Returns turns containing the exact phrase, with their session, speaker and timestamp — no LLM, no embedding, no credits.

Example input

{
  "campaign_id": "a1b2c3d4-…",
  "query": "white moose",
  "recap_id": "r1e2c3a4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to search.
querystringExact phrase to match (full-text search against transcript text).
recap_idstring (uuid) | omittedOptional session scope. Use list_sessions to discover recap ids.

Example output

{
  "query": "white moose",
  "matches": [
    {
      "recap_id": "…",
      "speaker": "GM",
      "t": "~01:04",
      "text": "All you know is that it’s a white moose."
    }
  ]
}

Field descriptions

FieldTypeDescription
querystringEcho of the submitted query.
matchesarrayTranscript turns containing the exact phrase, best match first.
matches[].recap_idstring (uuid)Recap/session the match came from.
matches[].speakerstringSpeaker of the matched turn.
matches[].tstringRelative timestamp, e.g. ~01:04.
matches[].textstringThe matched transcript text.

search_facts No extra tool charge

Keyword full-text search over your campaign’s scene beats — the dense, beat-by-beat summaries the chat lane reasons over. Best for exact terms (a name, an item, a place). Returns beat text with its scene/session context and turn range, so an agent can locate the moment and pull the exact transcript window next. No LLM, no embedding, no credits. For natural-language, meaning-based lookup use search_corpus.

Example input

{
  "campaign_id": "a1b2c3d4-…",
  "query": "white moose hunt",
  "recap_id": "r1e2c3a4-…"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to search.
querystringKeywords to match against beat text (full-text search).
recap_idstring (uuid) | omittedOptional session scope. Use list_sessions to discover recap ids.

Example output

{
  "matches": [
    {
      "recap_id": "…",
      "session_number": 12,
      "session_title": "The Choosing",
      "scene_title": "The hunt",
      "beat_index": 3,
      "turn_start": 120,
      "turn_end": 168,
      "text": "The party tracked the white moose north of the old inn…",
      "truncated": false
    }
  ]
}

Field descriptions

FieldTypeDescription
matchesarrayMatching beats, best match first.
matches[].recap_idstring (uuid)Session the beat came from.
matches[].session_numberinteger | nullThe GM’s session number, when set.
matches[].session_titlestring | nullThe session’s title, when set.
matches[].scene_titlestring | nullTitle of the scene the beat belongs to.
matches[].beat_indexintegerThe beat’s position within its scene.
matches[].turn_startintegerFirst turn of the beat’s window (feed into get_transcript_window).
matches[].turn_endintegerLast turn of the beat’s window.
matches[].textstringThe beat text (capped at 1200 chars).
matches[].truncatedbooleanTrue when the beat text was trimmed.

Uses chat credits

Three tools spend metered chat credits. Think finder vs answer: search_corpus (low chat credits) hands you the strongest matches as structured evidence for your own model to reason over — a fast finder, not an exhaustive index; ask_campaign_cited answers a GM-facing question with verbatim transcript quotes and turn citations; and ask_player_campaign_cited gives the shorter player-facing version through the player lane. All are rate-limited per API key in addition to your balance.

search_corpus Uses low chat credits

search_corpus finds relevant campaign moments even when you do not know the exact wording. It uses semantic search and reranking to return the strongest evidence matches, including summary, session context and turn ranges. It is a finder, not an exhaustive index: only the top matches come back, so treat an empty or thin result as “nothing strong surfaced,” not proof that nothing exists. Use the matches to discover the real terms or names the table used, then run an exhaustive free keyword search with search_facts / search_transcript, or take the turn ranges straight to get_transcript_window for the verbatim segment. For a definitive, verified answer to a question — verbatim quotes and citations — use ask_campaign_cited instead (pricier, a written reply rather than structured rows). Set player_safe: true to exclude GM-only content from search results. It uses low chat credits per search, charged on any completed search, even one with no matches.

Example input

{
  "campaign_id": "a1b2c3d4-…",
  "query": "when did the party first distrust the mayor?",
  "player_safe": false
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to search.
querystringA natural-language description of what you’re looking for.
player_safeboolean (default false)When true, the retrieval is visibility-filtered — GM-only, inferred and speculation memory and GM-typed lore are excluded. Use for anything players will see.

Example output

{
  "matches": [
    {
      "object_id": "…",
      "type": "plot_event",
      "visibility": "player_known",
      "recap_id": "…",
      "session_number": 8,
      "session_title": "The Mayor’s Bargain",
      "turn_start": 212,
      "turn_end": 240,
      "text": "The party noticed the mayor’s story about the missing caravan didn’t add up…",
      "truncated": false
    }
  ],
  "credit_cost": "<chat credits charged for this search>"
}

Field descriptions

FieldTypeDescription
matchesarrayRanked memory matches, best match first.
matches[].object_idstring (uuid)The memory object’s id.
matches[].typestringThe kind of memory (e.g. plot_event, npc_fact).
matches[].visibilitystringWho knows this. On a player_safe search it is only ever player-visible.
matches[].recap_idstring (uuid) | nullThe session the memory came from.
matches[].session_numberinteger | nullThe GM’s session number, when set.
matches[].session_titlestring | nullThe session’s title, when set.
matches[].turn_startinteger | nullFirst turn of the evidence window (feed into get_transcript_window).
matches[].turn_endinteger | nullLast turn of the evidence window.
matches[].textstringThe memory summary (capped at 1200 chars).
matches[].truncatedbooleanTrue when the summary was trimmed.
credit_costnumberChat credits charged for this completed search.

ask_campaign_cited Uses chat credits

Ask your campaign a GM-facing factual question and get the AI answer — the same beats answer engine as in-app chat, with verbatim transcript quotes and turn citations. Uses GM chat credits, the same cost as in-app chat. Rate-limited per API key in addition to your balance.

Example input

{
  "campaign_id": "a1b2c3d4-…",
  "question": "Where did the hunter see the moose?"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to ask.
questionstringYour question.
clarificationobject | omittedOptional: answer a clarifying question the engine asked (GM lane only).
clarification.questionstringThe clarifying question from the engine.
clarification.answerstringYour answer to that question.
supersedesstring | omittedMessage id of a prior answer to replace (GM lane only).

Example output

{
  "answer_markdown": "The hunter reported seeing the white moose near the old inn to the north of town [1].",
  "quotes": [
    {
      "turn": 104,
      "speaker": "Skoll",
      "text": "All you know is that it’s a white moose.",
      "recap_id": "…"
    }
  ],
  "confidence_label": "high",
  "credit_cost": 1
}

Field descriptions

FieldTypeDescription
answer_markdownstringThe cited answer, with inline [n] markers.
citationsarrayCitation metadata backing each marker.
quotesarrayVerbatim transcript quotes used as evidence.
confidence_labelstringCoarse confidence band.
values: high | medium | low
credit_costnumberCredits charged for this answer.
quotes[].turnintegerThe cited turn index.
quotes[].recap_idstring (uuid) | nullThe session the quote came from.

ask_player_campaign_cited Uses chat credits

Ask a player-facing factual campaign question and get a short player-safe answer with transcript citations. GM-only content is excluded by the server-side player lane. Uses player chat credits.

Example input

{
  "campaign_id": "a1b2c3d4-…",
  "question": "What do we know about the harbour?"
}

Field descriptions

FieldTypeDescription
campaign_idstring (uuid)The campaign to ask.
questionstringYour question.

Example output

{
  "answer_markdown": "The party knows the harbourmaster helped them find the missing crate [1].",
  "quotes": [
    {
      "turn": 104,
      "speaker": "Skoll",
      "text": "The harbourmaster pointed us to the crate.",
      "recap_id": "…"
    }
  ],
  "confidence_label": "medium",
  "credit_cost": 1
}

Field descriptions

FieldTypeDescription
answer_markdownstringThe cited answer, with inline [n] markers.
citationsarrayCitation metadata backing each marker.
quotesarrayVerbatim transcript quotes used as evidence.
confidence_labelstringCoarse confidence band.
values: high | medium | low
credit_costnumberCredits charged for this answer.

Player-safe example

Building a player-facing bot or overlay? Use the player cited tool so the answer comes from the player lane, whose visibility-filtered ledger never includes GM-only content:

{
  "tool": "ask_player_campaign_cited",
  "arguments": {
    "campaign_id": "a1b2c3d4-…",
    "question": "What do we know about the harbour?"
  }
}

Where your data goes

When you connect an MCP client and use these tools, your campaign content is sent to the AI client you chose — for example ChatGPT, Claude or Gemini. That provider’s data-use and training terms apply to what you send it, not ours.

Recap Raven’s no-training guarantee covers our own pipeline and the model providers we disclose. It cannot cover a client you connect: by wiring one up you are choosing a new processor for your campaign data. Please check that client’s terms before you connect it, and use ask_player_campaign_cited or player-safe search filters (and avoid GM-only reads like lore and chronicle) for anything players will see.

Questions or feedback? See how Recap Raven works or invite the bot below.

Run Recap Raven at your next session

Invite the bot, create your campaign, and get free recaps every month.

Run Recap Raven at your next session

No card required · Record only with your table’s consent · Delete or export your data anytime

Questions or feedback? Join our Discord community →

Built for Discord voice. We capture the whole session for you — no logging in afterwards, no files to send us, and no guessing who spoke. Memory plans keep your editable world wiki current as you play, ready for questions about what happened, who said what, open hooks, and what to prep next.