OpenZIM MCP Server
Provides access to ZIM format knowledge bases created by Kiwix, enabling AI agents to search and retrieve content from offline Wikipedia and other reference materials stored in compressed ZIM archives.
Allows access to Wikibooks content stored in ZIM format archives, enabling AI agents to search and retrieve educational content from offline Wikibooks collections.
Enables AI agents to access Wikipedia content stored in offline ZIM archives, providing tools for searching articles, browsing by namespace, extracting article structure, and retrieving detailed content without requiring internet connectivity.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@OpenZIM MCP Serversearch for information about quantum computing"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
π NEW in v1.1.0: Structured tool output! All 17 JSON-returning tools now emit MCP
structuredContentalongside the legacy text envelope β no more double-stringified JSON, no more escape soup. Plus a major namespace-handling fix for new-scheme archives (list_namespaces/browse_namespace/walk_namespacewere silently broken on Wikipedia-style ZIMs), pagination forextract_article_links, case-insensitivefind_entry_by_titlewith proper scoring, and CORS support for browser MCP clients. Learn more βStill on the v1.0.0 highlights? Streamable HTTP transport, batch entry retrieval, and per-entry resources are documented in the v1.0.0 section.
Dual Mode Support: Choose between Simple mode (1 intelligent natural language tool, default) or Advanced mode (21 specialized tools, plus 3 MCP prompts and 3 MCP resources) to match your LLM's capabilities.
Built for LLM Intelligence
OpenZIM MCP transforms static ZIM archives into dynamic knowledge engines for Large Language Models. Unlike basic file readers, this tool provides intelligent, structured access that LLMs need to effectively navigate and understand vast knowledge repositories.
Why LLMs Love OpenZIM MCP:
Smart Navigation: Browse by namespace (articles, metadata, media) instead of blind searching
Context-Aware Discovery: Get article structure, relationships, and metadata for deeper understanding
Intelligent Search: Advanced filtering, auto-complete suggestions, and relevance-ranked results
Performance Optimized: Cached operations and pagination prevent timeouts on massive archives
Relationship Mapping: Extract internal/external links to understand content connections
Whether you're building a research assistant, knowledge chatbot, or content analysis system, OpenZIM MCP gives your LLM the structured access patterns it needs to unlock the full potential of offline knowledge archives. No more fumbling through raw text dumps!
OpenZIM MCP is a modern, secure, and high-performance MCP (Model Context Protocol) server that enables AI models to access and search ZIM format knowledge bases offline.
ZIM (Zeno IMproved) is an open file format developed by the openZIM project, designed specifically for offline storage and access to website content. The format supports high compression rates using Zstandard compression (default since 2021) and enables fast full-text searching, making it ideal for storing entire Wikipedia content and other large reference materials in relatively compact files. The openZIM project is sponsored by Wikimedia CH and supported by the Wikimedia Foundation, ensuring the format's continued development and adoption for offline knowledge access, especially in environments without reliable internet connectivity.
Features
Dual Mode Support: Choose between Simple mode (1 intelligent natural language tool, default) or Advanced mode (21 specialized tools)
Streamable HTTP Transport: π Run as a long-running service over HTTP β bearer-token auth, CORS, health endpoints, multi-arch Docker image, and resource subscriptions
Batch Entry Retrieval: π Fetch up to 50 entries per call with
get_zim_entriesβ pairs naturally with HTTP, where round-trip cost mattersPer-Entry MCP Resources: π Stream individual entries via
zim://{name}/entry/{path}with native MIME types β browse HTML, PDFs, and images directlyResource Subscriptions: π Clients subscribe to
zim://filesandzim://{name}and receivenotifications/resources/updatedwhen archives changeMulti-Archive Search: Search every ZIM file at once with
search_allβ no need to know which archive holds the answerMCP Prompts: Pre-built workflow slash commands (
/research,/summarize,/explore) that orchestrate multi-step ZIM operationsFind Entries by Title: Resolve titles to entry paths instantly with
find_entry_by_titleβ case-insensitive, optionally cross-fileBinary Content Retrieval: Extract PDFs, images, videos, and other embedded media for multi-agent workflows
Security First: Comprehensive input validation and path traversal protection
High Performance: Intelligent caching and optimized ZIM file operations
Smart Retrieval: Automatic fallback from direct access to search-based retrieval for reliable entry access
Well Tested: 80%+ test coverage with comprehensive test suite
Modern Architecture: Modular design with dependency injection
Type Safe: Full type annotations throughout the codebase
Configurable: Flexible configuration with validation
Observable: Structured logging and health monitoring
What's new in v1.1.0
Structured tool output
The 17 JSON-returning tools now emit MCP structuredContent alongside the legacy content[].text envelope. Old clients keep parsing the text JSON; new clients read the dict directly. The biggest beneficiary is search_all, whose per_file[].result field used to be a pre-rendered markdown blob escaped twice through json.dumps β it's now a real nested dict.
The four prose/markdown tools (search_zim_file, search_with_filters, get_zim_entry, get_main_page) and the simple-mode zim_query stay on -> str by design.
Namespace handling, fixed
In new-scheme ZIM archives (the modern format used by current Kiwix Wikipedia builds), libzim's iterable surface only exposes the C namespace and reaches metadata through archive.metadata_keys. The previous code parsed the first character of each entry path as the namespace, so Evolution looked like namespace 'E', Bob_Dylan like 'B', favicon.png like 'F' β including emoji buckets like 'π'. search_with_filters(namespace='C') was silently dropping ~95% of legitimate hits.
list_namespaces, browse_namespace, walk_namespace, and the namespace= filter now branch on archive.has_new_namespace_scheme: new-scheme C uses entry_count as an authoritative total, M is enumerated from metadata_keys, W is surfaced via has_main_entry / has_illustration. Old-scheme archives are unaffected.
Pagination for extract_article_links
extract_article_links previously dumped every internal/external/media link in one call. On a heavily-linked Wikipedia article (~6k links) that was ~400 KB and overflowed the response token budget. The tool now accepts limit / offset / kind parameters; full counts ship in total_internal_links / total_external_links / total_media_links so callers can size the next page. Parsed extraction is cached once per entry and sliced in-memory (~40Γ speedup on cached pages).
Smarter find_entry_by_title
The fast path was case-sensitive, so "evolution" against an archive titled "Evolution" missed and fell through to suggestion fallback with a hardcoded score: 0.8. Now the fast path tries five case variants Γ C/A namespaces; suggestion results get rank-derived scores in (0, 0.95] so an exact case-insensitive match (promoted to 1.0) always outranks partials.
Per-entry resource size caps
zim://{name}/entry/{path} now caps text bodies at 256 KB UTF-8 with a notice pointing at get_zim_entry for paged reads. Oversize binary bodies are refused (not silently clipped, since a sliced PDF/PNG won't open) β callers should use get_binary_entry, which has explicit max_size_bytes and a truncated flag.
Simple mode actually works
_register_simple_tools was also calling _register_advanced_tools, so simple-mode clients received every advanced tool's schema in the prompt anyway β defeating the entire point of the mode and inflating prefill into the multi-thousand-token range. Fixed: simple mode now registers exactly one tool (zim_query). Confirmed against llama.cpp's MCP webui β single-turn prompt size dropped from ~6,200 tokens to ~1,100.
CORS for browser MCP clients
Two additions to the HTTP transport's CORS layer:
MCP-Protocol-Versionis now inallow_headers. Browser MCP clients send this on every post-init request per the MCP spec; without it, the second preflight returned400 Disallowed CORS headersand the connection dropped.DELETEis now inallow_methods. The MCP streamable-HTTP SDK uses DELETE for explicit session termination.
Polish
get_server_healthnow reports a realstarted_atanduptime_secondsinstead of"unknown".Configuration redaction format changed from the misleading
...datato unambiguous<redacted>/data.Server-tools timestamps go through a single
_utc_now_iso()helper so a response no longer mixes timezone-aware UTC with naive local.zim_query("")rejects empty input upfront with example queries instead of falling through to a no-op search.get_search_suggestionsschema now documents the 2-character minimum.The "cache hit rate is low" warning waits until β₯50 accesses before commenting (previously fired at 22% during normal session warm-up).
get_zim_entry's truncation tail now reads "of body content" so callers can tell the limit applies to the body, not the wrapper headers.
What's new in v1.0.0
Streamable HTTP transport
Run OpenZIM MCP as a long-running service. Pass --transport http (or set OPENZIM_MCP_TRANSPORT=http) and the server boots a Starlette app on 127.0.0.1:8000 by default with:
Bearer-token auth β set
OPENZIM_MCP_AUTH_TOKEN; comparison is timing-safe and the attempted token is never logged.Safe-default startup check β the server refuses to bind a non-localhost host without a token. (Bind
127.0.0.1for local-only access; put a reverse proxy in front for TLS.)CORS allow-list β explicit origins via
OPENZIM_MCP_CORS_ORIGINS; wildcard*is rejected at startup.Health endpoints β
/healthz(liveness) and/readyz(at least one allowed dir is readable). Both exempt from auth so probes work cleanly.Multi-arch Docker image β
ghcr.io/cameronrye/openzim-mcp:1.1.2, builds forlinux/amd64andlinux/arm64, runs as non-root.
Legacy SSE transport is also available via --transport sse (or OPENZIM_MCP_TRANSPORT=sse) for clients that haven't migrated to streamable-HTTP. SSE does not apply the bearer-token / CORS / health-endpoint middleware, so the server refuses to start with --transport sse bound to anything other than 127.0.0.1/::1/localhost. For exposed deployments use --transport http.
Batch entry retrieval
get_zim_entries fetches up to 50 entries in one call. Per-entry failures don't abort the batch β each result includes its index from the input order plus either content (success) or error (failure). Different zim_file_path values are allowed in one batch, so a multi-archive workflow can fan out from a single search. Single-archive batches can pass bare path strings paired with a top-level zim_file_path default, so the call site stays flat instead of dict-heavy.
Per-entry MCP resources
zim://{name}/entry/{path} exposes individual entries with their native MIME type:
HTML and text entries return text bodies (
text/html,text/plain,application/json, ...).Binary entries (images, PDFs) return raw bytes (FastMCP base64-wraps them).
Encoding requirement: clients MUST URL-encode / as %2F in the {path} segment. FastMCP's URI template engine treats / as a segment separator, so a literal slash won't route. Example: zim://wikipedia_en/entry/C%2FClimate_change. (This is a constraint of the current mcp[cli] SDK.)
Resource subscriptions
Subscribe to zim://files or zim://{name} and the server emits notifications/resources/updated whenever the directory contents change or a .zim file is replaced. Polling interval is configurable (OPENZIM_MCP_WATCH_INTERVAL_SECONDS, default 5 s) and the feature can be disabled with OPENZIM_MCP_SUBSCRIPTIONS_ENABLED=false. Implementation note: this depends on a private FastMCP attribute (_mcp_server) for handler registration.
Polish & fixes
Smarter archive handling
get_related_articlesresolves relative hrefs against the source entry's directory and identifies the content namespace correctly on domain-scheme archives (previously returned nothing).Suggestion fallback uses
SuggestionSearcher(archive).suggest(text)(the priorarchive.suggest()call didn't exist).list_zim_filesgains a case-insensitivename_filtersubstring argument; one shared cache slot regardless of filter value.search_zim_fileaccepts an opaquecursorparameter; passing the cursor alone resumes pagination without restating the query.
Cleaner content extraction
Heading-id resolution falls through
idβ mw-headline anchor β preceding<a name="">β slug, returning(id, source)so consumers can distinguish real anchors from synthetic slugs.Summary extraction skips USWDS banners and skip-nav blocks above the first
<h1>(MedlinePlus / NIH / NIST style sites).Link extraction drops non-navigable schemes (
javascript:,mailto:,tel:,data:,blob:,vbscript:).Per-entry paths sanitized in
get_zim_entries.
Server hygiene
__version__reads fromimportlib.metadata;serverInfo.versionreports openzim-mcp's actual version (no longer the FastMCP SDK default).HTTP transport's subscription watcher starts via wrapped lifespan.
Per-entry
zim://returns libzim's native MIME (was returning a placeholder).
Streamlined scope
v1.0.0 reduces the advanced-mode tool surface from 27 to 21 by removing administrative/inspection helpers that didn't pull their weight: warm_cache, cache_stats, cache_clear, get_random_entry, diagnose_server_state, and resolve_server_conflicts. The cache itself remains; the explicit management tools were dropped. Multi-instance conflict tracking was removed entirely β instance_tracker.py is gone β which means HTTP server instances coexist freely without configuration warnings.
Review pass
End-to-end review pass before tagging: tightened path/PID redaction in error and diagnostics responses, locked OPTIONS /mcp behind auth, fixed cache poisoning on transient libzim errors, resolved redirects before rendering with cycle detection, preserved Unicode in heading slugs (Arabic, Chinese, Cyrillic, Japanese), made rate-limiting atomic, and split zim_operations.py into a zim/ package via mixin classes.
What's new in v0.9.0
Multi-archive search
search_all queries every ZIM file in your allowed directories at once and merges the results β no need to know which archive holds the answer.
MCP Prompts
Three pre-built workflows you can invoke as slash commands in MCP-aware clients:
/research <topic>β search across all archives, then drill into top hits/summarize <zim_file_path> <entry_path>β TOC + summary + key links/explore <zim_file_path>β high-level briefing of a ZIM's contents
Find entries by title
find_entry_by_title resolves a title (or partial title) to one or more entry paths, with case-insensitive matching. Cheaper than full-text search when you already know the article name.
Power-user tools
walk_namespaceβ deterministic cursor-paginated namespace iteration (vs.browse_namespacewhich samples)get_related_articlesβ outbound link-graph neighbours of a given entry
MCP Resources
First use of the MCP resources primitive β your client's resource browser and @-mention picker now see ZIM files directly:
zim://filesβ index of all available ZIM fileszim://{name}β overview of one ZIM (metadata, namespaces, main page preview)zim://{name}/entry/{path}(new in 1.0.0) β single entry served with native MIME type (clients must URL-encode/as%2Fin the path segment)
Reliability fixes
Namespace listing now deterministically surfaces minority namespaces (M, W, X, I) that random sampling could miss
Search filtering uses streaming scan instead of a hard 1000-hit cap (rare-mime-type filters now return matches that were previously hidden)
Error messages route by failure mode first (no more "check disk space" for "entry not found")
Quick Start
Installation
# Install from PyPI as an isolated CLI tool (recommended)
uv tool install openzim-mcp
# Or install into your current environment with pip
pip install openzim-mcpDevelopment Installation
For contributors and developers:
# Clone the repository
git clone https://github.com/cameronrye/openzim-mcp.git
cd openzim-mcp
# Install dependencies
uv sync
# Install development dependencies
uv sync --devPrepare ZIM Files
Download ZIM files (e.g., Wikipedia, Wiktionary, etc.) from the Kiwix Library and place them in a directory:
mkdir ~/zim-files
# Download ZIM files to ~/zim-files/Running the Server
# Simple mode (default) - 1 intelligent natural language tool
openzim-mcp /path/to/zim/files
python -m openzim_mcp /path/to/zim/files
# Advanced mode - all 21 specialized tools
openzim-mcp --mode advanced /path/to/zim/files
python -m openzim_mcp --mode advanced /path/to/zim/files
# For development (from source)
uv run python -m openzim_mcp /path/to/zim/files
uv run python -m openzim_mcp --mode advanced /path/to/zim/files
# Or using make (development)
make run ZIM_DIR=/path/to/zim/filesTool Modes
OpenZIM MCP supports two modes:
Simple Mode (default): Provides 1 intelligent tool (
zim_query) that accepts natural language queriesAdvanced Mode: Exposes all 21 specialized MCP tools for maximum control
MCP Configuration
Add the appropriate snippet to your MCP client's config file (claude_desktop_config.json, Cursor's MCP settings, etc.). The mcpServers wrapper is required by Claude Desktop, Cursor, and most other MCP clients.
Simple Mode (default):
{
"mcpServers": {
"openzim-mcp": {
"command": "openzim-mcp",
"args": ["/path/to/zim/files"]
}
}
}Advanced Mode:
{
"mcpServers": {
"openzim-mcp-advanced": {
"command": "openzim-mcp",
"args": ["--mode", "advanced", "/path/to/zim/files"]
}
}
}Alternative configuration using Python module:
{
"mcpServers": {
"openzim-mcp": {
"command": "python",
"args": [
"-m",
"openzim_mcp",
"/path/to/zim/files"
]
}
}
}For development (from source):
{
"mcpServers": {
"openzim-mcp": {
"command": "uv",
"args": [
"--directory",
"/path/to/openzim-mcp",
"run",
"python",
"-m",
"openzim_mcp",
"/path/to/zim/files"
]
}
}
}Development
Running Tests
# Run all tests
make test
# Run tests with coverage
make test-cov
# Run specific test file
uv run pytest tests/test_security.py -v
# Run tests with ZIM test data (comprehensive testing)
make test-with-zim-data
# Run integration tests only
make test-integration
# Run tests that require ZIM test data
make test-requires-zim-dataZIM Test Data Integration
OpenZIM MCP integrates with the official zim-testing-suite for comprehensive testing with real ZIM files:
# Download essential test files (basic testing)
make download-test-data
# Download all test files (comprehensive testing)
make download-test-data-all
# List available test files
make list-test-data
# Clean downloaded test data
make clean-test-dataThe test data includes:
Basic files: Small ZIM files for essential testing
Real content: Actual Wikipedia/Wikibooks content for integration testing
Invalid files: Malformed ZIM files for error handling testing
Special cases: Embedded content, split files, and edge cases
Test files are automatically organized by category and priority level.
Code Quality
# Format code
make format
# Run linting
make lint
# Type checking
make type-check
# Run all checks
make checkProject Structure
openzim-mcp/
βββ openzim_mcp/ # Main package
β βββ __init__.py # Package init, exports __version__ via importlib.metadata
β βββ __main__.py # Module entry point (`python -m openzim_mcp`)
β βββ main.py # CLI entry point and arg parsing
β βββ server.py # MCP server setup, transport selection
β βββ http_app.py # Streamable HTTP / SSE transport, auth, CORS, health
β βββ config.py # Pydantic config + env var bindings
β βββ defaults.py # Default values and tunables
β βββ security.py # Path validation, traversal protection, sanitization
β βββ error_messages.py # User-facing error message catalog
β βββ exceptions.py # Custom exception hierarchy
β βββ cache.py # LRU cache with TTL
β βββ rate_limiter.py # Per-client + global token-bucket rate limiting
β βββ content_processor.py # HTMLβtext, heading-id, link extraction
β βββ async_operations.py # asyncio helpers and timeouts
β βββ timeout_utils.py # Timeout primitives
β βββ subscriptions.py # MtimeWatcher and SubscriberRegistry
β βββ simple_tools.py # Simple-mode `zim_query` tool
β βββ intent_parser.py # Natural-language intent parsing
β βββ types.py # Shared TypedDicts
β βββ constants.py # Shared constants
β βββ zim_operations.py # Backward-compat shim re-exporting from zim/ package
β βββ zim/ # ZIM access (split from monolithic zim_operations.py)
β β βββ __init__.py # ZimOperations facade composed of mixins
β β βββ archive.py # Archive open/close, file listing, name resolution
β β βββ content.py # Entry retrieval, summaries, batch get
β β βββ namespace.py # Namespace listing, browse, walk
β β βββ search.py # Full-text + suggestion search; cursor pagination
β β βββ structure.py # Article structure, links, related articles
β βββ tools/ # MCP tool registrations
β βββ __init__.py
β βββ file_tools.py # list_zim_files
β βββ content_tools.py # get_zim_entry, get_zim_entries
β βββ search_tools.py # search_zim_file, search_all, find_entry_by_title
β βββ navigation_tools.py # browse_namespace, walk_namespace, search_with_filters, get_search_suggestions
β βββ structure_tools.py # get_article_structure, extract_article_links, get_entry_summary, get_table_of_contents, get_binary_entry
β βββ metadata_tools.py # get_zim_metadata, get_main_page, list_namespaces
β βββ server_tools.py # get_server_health, get_server_configuration
β βββ resource_tools.py # MCP resources (zim://files, zim://{name}/...)
β βββ prompts.py # MCP prompts (/research, /summarize, /explore)
βββ tests/ # Test suite (pytest)
βββ website/ # GitHub Pages site source
βββ pyproject.toml # Project configuration
βββ Makefile # Development commands
βββ Dockerfile # Multi-stage container build
βββ README.md # This fileAPI Reference
Available Tools
list_zim_files - List all ZIM files in allowed directories
Optional parameters:
name_filter(string, default: ""): Case-insensitive substring; only files whose filename contains it are returned. Empty string lists everything. Useful for narrowing large listings (e.g."wikipedia","nginx").
search_zim_file - Search within ZIM file content
Required parameters:
zim_file_path(string): Path to the ZIM filequery(string): Search query term β required unlesscursoris provided.
Optional parameters:
limit(integer, default: 10): Maximum number of results to returnoffset(integer, default: 0): Starting offset for results (for pagination)cursor(string): Opaque pagination token from a previous result'snext_cursor. When provided, overridesoffset/limitwith the values encoded in the token, and suppliesqueryif it was not given explicitly. Cursors are only valid for the query they were issued for.
get_zim_entry - Get detailed content of a specific entry in a ZIM file
Required parameters:
zim_file_path(string): Path to the ZIM fileentry_path(string): Entry path, e.g., 'A/Some_Article'
Optional parameters:
max_content_length(integer, default: 100000, minimum: 1000): Maximum length of returned content
Smart Retrieval Features:
Automatic Fallback: If direct path access fails, automatically searches for the entry and uses the exact path found
Path Mapping Cache: Caches successful path mappings for improved performance on repeated access
Enhanced Error Guidance: Provides clear guidance when entries cannot be found, suggesting alternative approaches
Transparent Operation: Works seamlessly regardless of path encoding differences (spaces vs underscores, URL encoding, etc.)
get_zim_entries - Batch retrieve multiple ZIM entries in one call
Pairs naturally with HTTP transport, where round-trip cost matters. Up to 50 entries per batch. Each entry resolves independently β per-entry failures do not abort the batch.
Required parameters:
entries(list): Either a list of entry-path strings (paired withzim_file_pathdefault) OR a list of{zim_file_path, entry_path}dicts (for multi-archive batches). Limit: 50 per batch.
Optional parameters:
zim_file_path(string): Default archive path; required ifentriesare bare strings, optional when each dict carries its own.max_content_length(integer): Per-entry max content length.
Returns:
JSON {"results": [...], "succeeded": N, "failed": N}. Each result includes index (input order), success, and either content or error.
Notes: Rate limit is charged per entry, not per batch (anti-bypass).
get_zim_metadata - Get ZIM file metadata from M namespace entries
Required parameters:
zim_file_path(string): Path to the ZIM file
Returns: JSON string containing ZIM metadata including entry counts, archive information, and metadata entries like title, description, language, creator, etc.
get_main_page - Get the main page entry from W namespace
Required parameters:
zim_file_path(string): Path to the ZIM file
Returns: Main page content or information about the main page entry.
list_namespaces - List available namespaces and their entry counts
Required parameters:
zim_file_path(string): Path to the ZIM file
Returns: JSON string containing namespace information with entry counts, descriptions, and sample entries for each namespace (C, M, W, X, etc.).
browse_namespace - Browse entries in a specific namespace with pagination
Required parameters:
zim_file_path(string): Path to the ZIM filenamespace(string): Namespace to browse (C, M, W, X, A, I, etc.)
Optional parameters:
limit(integer, default: 50, range: 1-200): Maximum number of entries to returnoffset(integer, default: 0): Starting offset for pagination
Returns: JSON string containing namespace entries with titles, content previews, and pagination information.
walk_namespace - Deterministic cursor-paginated namespace iteration
Unlike browse_namespace (which samples and may cap at 200 entries on large archives), walk_namespace scans the archive by entry ID from cursor onward. Pair the returned next_cursor with a follow-up call to walk the rest. done: true indicates iteration is complete. Use this for exhaustive enumeration β e.g. dumping every M/* metadata entry, or finding an entry whose path doesn't follow common patterns.
Required parameters:
zim_file_path(string): Path to the ZIM filenamespace(string): Namespace to walk (C, M, W, X, A, I, etc.)
Optional parameters:
cursor(integer, default: 0): Entry ID to resume fromlimit(integer, default: 200, range: 1β500): Max entries per page
Returns:
JSON with entries, next_cursor, and done flag.
search_with_filters - Search within ZIM file content with advanced filters
Required parameters:
zim_file_path(string): Path to the ZIM filequery(string): Search query term
Optional parameters:
namespace(string): Optional namespace filter (C, M, W, X, etc.)content_type(string): Optional content type filter (text/html, text/plain, etc.)limit(integer, default: 10, range: 1-100): Maximum number of results to returnoffset(integer, default: 0): Starting offset for pagination
Returns: Filtered search results with namespace and content type information.
search_all - Search across every ZIM file in the allowed directories
Returns merged per-file results so the caller doesn't need to know which file holds the information. Files that can't be searched (corrupt, no full-text index) are skipped without aborting the rest.
Required parameters:
query(string): Search query term
Optional parameters:
limit_per_file(integer, default: 5, range: 1β50): Max hits per ZIM filelimit(integer): Alias forlimit_per_file. If both are provided,limit_per_filewins.
Returns: JSON containing per-file result groups and counts of files searched, files-with-results, and files that failed.
find_entry_by_title - Resolve a title to one or more entry paths
Cheaper than full-text search when the caller knows the article title. Tries an exact normalized C/<Title> match first (fast path), then falls back to libzim's title-indexed suggestion search.
Required parameters:
zim_file_path(string): Path to the ZIM file (used unlesscross_file=true)title(string): Title or partial title to resolve (case-insensitive)
Optional parameters:
cross_file(boolean, default: false): If true, search across all allowed ZIM fileslimit(integer, default: 10, range: 1β50): Max results to return
Returns:
JSON with query, ranked results, fast_path_hit flag, and files_searched count.
get_search_suggestions - Get search suggestions and auto-complete
Required parameters:
zim_file_path(string): Path to the ZIM filepartial_query(string): Partial search query (minimum 2 characters)
Optional parameters:
limit(integer, default: 10, range: 1-50): Maximum number of suggestions to return
Returns: JSON string containing search suggestions based on article titles and content.
get_article_structure - Extract article structure and metadata
Required parameters:
zim_file_path(string): Path to the ZIM fileentry_path(string): Entry path, e.g., 'C/Some_Article'
Returns: JSON string containing article structure including headings, sections, metadata, and word count.
extract_article_links - Extract internal and external links from an article
Required parameters:
zim_file_path(string): Path to the ZIM fileentry_path(string): Entry path, e.g., 'C/Some_Article'
Returns: JSON string containing categorized links (internal, external, media) with titles and metadata.
get_related_articles - Find articles related to a given entry via outbound links
Composes extract_article_links and deduplicates internal links, returning up to limit outbound targets. (Inbound discovery was removed β it required a bounded full-archive scan that was too expensive for interactive use; reach for full-text search instead.)
Required parameters:
zim_file_path(string): Path to the ZIM fileentry_path(string): Source entry, e.g. 'C/Some_Article'
Optional parameters:
limit(integer, default: 10, range: 1β100): Max results
Returns:
JSON with outbound_results.
get_entry_summary - Get a concise article summary
Required parameters:
zim_file_path(string): Path to the ZIM fileentry_path(string): Entry path, e.g., 'C/Some_Article'
Optional parameters:
max_words(integer, default: 200, range: 10-1000): Maximum number of words in the summary
Returns: JSON string containing a concise summary extracted from the article's opening paragraphs, with metadata including title, word count, and truncation status.
Features:
Extracts opening paragraphs while removing infoboxes, navigation, and sidebars
Provides quick article overview without loading full content
Useful for LLMs to understand article context before deciding to read more
get_table_of_contents - Extract hierarchical table of contents
Required parameters:
zim_file_path(string): Path to the ZIM fileentry_path(string): Entry path, e.g., 'C/Some_Article'
Returns: JSON string containing a hierarchical tree structure of article headings (h1-h6), suitable for navigation and content overview.
Features:
Hierarchical tree structure with nested children
Includes heading levels, text, and anchor IDs
Provides heading count and maximum depth statistics
Enables LLMs to navigate directly to specific sections
get_binary_entry - Retrieve binary content from a ZIM entry
Required parameters:
zim_file_path(string): Path to the ZIM fileentry_path(string): Entry path, e.g., 'I/image.png' or 'I/document.pdf'
Optional parameters:
max_size_bytes(integer): Maximum size of content to return (default: 10MB). Content larger than this will return metadata only.include_data(boolean): If true (default), include base64-encoded data. Set to false to retrieve metadata only.
Returns:
JSON string containing:
path: Entry path in ZIM filetitle: Entry titlemime_type: Content type (e.g., "application/pdf", "image/png")size: Size in bytessize_human: Human-readable size (e.g., "1.5 MB")encoding: "base64" when data is included, null otherwisedata: Base64-encoded content (if include_data=true and under size limit)truncated: Boolean indicating if content exceeded size limit
Use Cases:
Retrieve PDFs for processing with PDF parsing tools
Extract images for vision models or OCR tools
Get video/audio files for transcription services
Enable multi-agent workflows with specialized content processors
Examples
Listing ZIM files
{
"name": "list_zim_files"
}Response:
Found 1 ZIM files in 1 directories:
[
{
"name": "wikipedia_en_100_2025-08.zim",
"path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"directory": "C:\\zim",
"size": "310.77 MB",
"modified": "2025-09-11T10:20:50.148427"
}
]Searching ZIM files
{
"name": "search_zim_file",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"query": "biology",
"limit": 3
}
}Response:
Found 51 matches for "biology", showing 1-3:
## 1. Taxonomy (biology)
Path: Taxonomy_(biology)
Snippet: # Taxonomy (biology) Part of a series on
---
Evolutionary biology
Darwin's finches by John Gould
* Index
* Introduction
* [Main](Evolution "Evolution")
* Outline
## 2. Protein
Path: Protein
Snippet: # Protein A representation of the 3D structure of the protein myoglobin showing turquoise Ξ±-helices. This protein was the first to have its structure solved by X-ray crystallography. Toward the right-center among the coils, a prosthetic group called a heme group (shown in gray) with a bound oxygen molecule (red).
## 3. Ant
Path: Ant
Snippet: # Ant Ants
Temporal range: Late Aptian β Present
---
Fire ants
[Scientific classification](Taxonomy_\(biology\) "Taxonomy \(biology\)")
Kingdom: | [Animalia](Animal "Animal")
Phylum: | [Arthropoda](Arthropod "Arthropod")
Class: | [Insecta](Insect "Insect")
Order: | Hymenoptera
Infraorder: | Aculeata
Superfamily: |
Latreille, 1809[1]
Family: |
Latreille, 1809Getting ZIM entries
{
"name": "get_zim_entry",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"entry_path": "Protein"
}
}Response:
# Protein
Path: Protein
Type: text/html
## Content
# Protein
A representation of the 3D structure of the protein myoglobin showing turquoise Ξ±-helices. This protein was the first to have its structure solved by X-ray crystallography. Toward the right-center among the coils, a prosthetic group called a heme group (shown in gray) with a bound oxygen molecule (red).
**Proteins** are large biomolecules and macromolecules that comprise one or more long chains of amino acid residues. Proteins perform a vast array of functions within organisms, including catalysing metabolic reactions, DNA replication, responding to stimuli, providing structure to cells and organisms, and transporting molecules from one location to another. Proteins differ from one another primarily in their sequence of amino acids, which is dictated by the nucleotide sequence of their genes, and which usually results in protein folding into a specific 3D structure that determines its activity.
A linear chain of amino acid residues is called a polypeptide. A protein contains at least one long polypeptide. Short polypeptides, containing less than 20β30 residues, are rarely considered to be proteins and are commonly called peptides.
... [Content truncated, total of 56,202 characters, only showing first 1,500 characters] ...Smart Retrieval in Action
Example: Automatic path resolution
{
"name": "get_zim_entry",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"entry_path": "C/Test Article"
}
}Response (showing smart retrieval working):
# Test Article
Requested Path: C/Test Article
Actual Path: C/Test_Article
Type: text/html
## Content
# Test Article
This article demonstrates the smart retrieval system automatically handling
path encoding differences. The system tried "C/Test Article" directly,
then automatically searched and found "C/Test_Article".
... [Content continues] ...get_server_health - Get server health and statistics
No parameters required.
Returns:
Overall status (
healthy/warning/error)Cache performance metrics (hits, misses, hit rate, size)
Directory and ZIM-file accessibility checks
Recommendations and warnings
Sanitized configuration summary
Example Response:
{
"timestamp": "2026-05-03T10:42:11.123456",
"status": "healthy",
"server_name": "openzim-mcp",
"uptime_info": {
"process_id": "[REDACTED]",
"started_at": "2026-05-03T10:30:00"
},
"configuration": {
"allowed_directories": 1,
"cache_enabled": true,
"config_hash": "abc12345..."
},
"cache_performance": {
"enabled": true,
"size": 4,
"max_size": 100,
"hit_rate": 0.62
},
"health_checks": {
"directories_accessible": 1,
"zim_files_found": 3,
"permissions_ok": true
},
"recommendations": [],
"warnings": []
}get_server_configuration - Get detailed server configuration
No parameters required.
Returns: Comprehensive server configuration plus diagnostics. Sensitive fields (PIDs, raw filesystem paths) are redacted/sanitized β diagnostic output is intended to be safe to paste into bug reports.
Example Response:
{
"configuration": {
"server_name": "openzim-mcp",
"allowed_directories": ["[REDACTED]/zim"],
"allowed_directories_count": 1,
"cache_enabled": true,
"cache_max_size": 100,
"cache_ttl_seconds": 3600,
"content_max_length": 100000,
"content_snippet_length": 1000,
"search_default_limit": 10,
"config_hash": "abc12345...",
"server_pid": "[REDACTED]"
},
"diagnostics": {
"validation_status": "ok",
"warnings": [],
"recommendations": []
},
"timestamp": "2026-05-03T10:42:11.123456"
}Additional Search Examples
Computer-related search:
{
"name": "search_zim_file",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"query": "computer",
"limit": 2
}
}Response:
Found 39 matches for "computer", showing 1-2:
## 1. Video game
Path: Video_game
Snippet: # Video game First-generation _Pong_ console at the Computerspielemuseum Berlin
---
Platforms
## 2. Protein
Path: Protein
Snippet: # Protein A representation of the 3D structure of the protein myoglobin showing turquoise Ξ±-helices. This protein was the first to have its structure solved by X-ray crystallography. Toward the right-center among the coils, a prosthetic group called a heme group (shown in gray) with a bound oxygen molecule (red).Getting detailed content:
{
"name": "get_zim_entry",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"entry_path": "Evolution",
"max_content_length": 1500
}
}Response:
# Evolution
Path: Evolution
Type: text/html
## Content
# Evolution
Part of the Biology series on
---
****
Mechanisms and processes
* Adaptation
* Genetic drift
* Gene flow
* History of life
* Maladaptation
* Mutation
* Natural selection
* Neutral theory
* Population genetics
* Speciation
... [Content truncated, total of 110,237 characters, only showing first 1,500 characters] ...Advanced Knowledge Retrieval Examples
Getting ZIM metadata:
{
"name": "get_zim_metadata",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim"
}
}Response:
{
"entry_count": 100000,
"all_entry_count": 120000,
"article_count": 80000,
"media_count": 20000,
"metadata_entries": {
"Title": "Wikipedia (English)",
"Description": "Wikipedia articles in English",
"Language": "eng",
"Creator": "Kiwix",
"Date": "2025-08-15"
}
}Browsing a namespace:
{
"name": "browse_namespace",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"namespace": "C",
"limit": 5,
"offset": 0
}
}Response:
{
"namespace": "C",
"total_in_namespace": 80000,
"offset": 0,
"limit": 5,
"returned_count": 5,
"has_more": true,
"entries": [
{
"path": "C/Biology",
"title": "Biology",
"content_type": "text/html",
"preview": "Biology is the scientific study of life..."
}
]
}Filtered search:
{
"name": "search_with_filters",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"query": "evolution",
"namespace": "C",
"content_type": "text/html",
"limit": 3
}
}Getting article structure:
{
"name": "get_article_structure",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"entry_path": "C/Evolution"
}
}Response:
{
"title": "Evolution",
"path": "C/Evolution",
"content_type": "text/html",
"headings": [
{"level": 1, "text": "Evolution", "id": "evolution"},
{"level": 2, "text": "History", "id": "history"},
{"level": 2, "text": "Mechanisms", "id": "mechanisms"}
],
"sections": [
{
"title": "Evolution",
"level": 1,
"content_preview": "Evolution is the change in heritable traits...",
"word_count": 150
}
],
"word_count": 5000
}Getting article summary:
{
"name": "get_entry_summary",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"entry_path": "C/Evolution",
"max_words": 100
}
}Response:
{
"title": "Evolution",
"path": "C/Evolution",
"content_type": "text/html",
"summary": "Evolution is the change in heritable characteristics of biological populations over successive generations. These characteristics are the expressions of genes, which are passed from parent to offspring during reproduction...",
"word_count": 100,
"is_truncated": true
}Getting table of contents:
{
"name": "get_table_of_contents",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"entry_path": "C/Evolution"
}
}Response:
{
"title": "Evolution",
"path": "C/Evolution",
"content_type": "text/html",
"toc": [
{
"level": 1,
"text": "Evolution",
"id": "evolution",
"children": [
{
"level": 2,
"text": "History of evolutionary thought",
"id": "history",
"children": []
},
{
"level": 2,
"text": "Mechanisms",
"id": "mechanisms",
"children": []
}
]
}
],
"heading_count": 15,
"max_depth": 4
}Getting search suggestions:
{
"name": "get_search_suggestions",
"arguments": {
"zim_file_path": "C:\\zim\\wikipedia_en_100_2025-08.zim",
"partial_query": "bio",
"limit": 5
}
}Response:
{
"partial_query": "bio",
"suggestions": [
{"text": "Biology", "path": "C/Biology", "type": "title_start_match"},
{"text": "Biochemistry", "path": "C/Biochemistry", "type": "title_start_match"},
{"text": "Biodiversity", "path": "C/Biodiversity", "type": "title_start_match"}
],
"count": 3
}Server Management and Diagnostics Examples
Getting server health:
{
"name": "get_server_health"
}Response:
{
"status": "healthy",
"server_name": "openzim-mcp",
"uptime_info": {
"process_id": "[REDACTED]",
"started_at": "2026-05-03T10:30:00"
},
"cache_performance": {
"enabled": true,
"size": 15,
"max_size": 100,
"hit_rate": 0.85
}
}ZIM Entry Retrieval Best Practices
Smart Retrieval System
OpenZIM MCP implements an intelligent entry retrieval system that automatically handles path encoding inconsistencies common in ZIM files:
How It Works:
Direct Access First: Attempts to retrieve the entry using the provided path exactly as given
Automatic Fallback: If direct access fails, automatically searches for the entry using various search terms
Path Mapping Cache: Caches successful path mappings to improve performance for repeated access
Enhanced Error Guidance: Provides clear guidance when entries cannot be found
Benefits for LLM Users:
Transparent Operation: No need to understand ZIM path encoding complexities
Single Tool Call: Eliminates the need for manual search-first methodology
Reliable Results: Consistent success across different path formats (spaces vs underscores, URL encoding, etc.)
Performance Optimized: Cached mappings improve repeated access speed
Example Scenarios Handled Automatically:
C/Test ArticleβC/Test_Article(space to underscore conversion)C/CafΓ©βC/Caf%C3%A9(URL encoding differences)A/Some-PageβA/Some_Page(hyphen to underscore conversion)
Usage Recommendations
For Direct Entry Access:
{
"name": "get_zim_entry",
"arguments": {
"zim_file_path": "/path/to/file.zim",
"entry_path": "C/Article_Name"
}
}When Entry Not Found: The system will automatically provide guidance:
Entry not found: 'A/Article_Name'.
The entry path may not exist in this ZIM file.
Try using search_zim_file() to find available entries,
or browse_namespace() to explore the file structure.Important Notes and Limitations
Content Length Requirements
The
max_content_lengthparameter forget_zim_entrymust be at least 1000 charactersContent longer than the specified limit will be truncated with a note showing the total character count
Search Behavior
Search results may include articles that contain the search terms in various contexts
Results are ranked by relevance but may not always be directly related to the primary meaning of the search term
Search snippets provide a preview of the content but may not show the exact location where the search term appears
File Format Support
Currently supports ZIM files (Zeno IMproved format)
Tested with Wikipedia ZIM files (e.g.,
wikipedia_en_100_2025-08.zim)File paths must be properly escaped in JSON (use
\\for Windows paths)
Configuration
OpenZIM MCP supports configuration through environment variables with the OPENZIM_MCP_ prefix:
# Cache configuration
export OPENZIM_MCP_CACHE__ENABLED=true
export OPENZIM_MCP_CACHE__MAX_SIZE=200
export OPENZIM_MCP_CACHE__TTL_SECONDS=7200
# Content configuration
export OPENZIM_MCP_CONTENT__MAX_CONTENT_LENGTH=200000
export OPENZIM_MCP_CONTENT__SNIPPET_LENGTH=2000
export OPENZIM_MCP_CONTENT__DEFAULT_SEARCH_LIMIT=20
# Logging configuration
export OPENZIM_MCP_LOGGING__LEVEL=DEBUG
export OPENZIM_MCP_LOGGING__FORMAT="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
# Server configuration
export OPENZIM_MCP_SERVER_NAME=my_openzim_mcp_serverConfiguration Options
Setting | Default | Description |
|
| Tool surface: |
|
| Transport protocol: |
|
| HTTP/SSE bind host. Non-loopback hosts require |
|
| HTTP/SSE bind port. |
| (unset) | Bearer token required when binding HTTP/SSE to a non-loopback interface. |
| (empty) | JSON array of allowed CORS origins for the HTTP transport. Wildcard |
| (empty) | JSON array of public-facing hostnames the HTTP transport accepts in the |
|
| Enable MCP resource subscriptions (HTTP transport only). When |
|
| Polling interval (1β60s) for the subscription mtime watcher. |
|
| Enable/disable caching |
|
| Maximum cache entries |
|
| Cache TTL in seconds |
|
| Max content length |
|
| Max snippet length |
|
| Default search result limit |
|
| Logging level |
|
| Log message format |
|
| Server instance name |
Security Features
Path Traversal Protection: Secure path validation prevents access outside allowed directories
Input Sanitization: All user inputs are validated and sanitized
Resource Management: Proper cleanup of ZIM archive resources
Error Handling: Sanitized error messages prevent information disclosure
Type Safety: Full type annotations prevent type-related vulnerabilities
Performance Features
Intelligent Caching: LRU cache with TTL for frequently accessed content
Resource Pooling: Efficient ZIM archive management
Optimized Content Processing: Fast HTML to text conversion
Lazy Loading: Components initialized only when needed
Memory Management: Proper cleanup and resource management
Testing
The project includes comprehensive testing with 80%+ coverage using both mock data and real ZIM files:
Test Categories
Unit Tests: Individual component testing with mocks
Integration Tests: End-to-end functionality testing with real ZIM files
Security Tests: Path traversal and input validation testing
Performance Tests: Cache and resource management testing
Format Compatibility: Testing with various ZIM file formats and versions
Error Handling: Testing with invalid and malformed ZIM files
Test Infrastructure
OpenZIM MCP uses a hybrid testing approach:
Mock-based tests: Fast unit tests using mocked libzim components
Real ZIM file tests: Integration tests using official zim-testing-suite files
Automatic test data management: Download and organize test files as needed
Test Data Sources
Built-in test data: Basic test files included in the repository
zim-testing-suite integration: Official test files from the OpenZIM project
Environment variable support:
ZIM_TEST_DATA_DIRfor custom test data locations
# Run tests with coverage report
make test-cov
# View coverage report
open htmlcov/index.html
# Run comprehensive tests with real ZIM files
make test-with-zim-dataTest Markers
Tests are organized with pytest markers:
@pytest.mark.requires_zim_data: Tests requiring ZIM test data files@pytest.mark.integration: Integration tests@pytest.mark.slow: Long-running tests
Monitoring
OpenZIM MCP provides built-in monitoring capabilities:
Health Checks: Server health and status monitoring
Cache Metrics: Cache hit rates and performance statistics
Structured Logging: JSON-formatted logs for easy parsing
Error Tracking: Comprehensive error logging and tracking
Versioning
This project uses Semantic Versioning with automated version management through release-please.
Automated Releases
Version bumps and releases are automated based on Conventional Commits:
feat:- New features (minor version bump)fix:- Bug fixes (patch version bump)feat!:orBREAKING CHANGE:- Breaking changes (major version bump)perf:- Performance improvements (patch version bump)docs:,style:,refactor:,test:,chore:- No version bump
Release Process
The project uses an improved, consolidated release system with automatic validation:
Automatic (Recommended): Push conventional commits β Release Please creates PR β Merge PR β Automatic release
Manual: Use GitHub Actions UI for direct control over releases
Emergency: Push tags directly for critical fixes
Key Features:
Zero-touch releases from main branch
Automatic version synchronization validation
Comprehensive testing before every release
Improved error handling and rollback capabilities
Branch protection prevents broken releases
The release flow is implemented in .github/workflows/release-please.yml and .github/workflows/release.yml.
Commit Message Format
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]Examples:
feat: add search suggestions endpoint
fix: resolve path traversal vulnerability
feat!: change API response format
docs: update installation instructionsContributing
Fork the repository
Create a feature branch (
git checkout -b feature/amazing-feature)Make your changes
Run tests (
make check)Use conventional commit messages (
git commit -m 'feat: add amazing feature')Push to the branch (
git push origin feature/amazing-feature)Open a Pull Request
Development Guidelines
Follow PEP 8 style guidelines
Add type hints to all functions
Write tests for new functionality
Update documentation as needed
Use conventional commit messages for automatic versioning
Ensure all tests pass before submitting
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
Kiwix for the ZIM format and libzim library
MCP for the Model Context Protocol
The open-source community for the excellent libraries used in this project
Made with β€οΈ by Cameron Rye
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/cameronrye/openzim-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server