Each project is one URL. Point a client at it and the agent gains three documentation tools — no other configuration, no API key, no plugin.
http://localhost:3444/mcp/<project-name>
The dashboard’s Connect an agent panel prints every snippet below filled in with the real URL, ready
to copy — including the Authorization header when the project requires a token. Replace demo with
your project name in the examples, and ctxm_9f3a… with the token the project page showed you.
Claude Code
A new project requires a token by default, so add the header with your project’s token:
claude mcp add --transport http demo-docs http://localhost:3444/mcp/demo \
--header "Authorization: Bearer ctxm_9f3a…"
If the project’s MCP access was switched to open, the header can be left off:
claude mcp add --transport http demo-docs http://localhost:3444/mcp/demo
Check it worked:
claude mcp list
Then just ask a question about your documentation — Claude Code decides to call search_docs on its own,
because the server describes what it holds.
Cursor
~/.cursor/mcp.json for every project, or .cursor/mcp.json inside one repository. A new project
requires a token by default:
{
"mcpServers": {
"demo-docs": {
"url": "http://localhost:3444/mcp/demo",
"headers": { "Authorization": "Bearer ctxm_9f3a…" }
}
}
}
If the project’s MCP access was switched to open, the headers block can be left off:
{
"mcpServers": {
"demo-docs": { "url": "http://localhost:3444/mcp/demo" }
}
}
Restart Cursor, then check Settings → MCP for a green indicator.
Claude Desktop
Claude Desktop speaks stdio, so it needs a bridge. A new project requires a token by default, and
mcp-remote passes it on with --header, in claude_desktop_config.json:
{
"mcpServers": {
"demo-docs": {
"command": "npx",
"args": [
"-y", "mcp-remote", "http://localhost:3444/mcp/demo",
"--header", "Authorization: Bearer ctxm_9f3a…"
]
}
}
}
If the project’s MCP access was switched to open, the --header argument can be left off:
{
"mcpServers": {
"demo-docs": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:3444/mcp/demo"]
}
}
}
Restart Claude Desktop. The tools appear under the connectors icon.
Config file locations: macOS ~/Library/Application Support/Claude/claude_desktop_config.json,
Windows %APPDATA%\Claude\claude_desktop_config.json.
Any other MCP client
Give it the URL. Contextator serves both MCP transports on the same address and picks by what the client sends:
- Streamable HTTP (current clients):
POSTaninitializerequest; the session id comes back in themcp-session-idheader. - HTTP + SSE (protocol
2024-11-05, older clients):GETthe URL; the server answers with anendpointevent pointing at/mcp/demo/messages?sessionId=….
No configuration switches between them.
Projects that require a token
A project whose MCP access is token required answers every request without a valid token with
401 and a www-authenticate: Bearer realm="<project-name>" header, whichever transport the client
uses. Mint the token on the project page — it is shown once — and send it as an ordinary header:
Authorization: Bearer ctxm_9f3a…
- The header is checked on every
/mcp/*request. On the legacy SSE transport that means theGETthat opens the stream and thePOSTs to/mcp/demo/messages, so a client that can set a header on only one of the two cannot connect. A browserEventSourceis the usual case — leave those projects open, or put the whole instance behind an authenticating proxy. - Tokens are per project. A client connected to two token-protected projects needs one for each.
- Revoking a token cuts its client off immediately, mid-session; so does switching a project from open to token required. Reconnecting with a current token is all that is needed.
Where the switch lives and who may flip it: Projects.
Projects that require an account
account required is the third mode, and the one where this dashboard’s memberships reach /mcp/*.
A static ctxm_… token names nobody and is refused there; a client has to act as an account that is a
member of the project, and that is re-checked on every request — removing a membership,
disabling an account or resetting its password cuts the connection off on its next call rather than at
some expiry.
A client gets that credential through OAuth 2.1, which is what browser-based MCP connectors already
speak. You configure nothing: point the connector at http://host:3444/mcp/<project-name>, and it
discovers this server’s authorization endpoints, registers itself, and sends you to a page here to sign
in and approve it. What it gets back acts as your account, renews itself quietly, and expires if the
connector goes unused for a month. Changing your password disconnects every connector acting as you.
MCP_OAUTH=0 removes the flow entirely, and then only static tokens open a closed project — which also
means browser-based connectors cannot connect at all.
Several projects at once
Add one entry per project. They stay isolated — an agent connected to both simply has two sets of tools, and a search in one never returns documents from the other.
{
"mcpServers": {
"billing-docs": { "url": "http://localhost:3444/mcp/billing" },
"mobile-docs": { "url": "http://localhost:3444/mcp/mobile" }
}
}
Connecting from another machine
- Make sure the server is reachable:
HOST=0.0.0.0(the default) and the port is open. - Use the server’s address, not
localhost:http://docs.internal:3444/mcp/demo. - Behind a reverse proxy, set
PUBLIC_BASE_URLso the dashboard prints the right URLs, and disable response buffering — see Installation.
Verifying without an agent
A smoke test ships with the repository. From a source checkout:
npm install
npm run smoke -- http://localhost:3444/mcp/demo "how do I re-index"
npm run smoke -- http://localhost:3444/mcp/demo "kurulum" --sse # exercise the legacy transport
It performs a real MCP handshake, lists the tools and runs a search. It cannot send an Authorization
header, so a project that requires a token answers it 401 — check that one with curl instead:
curl -s -i -X POST http://localhost:3444/mcp/demo \
-H "Authorization: Bearer ctxm_9f3a…" \
-H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
If it does not connect
| Symptom | Check |
|---|---|
Unknown project "…" |
The project name in the URL, exactly as the dashboard shows it |
| The client connects but finds nothing | The project has been indexed — the dashboard shows the document and chunk counts |
| “indexed with another model” | Re-index the project (Embedding Models) |
| Works locally, not from another machine | HOST, firewall, and whether you used the machine’s address rather than localhost |
| A browser-based client is refused | Add its origin to ALLOWED_ORIGINS |
401 This project requires an MCP token |
The project is in token required mode and the client is sending no Authorization header |
401 This project requires an account-backed credential |
The project is in account required mode and the client is sending a static ctxm_… token, which names nobody |
403 That account is not a member of this project |
The connector signed in as an account that is not a member of the project. This is checked in every mode, open included |
| A browser-based connector cannot connect at all | MCP_OAUTH=0 on this instance, so there is no flow for it to use |
401 Unknown or revoked MCP token |
The token was revoked, or belongs to another project. Mint a fresh one on the project page |
More in Troubleshooting.