MCP servers
Connect Claude or any MCP client to your organization — read, write, search and publish with the tools already defined.
Forumulate speaks the Model Context Protocol so an AI client (Claude, Cursor, a custom agent) can read and — with a token — write your content directly, without you building an integration.
This is the one MCP page. It covers both servers, how to connect, and what each one refuses. Three pages go deeper on the agent server: Agent server tools, Resources, and a worked example.
The two servers
There are two MCP servers, and the difference between them is the entire security model:
A public, read-only server for a single published docs space. Anonymous, no token. It serves readers — an assistant answering "how do I do X" from your public docs and community.
serverInfo.nameisforumulate-docs.A per-organization agent server. Authenticated with a token, scoped and RBAC-checked per tool. It can create spaces, write pieces, publish, moderate, and reach the rest of the REST API through an escape hatch. A piece written by an agent here is indistinguishable from one written in the editor — same revisions, same publishing, same events.
Both speak JSON-RPC 2.0 over HTTP (Streamable-HTTP, JSON response mode — no SSE), protocol version 2025-06-18. Every request gets a single JSON response. Each accepts a single JSON-RPC message or a batch (a JSON array). A GET on either endpoint returns a small transport-info object; notifications receive no reply.
Which one to use
Reading public docs / community (an assistant embedded on your site, or a third party pointing their AI tool at your published documentation): the public server.
Doing work in your workspace — authoring pages, filling declared sections, publishing, triaging the forum, moving roadmap items, editing a status page, or anything else in the API: the agent server with a token. It is the only one that can reach a private space, and the only one that can write.
Endpoints
Public docs: https://forumulate.com/api/public/orgs/{orgId}/spaces/{spaceId}/mcp
Agent: https://forumulate.com/api/orgs/{orgId}/mcpThe site's browser origin proxies the API under /api/*, so the public-facing MCP URLs live under https://forumulate.com/api/…. If you talk to the API host directly, drop the /api prefix.
Replace {orgId} (and {spaceId} for the public server) with real ids. list_spaces on the agent server returns space ids; the public space id is the one in your public reader URLs.
Authenticating
Public server: none. It is anonymous on purpose — it only ever exposes published, publicly-visible content.
Agent server: every request must authenticate as a real user. Three credentials work, and all resolve the same way:
Personal Access Token —
Authorization: Bearer cft_…. The recommended path for an agent. Create one in the console; give it the scopes the agent needs.OAuth access token —
Authorization: Bearer cfo_…, same scope vocabulary.Session cookie — for a browser-side client already logged in.
Token scope is enforced: the endpoint itself only requires the read scope (so a read-only token can connect and use the read tools), but any tool that writes checks for write itself. A read-only token calling a write tool gets 403 This token is read-only. On top of that, every tool re-checks the caller's RBAC capability (docs.read / docs.write) in the specific space it names — a long-lived MCP connection never grants more than the caller already holds. The account must also have a verified email and belong to the org.
Rate limit: 600–6,000 requests/minute for an authenticated caller depending on plan, and 120/minute per IP for an anonymous one.
Configuring a client (mcp.json style)
Most MCP clients configure a remote server with a URL and headers. Agent server with a PAT:
{
"mcpServers": {
"forumulate": {
"type": "http",
"url": "https://forumulate.com/api/orgs/YOUR_ORG_ID/mcp",
"headers": {
"Authorization": "Bearer cft_YOUR_TOKEN"
}
}
}
}Public docs server (no auth):
{
"mcpServers": {
"forumulate-docs": {
"type": "http",
"url": "https://forumulate.com/api/public/orgs/YOUR_ORG_ID/spaces/YOUR_SPACE_ID/mcp"
}
}
}Raw JSON-RPC (any HTTP client)
Handshake, then list tools:
POST /api/orgs/{orgId}/mcp
Authorization: Bearer cft_…
Content-Type: application/json
{ "jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": { "protocolVersion": "2025-06-18" } }{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }Call a tool:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": { "name": "list_spaces", "arguments": {} }
}You may also send a batch — a JSON array of messages — and receive an array of responses. Notification methods (notifications/initialized, notifications/cancelled) return no body.
What the agent server exposes
Tools for listing spaces, reading and searching content, creating and publishing pieces, and calling the rest of the API for anything a tool does not cover — with the caller's own credentials, so it can never do more than the token allows. Tool results come back as MCP content blocks. Resources expose a space's shape, so an agent can read what a piece needs before writing one. Agent server tools lists every tool and its arguments; Resources covers the resource side.
What the public server refuses
Before serving anything, the public server asserts the space is one an anonymous caller may read. It returns 404 unless all of these hold:
the space exists and is not deleted,
its type is
DOCS,its visibility is
PUBLIC,it is not behind a reader sign-in gate (a password-gated space has no public MCP server — there is no way for an anonymous client to complete the unlock),
AI ingestion is not switched off for the space (a space can opt out entirely).
Only published, publicly-visible content is ever exposed. There is no create/update tool on this server, because that would be a write hole open to the internet. An agent that needs to write authenticates against the agent server (/orgs/{orgId}/mcp) with a token instead.
search_community spans the org's PUBLIC, non-DOCS community spaces (forum, blog, roadmap) and only returns published, listed topics; staff whispers and hidden posts are stripped from get_topic. An assistant asked "how do I do X" should find the forum answer when the docs do not cover it yet — which is exactly the case where it matters — so the public corpus is docs and public community, not just this space's pages.