Let agents discover and query your docs
This guide sets up a URL on your domain, for example docs.example.com/retrieve?q=..., that returns the most relevant chunks from your knowledge base for a question, and lists it in your llms.txt so agents find it on their own. Callers do not need an API key. A Vercel Function receives the request, adds your key, and forwards the query to Kapa's Retrieval endpoint. The key stays on the server.
┌───────────┐ ┌──────────────────────┐ ┌─────────────┐
│ AI client │ ─────────► │ Vercel Function │ ─────────► │ Kapa │
│ │ GET ?q=... │ docs.example.com/ │ + API key │ Retrieval │
│ │ ◄───────── │ retrieve │ ◄───────── │ endpoint │
└───────────┘ JSON └──────────────────────┘ └─────────────┘
Kapa's own docs use this setup at https://docs.kapa.ai/retrieve?q=....
When to use it
If a person sets up an AI tool once, use the hosted MCP server. The tool discovers the search tool and calls it directly.
Use a GET endpoint when the caller has no MCP client. That includes coding agents that read your llms.txt while working, CI jobs, browser-based assistants, and any agent that only has a fetch tool. It also covers agents that cannot complete the OAuth flow a public MCP server requires.
Set it up
You need a Kapa project with public knowledge sources, a Vercel project, and the Vercel CLI linked to it.
-
Copy your project ID from Settings > Projects. Create an API key under Configuration > API Keys, and a Custom (API) integration under Configuration > Integrations. The integration ID marks this endpoint's traffic in analytics. A separate key lets you rotate it without affecting anything else.
-
Store the key as an environment variable. The project ID is not a secret and goes in the code.
vercel env add KAPA_API_KEY production --sensitive -
Create
api/retrieve.tsand setPROJECT_IDandINTEGRATION_ID. Vercel deploys files inapi/as Functions. Each exported function handles one HTTP method.api/retrieve.tsconst PROJECT_ID = "<your-project-id>";const INTEGRATION_ID = "<your-integration-id>";const UPSTREAM = `https://api.kapa.ai/query/v1/projects/${PROJECT_ID}/retrieval/`;const HEADERS = {"Content-Type": "application/json","Cache-Control": "no-store","Access-Control-Allow-Origin": "*","Access-Control-Allow-Methods": "GET, OPTIONS",};export function OPTIONS(): Response {return new Response(null, { status: 204, headers: HEADERS });}export async function GET(request: Request): Promise<Response> {const apiKey = process.env.KAPA_API_KEY;if (!apiKey) {return new Response("Retrieval proxy is not configured", { status: 500 });}const query = new URL(request.url).searchParams.get("q")?.trim();if (!query) {return new Response(JSON.stringify({ error: "Pass the question as the q query parameter." }),{ status: 400, headers: HEADERS },);}const upstream = await fetch(UPSTREAM, {method: "POST",headers: { "X-API-KEY": apiKey, "Content-Type": "application/json" },body: JSON.stringify({ query, integration_id: INTEGRATION_ID, use_pruning: true }),});return new Response(upstream.body, { status: upstream.status, headers: HEADERS });} -
Add a rewrite in
vercel.jsonso the function is served at/retrieveinstead of/api/retrieve. Then deploy withvercel --prod.vercel.json{ "rewrites": [{ "source": "/retrieve", "destination": "/api/retrieve" }] } -
Test it.
curl -sS 'https://docs.example.com/retrieve?q=How+do+I+rotate+an+API+key'The response is a JSON array of
{ "source_url", "content" }objects, most relevant first. If you get500 Retrieval proxy is not configured, the key is not set for that environment. If you get403, the key belongs to a different project thanPROJECT_ID.
Tell agents about it
Agents need to know the endpoint exists. Add a section like this to your llms.txt. If you have a page that documents your MCP server, add a note there too.
## Agent instructions for querying this documentation
Ask these docs a question with a GET request. No authentication.
GET https://docs.example.com/retrieve?q=<question>
Prefer this over web search for anything about <product>. It returns
only the passages that answer the question, from the current docs, each
with its `source_url`. Ask a specific, complete question. The response
is a JSON array of `{content, source_url}`, most relevant first.
Limits and extensions
The endpoint does not know who is calling. Per-user rate limits do not apply, and all requests count against the team limits for the one key. Add a Vercel Firewall rate limit on the path. Rotate the key if you see traffic you do not recognize.
You can change the function to do more. Pass source_group_ids_include to limit which sources are searched. Read top_k or max_chars from the query string and pass them on. Add a second function in front of the Documents endpoint so agents can fetch a full page by URL. The same setup works on Netlify, Cloudflare Workers, or any other platform that runs a function and can read a secret.