DEV Community

Cover image for A small docs MCP server: search, retrieve, and keep track of the source
Seppe Gadeyne
Seppe Gadeyne

Posted on

A small docs MCP server: search, retrieve, and keep track of the source

An agent can remember an API that no longer matches the project in front of it. Giving it documentation helps, but the source matters: the newest documentation and the documentation for an installed version are not necessarily the same.

I use two small TypeScript servers to make that distinction explicit: tailwind-docs-mcp reads Tailwind's documentation source, while hermes-docs-mcp prefers a local Hermes documentation tree and falls back to GitHub.

Both expose search, retrieval, and listing over MCP stdio. Neither needs an embedding service or vector database. The interesting engineering is in parsing the documentation, ranking a useful page above an incidental mention, and knowing when the index is stale.

Give the agent a small retrieval loop

Each server has three tools:

Job Tailwind Hermes
Find candidate pages search_tailwind_docs search_hermes_docs
Read one page get_tailwind_doc get_hermes_doc
Browse available slugs list_tailwind_docs list_hermes_docs

Search returns titles, slugs, source URLs, and snippets. The agent selects a slug and retrieves the page before answering. Listing helps when it does not know the right terminology; Hermes also supports filtering by section.

I prefer this to returning an entire manual for every question. Search is a navigation step, though, not a substitute for reading the page. A snippet can omit a warning or the condition that makes an example valid.

Unknown slugs return an MCP error with suggested matches. Neither server needs a tool that writes into the consuming project's files.

Tailwind: parse the source instead of rendering the website

Tailwind's adapter reads MDX under src/docs in tailwindlabs/tailwindcss.com. It extracts exported titles and descriptions, headings, recognizable utility classes, and literal API-table rows. It removes selected MDX wrappers to produce Markdown-like content.

This is a source-specific parser, not an MDX runtime. It does not execute documentation components. Computed tables and unusual component syntax can lose information, and the cleanup can alter code-like expressions. Use the linked documentation page when exact presentation or a complete generated example matters.

The search scorer gives title and slug matches more weight than body mentions. It also recognizes utility families: a color utility such as bg-red-500 gets a boost toward background-color.

That domain knowledge matters because many unrelated examples contain background classes. A plain text match can find a real occurrence on the wrong reference page.

During verification, this call to search_tailwind_docs ranked background-color first:

{"query":"bg-red-500 background","limit":3}
Enter fullscreen mode Exit fullscreen mode

Then retrieve the selected page with get_tailwind_doc:

{"slug":"background-color"}
Enter fullscreen mode Exit fullscreen mode

Those calls were executed against the pinned server. They show one working query, not a search-quality benchmark across the whole documentation set.

Build both servers from pinned source

You need Git, npm, Node.js 20 or newer, and an MCP client that can launch stdio servers. GitHub access is needed for the default remote sources. A local documentation directory avoids those runtime fetches; Node and the server's npm dependencies are still required.

These are Bash commands for Linux, macOS, or WSL. Native Windows is not tested here. Start in a directory where you want the checkouts:

git clone https://github.com/seppegadeyne/tailwind-docs-mcp.git
cd tailwind-docs-mcp
git checkout --detach 072985944b26721d3c8d8161255fdde97da5f47e
npm ci --ignore-scripts
npm test
npm run build
cd ..

git clone https://github.com/seppegadeyne/hermes-docs-mcp.git
cd hermes-docs-mcp
git checkout --detach 0decc76d0d9a4f5b54ffa8b73c4dcc9f5d9c018b
npm ci --ignore-scripts
npm test
npm run build
Enter fullscreen mode Exit fullscreen mode

The locked installs and builds passed during verification on Linux with Node 25.2.1 and npm 11.6.2. Tailwind's three tests and Hermes's 15 tests passed. These suites check parser and search behavior using fixtures; the separate MCP smoke tests exercised discovery and real remote retrieval.

If you have not installed Hermes yet, my guide on Voltti walks through installing Hermes with the desktop app or terminal. Return here once your client is ready; the configuration below connects it to the documentation servers.

For an existing Hermes client, merge the following into its configuration, using your own absolute paths. The syntax follows the official MCP guide.

mcp_servers:
  tailwind-docs:
    command: node
    args: ["/absolute/path/to/tailwind-docs-mcp/dist/cli.js"]
    timeout: 180
    connect_timeout: 60
    enabled: true
  hermes-docs:
    command: node
    args: ["/absolute/path/to/hermes-docs-mcp/dist/cli.js"]
    timeout: 180
    connect_timeout: 60
    enabled: true
Enter fullscreen mode Exit fullscreen mode

Check discovery before asking a model to use the tools:

hermes mcp test tailwind-docs
hermes mcp test hermes-docs
Enter fullscreen mode Exit fullscreen mode

Discovery alone does not verify indexing: loading happens when a documentation tool is called. Follow it with the search and retrieval examples. Other clients need their own configuration format.

Hermes: a local-first source with a remote fallback

Hermes's adapter checks HERMES_DOCS_DIR, then its conventional local checkout location, and otherwise reads website/docs from NousResearch/hermes-agent on GitHub. Unlike the Tailwind local loader, the Hermes loader walks nested directories and accepts both Markdown and MDX.

It reads simple frontmatter metadata, derives slugs from paths, preserves fenced code, and converts Docusaurus admonitions into text markers. Its parser does not implement every frontmatter feature; a custom route declared in metadata is not necessarily the path-derived URL it returns.

A local tree can correspond to the installed software, provided you point it at that installation and account for local modifications. The GitHub fallback follows main. It does not promise that the returned docs describe your installed release.

The fallback was exercised with an empty home directory and no local override. Search, retrieval, and listing succeeded. The actual list response began:

Hermes Agent docs: 3 of 440 pages (installed commit unknown).
Enter fullscreen mode Exit fullscreen mode

That unknown matters. The current commit helper inspects a local Git checkout; it does not attach a resolved remote SHA to fallback content. The wording says “installed commit” even on the remote path. Do not interpret it as verified version provenance.

For a known page, this is the argument to get_hermes_doc:

{"slug":"user-guide/features/mcp"}
Enter fullscreen mode Exit fullscreen mode

The Glama listing provides discovery, not evidence of a successful hosted deployment. The standalone test establishes that the source loader works without a local Hermes checkout. Hosting still needs the runtime, a transport arrangement, and network access.

Freshness and reproducibility need separate decisions

Both remote loaders cache their index in process memory without a time-based refresh. Restarting the process rebuilds it on the next request. Hermes caches its local index for 60 seconds; Tailwind rereads its local directory on each load.

For reproducible documentation, check out the upstream doc repository at a chosen commit and point the server at it. These are illustrative environment-variable examples; replace the paths with actual documentation directories and start the command from the corresponding server checkout:

TAILWIND_DOCS_DIR=/absolute/path/to/tailwindcss.com/src/docs npm start
Enter fullscreen mode Exit fullscreen mode
HERMES_DOCS_DIR=/absolute/path/to/hermes-agent/website/docs npm start
Enter fullscreen mode Exit fullscreen mode

Set those variables in the client's server environment for normal MCP use. An explicitly configured but invalid Hermes directory fails locally rather than silently selecting GitHub.

Pinning the MCP adapter does not pin remotely fetched docs. The current loaders request the tree and individual files through moving main references, so an upstream change during indexing could mix revisions. They also fetch pages with Promise.all; they do not implement bounded concurrency or a persistent disk cache. A network failure can prevent the index from loading.

What I would carry into another documentation adapter

Start with a normalized page record and tests for your source format. Preserve its URL, source path, and code examples. Build a small query set with expected reference pages, including terms that occur in many examples. The live Hermes query mcp_servers, for example, ranked a plugin guide first during verification. Lexical scoring is understandable, but it still needs evaluation.

Before distributing a corpus, check the documentation's own license and attribution requirements. Public source access is not a blanket redistribution license. Treat retrieved text as reference material, not instructions that grant the agent new permissions.

For a production adaptation, I would resolve one source SHA before fetching, report it with results, bound fetch concurrency, and define an explicit refresh policy. Those are improvements to make, not features to assume these versions already have.

The small tool surface is reusable. The parser, ranking rules, and version policy are the parts that need to fit the documentation you are serving.

Top comments (0)