feat: ship Gusto SDK docs as a local MCP server (npx-installable)#2288
Draft
jeffredodd wants to merge 5 commits into
Draft
feat: ship Gusto SDK docs as a local MCP server (npx-installable)#2288jeffredodd wants to merge 5 commits into
jeffredodd wants to merge 5 commits into
Conversation
Adds docusaurus-plugin-mcp-server, which emits build/mcp/ artifacts (docs.json, search-index.json, manifest.json) during `docusaurus build`. An adapter at the hosting layer wraps those artifacts into a live /mcp endpoint so AI agents (Claude Code, Cursor, etc.) can search and fetch the SDK docs over JSON-RPC. Wiring the runtime adapter into the downstream hosting (Gusto/embedded-sdk-docs gh-pages → a serverless/edge function) is a follow-up — this PR just lands the artifact pipeline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ships the docs-site/build/mcp/ artifacts (docs.json, search-index.json,
manifest.json) bundled with a thin stdio MCP CLI so partners can install
the server with one command:
claude mcp add gusto-sdk-docs -- npx -y @gusto/embedded-sdk-docs-mcp
The CLI reuses docusaurus-plugin-mcp-server's runtime (McpDocsServer +
its docs_search / docs_fetch tools) and connects the underlying McpServer
to StdioServerTransport instead of the plugin's HTTP adapters, since
stdio is the conventional transport for npx-launched MCP servers.
Two-package split:
- docs-site/ owns the docs and runs the build that emits artifacts
- docs-mcp/ is a runtime-only package that bundles those artifacts
and serves them; ~22 MB published, ~1 MB of code
(mostly FlexSearch + MCP SDK)
Build deps stay in docs-site so partners never download Docusaurus +
MDX + Webpack just to run a local search server.
prepublishOnly runs the docs-site build (if needed), copies the
artifacts into docs-mcp/artifacts/, and compiles the CLI to dist/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surfaces @gusto/embedded-sdk-docs-mcp in Getting Started so partners discover the install during onboarding, not after they've already fought their AI to get an answer. Covers Claude Code, Cursor/VS Code, the two tools the server exposes, SDK-version pinning, and the local-only privacy story. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Foregrounds the protocol name (MCP) rather than the broader "AI" framing — partners scanning the sidebar know exactly what kind of integration this is. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- eslint.config.ts: add docs-mcp/**/* to the ignore list, matching how docs-site/ and sdk-app/public/ are handled (standalone subpackage with its own tsconfig + ESM-flavored .js imports the root rules don't agree with) - .prettierignore: skip docs-mcp/artifacts/ and docs-mcp/dist/, which contain large build outputs (the search index choked Prettier's estree printer with a stack overflow) - Re-format docs-mcp/README.md and docs/getting-started/mcp-assistant.md to satisfy the root Prettier config (table padding, em-dash quoting) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
TL;DR
Adds an MCP (Model Context Protocol) server that exposes our SDK docs to AI coding tools like Claude Code and Cursor. Partners install it once with:
…and from then on the agent can search and fetch the actual SDK docs while writing integration code. No copy/paste, no "open the docs tab," answers cite the version of the SDK they have installed.
Draft because we still need to decide on a final package name, npm scope, and how to wire the second package into
publish.yaml. The code is working and smoke-tested end to end.What is MCP and why are we doing this?
MCP is an open protocol (Anthropic, late 2024) that lets AI agents call external tools — search a database, fetch a doc, run a command — without bespoke integration per agent. The agent connects to an MCP "server," asks what tools it has, and calls them with structured arguments. Claude Code, Cursor, VS Code Copilot, Windsurf, etc. all speak it.
For us, the win is partner ergonomics. Today when a partner integrating the SDK asks their AI "how do I theme the SDK?", the agent either guesses from training data (often wrong, always stale) or asks them to paste docs in. With an MCP server they install once, the agent gets ground-truth answers from
sdk.gusto.comcontent — at the version of the SDK they're actually using.The two tools this server exposes:
docs_searchdocs_fetchThat's enough for an agent to do "find the right page, then read it" — which covers the vast majority of "how do I do X with the SDK?" questions.
Architecture: two packages, one source of truth
docs-site/— build halfdocusaurus-plugin-mcp-serverhooks intodocusaurus buildand writes three JSON files intobuild/mcp/:docs.json(~3 MB) — every page's content as markdownsearch-index.json(~19 MB) — pre-built FlexSearch indexmanifest.json— server metadataVerified locally:
docs-mcp/— runtime halfA new sibling package (
@gusto/embedded-sdk-docs-mcp) that:artifacts/is populated by the prepublish step fromdocs-site/build/mcp/)bin: gusto-sdk-docs-mcp)docusaurus-plugin-mcp-server's runtime (McpDocsServer+ thedocs_search/docs_fetchtool implementations) and connects the underlyingMcpServertoStdioServerTransportinstead of the plugin's built-in HTTP adaptersWhy split it across two packages?
Build-time and runtime have completely different dependency graphs:
If we shipped the build deps to every partner running
npx, we'd waste ~100 MB of bandwidth per install for code that never runs. Splitting means partners download only what's needed at runtime, plus the JSON artifacts.Bonus: a docs-only fix doesn't have to bump the SDK, and an SDK release auto-refreshes the MCP package because the build runs from the same source tree.
Versioning
@gusto/embedded-sdk-docs-mcpis versioned in lock-step with@gusto/embedded-react-sdk. A partner on SDK0.47.xrunsnpx @gusto/embedded-sdk-docs-mcp@0.47and gets the docs that match. Older minors stay available on npm just by being old published versions.Why not just host an HTTP MCP endpoint at
sdk.gusto.com/mcp?We could — it's a real follow-up, not "instead of." It needs a serverless function (Cloudflare Worker, Vercel Edge, etc.) wrapping the artifacts, because the docs site currently deploys to gh-pages, which is static-only. The
npxpath:So we ship
npxfirst, hosted endpoint later.Do we need a PR on
Gusto/embedded-sdk-docs?No. That repo is a sync target — it hosts the static site. Its Buildkite pipeline will incidentally build the MCP artifacts as part of
docusaurus buildand they'll end up atsdk.gusto.com/mcp/*.jsonas static files (which a future hosted endpoint could consume), but no code changes are needed there for this PR to land.Smoke test
Sending a real MCP initialize → tools/list → tools/call sequence over stdio:
Tools/list returns
docs_searchanddocs_fetchwith proper input schemas. Real query returns real results from the actual built artifacts.Step-by-step repro in
docs-mcp/README.md.Open questions before merge
@gusto/embedded-sdk-docs-mcp?@gusto/embedded-react-sdk-mcp?@gusto/sdk-docs-mcp?publish.yamlto publish both packages on each SDK release. (Currently just publishes@gusto/embedded-react-sdk. Adding a second publish step is mechanical but I'd love a sanity check on the version-bump flow first.)docs.jsonon first run (smaller download, slower first start). Worth doing now or later?McpDocsServer.createMcpServer(), which is typedprivatein the plugin's.d.tsbut is the documented internal integration point its HTTP adapters use. Cast throughunknown as { createMcpServer(): McpServer }. Acceptable, or should we upstream anexposeMcpServerPR to the plugin?sdk.gusto.com/mcpendpoint via a serverless function — separate PR.How to try it locally
Then ask the agent something like "Use gusto-sdk-docs-dev to find how the SDK handles theming." It will call
docs_search, thendocs_fetch, and answer from real docs.🤖 Generated with Claude Code