---
title: Docs MCP service
description: Anonymous MCP service at mcp.ggui.ai/docs — three tools (docs_search, docs_read, docs_list) for any LLM to query ggui documentation programmatically.
---

:::caution[Coming soon]
This page describes a **managed hosted surface** (`mcp.ggui.ai/docs`), which is **not yet live** — it is not part of GGUI Preview 0.1.0. The self-hosted path is available today: start with the [Quickstart](/oss-quickstart/). This page is kept as forward documentation of the wire surface and goes live when hosted ggui ships.
:::

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

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](/architecture/mcp-services/) for the anonymous-mode mechanics and how multiple services are composed into one `createGguiServer` boot.

## 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

```
POST https://mcp.ggui.ai/docs
```

Served 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

### `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[] }`

```ts
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`

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 }`

```ts
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`

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 }`

```ts
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

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.

```ts
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

### Claude Agent SDK

Add the service to the `mcpServers` block of your agent config:

```ts
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`

For non-Claude agents (Gemini, GPT, local models) talking MCP directly:

```ts
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 },
});
```

### cURL

For one-off probes or wiring into a shell pipeline:

```bash
# Search
curl -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 doc
curl -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 section
curl -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/"}}}'
```

:::tip[Coding-assistant docs lookup]
If you're building an LLM coding-assistant that helps developers write ggui agents — IDE plugin, chat bot, code-review tool — the cleanest pattern is: wire `docs_search` + `docs_read` into your agent's `mcpServers`, then add a system-prompt instruction along the lines of "before answering questions about ggui APIs, call `docs_search` to ground your answer in the current docs." The model learns the two-step pattern (search → read top hit) on its own, and your assistant's answers track the live docs corpus instead of drifting with the model's training cutoff.
:::

## 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](/agents/) — 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`](/api/mcp-protocol/). 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

- **Search hit cap: 50.** Any `limit` above 50 is silently clamped server-side. For corpus-wide enumeration use `docs_list`, not a huge `docs_search` limit.
- **No streaming.** All three tools are request/response. There is no `tools/streaming` variant — 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-docs` service — typically when `main` ships to `mcp.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 `.md` files. 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

- [MCP services architecture](/architecture/mcp-services/) — how anonymous services compose with auth'd services on one host
- [MCP Protocol Reference](/api/mcp-protocol/) — the main agent API at `mcp.ggui.ai`
- [LLM agents](/agents/) — narrative track for building agents against ggui