feat(docs-mcp): local/offline stdio MCP server for docs search#442
Open
rejifald wants to merge 2 commits into
Open
feat(docs-mcp): local/offline stdio MCP server for docs search#442rejifald wants to merge 2 commits into
rejifald wants to merge 2 commits into
Conversation
Adds @stitchapi/docs-mcp — a stdio counterpart to the hosted stitchapi.dev/api/mcp docs-search server, for users who need zero per-query network calls (air-gapped or strict-egress environments, or just no third-party dependency). Same search_docs/ get_doc contract, verified end-to-end against a real build of the docs corpus. Docs content is bundled at build time (not fetched at runtime) via a new, purely-additive apps/docs/scripts/build-docs-pages.ts script plus scripts/copy-bundle.mjs, so freshness rides the existing lockstep release train (no new publish workflow needed) and the package stays reproducible/auditable per version. A Dockerfile builds from the monorepo source, giving Glama's Dockerfile- based catalog check something real to introspect.
rejifald
commented
Jul 4, 2026
| // Local-open embedding model (transformers.js) — must match | ||
| // apps/docs/lib/search-index/config.ts EMBED_MODEL exactly, or a query embeds | ||
| // into a different vector space than the bundled index was built in. | ||
| export const EMBED_MODEL = 'Xenova/all-MiniLM-L6-v2'; |
Owner
Author
There was a problem hiding this comment.
This has to be in the shared config or global env variable so it stays always in sync and won't drift.
rejifald
commented
Jul 4, 2026
| return flat.length > EXCERPT_LEN ? `${flat.slice(0, EXCERPT_LEN)}…` : flat; | ||
| } | ||
|
|
||
| function absoluteUrl(pageUrl: string, anchor: string): string { |
Owner
Author
There was a problem hiding this comment.
We might have function that does it in the core, we should reuse it
rejifald
commented
Jul 4, 2026
| const SITE_URL = 'https://stitchapi.dev'; | ||
| const EXCERPT_LEN = 300; | ||
|
|
||
| function excerpt(text: string): string { |
Owner
Author
There was a problem hiding this comment.
Probably in the core, reuse
rejifald
commented
Jul 4, 2026
| }, | ||
| ); | ||
|
|
||
| server.tool( |
rejifald
commented
Jul 4, 2026
| # Build (from repo root): docker build -f packages/docs-mcp/Dockerfile -t stitchapi-docs-mcp . | ||
| # Run: docker run -i stitchapi-docs-mcp | ||
|
|
||
| FROM node:24-slim AS builder |
Owner
Author
There was a problem hiding this comment.
Lock version number for deterministic builds
…/doc-path The docs-mcp README already flagged config.ts/doc-path.ts/SITE_URL/EXCERPT_LEN as deliberate hand-mirrors of apps/docs's search-index code — but that was only a comment. Adds scripts/probe-docs-mcp-parity.mjs (imports both sides' config constants and runs parseDocPath against a fixed input table) and a new docs-mcp-config-parity yakir tether comparing the two sides' output, so a real divergence now fails CI instead of silently shipping.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
@stitchapi/docs-mcp— a local, offline stdio counterpart to the hosted docs-search MCP server atstitchapi.dev/api/mcp. Samesearch_docs/get_doccontract, for the cases where a hosted third-party dependency is a dealbreaker: air-gapped/strict-egress environments, or just no per-query network call.apps/docs/scripts/build-docs-pages.ts(mirrors the existingbuild-search-index.tspipeline) pluspackages/docs-mcp/scripts/copy-bundle.mjsproduce the package'sdata/snapshot from the realapps/docscontent pipeline — no separate publish cadence, no live network dependency. Freshness rides the repo's existing lockstep release train (npm-publish.ymlalready builds everypackages/*package before publishing), so no new CI workflow was needed.transformers.js(same model as the hosted route), cached to a persistent OS cache dir instead of a per-cold-start/tmp— downloads once, then fully offline.Dockerfilebuilds from the monorepo source (not a published npm version), giving Glama's Dockerfile-based MCP catalog check something real to introspect.src/config.ts/doc-path.tsare deliberate, flagged mirrors ofapps/docs/lib/search-index/*(same hand-mirror tradeoff@stitchapi/aws-sigv4already documents for itsformEncodehelper) — see the package README's "Keeping this in sync".This diff went through a 5-lens adversarial review (security, correctness/parity with the hosted server, reliability, test coverage, packaging/release) before opening — security and correctness/parity came back clean; reliability found two "cache a rejected promise forever" bugs in the long-lived stdio process (fixed:
getEmbedder()/loadIndex()now reset and retry after a transient failure instead of wedging every latersearch_docscall), plus a missingprepackscript (fixed —npm pack/publishwas silently shipping an empty tarball) and an opaque error message in the build script (fixed). All test-coverage gaps the review found were also closed.Test plan
pnpm --filter @stitchapi/docs-mcp test— 42 tests across 8 files (doc-path parsing, query-cap DoS guard, hit-mapping shape, MCP tool contract end-to-end viaInMemoryTransport, error-recovery/retry behavior, CLI--help/--version, barrel exports)pnpm --filter @stitchapi/docs-mcp check:typesapps/docsbuild pipeline (583 chunks, 109 pages, real model download), copied it into the package, and queried it —search_docs("how do I retry a failed request")correctly ranked the Retry & backoff page first (0.916),get_doc({slug: "guides/resilience/throttle"})returned the real page contentnode scripts/check-release.mjs(lockstep version/license/engines, LICENSE+README presence)npx @stitchapi/docs-mcpafter this is published, and confirm the real npm tarball installs and runs correctly🤖 Generated with Claude Code