Skip to main content
Glama

openkore-mcp

A local MCP server that gives an AI agent two layers of knowledge about OpenKore — the official wiki and the Perl source code — with no dependencies beyond the Python standard library.

Runs entirely on your machine. After the indexes are built, it never touches the network.

┌─ Layer 1 · WIKI ──── openkore.com (MediaWiki)  → "how do I configure X?"
├─ Layer 2 · SOURCE ── Perl source @ git commit  → "how does it actually work?"
└─ Layer 3 · SEMANTIC ─ embeddings (optional)    → "my bot just stands there"

Why two layers

The wiki is the configuration reference, but it is incomplete, and that is measurable rather than a matter of opinion:

  • Of a 50-page sample, about half are stubs under 200 bytes (BaseLevel = 99 bytes, Delay = 89).

  • timeouts.txt contains entries literally marked TODO: add description.

  • The Main Page has a "Current Wiki TODO's" section admitting stale and orphan pages.

Comparing both indexes on this machine:

Keys documented in the wiki

Keys read by the source

Count

961

447

Only in one layer

655

141

Those 141 options (castleWatch_active, autoWarp_from, avoidSkill, buyerShopAuto_open, …) exist in the code and are not documented anywhere. An agent trained only on the wiki will never know they exist. That gap is the reason this server indexes both, and config_coverage exists to expose it.


Related MCP server: GitHits

Requirements

  • Python 3.8+ (tested on 3.13). Nothing else — the three core scripts use only the standard library.

  • git (only for the source layer)

  • Optional: pip install fastembed for semantic search

Roughly 300 MB of disk: the source clone (~204 MB) plus the indexes (~35 MB).

Quick start

git clone https://github.com/Sumiallan/openkore-mcp.git
cd openkore-mcp
.\setup.cmd

setup.cmd detects Python, builds all three layers and prints the config block for your MCP client. Flags: -SkipWiki, -SkipCode, -SkipEmbeddings, -PythonExe <path>, -SourceDir <path>.

Measured end to end on a clean checkout with an empty virtualenv: 6 min 32 s — wiki ~1 min, 204 MB source clone ~4 min, pip install ~1 min, embeddings ~3 s. Downloads: ~260 MB for the source and dependencies, plus ~500 MB for the embedding model on first run (~760 MB total).

Why setup.cmd and not setup.ps1? Windows ships with script execution disabled by default, so .\setup.ps1 fails with "running scripts is disabled on this system". The .cmd wrapper calls PowerShell with -ExecutionPolicy Bypass, so it works by double-click, from cmd or from PowerShell without changing any machine setting. If you prefer the script directly:

powershell -ExecutionPolicy Bypass -File .\setup.ps1

Manual install

1 · Wiki index (~1 minute, uses the network)

python ingest.py --md docs

~22 requests with a 3-second pause, honouring the site's robots.txt (Crawl-delay: 3). Everything is written in a single transaction, so a failure never leaves a half-built index.

2 · Source index (~10 seconds, fully offline)

git clone --depth 1 https://github.com/OpenKore/openkore.git ..\openkore-src
python ingest_code.py --src ..\openkore-src --db code.db

3 · Embeddings (optional, ~6 seconds after the model downloads)

pip install -r requirements.txt
python build_vectors.py

Without this step everything still works — search just stays lexical.

Check it

python server.py --selftest

Register with your MCP client

The server speaks MCP over stdio: your client launches it as a child process. There is no port, no daemon and no service to install.

Claude Desktop, Cursor, VS Code

Add to your MCP config (claude_desktop_config.json, .cursor/mcp.json, …), replacing the two paths:

{
  "mcpServers": {
    "openkore": {
      "command": "<PYTHON_EXE>",
      "args": ["<ABSOLUTE_PATH_TO_REPO>/server.py"]
    }
  }
}

DSH

Append to the insert: list in your profile patch (~/.dsh/profiles/desktop/cordis.patch.yml):

    - id: mcp-openkore
      name: "@deepseek-ai/dsh-mcp-client"
      config:
        serverName: openkore
        transport: stdio
        command: '<PYTHON_EXE>'
        args: [ '<ABSOLUTE_PATH_TO_REPO>/server.py' ]
        toolCallTimeoutMs: 300000

toolCallTimeoutMs is generous because the first semantic query loads the embedding model on demand.

Any other client

server.py reads newline-delimited JSON-RPC 2.0 on stdin and writes it on stdout. It implements initialize, ping, tools/list and tools/call. Logs go to stderr, so stdout stays clean.


Tools

Nine tools, exposed conditionally: the source tools appear only when code.db exists, config_coverage only when both do.

Wiki layer

Tool

Use

search_docs

Hybrid search. Returns short snippets with url + revision.

get_page

One full page as clean text.

get_config_key

One option's definition, optionally scoped to a file.

list_pages

What exists — check before claiming something is absent.

docs_stats

Coverage, index age, stale flag, active model.

Source layer

Tool

Use

search_code

Search symbols (packages + subs). Returns file:line.

get_symbol

Full body of one sub or package.

find_in_source

Where a config key or hook is actually read.

config_coverage

Gap analysis between the two layers.

Examples

// "What does ai_sync do?"
get_config_key(file="timeouts.txt", key="ai_sync")
// → entry_key "ai_sync ()", description, url, revid

// "Is this option really read by the code?"
find_in_source(name="teleportAuto_hp")
// → src/AI/CoreLogic.pm:3652
//   ($config{teleportAuto_hp} && percent_hp($char) <= $config{teleportAuto_hp})

// "How is attack distance handled internally?"
find_in_source(name="attackDistance")
// → 10 sites, incl. src/Actor/You.pm:401 where Kore WRITES the config at runtime

// "What does the wiki not cover?"
config_coverage()
// → 141 keys used in the source and never documented

The attackDistance example is the kind of thing only the source tells you: the wiki describes the option; the code reveals that src/Actor/You.pm mutates it at runtime based on equipped items.


How it works

Two-tier chunking. The wiki is a dictionary of config keys (; key followed by : description). Splitting by heading alone makes one chunk carry 15 different keys and retrieval misses the target. So every definition becomes an atomic record, alongside the prose sections.

Normalisation that handles camelCase. teleportAuto_hp is indexed as teleport auto hp, so teleport, teleportAuto_hp and "teleport when hp is low" all land in the same place. timeouts.txt becomes timeouts txt, so timeouts matches too.

Adaptive lexical weight. In a Portuguese question the lexical side matches mostly on common words like "bot" and produces a poor ranking. The server measures how many query terms the best hit covers and, when coverage is low, lets the semantic side take over. For an identifier query coverage is high and lexical stays in charge.

Exactness beats consensus. RRF rewards "appears in both lists", which demoted literal matches — searching ai_sync returned the ai page. An exact title or key match is a certainty, not a heuristic, so those results are pinned to the top and flagged exact_match: true. That is why a pinned result can show a lower score than the next one: the score is fusion, the order is certainty.

Citations are mandatory. Wiki results carry url + revid; source results carry file:line + commit. An empty search returns an explicit instruction to say "not documented" rather than guess. Configuration documentation is exactly the domain where a model invents plausible names — ai_teleport_hp exists, ai_teleport_hpp does not.

Nothing is fetched at query time. The server opens two SQLite files and never makes a network request, so the wiki being down has no effect on daily use. Only re-indexing needs the network, and it fails cleanly when it can't.


Maintenance

python ingest.py --update        # re-fetch only changed pages (recentchanges)
git -C ..\openkore-src pull      # update the source
python ingest_code.py --src ..\openkore-src --db code.db
python build_vectors.py          # re-embed after any re-index

--update never deletes; pages removed from the wiki stay in the index. Run a full re-ingest if that matters to you.

Troubleshooting

Symptom

Cause / fix

indice nao encontrado: openkore.db

Run python ingest.py.

Source tools missing

code.db not found. Run ingest_code.py.

semantic_search: false in docs_stats

fastembed not installed, or build_vectors.py not run.

First query is slow

The embedding model loads on demand (measured: ~20 s first time).

database is locked

Another process is writing the index. Wait for it to finish.

Dimension mismatch error

Vectors were rebuilt with a different model than the one recorded. Re-run build_vectors.py.

Model choice

Measured on this project's corpus (9834 passages, CPU only):

Model

Build

Dims

Source search: "meu bot fica parado sem atacar"

paraphrase-multilingual-MiniLM-L12-v2

4.5 min

384

merchantdb::connect ❌

potion-multilingual-128M (default)

6.3 s

256

AI::Attack::finishAttacking ✅

intfloat/multilingual-e5-large

>70 min

1024

not tested (aborted on cost)

The smallest and fastest model won on both axes. For maximum quality:

python build_vectors.py --model Qwen/Qwen3-Embedding-0.6B-Q   # 1024 dims, ~31 min

Limitations

  • Semantic search has a ceiling. For a vague question like "my bot stands still and doesn't attack", neither the 384-dim nor the 256-dim model puts attackAuto first. The ceiling is the question, not the model: it doesn't point at any single key. Pushing to 1024 dims will not fix ambiguity.

  • config_coverage is a hint, not proof. Keys read dynamically never appear as $config{...}, so "not in the source" can be a false negative. The tool says so in its own note field.

  • The source layer is a snapshot. Without git pull + ingest_code.py it ages silently — which is why the commit hash appears in every source result.

  • The wiki layer ages too. docs_stats reports age_days and a stale flag past 30 days.

Licence and attribution

  • Code in this repository: MIT — see LICENSE.

  • Wiki content is CC BY-SA 3.0. This repository does not redistribute it: *.db and docs/ are gitignored and rebuilt locally by ingest.py. Every result carries its url and revid for attribution.

  • OpenKore source is not redistributed either; ingest_code.py indexes a clone you make yourself.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to perform hybrid code search, get explanations, analyze relations and impacts, retrieve context packs, and generate documentation across ~45 languages via 17 MCP tools, all powered by a local vector database and LLM.
    2
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to retrieve hyper-targeted, token-efficient context from a codebase by querying a locally built AST knowledge graph, and to persist cross-session agent memory through 17 MCP tools. All indexing and retrieval run entirely offline, so proprietary code never leaves the machine.
    146 PyPI
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables coding agents to search local repositories through hybrid keyword and embedding retrieval, read matched chunks, and list indexed repos via MCP, without API keys or cloud services.
    MIT