LLMs hallucinate most confidently about exactly the domain we publish on — Chinese
licenses, store policies, cloud partitions. I run chinaready.co, an editorial site
about China market entry for Western software teams (full disclosure: it's my site;
the server below is free and MIT-licensed, no account needed). This post documents
how we turned that corpus into a public MCP server anyone can add to their AI
client — and the protocol and platform quirks we hit along the way.
What we built
An endpoint your MCP client can use right now:
https://mcp.chinaready.co/mcp
Three read-only tools:
| Tool | What it does |
|---|---|
| search | Hybrid (BM25 + vector) search; ranked excerpts, each with its absolute source URL |
| list_topics | Topic catalog with example queries and hub pages |
| get_article | Any site page as clean Markdown |
I verified the flow end-to-end in Cursor and opencode: exact terms like "B25"
(the ICP license threshold) land on the right article, answers carry full
chinaready.co URLs to cite.
Install snippets are on our docs page (Cursor and VS Code have working one-click
links): https://chinaready.co/docs/api/
1. Retrieval: why hybrid matters
The corpus is ~2,700 chunks extracted at build time from published guides
(legal pages and forms excluded). Retrieval runs on Cloudflare AI Search
(https://developers.cloudflare.com/ai-search/).
We started vector-only. That misses exact terms — a query for "B25" (a specific
license threshold) returned semantically-near-but-wrong articles. Switching the
index to hybrid (BM25 keyword + vector, RRF fusion) made exact-term queries land
first try. If your corpus has codes, product names, or policy numbers, hybrid
is not optional.
2. The MCP layer: a stateless Worker, no SDK
Cloudflare AI Search hosts an MCP endpoint itself, but it couldn't give us two
things some clients and directories expect: tool annotations (readOnlyHint,
title) and a branded serverInfo.name (it reports "Cloudflare AI Search").
So the public endpoint is a thin wrapper — a stateless Cloudflare Worker
implementing Streamable HTTP JSON-RPC directly (initialize / tools/list /
tools/call), about 300 lines, no SDK and no session state:
https://github.com/chinaready/mcp
Stateless JSON responses (no SSE stream) turned out to be fine for every client
we tested; the MCP spec allows both.
3. Tool design for citations
The rule we settled on: every machine-readable result carries a human-checkable
absolute URL. search dedupes chunks to article level and prints title + URL +
relevance + snippet; get_article returns Markdown negotiated via
Accept: text/markdown (with an HTML-stripping fallback for subrequests that
skip the converter).
4. Discovery surfaces
- llms.txt with the endpoints: https://chinaready.co/llms.txt
- MCP server card: /.well-known/mcp/server-card.json
- OpenAPI 3.1 for the REST side: /openapi.json
- A plain REST search endpoint for agents that don't speak MCP: https://search.chinaready.co/search
Gotchas (the honest list)
- Every configuration PUT on AI Search triggers a full corpus re-index — batch your config changes.
- The hosted endpoint's rate limit is aggregate across all callers, not per-IP.
- The hosted MCP endpoint can't emit tool annotations; if you need them, you're writing a wrapper too.
- Worker subrequests to your own zone may not get the zone's Markdown converter — keep a fallback.
The wrapper is MIT and makes a decent template if you want to expose your own
docs the same way. Questions and PRs welcome.


Top comments (0)