An MCP server for managing RPG campaigns, characters, locations, factions, and world state with persistence and logging
RpgLedger demonstrates mixed quality across its 13 tools. Strengths: all tools have names starting with action verbs (list_, get_, create_, update_, upsert_, delete_, roll_); schemas are present for all tools with typed parameters; many parameters have descriptions. Weaknesses: (1) Descriptions vary significantly in quality, some are detailed and multi-line (create_campaign, create_character, update_character, upsert_location, delete_location), others are minimal or generic (list_campaigns 'List all available campaigns', roll_dice description is English but terse); (2) Mixed language use, descriptions for some tools are in Polish (list_locations, create_campaign, create_character, update_character, upsert_location, delete_location, list_factions, upsert_faction, faction_rep_add), others in English, which reduces clarity for English-speaking agents; (3) Output schemas are not documented, callers cannot see what structure list_campaigns, get_campaign, or get_character return; (4) No error handling guidance in descriptions, tools like delete_location (destructive) do not warn of consequences or recovery paths; (5) Parameters lack constraints, e.g., campaign_id and char_id accept any string, with no format validation guidance; (6) Roll_dice includes optional logging but no guidance on when or why to use it. Average tool description length is ~150 chars (within rubric baseline of 194), but variance is high and Polish descriptions reduce usefulness. Per-tool assessment: naming is consistently strong (100 across all tools), descriptions range 20 - 90 depending on language and detail, schemas are complete but undocumented return types.
Utwórz nową kampanię i zapisz ją w `data/{campaign_id}.json`. - `campaign_id` – docelowe id kampanii (nazwa pliku bez `.json`), np. `smolenia-na-wieczne-tymczasem` - `name` – opcjonalna nazwa wyświetlana (jeśli brak, użyje `campaign_id`) - `from_campaign_id`: - jeśli **pominięte** / `null` → tworzona jest **pusta, nowa kampania** z bazową strukturą - jeśli podane → nowa kampania będzie **kopią istniejącej** (snapshot), z podmienionym `id` i `name` - `overwrite` – jeśli `true`, pozwala nadpisać istniejący plik kampanii o tym samym id Użycie: - **nowa kampania od zera**: - `create_campaign(campaign_id="smolenia-na-wieczne-tymczasem", name="Smolenia na wieczne tymczasem")` - *nie ustawiaj* `from_campaign_id`, jeśli nie chcesz kopiować starej kampanii. - **kopiowanie istniejącej kampanii jako szablonu**: - `create_campaign(campaign_id="kampania-kopia", from_campaign_id="kampania-1") Skutek uboczny: zapis pliku `data/{campaign_id}.json` oraz wpis w logu kampanii (type="history").
Dodaj nową postać do kampanii. - campaign_id: id kampanii - character: słownik z danymi postaci (name, class, level, hp, xp, stats, gold, inventory, notes, itp.) Jeśli character.id jest puste, zostanie wygenerowane z name w formie slug. Skutek uboczny: zapis kampanii oraz wpis typu "mutate" (op="create_character").
Usuń lokację z kampanii na podstawie jej id lub nazwy. - campaign_id: id kampanii - location_id_or_name: id lub nazwa lokacji do usunięcia Skutek uboczny: usunięcie lokacji z `campaign.locations` oraz wpis typu "mutate" (op="delete_location").
Zmień reputację frakcji w kampanii
Output schemas not documented. Tools like list_campaigns, get_campaign, get_character, list_locations, list_factions return complex structures but do not declare field names, types, or expected properties. LLMs cannot plan downstream tool calls or extract the right data without documented outputs.
Mixed language descriptions reduce clarity. Tools use Polish for descriptions (list_locations, create_campaign, create_character, update_character, upsert_location, delete_location, list_factions, upsert_faction, faction_rep_add), while others use English. This inconsistency confuses LLMs trained primarily on English documentation and reduces prompt clarity.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 59 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 0 | - | v1 |
Get full campaign data by campaign_id
Get a specific character from a campaign
List all available campaigns
Zwróć listę frakcji kampanii (`campaign.factions`).
Zwróć listę lokacji kampanii (`campaign.locations`)
Roll dice with D20 expression notation and optionally log to campaign history
Zaktualizuj istniejącą postać w kampanii. - campaign_id: id kampanii - char_id: id postaci do aktualizacji - patch: słownik z polami do nadpisania (np. name, class, level, hp, stats, gold, notes) Skutek uboczny: zapis kampanii oraz wpis typu "mutate" (op="update_character").
Dodaj lub zaktualizuj frakcję w kampanii. - campaign_id: id kampanii - faction: obiekt JSON z polami np. id, name, description, rep Jeśli frakcja z podanym id istnieje, pola name/description/rep zostaną zaktualizowane (rep ustawione na podaną wartość). Jeśli nie istnieje, powstanie nowa frakcja.
Dodaj lub zaktualizuj jedną lokację w kampanii. - campaign_id: id kampanii - location: obiekt JSON z polami np. id, name, type, tags, description Jeśli lokacja z podanym id (lub name) istnieje, zostanie zaktualizowana. Jeśli nie istnieje, zostanie dodana nowa lokacja. Skutek uboczny: zapis kampanii oraz wpis typu "mutate" (op="upsert_location").
Generic descriptions lack context for selection. Tools like list_campaigns ('List all available campaigns'), get_campaign ('Get full campaign data by campaign_id'), and list_locations (Polish: 'Zwróć listę lokacji kampanii') are too brief to guide LLM selection between similar tools. Descriptions under 40 chars provide no guidance on WHEN to call them vs alternatives.
No error handling guidance. Destructive tools like delete_location and irreversible operations like create_campaign do not document recovery steps. Descriptions lack guidance like 'If campaign already exists, use overwrite=true or call list_campaigns to check.' This forces LLMs to guess recovery paths.
Parameters lack format constraints. String parameters like campaign_id, char_id, location_id_or_name, faction_id accept any value with no documented format, length, character restrictions, or validation guidance. LLMs generate invalid values like 'campaign #1' or 'char-@invalid' without guidance.
No pagination guidance. list_campaigns, list_locations, and list_factions return entire lists with no limit, offset, or total count. If campaigns grow to hundreds, the response bloats the context window. Descriptions should state 'Returns up to 100 campaigns' and offer pagination parameters.
Optional parameters with side effects lack explicit guidance. create_campaign has 'overwrite' (defaults false) and 'from_campaign_id' (optional), but no clear statement of what happens if overwrite is omitted vs true. faction_rep_add includes optional 'name' parameter with no guidance on when it is required vs optional.