Skip to main content
Glama

library-tools-mcp

An MCP server that lets an agent browse an indexed markdown library and pull exactly the context it needs (whole docs, directory roots, sections, line ranges, or any mix) in one call. Markdown and images come back interleaved in document order.

library_server.py    the server
library/             sample library (proof-of-concept data and conventions)
tests/smoke.py       end-to-end tests over stdio, one check per behaviour
tests/fixtures/      edge cases: markdown parsing, symlink escapes, repeated
                     images, malformed indexes

Some fixtures are symlinks. Clone on macOS/Linux (or with symlinks enabled on Windows) for the tests to pass.

Quick start

pip install -r requirements.txt
python tests/smoke.py        # -v prints every request and response

Register it in a project's .mcp.json:

{
  "mcpServers": {
    "library": {
      "command": "python",
      "args": ["path/to/library-tools-mcp/library_server.py"],
      "env": { "LIBRARY_ROOT": "path/to/your/library" }
    }
  }
}

Requires the v2 Python MCP SDK (mcp>=2, where FastMCP became MCPServer).

Related MCP server: pasr

Library conventions

These are proof-of-concept conventions. Swap in your real formats by changing load_doc_index, load_image_index and IMAGE_STUB; nothing else depends on them.

library/
  index.json             document index
  images/index.json      image index
  images/*.png
  guides/getting-started.md
  backend/auth.md
  backend/storage/caching.md
  backend/storage/migrations.md

Document index (index.json): one entry per doc; path is relative to the library root. Token counts are taken from here as-is. The server does not estimate tokens itself.

{"documents": [
  {"id": "auth", "path": "backend/auth.md", "title": "Authentication",
   "summary": "Gateway token validation, login flow, ...", "approx_tokens": 353}
]}

Image index (images/index.json): file is relative to the index's folder.

{"images": [
  {"id": "img-login-sequence", "file": "login-sequence.png",
   "caption": "Login sequence: client, gateway, token service"}
]}

Image stubs in markdown: ![[image:img-login-sequence]]

Index validation: both indexes are checked on every call. A missing required field, wrong type, duplicate id or bad path fails the call with a list of every problem and the entry it belongs to, e.g. documents[3] (id 'dots'): 'path' may not contain '..'.

Paths are strictly enforced. Index paths must be relative, use / and contain no ... Every file is resolved, symlinks included, and must land inside the library root; images must also land inside the image index's folder. A file that resolves anywhere else is refused, with a note. The index files themselves must be inside the library root.

Sections are not cached. They are parsed from # headings on request (re-parsed only when a file changes), following CommonMark for headings and fenced code blocks:

  • A section id is its heading slug, nested with /: refresh-tokens/rotation. list_documents always shows full ids. A unique leaf (rotation) is also accepted as a convenience; an ambiguous one returns the candidates.

  • If a doc's first heading is its only # heading, it is the title: sections start at ##, and the text under the title is the _intro section.

  • Text before the first heading is _preamble.

  • Line numbers are the file's own 1-based lines (split on \n only), matching Read and grep.

Code means fenced blocks inside the markdown (``` or ~~~).

Tools

list_documents(root=".", line_bounds=false, section_depth=null)

A tree from root (a directory relative to the library root). Each doc shows its id, token count and summary from the index, then its section outline:

backend/storage/  (2 docs, ~351 tokens)

- caching  [caching.md]  ~226 tokens  — Redis key conventions, invalidation ...
    sections, 38 lines:
      § _intro  "Caching"  L1–5
      § key-conventions  "Key conventions"  L6–16
      § invalidation  "Invalidation"  L17–34
        § invalidation/fan-out-deletes  "Fan-out deletes"  L21–34
      § ttl-rules  "TTL rules"  L35–38

Each section shows its full id (copy it verbatim into a sections selector) and its heading text. line_bounds adds the L… ranges; section_depth caps heading levels (0 = none).

get_documents(select, include_images=false, include_code=true)

select is a list. Each item is a doc id string or a selector object:

{
  "select": [
    "getting-started",                                       // whole doc
    { "root": "backend/storage" },                           // every doc under a dir
    { "doc": "auth", "sections": ["login-flow", "refresh-tokens/rotation"] },
    { "doc": "auth", "lines": [43, 50] },
    { "doc": "caching", "include_images": true }             // per-selector override
  ],
  "include_images": false,
  "include_code": true
}
  • Order: output follows select, and sections follow the order they were listed; roots expand in path order.

  • Headers: each chunk starts with === auth (backend/auth.md) · section refresh-tokens/rotation · L18–38 ===.

  • Content is verbatim: document lines are returned exactly as written. Only headers, placeholders and fence repairs are added.

  • Images: sent only when the doc containing their stub is requested with include_images. An image that appears more than once in the same doc is sent the first time, and later stubs point back to it. The same image in a different doc is sent again with that doc.

  • Placeholders: omitted content becomes a one-line note, e.g. [code block omitted: python, 11 lines, L22–34] or [image omitted: img-fanout — caption].

  • Code cuts: a line range that starts or ends inside a code block gets its fences re-balanced.

  • Overlap within one call: if selectors overlap, the overlapping lines are returned once, and an overlap notice at the end says which selectors overlapped and where the lines appear. If the earlier selector used different image or code settings, the notice says how the lines were returned, so the agent can request them again separately. Separate calls are independent: nothing is remembered between them.

  • Errors: unknown docs, sections or images, missing files and refused paths are reported in a Notes: block and don't fail the call. Malformed selectors are rejected: both doc and root, unknown fields (e.g. section for sections), or an empty sections list.

Settings

env var

default

meaning

LIBRARY_ROOT

./library beside the script

library root

LIBRARY_DOC_INDEX

index.json

doc index, relative to root

LIBRARY_IMAGE_INDEX

images/index.json

image index, relative to root

LIBRARY_MAX_IMAGE_BYTES

5 MB

larger images become placeholders

Suggested CLAUDE.md line

Project knowledge lives in the library MCP tools. Browse with list_documents, then fetch everything relevant in ONE get_documents call, mixing docs, roots, sections and line ranges as needed. Request images only where the visual detail matters.

License

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides read-only search and context-pack creation over a local source library, letting AI assistants retrieve relevant excerpts and audit cited quotations.
    20
    MIT
  • A
    license
    B
    quality
    B
    maintenance
    Enables coding agents to retrieve local, token-budgeted, source-traceable context from a workspace, with file:line provenance and selection receipts for chosen spans.
    5
    28 PyPI
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables assistants such as Claude Desktop to call document search over a folder of markdown as a tool, returning grounded answers with citations or measured refusals when the evidence is insufficient.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Serves a git-backed markdown corpus to Claude Code over MCP as a read-only memory and search layer, combining semantic and full-text search with filters, document retrieval, and freshness verdicts. Also returns per-host journal entries and host-record notes newest-first, so prior work on a machine can be reviewed before running procedures there.
    Apache 2.0