The Problem
Shopify's developer documentation is spread across 14+ distinct sources: the GraphQL Admin API, Liquid language reference, theme docs, Shopify Functions, Hydrogen, Polaris React components, Polaris Web Components, Checkout UI Extensions, App Home, and several more. The official Shopify MCP server returns entire documentation pages — sometimes 50,000+ tokens for a single query. When you're working inside Claude Code with a finite context window, that's catastrophic. A developer asking "how do I create a metafield definition" doesn't need 50K tokens of the entire Admin API reference. They need the 500 tokens that actually answer the question.
The existing alternatives were: use the official Shopify MCP server and burn context, or paste documentation manually into the conversation. Neither scales.
Design
The tool does three things: route the query to the right documentation source, fetch only what's relevant, and cap the output to a token budget.
Routing is a keyword scoring system. Each of the 14 libraries has a curated list of keywords with varying specificity. The query "metafield definition create" matches "metafield" and "mutation" in the GraphQL library's keyword list, scoring higher than any other library. The scoring weights keyword length — matching "checkout ui extension" (21 characters) scores higher than matching "checkout" (8 characters) alone. This favors specific matches over generic ones.
When scores are close (second place scores ≥60% of first), both libraries are queried and their results combined. This handles cross-domain questions like "liquid filter for money formatting" which could live in either the Liquid reference or the Theme Liquid Docs.
When nothing matches, the system falls back to the full Shopify Developer Docs index — a 53K snippet catch-all. If the primary query fails at the API level, the same fallback applies per-target. Error handling is two layers deep: per-library failure falls back to the general index, total failure returns a clear "no documentation found" message.
Fetching goes through Context7's API, which provides pre-indexed, chunked documentation. The library IDs are hardcoded and pre-verified — the tool skips Context7's library search step entirely, saving an API round-trip on every query. This was a deliberate choice: the library IDs are stable, and eliminating the search step cuts latency in half.
Token capping uses a character-based approximation (4 chars ≈ 1 token for code content). When multiple libraries are queried, the budget is split evenly between them. The truncation appends a clear marker so the consuming LLM knows content was cut.
The MCP Server
The same core library powers a standalone MCP server that communicates over stdio using JSON-RPC 2.0. The server implements the MCP protocol by hand — no SDK dependency, just readline parsing newline-delimited JSON and dispatching to three method handlers: initialize, tools/list, and tools/call.
The tool definition exposes a single shopify_docs tool with query and optional library parameters. The description is written specifically for LLM consumption — it tells Claude when to use this tool versus the official Shopify MCP server.
Adding this to Claude Code is one line in settings:
"shopify-docs": {
"command": "node",
"args": ["/path/to/mcp-wrapper.mjs"]
}
Once configured, Claude Code automatically routes Shopify documentation questions through this tool instead of performing web searches or using the heavier official server.
Why Zero Dependencies
The entire tool is three files: core.mjs (library definitions + routing + API client + lookup), bin/shopify-docs.mjs (CLI), and mcp-wrapper.mjs (MCP server). No build step. No node_modules. Runs on Node 18+ with native fetch and parseArgs.
This is intentional. The tool is designed to be copied into any project or installed globally without dependency conflicts. It's infrastructure that should be invisible — no version conflicts, no security advisories, no supply chain risk. The MCP wrapper implements the protocol directly rather than pulling in the MCP SDK because the protocol surface it needs is tiny (3 methods) and the SDK would be 95% unused code.
What I'd Do Differently
The keyword scoring is static. A query like "how do I make my theme faster" doesn't match any keywords well because it uses natural language rather than technical terms. An embedding-based router that maps queries to library descriptions semantically would handle this better, but it would add a dependency and latency. The current approach optimizes for the 90% case where developers use technical terms.
The token budget approximation (4 chars/token) is rough. For heavily structured content like GraphQL schemas, the actual ratio is closer to 3 chars/token. A tiktoken-based counter would be more accurate but would add a dependency and ~100ms of overhead per query. For a developer tool where ±20% token accuracy is fine, the approximation holds.
The library ID list requires manual updates when Shopify reorganizes their docs. A nightly validation script that checks each ID against Context7's API would catch stale entries automatically.
Numbers
| Metric | Value |
|---|---|
| Source code | 676 lines across 3 files |
| Dependencies | 0 |
| Libraries indexed | 14 Shopify documentation sources |
| Token savings | 10-20x versus official Shopify MCP server |
| Latency | ~200-400ms per query (single API call, no search step) |
Stack
Node.js 18+ (native fetch, parseArgs) · Context7 API · MCP Protocol (hand-implemented stdio JSON-RPC) · MIT licensed