Skip to main content
Glama

Kanka MCP Tool

Local MCP tool for the Kanka worldbuilding API. Runs on your machine via stdio — no remote server needed.

Prerequisites

Related MCP server: kanka-mcp

Install

npm install

Usage

MCP client configuration (Claude Desktop, Cursor, etc.)

Add this to your MCP client config:

{
  "mcpServers": {
    "kanka": {
      "command": "node",
      "args": ["<ABSOLUTE_PATH_TO_REPO>/index.js"],
      "env": {
        "KANKA_API_TOKEN": "<YOUR_KANKA_TOKEN>"
      }
    }
  }
}

Notes:

  • Replace <ABSOLUTE_PATH_TO_REPO> with the full path to this repository.

  • On Windows, prefer forward slashes (C:/Users/...) or escaped backslashes (C:\\Users\\...).

  • Restart the MCP client after editing the config.

Run directly

KANKA_API_TOKEN=your_token npm start

Install globally

npm install -g .
KANKA_API_TOKEN=your_token kanka-mcp

Available tools

The tool exposes 77 MCP tools for interacting with Kanka:

  • list_campaigns — List all campaigns

  • search — Search entities within a campaign

  • For each entity type (Character, Location, Family, Organization, Item, Note, Event, Calendar, Timeline, Creature, Race, Quest, Map, Journal, Ability):

    • list_<entities> — List all

    • get_<entity> — Get details

    • create_<entity> — Create new

    • update_<entity> — Update existing

    • delete_<entity> — Delete existing

Listing options

Big campaigns return list pages that are too large for some MCP clients to show in full, which hides the records in the middle of the response. Every list_<entities> tool takes these options to keep a page small and complete:

Option

Values

Description

page

number

Page number, default 1.

per_page

1–100

Records per page. Enforced by this server, so it holds even when the Kanka API answers with a different page size. Omit it to return the API's own page untouched.

entries

boolean

Default true. Set false to drop the long entry/entry_parsed fields and get a has_entry boolean per record instead.

entry_filter

all, empty, filled

Default all. Keep only records whose entry is empty, or only those with text. Applied across the whole collection, not just one API page.

When per_page or entry_filter is used, the response carries this server's own meta instead of the API's:

{
  "data": [{ "id": 42, "name": "Thalion", "has_entry": false }],
  "meta": {
    "page": 1,
    "per_page": 100,
    "count": 1,
    "has_more": false,
    "entry_filter": "empty",
    "entries_included": false,
    "total_unfiltered": 168,
    "api_pages_fetched": 12
  }
}

Notes:

  • per_page defaults to 25 with entries, 100 without, and pages are stitched from as many API requests as needed.

  • total_unfiltered is the size of the whole collection as reported by Kanka — it is not the number of records left after entry_filter. Walk pages until has_more is false.

  • scan_incomplete: true means the budget of 50 API requests per call ran out before the window was filled, so records past that point were not scanned.

Example — find every character with no entry yet, in as few calls as possible:

{ "campaignId": 235025, "per_page": 100, "entries": false, "entry_filter": "empty" }

Contributing

Community contributions are welcome.

Local quality checks

npm run lint
npm run format:check
npm test

Claude Desktop bundle (.mcpb)

The .mcpb bundle is built by the MCP bundle (.mcpb) GitHub Actions workflow, not by hand. On every pull request it validates manifest.json, packs the bundle with the official mcpb CLI and starts it to check that it answers an MCP initialize request. Pushes to main keep the bundle as a workflow artifact, and v* tags attach it to the matching GitHub release.

To cut a release, bump the version in both package.json and manifest.json (CI fails if they differ), merge, then push the tag v<version>. The workflow creates the GitHub release for that tag if it does not exist yet and attaches the .mcpb to it; write the release notes by editing that release afterwards. Files that must stay out of the bundle are listed in .mcpbignore.

To build one locally:

npm ci --omit=dev
npx @anthropic-ai/mcpb@2.1.2 pack . kanka-mcp.mcpb
npm ci   # restore the dev dependencies (eslint, prettier) removed above

Available Tools

77 tools
create_abilityC

Create a new ability

Inputs: data (required), campaignId (required).

create_calendarC

Create a new calendar

Inputs: data (required), campaignId (required).

create_characterC

Create a new character

Inputs: data (required), campaignId (required).

create_creatureC

Create a new creature

Inputs: data (required), campaignId (required).

create_eventD

Create a new event

Inputs: data (required), campaignId (required).

create_familyC

Create a new family

Inputs: data (required), campaignId (required).

create_itemC

Create a new item

Inputs: data (required), campaignId (required).

create_journalC

Create a new journal

Inputs: data (required), campaignId (required).

create_locationD

Create a new location

Inputs: data (required), campaignId (required).

create_mapC

Create a new map

Inputs: data (required), campaignId (required).

create_noteC

Create a new note

Inputs: data (required), campaignId (required).

create_organizationC

Create a new organization

Inputs: data (required), campaignId (required).

create_questC

Create a new quest

Inputs: data (required), campaignId (required).

create_raceD

Create a new race

Inputs: data (required), campaignId (required).

create_timelineD

Create a new timeline

Inputs: data (required), campaignId (required).

delete_abilityD

Delete an existing ability

Inputs: id (required), campaignId (required).

delete_calendarC

Delete an existing calendar

Inputs: id (required), campaignId (required).

delete_characterC

Delete an existing character

Inputs: id (required), campaignId (required).

delete_creatureC

Delete an existing creature

Inputs: id (required), campaignId (required).

delete_eventC

Delete an existing event

Inputs: id (required), campaignId (required).

delete_familyC

Delete an existing family

Inputs: id (required), campaignId (required).

delete_itemC

Delete an existing item

Inputs: id (required), campaignId (required).

delete_journalB

Delete an existing journal

Inputs: id (required), campaignId (required).

delete_locationC

Delete an existing location

Inputs: id (required), campaignId (required).

delete_mapC

Delete an existing map

Inputs: id (required), campaignId (required).

delete_noteB

Delete an existing note

Inputs: id (required), campaignId (required).

delete_organizationC

Delete an existing organization

Inputs: id (required), campaignId (required).

delete_questC

Delete an existing quest

Inputs: id (required), campaignId (required).

delete_raceC

Delete an existing race

Inputs: id (required), campaignId (required).

delete_timelineC

Delete an existing timeline

Inputs: id (required), campaignId (required).

get_abilityC

Get details of a ability

Inputs: id (required), campaignId (required).

get_calendarC

Get details of a calendar

Inputs: id (required), campaignId (required).

get_characterB

Get details of a character

Inputs: id (required), campaignId (required).

get_creatureC

Get details of a creature

Inputs: id (required), campaignId (required).

get_eventC

Get details of a event

Inputs: id (required), campaignId (required).

get_familyC

Get details of a family

Inputs: id (required), campaignId (required).

get_itemC

Get details of a item

Inputs: id (required), campaignId (required).

get_journalC

Get details of a journal

Inputs: id (required), campaignId (required).

get_locationC

Get details of a location

Inputs: id (required), campaignId (required).

get_mapC

Get details of a map

Inputs: id (required), campaignId (required).

get_noteB

Get details of a note

Inputs: id (required), campaignId (required).

get_organizationC

Get details of a organization

Inputs: id (required), campaignId (required).

get_questB

Get details of a quest

Inputs: id (required), campaignId (required).

get_raceC

Get details of a race

Inputs: id (required), campaignId (required).

get_timelineC

Get details of a timeline

Inputs: id (required), campaignId (required).

list_abilitiesB

List abilities. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_calendarsB

List calendars. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_campaignsA

List all campaigns

Inputs: no named parameters.

list_charactersA

List characters. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_creaturesB

List creatures. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_eventsA

List events. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_familiesB

List families. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_itemsA

List items. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_journalsB

List journals. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_locationsB

List locations. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_mapsA

List maps. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_notesB

List notes. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_organisationsA

List organisations. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_questsB

List quests. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_racesA

List races. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

list_timelinesA

List timelines. Use per_page to keep responses small enough to be returned whole, and entry_filter to return only records whose entry is empty or filled.

Inputs: campaignId (required), page, entries, per_page, +1 more.

update_abilityC

Update an existing ability

Inputs: id (required), data (required), campaignId (required).

update_calendarC

Update an existing calendar

Inputs: id (required), data (required), campaignId (required).

update_characterC

Update an existing character

Inputs: id (required), data (required), campaignId (required).

update_creatureC

Update an existing creature

Inputs: id (required), data (required), campaignId (required).

update_eventC

Update an existing event

Inputs: id (required), data (required), campaignId (required).

update_familyC

Update an existing family

Inputs: id (required), data (required), campaignId (required).

update_itemC

Update an existing item

Inputs: id (required), data (required), campaignId (required).

update_journalC

Update an existing journal

Inputs: id (required), data (required), campaignId (required).

update_locationC

Update an existing location

Inputs: id (required), data (required), campaignId (required).

update_mapC

Update an existing map

Inputs: id (required), data (required), campaignId (required).

update_noteC

Update an existing note

Inputs: id (required), data (required), campaignId (required).

update_organizationC

Update an existing organization

Inputs: id (required), data (required), campaignId (required).

update_questC

Update an existing quest

Inputs: id (required), data (required), campaignId (required).

update_raceC

Update an existing race

Inputs: id (required), data (required), campaignId (required).

update_timelineC

Update an existing timeline

Inputs: id (required), data (required), campaignId (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.5/5.0

    Scored across 77 tools

    Disambiguation4/5

    Each entity type has a clear list/get/create/update/delete split, so tool selection is mostly determined by the entity being managed. The generic `search` tool and the broad `list_*`/`get_*` surface add slight overlap, but no two tools appear to do the same job.

    Naming Consistency4/5

    Nearly all tools follow a snake_case verb_noun pattern, such as `create_event` and `delete_calendar`. Minor deviations exist: `search` is a bare verb, and the spelling inconsistency between `list_organisations` and the CRUD tools using `organization` breaks the otherwise predictable naming.

    Tool Count1/5

    77 tools for CRUD across 16 entity types is extremely heavy and well past the 50+ threshold for an oversized surface. The same coverage could be exposed with fewer generic or grouped tools, and this count is likely to overwhelm tool selection.

    Completeness4/5

    Core lifecycle coverage is strong: every major entity type has list/get/create/update/delete operations, avoiding obvious CRUD dead ends. However, campaign-level operations, relations, tags, posts, members, and other Kanka features are absent, so non-CRUD workflows may hit gaps.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      B
      maintenance
      Enables AI assistants to interact with Kanka campaigns through CRUD operations on entities like characters, locations, organizations, and quests, with support for markdown content, batch operations, and efficient synchronization.
      9
      4
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      Enables MCP-compatible agents to authenticate to a Kanka account and interact with campaigns, entities (CRUD on all 18 entity types, posts, relations), and full-text search.
      15
      2
      MIT
    • A
      license
      Not graded
      quality
      F
      maintenance
      A local MCP server for Dungeons & Dragons campaign management, combining core runtime with skill and module-generation packs. It enables campaign creation, module generation and import, rule and skill searching via tools, resources, and prompts.
      Apache 2.0