# MCP server

Every writedocs site can run an [MCP](https://modelcontextprotocol.io) server at `/mcp`. An AI tool connected to it searches and reads your docs directly, always the published version, instead of relying on what it remembers.

It's on by default. `writedocs build` writes everything it needs into `dist/`, and `writedocs dev` serves it at `http://localhost:4321/mcp` while you write.

## What an AI tool gets

Three tools, answered from your pages:

- **`list_docs`** — every page, with its title, URL and description.
- **`search_docs`** — the best-matching pages for a query, optionally only one docs version or language (taken from your navigation's `versions` and `languages`).
- **`get_doc`** — a page's full content as Markdown. It accepts the page's path or URL, as the tool or a reader would write it.

Pages read the way they show on the site: imported snippets included, [`variables`](/docs/configuration/site-config/#variables) filled in, code examples exactly as written. Pages with `seo.noindex` are left out, as they are from `llms.txt`.

## Connecting

Point the client at your site's `/mcp` address:

```json title=".claude/settings.json (Claude Code)"
{
  "mcpServers": {
    "acme-docs": { "url": "https://docs.example.com/mcp" }
  }
}
```

In Cursor: **Settings → MCP → Add server**, and paste the URL. Opening `/mcp` in a browser shows a short description of the server.

Readers don't need to find the URL themselves: every page's [contextual menu](/docs/configuration/context-menu/) has **Copy MCP server URL**, **Connect to Cursor** and **Connect to VS Code** - the last two add the server to the editor in one click.

## Deploying

`writedocs build` adds these to `dist/`:

| File | What it's for |
| --- | --- |
| `mcp-index.json` | Your pages, as the server reads them |
| `_worker.js` | The server, ready to run on Cloudflare |
| `_routes.json` | Cloudflare Pages: only `/mcp` runs the server; every other path stays a static file |
| `.assetsignore` | Cloudflare Workers: keeps `_worker.js` and `_routes.json` from being published as files |

**Cloudflare Pages** — deploy `dist/` as usual. Pages picks up `_worker.js` on its own, and `/mcp` works.

**Cloudflare Workers** (static assets) — use `dist/_worker.js` as the Worker and `dist/` as its assets:

```jsonc title="wrangler.jsonc"
{
  "name": "my-docs",
  "main": "dist/_worker.js",
  "compatibility_date": "2026-09-01",
  "assets": { "directory": "dist", "binding": "ASSETS" }
}
```

**Other hosts that run code** (Netlify, Vercel, your own server) — the same server is part of the package. Serve `dist/` as usual and route `/mcp` to it:

```js
import { handleMcpHttp } from '@writedocs/generator/mcp';

// Any runtime with Web-standard Request/Response - a Netlify or Vercel function, a Worker, Node 18+.
export default async function mcp(request) {
  return handleMcpHttp(request, {
    loadIndex: async () => (await fetch(new URL('/mcp-index.json', request.url))).json(),
  });
}
```

**Static-only hosts** (GitHub Pages, S3) can't run a server, so `/mcp` isn't available there. `llms.txt` and `llms-full.txt` still are.

<Callout type="note">
  A project that already has its own `_worker.js` or `_routes.json` in `public/`, or a Cloudflare Pages `functions/` folder, keeps them: the build doesn't write `_worker.js` (Pages ignores `functions/` once one exists) and says so. Add `/mcp` to your own worker with `@writedocs/generator/mcp`.
</Callout>

## Turning it off

```json title="writedocs.json"
{
  "mcp": false
}
```

The build then leaves out `mcp-index.json`, `_worker.js`, `_routes.json` and `.assetsignore`.