For custom AI workflows
Connect ChatGPT or Claude to your campaign memory.
Want to ask questions of your campaign from ChatGPT, Claude, Gemini or your own agent? Recap Raven’s MCP server exposes your campaign memory and AI answers as tools any MCP client can call — so the recap engine is reachable from wherever your agent lives.
What is MCP?
MCP lets apps like ChatGPT, Claude, Gemini, or your own agent 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
Connect your MCP client to https://mcp.recapraven.com/mcp using a personal API key from your Recap Raven account area. Create or rotate keys from API Keys. You need a plan with memory. Choose your client to see the setup steps:
Most users only need this
- Create an API key.
- Add
https://mcp.recapraven.com/mcpto your MCP client. - Use
Authorization: Bearer <your key>. - Ask: “List my Recap Raven campaigns”.
https://mcp.recapraven.com/mcpAuthorization: Bearer raven_sk_...ChatGPT Desktop setup
- In your Recap Raven account area, open API Keys → Create key. You need a plan with memory, and the key is shown once.
- Open ChatGPT Desktop → Settings → Connectors → Add a new connector.
- Set the URL to https://mcp.recapraven.com/mcp
- Set the Authorization header to: Bearer raven_sk_... (the key from step 1).
- Save, then start a chat — Recap Raven’s tools become available to ChatGPT.
- Test it by asking ChatGPT: “List my Recap Raven campaigns.”
URL: https://mcp.recapraven.com/mcp
Authorization: Bearer <YOUR_RECAP_RAVEN_API_KEY>Available on plans with memory
MCP is unavailable on Free and Recap Only. Most deterministic tool calls run on every memory plan (Premium, Studio, Pro) at no extra cost. Semantic search and AI answers spend your existing Ask credits.
| Free tier | Unavailable |
|---|---|
| Recap Only | Unavailable |
| Plans with memory | Deterministic tools at no extra cost; search_corpus uses low chat credits per search, and cited ask tools spend Ask credits. |
| API key | Create one personal API key from API Keys. Revoke any time; keys are disabled if the account leaves a plan with memory. |
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.
| Server fair-use | Per-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 | 15 requests/minute and 150 requests/hour. |
| Studio | 30 requests/minute and 450 requests/hour. |
| Pro | 60 requests/minute and 1200 requests/hour. |
| Free tools | Rate-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 tools | search_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 they cost no credits on any memory plan. 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_campaignsIncluded with your memory planDiscovery tool: list the campaigns available to your MCP key so other tools can use the right campaign_id.
list_campaignsIncluded with your memory planDiscovery 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 | Type | Description |
|---|---|---|
| campaigns | array | Campaigns owned by your account. |
| campaigns[].campaign_id | string (uuid) | Use this id with campaign-scoped MCP tools. |
| campaigns[].name | string | Campaign name. |
| campaigns[].module | string | null | Optional module/system label. |
list_sessionsIncluded with your memory planDiscovery tool: list ready sessions with their session_number and title, so an agent can map "session 12" to the right recap_id.
list_sessionsIncluded with your memory planDiscovery 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 | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | omitted | Optional campaign filter. |
Example output
{
"recaps": [
{
"recap_id": "r1e2c3a4-…",
"campaign_id": "a1b2c3d4-…",
"session_number": 12,
"title": "The Choosing",
"status": "ready"
}
]
}| Field | Type | Description |
|---|---|---|
| recaps | array | Ready sessions visible to your account. |
| recaps[].recap_id | string (uuid) | Use this id with get_recap, get_transcript_window and get_mechanics_events. |
| recaps[].campaign_id | string (uuid) | null | Campaign that owns the session. |
| recaps[].session_number | integer | null | The GM’s own 1-based session numbering (DM-editable), when set. |
| recaps[].title | string | null | Session title. |
| recaps[].status | string | Only ready sessions are listed. |
get_engagementIncluded with your memory planThe campaign engagement dashboard: per-speaker participation trend and the cached cross-session synthesis (summary + key-events timeline).
get_engagementIncluded with your memory planThe campaign engagement dashboard: per-speaker participation trend and the cached cross-session synthesis (summary + key-events timeline).
Example input
{
"campaign_id": "a1b2c3d4-…"
}| Field | Type | Description |
|---|---|---|
| campaign_id | string (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 | Type | Description |
|---|---|---|
| sessions | array | One entry per ready session in the campaign. |
| sessions[].recap_id | string (uuid) | The recap/session id. |
| sessions[].session_ref | string | Short human reference, e.g. S01AB. |
| sessions[].session_at | string (ISO 8601) | When the session started (UTC). |
| players | array | One entry per recognised speaker. |
| players[].speaker | string | Speaker display name. |
| players[].points | array | Per-session metrics for this speaker. |
| players[].points[].participation_pct | number | Share of conversation, 0–100. |
| players[].points[].turn_count | integer | Number of speaking turns taken. |
| players[].points[].talk_time_seconds | integer | Total seconds speaking. |
| players[].points[].word_count | integer | Total words spoken. |
get_credit_balanceIncluded with your memory planYour Ask allowance as percentage used, plus a recent-activity feed. Raw allowance counts are not exposed to MCP clients.
get_credit_balanceIncluded with your memory planYour 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 | Type | Description |
|---|---|---|
| usage.percent_used | integer | Whole-number Ask usage from 0 to 100. |
| recent_activity | array | Newest-first feed (session-recorded, recap-ready, credit-ledger events). |
| recent_activity[].type | string | Event kind.values: recap_ready | session_recorded | credit |
| recent_activity[].label | string | Human-readable summary. |
| recent_activity[].date | string (ISO 8601) | When the event occurred. |
| recent_activity[].recap_id | string (uuid) | null | Present for recap/session-related events. |
get_memory_overviewIncluded with your memory planRichest GM situational-awareness blob: distribution, engagement trend + overall score, per-session participation, pillars of play.
get_memory_overviewIncluded with your memory planRichest GM situational-awareness blob: distribution, engagement trend + overall score, per-session participation, pillars of play.
Example input
{
"campaign_id": "a1b2c3d4-…"
}| Field | Type | Description |
|---|---|---|
| campaign_id | string (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 | Type | Description |
|---|---|---|
| distribution | array | Share of play by category. |
| distribution[].label | string | Category name. |
| distribution[].pct | number | Share, 0–100. |
| engagement.overall.score | number | null | Composite engagement score, 0–100. |
| engagement.overall.label | string | null | Qualitative label, e.g. "Playful / tense". |
| pillars | string[] | null | Recurring themes / pillars of play. |
get_open_hooksIncluded with your memory planUnresolved narrative hooks — what to prep next session.
get_open_hooksIncluded with your memory planUnresolved narrative hooks — what to prep next session.
Example input
{
"campaign_id": "a1b2c3d4-…"
}| Field | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to read. |
Example output
{
"hooks": [
{
"text": "Nobody bypasses the Choosing.",
"session_ref": "S01AB",
"session_date": "2026-06-20"
}
]
}| Field | Type | Description |
|---|---|---|
| hooks | array | Currently open narrative hooks. |
| hooks[].text | string | The hook description. |
| hooks[].session_ref | string | Session where the hook was introduced. |
| hooks[].session_date | string (YYYY-MM-DD) | Date of that session. |
get_previously_onIncluded with your memory planPreviously-on context for the campaign, built from owner-scoped memory.
get_previously_onIncluded with your memory planPreviously-on context for the campaign, built from owner-scoped memory.
Example input
{
"campaign_id": "a1b2c3d4-…"
}| Field | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to read. |
Example output
{
"sections": [
{
"title": "Last time",
"bullets": [
"The party followed the white moose into the mist."
]
}
]
}| Field | Type | Description |
|---|---|---|
| sections | array | Campaign memory sections for recap/context. |
| sections[].title | string | Section heading. |
| sections[].bullets | string[] | Concise memory bullets. |
get_next_session_prep_packIncluded with your memory planGM-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.
get_next_session_prep_packIncluded with your memory planGM-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 | Type | Description |
|---|---|---|
| campaign_id | string (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 | Type | Description |
|---|---|---|
| likely_directions | array | GM-facing likely next directions. |
| threads_to_watch | string[] | Threads the GM may want to keep in mind. |
| prep_suggestions | string[] | Concrete prep prompts for the GM. |
| credit_cost | number | Always 0 for this MCP tool. |
| cache_policy | string | cached_until_next_recap_ingested. |
get_chronicleIncluded with your memory planScenes-per-session map — the structured campaign chronicle.
get_chronicleIncluded with your memory planScenes-per-session map — the structured campaign chronicle.
Example input
{
"campaign_id": "a1b2c3d4-…"
}| Field | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to read. |
Example output
{
"sessions": [
{
"recap_id": "…",
"session_ref": "S01AB",
"scenes": [
"The market",
"The hunt",
"The banshee fight"
]
}
]
}| Field | Type | Description |
|---|---|---|
| sessions | array | One entry per session. |
| sessions[].recap_id | string (uuid) | The recap/session id. |
| sessions[].scenes | string[] | Ordered scene labels for that session. |
get_entityIncluded with your memory planThe 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.
get_entityIncluded with your memory planThe 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 | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to read. |
| name | string | omitted | Entity 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 | Type | Description |
|---|---|---|
| entities | array (roster mode) | When no name is given: {canonical_name, kind, aliases, search_aliases} for each entity. |
| entity | object | null (card mode) | When a name is given: the resolved entity card, or null if the name matches nothing. |
| candidates | array | Possible entity cards when an alias is ambiguous; empty when resolution is unique. |
| entity.id | string (uuid) | Stable campaign entity id. |
| entity.canonical_name | string | Canonical entity name. |
| entity.kind | string | Entity kind.values: pc | npc | creature | place | item | faction | deity | other |
| entity.aliases | string[] | Display aliases. |
| entity.search_aliases | string[] | Retrieval-only aliases. |
| entity.interactions | array | Grounded history entries with recap and transcript-turn links. |
| entity.interactions[].recap_id | string (uuid) | Session containing the interaction. |
| entity.interactions[].turn_start | integer | null | First transcript turn for the evidence link. |
| entity.interactions[].turn_end | integer | null | Last transcript turn for the evidence link. |
| entity.interactions[].identity_match | string | confirmed or suggested; suggested links are possible alias matches, not canonical identity claims.values: confirmed | suggested |
| entity.relations | array | Relations graph (oldest → newest). |
| entity.relations[].predicate | string | e.g. located_in, ally_of, owns. |
| entity.relations[].recap_id | string (uuid) | Session that evidences the relation. |
| entity.relations[].visibility | string | GM-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_current | boolean | The live leg of a stateful predicate. |
get_mechanics_eventsIncluded with your memory planTurn-anchored mechanics events (attacks, spells, damage, rests, conditions) for one session — the same data as the mechanics tab.
get_mechanics_eventsIncluded with your memory planTurn-anchored mechanics events (attacks, spells, damage, rests, conditions) for one session — the same data as the mechanics tab.
Example input
{
"recap_id": "…"
}| Field | Type | Description |
|---|---|---|
| recap_id | string (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 | Type | Description |
|---|---|---|
| recap_id | string (uuid) | The session these events belong to. |
| mechanics | array | Turn-anchored mechanics events. |
| mechanics[].action | string | Event kind.values: attack | spell_cast | short_rest | long_rest | consumable_used | condition_applied | ability_used | form_changed |
| mechanics[].actor | string | Who acted. |
| mechanics[].evidence_turn_start | integer | First turn evidencing the event. |
| mechanics[].evidence_turn_end | integer | Last turn evidencing the event. |
get_lore_documentsIncluded with your memory planThe list of lore documents for the campaign.
get_lore_documentsIncluded with your memory planThe list of lore documents for the campaign.
Example input
{
"campaign_id": "a1b2c3d4-…"
}| Field | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to read. |
Example output
{
"documents": [
{
"id": "…",
"title": "The Choosing",
"updated_at": "2026-06-20"
}
]
}| Field | Type | Description |
|---|---|---|
| documents | array | Lore documents. |
| documents[].id | string (uuid) | Document identifier. |
| documents[].title | string | Document title. |
get_lore_documentIncluded with your memory planA single lore document with its full body.
get_lore_documentIncluded with your memory planA single lore document with its full body.
Example input
{
"campaign_id": "a1b2c3d4-…",
"doc_id": "…"
}| Field | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to read. |
| doc_id | string (uuid) | The lore document to fetch. |
Example output
{
"document": {
"id": "…",
"title": "The Choosing",
"body": "…"
}
}| Field | Type | Description |
|---|---|---|
| document.id | string (uuid) | Document identifier. |
| document.title | string | Document title. |
| document.body | string | Full document body (markdown). |
get_lexiconIncluded with your memory planLexicon entries (canonical names, aliases, type, confidence).
get_lexiconIncluded with your memory planLexicon entries (canonical names, aliases, type, confidence).
Example input
{
"campaign_id": "a1b2c3d4-…"
}| Field | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to read. |
Example output
{
"entries": [
{
"id": "…",
"canonical": "Sol’Thun",
"type": "npc",
"aliases": [
"the archmage"
],
"confidence": 0.92,
"enabled": true
}
]
}| Field | Type | Description |
|---|---|---|
| entries | array | Lexicon entries. |
| entries[].canonical | string | Canonical name. |
| entries[].type | string | Entity kind.values: npc | place | item | creature | other |
| entries[].aliases | string[] | Alternate names / aliases. |
| entries[].confidence | number | Extraction confidence, 0–1. |
| entries[].enabled | boolean | Whether this entry is active. |
get_transcript_windowIncluded with your memory planA positional slice of a session’s stored turns (no content search — just from/to turn indices).
get_transcript_windowIncluded with your memory planA 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 | Type | Description |
|---|---|---|
| recap_id | string (uuid) | The recap/session to read. Use list_sessions to discover ids. |
| from | integer | Start turn index (inclusive, default 0). |
| to | integer | End 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 | Type | Description |
|---|---|---|
| total_turns | integer | Total turns in the session. |
| from | integer | Start index returned. |
| to | integer | End index returned. |
| turns | array | Turns in the requested window. |
| turns[].index | integer | Positional index in the session. |
| turns[].t | string | Relative timestamp, e.g. ~00:01. |
| turns[].speaker | string | Speaker display name. |
| turns[].text | string | The spoken text. |
get_recapIncluded with your memory planThe player-safe recap HTML for a ready recap. The artifact type is server-owned; there is no type input.
get_recapIncluded with your memory planThe player-safe recap HTML for a ready recap. The artifact type is server-owned; there is no type input.
Example input
{
"recap_id": "…"
}| Field | Type | Description |
|---|---|---|
| recap_id | string (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 | Type | Description |
|---|---|---|
| recap.recap_id | string (uuid) | The recap/session id. |
| recap.content_type | string | Always player_recap_html.values: player_recap_html |
| recap.html | string | The player-safe recap HTML. |
| recap.truncated | boolean | True when the MCP response cap trimmed the HTML. |
search_transcriptIncluded with your memory planExact 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.
search_transcriptIncluded with your memory planExact 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 | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to search. |
| query | string | Exact phrase to match (full-text search against transcript text). |
| recap_id | string (uuid) | omitted | Optional 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 | Type | Description |
|---|---|---|
| query | string | Echo of the submitted query. |
| matches | array | Transcript turns containing the exact phrase, best match first. |
| matches[].recap_id | string (uuid) | Recap/session the match came from. |
| matches[].speaker | string | Speaker of the matched turn. |
| matches[].t | string | Relative timestamp, e.g. ~01:04. |
| matches[].text | string | The matched transcript text. |
search_factsIncluded with your memory planKeyword 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.
search_factsIncluded with your memory planKeyword 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 | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to search. |
| query | string | Keywords to match against beat text (full-text search). |
| recap_id | string (uuid) | omitted | Optional 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 | Type | Description |
|---|---|---|
| matches | array | Matching beats, best match first. |
| matches[].recap_id | string (uuid) | Session the beat came from. |
| matches[].session_number | integer | null | The GM’s session number, when set. |
| matches[].session_title | string | null | The session’s title, when set. |
| matches[].scene_title | string | null | Title of the scene the beat belongs to. |
| matches[].beat_index | integer | The beat’s position within its scene. |
| matches[].turn_start | integer | First turn of the beat’s window (feed into get_transcript_window). |
| matches[].turn_end | integer | Last turn of the beat’s window. |
| matches[].text | string | The beat text (capped at 1200 chars). |
| matches[].truncated | boolean | True 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_corpusUses low chat creditssearch_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.
search_corpusUses low chat creditssearch_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 | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to search. |
| query | string | A natural-language description of what you’re looking for. |
| player_safe | boolean (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 | Type | Description |
|---|---|---|
| matches | array | Ranked memory matches, best match first. |
| matches[].object_id | string (uuid) | The memory object’s id. |
| matches[].type | string | The kind of memory (e.g. plot_event, npc_fact). |
| matches[].visibility | string | Who knows this. On a player_safe search it is only ever player-visible. |
| matches[].recap_id | string (uuid) | null | The session the memory came from. |
| matches[].session_number | integer | null | The GM’s session number, when set. |
| matches[].session_title | string | null | The session’s title, when set. |
| matches[].turn_start | integer | null | First turn of the evidence window (feed into get_transcript_window). |
| matches[].turn_end | integer | null | Last turn of the evidence window. |
| matches[].text | string | The memory summary (capped at 1200 chars). |
| matches[].truncated | boolean | True when the summary was trimmed. |
| credit_cost | number | Chat credits charged for this completed search. |
ask_campaign_citedUses chat creditsAsk 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.
ask_campaign_citedUses chat creditsAsk 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 | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to ask. |
| question | string | Your question. |
| clarification | object | omitted | Optional: answer a clarifying question the engine asked (GM lane only). |
| clarification.question | string | The clarifying question from the engine. |
| clarification.answer | string | Your answer to that question. |
| supersedes | string | omitted | Message 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 | Type | Description |
|---|---|---|
| answer_markdown | string | The cited answer, with inline [n] markers. |
| citations | array | Citation metadata backing each marker. |
| quotes | array | Verbatim transcript quotes used as evidence. |
| confidence_label | string | Coarse confidence band.values: high | medium | low |
| credit_cost | number | Credits charged for this answer. |
| quotes[].turn | integer | The cited turn index. |
| quotes[].recap_id | string (uuid) | null | The session the quote came from. |
ask_player_campaign_citedUses chat creditsAsk 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.
ask_player_campaign_citedUses chat creditsAsk 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 | Type | Description |
|---|---|---|
| campaign_id | string (uuid) | The campaign to ask. |
| question | string | Your 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 | Type | Description |
|---|---|---|
| answer_markdown | string | The cited answer, with inline [n] markers. |
| citations | array | Citation metadata backing each marker. |
| quotes | array | Verbatim transcript quotes used as evidence. |
| confidence_label | string | Coarse confidence band.values: high | medium | low |
| credit_cost | number | Credits 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.