Worked examples
Five case studies that walk through real retrieval workflows against a Wikipedia ZIM archive. Each one starts from a research question, threads the advanced-mode tools (zim_query, zim_search, zim_get, zim_get_section, zim_browse, zim_metadata, zim_links, zim_health) into an end-to-end answer, and notes the response shape at each step.
Notation: examples on this page use Python pseudo-call syntax (
zim_search(zim_file_path="...", query="...")). The MCP wire format is JSON-RPC{"name": "...", "arguments": {...}}— your MCP client handles the framing. Argument names and types match the 8-tool advanced surface.
All examples assume a Wikipedia ZIM file at
C:\zim\wikipedia_en_100_2025-08.zim. Substitute your own archive path. Sample responses are truncated for readability.
Setup: confirm the archive is loaded#
Before any case study, a quick health check pins down which archives are available and that the cache + permissions are healthy.
zim_health()
Response (truncated):
{
"health": {
"status": "healthy",
"server_name": "openzim-mcp",
"cache_performance": { "enabled": true, "size": 0, "max_size": 100, "hit_rate": 0.0 },
"configuration": { "allowed_directories": 1, "cache_enabled": true, "config_hash": "a1b2c3d4..." }
},
"configuration": {
"configuration": { "server_name": "openzim-mcp", "allowed_directories": ["...zim-files"], "allowed_directories_count": 1, "cache_enabled": true },
"diagnostics": { "validation_status": "ok", "warnings": [], "recommendations": [] }
},
"loaded_archives": [
{
"name": "wikipedia_en_100_2025-08.zim",
"path": "...wikipedia_en_100_2025-08.zim",
"size": "310.77 MB",
"size_bytes": 325866721,
"modified": "2025-09-11T10:20:50"
}
]
}
zim_health returns server status, configuration, and loaded archives together in one consolidated payload (the shape above) — three v1 tools folded into a single call, with no view selector. Pass a zim_file_path to validate one archive instead (integrity + checksum + index/identity).
Case study 1: Taxonomy — broad search to top hit#
Research goal: find the canonical Wikipedia article on biological taxonomy.
Start with a broad search. The Wikipedia archive has dozens of articles touching “biology”, so cap the result set and inspect the top hits.
zim_search(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
query="biology",
mode="fulltext",
limit=3,
)
Response (truncated — zim_search returns a JSON SearchResponse):
{
"query": "biology",
"results": [
{ "path": "Taxonomy_(biology)", "title": "Taxonomy (biology)",
"snippet": "# Taxonomy (biology) Part of a series on Evolutionary biology ..." },
{ "path": "Protein", "title": "Protein",
"snippet": "# Protein A representation of the 3D structure of the protein myoglobin ..." },
{ "path": "Ant", "title": "Ant",
"snippet": "# Ant Ants — Temporal range: Late Aptian – Present ..." }
],
"total": 51,
"next_cursor": null,
"done": false,
"page_info": { "offset": 0, "limit": 3 }
}
The top hit, Taxonomy_(biology), is the canonical disambiguated article. The snippet confirms it’s part of an evolutionary-biology series, which is what we want. Fetch the article:
zim_get(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="Taxonomy_(biology)",
)
If you had guessed entry_path="Taxonomy (biology)" (with a literal space instead of underscore), zim_get’s smart-retrieval fallback would still resolve to Taxonomy_(biology) automatically. See Smart retrieval for the algorithm.
Takeaway: start broad, narrow on the snippet, then zim_get. Two calls, one canonical article.
Case study 2: Protein — full entry, then summary, then structure#
Research goal: answer “what is a protein?” with an LLM-friendly progressive disclosure (one-paragraph summary → outline → focused section).
zim_get defaults to view="full", which returns the rendered article body up to the per-entry content cap (max_content_length, default 100,000 chars — pass a smaller value plus content_offset to page through long articles).
zim_get(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="Protein",
)
Response (truncated) — zim_get returns an EntryResponse dict, and the article body is the content value:
{
"path": "Protein",
"title": "Protein",
"content_type": "text/html",
"content": "# Protein\n\nA 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 ...\n\n**Proteins** are large biomolecules and macromolecules that comprise one or more long chains of amino acid residues ...\n\n... [Content truncated, total of 156,202 characters of body content, only showing first 100,000. Pass `content_offset=100000` to read the next page.] ...",
"_meta": { "chars": 101504, "truncated": true, "tokens_est": 23811, "more_at_offset": 100000, "total_chars": 156202 }
}
The truncation footer is part of the content string, and the content_offset it names is the value to pass back to read the next page.
For an LLM-sized summary instead of the full body, switch to view="summary":
zim_get(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="C/Protein",
view="summary",
)
Response:
{
"title": "Protein",
"path": "C/Protein",
"content_type": "text/html",
"summary": "Proteins are large biomolecules comprising one or more long chains of amino acid residues. They 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 ...",
"word_count": 200,
"is_truncated": true
}
For an outline before drilling in, use view="toc":
zim_get(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="C/Protein",
view="toc",
)
Response:
{
"title": "Protein",
"path": "C/Protein",
"toc": [
{ "level": 1, "text": "Protein", "section_id": "protein", "children": [
{ "level": 2, "text": "Biochemistry", "section_id": "biochemistry", "children": [] },
{ "level": 2, "text": "Synthesis", "section_id": "synthesis", "children": [] },
{ "level": 2, "text": "Cellular functions", "section_id": "cellular-functions", "children": [] }
]}
],
"heading_count": 15,
"max_depth": 4
}
Then read just the section you want with zim_get_section (defaults to compact=True — no surrounding HTML chrome):
zim_get_section(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="C/Protein",
section_id="biochemistry",
)
Takeaway: zim_get is one tool, four views — full, summary, toc, structure. Pair with zim_get_section to read one section without pulling the whole article.
Case study 3: Ant — taxonomy box plus related articles#
Research goal: build a knowledge graph around the article “Ant” — the taxonomic classification box plus a handful of related articles.
Search to confirm the canonical path:
zim_search(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
query="ant insect",
mode="fulltext",
limit=3,
)
The Ant article’s intro contains an inline classification box pulling in Taxonomy_(biology), Animal, Arthropod, and Insect. To get the full set of outbound links programmatically, use zim_links:
zim_links(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="C/Ant",
direction="outbound",
)
Response (truncated — one kind per call, "internal" by default):
{
"results": [
{ "url": "Taxonomy_(biology)", "path": "C/Taxonomy_(biology)", "text": "Scientific classification", "type": "internal" },
{ "url": "Animal", "path": "C/Animal", "text": "Animalia", "type": "internal" },
{ "url": "Arthropod", "path": "C/Arthropod", "text": "Arthropoda", "type": "internal" },
{ "url": "Insect", "path": "C/Insect", "text": "Insecta", "type": "internal" }
],
"kind": "internal",
"category_totals": { "internal": 187, "external": 23, "media": 12, "anchor": 9 },
"next_cursor": "eyJ2IjoyLCJ0IjoiZXh0cmFjdF9hcnRpY2xlX2xpbmtzIiwicyI6ey4uLg",
"done": false
}
url is the raw href as written in the article — it is not necessarily fetchable. Internal rows also carry path, the resolved entry path, and that is what you pass to zim_get.
Pass kind="external" or kind="media" to fetch the other buckets. category_totals reports four counts either way: the three fetchable buckets plus anchor (same-page #fragment links), which is excluded from internal and which no kind can retrieve.
For semantically related articles (not just inline links), flip the direction:
zim_links(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="C/Ant",
direction="related",
limit=5,
)
Response:
{
"results": [
{ "path": "C/Bee", "title": "Bee", "mention_count": 4 },
{ "path": "C/Wasp", "title": "Wasp", "mention_count": 3 },
{ "path": "C/Hymenoptera", "title": "Hymenoptera", "mention_count": 3 },
{ "path": "C/Eusociality", "title": "Eusociality", "mention_count": 2 },
{ "path": "C/Termite", "title": "Termite", "mention_count": 1 }
]
}
Both direction="outbound" and direction="related" carry a rate-limit cost of 2 (inbound costs 1); related additionally deduplicates and ranks the link-graph neighbors. Use outbound when you need the raw inline link list.
Takeaway: zim_links does both inline link extraction and related-article surfacing in one tool — pick the direction that matches the question.
Case study 4: Video game — cross-topic search with computer concepts#
Research goal: find a Wikipedia article connecting “computer” with another high-traffic topic.
A search for “computer” against the same archive surfaces unexpected cross-topic hits:
zim_search(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
query="computer",
mode="fulltext",
limit=2,
)
Response (truncated):
{
"query": "computer",
"results": [
{ "path": "Video_game", "title": "Video game",
"snippet": "# Video game First-generation _Pong_ console at the Computerspielemuseum Berlin ..." },
{ "path": "Protein", "title": "Protein",
"snippet": "# Protein A representation of the 3D structure of the protein myoglobin ..." }
],
"total": 39,
"next_cursor": null,
"done": false
}
“Video game” is the top hit for “computer” — the article opens with a museum exhibit photo of an early console. Fetch the article with a small content cap when you want to keep the response token-friendly (the default cap is 100,000 chars):
zim_get(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="Video_game",
max_content_length=3000,
)
To narrow further, use zim_search with mode="suggest" for typeahead-style completion against the title index — fast (sub-50ms typical) and useful for disambiguation:
zim_search(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
query="vide",
mode="suggest",
limit=5,
)
Response:
{
"partial_query": "vide",
"results": [
{ "text": "Video game", "path": "C/Video_game", "type": "title_start_match" },
{ "text": "Video", "path": "C/Video", "type": "title_start_match" },
{ "text": "Video camera", "path": "C/Video_camera", "type": "title_start_match" }
],
"total": 3,
"done": true
}
Takeaway: zim_search is one tool, three modes — fulltext, title, suggest. Pair fulltext for discovery with suggest for disambiguation.
Case study 5: Protein-redux — metadata, namespace browse, filtered search#
Research goal: characterize the archive itself — how many entries, what namespaces exist, and what’s available under the C namespace.
Start with zim_metadata:
zim_metadata(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
)
Response (truncated):
{
"metadata": {
"Title": "Wikipedia (English)",
"Description": "Wikipedia articles in English",
"Language": "eng",
"Creator": "Kiwix",
"Date": "2025-08-15"
},
"namespaces": [
{ "letter": "C", "total": 80000, "is_authoritative": true },
{ "letter": "M", "total": 22, "is_authoritative": true },
{ "letter": "W", "total": 3, "is_authoritative": true },
{ "letter": "X", "total": 5, "is_authoritative": true }
],
"archive_identity": { "uuid": "8e9f...", "is_multipart": false },
"index_capabilities": { "has_fulltext_index": true, "has_title_index": true },
"counter_breakdown": { "text/html": 80000, "image/webp": 19500 }
}
zim_metadata returns the archive’s metadata records, the namespace inventory with counts, the libzim archive identity, index capabilities, and the M/Counter mimetype breakdown in one call.
Browse the C (content) namespace to see what’s there:
zim_browse(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
namespace="C",
mode="page",
limit=5,
offset=0,
)
Response (truncated):
{
"namespace": "C",
"results": [
{
"path": "C/Biology",
"title": "Biology",
"content_type": "text/html",
"preview": "Biology is the scientific study of life..."
}
],
"total": 80000,
"next_cursor": "eyJ2IjoyLCJ0IjoiYnJvd3NlX25hbWVzcGFjZSIsInMiOnsuLi4",
"done": false,
"page_info": { "offset": 0, "limit": 5 }
}
For programmatic enumeration across the whole namespace, switch to mode="walk" and follow the cursor:
zim_browse(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
namespace="C",
mode="walk",
limit=500,
)
Returns the same paginated envelope (results, next_cursor, done, …). Pass next_cursor on the next call until done=True.
Now filter a full-text search to that namespace and content type — keeps results focused on rendered HTML articles (vs media or redirects):
zim_search(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
query="evolution",
mode="fulltext",
namespace="C",
content_type="text/html",
limit=3,
)
Finally, jump back to the Protein article and pull its structural outline with view="structure" — same zim_get tool, different view:
zim_get(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="C/Protein",
view="structure",
)
Response:
{
"title": "Protein",
"path": "C/Protein",
"content_type": "text/html",
"headings": [
{ "level": 1, "text": "Protein", "id": "protein" },
{ "level": 2, "text": "Biochemistry", "id": "biochemistry" },
{ "level": 2, "text": "Synthesis", "id": "synthesis" }
],
"sections": [
{
"title": "Protein",
"level": 1,
"content_preview": "Proteins are large biomolecules ..."
}
],
"word_count": 5000
}
view="structure" is the lighter sibling of view="toc" — flatter, with a short preview per section, optimized for “how big is this article and where do I start reading?” decisions. Note that word_count is a document-level figure: section objects carry exactly title, level and content_preview, with no per-section count.
Takeaway: the metadata, browse, and filtered-search trio is the right opening pattern when you don’t know the archive’s shape yet. Once you do, jump straight to zim_search + zim_get.
Smart retrieval in action#
A zim_get call for the full or summary view goes through smart retrieval if the direct lookup fails; the toc and structure views resolve the path directly and return a not-found error instead. Asking for a path with a space when the canonical form uses an underscore:
zim_get(
zim_file_path="C:\\zim\\wikipedia_en_100_2025-08.zim",
entry_path="C/Test Article", # space, not underscore
)
Response (showing the resolved path) — the requested_path key appears only when the fallback landed somewhere other than what you asked for:
{
"path": "C/Test_Article",
"requested_path": "C/Test Article",
"title": "Test Article",
"content_type": "text/html",
"content": "# Test Article\n\nThis 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\". ...",
"_meta": { "chars": 359, "truncated": false, "tokens_est": 85 }
}
The resolved path is cached for subsequent calls within the same archive, so the fallback search runs once per unique guess. See Smart retrieval for the full algorithm.
Using simple mode instead#
Every case study above can be expressed as a single zim_query call in simple mode — the natural-language intent parser routes to the right advanced operation:
zim_query(query="summarize the Protein article")
zim_query(query="find articles about computers in the wikipedia archive")
zim_query(query="show me the table of contents for Evolution")
zim_query(query="what articles are related to Ant?")
Simple mode is the default (one tool exposed) and is the right choice when your host LLM struggles with large tool catalogues. Switch to advanced mode with OPENZIM_MCP_TOOL_MODE=advanced for fine-grained control over the 8-tool surface. See LLM integration patterns for the trade-off in more depth.
Next steps:
- API reference — full tool signatures and argument shapes.
- Smart retrieval — the five-step fallback inside
zim_get. - LLM integration patterns — simple vs advanced mode, progressive discovery, batching, error handling.
The v1.x maintenance window closed when v2.5.0 shipped (2026-06-18); only the current major line is supported — see SECURITY.md for the policy. The CHANGELOG carries the v1 → v2 migration table and the v3.0.0 breaking-changes entry.