Docs MCP service
read as.md The Docs MCP service is a public, anonymous-auth Model Context Protocol surface that lets any LLM agent query the ggui documentation corpus from inside its tool loop. Three read-only tools, one in-memory index, no token required.
One clarification on what’s open vs. hosted: the McpService mount primitive this service is built on is OSS (it ships in @ggui-ai/mcp-server — you can mount your own anonymous docs-style service today), but the hosted docs corpus service itself launches with mcp.ggui.ai.
If you’re building a coding assistant, an IDE plugin, or any agent that needs to ground its answers in ggui’s docs, point it at https://mcp.ggui.ai/docs and the model gets docs_search / docs_read / docs_list for free.
What it is
Section titled “What it is”A first-party MCP service mounted at https://mcp.ggui.ai/docs exposing three tools that wrap an in-memory index of the docs corpus:
docs_search— keyword search, ranked by occurrence count with a 3× title weighting.docs_read— fetch the raw markdown body of one doc by path.docs_list— enumerate every doc in the corpus, optionally filtered by path prefix.
The service is built on the same McpService primitive that hosts the main agent API at mcp.ggui.ai. See MCP services architecture for the anonymous-mode mechanics and how multiple services are composed into one createGguiServer boot.
Why anonymous
Section titled “Why anonymous”The ggui docs corpus is public — every page on this site is reachable without a login, and every byte the service returns is something a browser could fetch by following a URL. There is no per-user state, no rate-shaped quota tied to identity, no privileged content behind the corpus.
Forcing OAuth on a read-only public surface adds setup friction (Dynamic Client Registration ceremony, token refresh, secret storage) for zero security gain. Anonymous mode is the intentional default for surfaces that meet all of:
- The data is already public.
- The service has no side effects (no writes, no mutating tool calls, no external API spend).
- Per-user state would be a lie (the same query produces the same answer for everyone).
The agent surface at mcp.ggui.ai does not meet these criteria — sessions, BYOK credentials, and per-app rate limits all require an identified caller. The docs surface does, so it skips auth.
Endpoint
Section titled “Endpoint”POST https://mcp.ggui.ai/docsServed as a separate MCP server, not folded into /mcp. The two surfaces have different auth modes (anonymous vs. OAuth) and different tool catalogs; keeping them on distinct paths means a docs-only client never has to discover or skip past agent-loop tools, and the agent loop never has to surface read-only docs tools alongside its session lifecycle.
No Authorization header is required. Content-Type: application/json and the JSON-RPC 2.0 envelope are still required — this is MCP over HTTP, identical wire shape to /mcp, only the auth handshake is skipped.
The same three tools are also co-hosted on the unified /dev developer endpoint (coming soon with the hosted platform), alongside the executable ggui_protocol_* tools — one .mcp.json line for learn + author + do.
The three tools
Section titled “The three tools”docs_search
Section titled “docs_search”Keyword search over the corpus. The query is tokenized on whitespace and lowercased; matches are case-insensitive. Each hit’s score is the sum of token-occurrence counts in the doc — title occurrences are weighted 3×, body occurrences 1×.
The server clamps results to a maximum of 50 hits regardless of the requested limit. Default limit is 10.
| Field | Type | Required | Description |
|---|---|---|---|
q | string | Yes | Search terms — whitespace-separated; case-insensitive. |
limit | number | No | Cap on returned hits. Default 10. Server-clamped to 50. |
Returns: { hits: SearchHit[] }
interface SearchHit { path: string; // corpus-relative path, e.g. "principles/strict-typing.md" title: string; // first H1, or filename if none summary: string; // first ~200 chars of prose after the title score: number; // keyword score, higher is better}Hits are sorted by score descending, with a stable secondary sort on path ascending so identical-score hits land in deterministic order. Follow up with docs_read to fetch the full body of any hit.
docs_read
Section titled “docs_read”Fetch the full markdown body of one doc by its corpus-relative path. Pair with docs_search (search → read top hit) or docs_list (browse → read).
| Field | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Path relative to the corpus root, forward slashes. Leading ./ or / is trimmed before lookup. |
Returns: { found, path, title, summary, body, bytes }
interface DocsReadOutput { found: boolean; // false when the path isn't in the corpus path: string; title: string; summary: string; body: string; // full raw markdown, including any frontmatter bytes: number; // utf-8 byte length of body}When the path doesn’t match any doc, the tool returns found: false with empty body / title / summary rather than throwing — agents see a clean “no such doc” result and can fall back to docs_search or ask the user to refine.
docs_list
Section titled “docs_list”Enumerate every doc in the corpus, optionally filtered by a path prefix. Returns metadata only (path / title / summary); bodies are not included.
| Field | Type | Required | Description |
|---|---|---|---|
prefix | string | No | Case-sensitive path-prefix filter. When set, only entries whose path starts with this prefix are returned. |
Returns: { entries: DocMeta[], total }
interface DocMeta { path: string; title: string; summary: string;}Entries are sorted by path ascending. total is the count after the prefix filter — use it to detect a typo’d prefix (e.g. total: 0 when you expected dozens) without scanning entries.
The full unfiltered list weighs roughly 50 KB for the current corpus, comfortably under any sane MCP response budget. Use prefix to scope to one section (principles/, protocol/, architecture/, etc.) when you want a focused slice.
Corpus loading
Section titled “Corpus loading”The corpus is loaded once at service boot via loadDocsCorpus(rootDir), which walks the docs directory recursively, reads every .md file into RAM, extracts the title (first # Heading or filename fallback) and a short prose summary, and returns an immutable DocsCorpus handle.
interface DocsCorpus { list(): readonly DocMeta[]; read(docPath: string): DocEntry | null; search(query: string, limit?: number): readonly SearchHit[];}Total weight: roughly 10 MB across ~400 files, all held in process memory. Every request is served from RAM — no filesystem hit per call, no vector DB, no Algolia. Symlinks are skipped (prevents loops), non-.md files are skipped (no images, no frontmatter sidecars, no generated artifacts).
The DocsCorpus interface is the upgrade seam. A future v2 can drop in an embedding-backed implementation — precomputed vectors, semantic ranking — without changing the tool surface or the wire shape. Agents written against today’s keyword search keep working; quality improves under their feet.
Connecting from agents
Section titled “Connecting from agents”Claude Agent SDK
Section titled “Claude Agent SDK”Add the service to the mcpServers block of your agent config:
import { query } from "@anthropic-ai/claude-agent-sdk";
const result = await query({ prompt: "How do I write a blueprint for a contact form?", options: { mcpServers: { docs: { type: "http", url: "https://mcp.ggui.ai/docs", }, }, // No `authToken` needed — service is anonymous. },});Tools surface to the model as mcp__docs__docs_search, mcp__docs__docs_read, and mcp__docs__docs_list. The SDK auto-discovers them on the first turn and injects them into the model’s tool list.
Raw @modelcontextprotocol/sdk
Section titled “Raw @modelcontextprotocol/sdk”For non-Claude agents (Gemini, GPT, local models) talking MCP directly:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const client = new Client({ name: "my-agent", version: "1.0.0" });const transport = new StreamableHTTPClientTransport(new URL("https://mcp.ggui.ai/docs"));await client.connect(transport);
const tools = await client.listTools();// ["docs_search", "docs_read", "docs_list"]
const hits = await client.callTool({ name: "docs_search", arguments: { q: "audience routes principle", limit: 5 },});For one-off probes or wiring into a shell pipeline:
# Searchcurl -X POST https://mcp.ggui.ai/docs \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"docs_search","arguments":{"q":"blueprint first","limit":5}}}'
# Read one doccurl -X POST https://mcp.ggui.ai/docs \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"docs_read","arguments":{"path":"principles/blueprint-first-architecture.md"}}}'
# List one sectioncurl -X POST https://mcp.ggui.ai/docs \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"docs_list","arguments":{"prefix":"principles/"}}}'What it pairs with
Section titled “What it pairs with”The Docs MCP service is the runtime half of ggui’s LLM-agent surface. The static half lives at Agents track — narrative guides, recipe cookbooks, and step-by-step walkthroughs of agent patterns. Use the static docs to learn the protocol; use the Docs MCP service inside your agent to look up the specifics on demand.
It also pairs naturally with the main agent API at /mcp. An agent that’s already authenticated for session work can add the docs service as a second mcpServers entry — the two are independent endpoints with independent auth.
Limits
Section titled “Limits”- Search hit cap: 50. Any
limitabove 50 is silently clamped server-side. For corpus-wide enumeration usedocs_list, not a hugedocs_searchlimit. - No streaming. All three tools are request/response. There is no
tools/streamingvariant — bodies are small enough that streaming would add latency without benefit. - No live refresh. The corpus is loaded once at service boot and held immutable for the life of the process. Docs changes land in the corpus at the next deploy of the
@ggui-private/mcp-docsservice — typically whenmainships tomcp.ggui.ai. Expect a lag of minutes-to-hours between a docs PR merging and the new content appearing in tool responses. - Markdown only. The corpus walker reads
.mdfiles. Images, code samples in separate files, and any non-markdown content are not indexed. The agent gets the markdown the docs site renders from; it does not get screenshots, generated diagrams, or compiled HTML. - Anonymous. Anyone can call these tools without identifying themselves. Do not assume there is a per-user audit trail on this surface — there isn’t. (Rate limiting at the edge still applies; behavior under high load is best-effort.)
See also
Section titled “See also”- MCP services architecture — how anonymous services compose with auth’d services on one host
- MCP Protocol Reference — the main agent API at
mcp.ggui.ai - LLM agents — narrative track for building agents against ggui