MCP & AI integrations
The Model Context Protocol (MCP) lets AI assistants read and (with the right API key scopes) update your team knowledge base through tools such as search, list documents, and create/update document.
Orimora ships two MCP transports:
- HTTP endpoint at
/api/mcp— recommended for remote access. Uses the MCP Streamable HTTP transport. Authenticate with an API key as Bearer token. - stdio server in the repository — for local development. You point your MCP client at
yarn mcp.
Who can turn this on
Section titled “Who can turn this on”Enabling MCP is a team-admin action. It is gated by the developer.mcp permission, which controls turning the MCP endpoint on or off and managing connected apps. By default only the Admins group has it — the Editors, Members, and Viewers groups do not.
So if you don’t see the MCP Server section under Settings → Developers, you’re missing this permission: ask a workspace admin to enable MCP, or to grant your group the developer.mcp capability under Settings → Groups.
What you need
Section titled “What you need”- A team admin (or the
developer.mcppermission) to turn MCP on — see above. - For Cursor, Claude Desktop, or Obsidian: nothing more — these connect with a personal API key (
kb_…) you create in Settings → Developers. - For the Claude mobile app or claude.ai custom connectors: your Orimora must be reachable at a public
https://address. Phones can’t reachlocalhost, and the OAuth login requires HTTPS. Self-hosters set this viaAPP_URL(see the caution below); on hosted Orimora it already is. Nothing else to configure — login and discovery are built in, with no key to copy.
Set it up in three steps
Section titled “Set it up in three steps”- Turn MCP on. In the web app: Settings → Developers → MCP Server (HTTP) → toggle on. The page then shows your connection URL (
{APP_URL}/api/mcp) and the copy buttons. - Connect your client. Follow the matching section below:
- Cursor → HTTP endpoint with an API key.
- Claude Desktop → bridge or custom connector.
- Claude mobile / claude.ai → OAuth custom connector — you log in and approve, no key to copy.
- Any other MCP client → Any MCP client.
- Verify and manage access. Once connected, your assistant can search and — with write access — edit documents. Apps connected via OAuth appear under Settings → Developers → Authorized Apps with their scope and last-used date; revoke any of them there to cut access immediately.
Prerequisites
Section titled “Prerequisites”- Same stack as Installation: Node.js, Yarn, PostgreSQL, and a working
.envwith at leastDATABASE_URL(and the other required secrets described there). - An API key from the web app: Settings → Developers. Copy the full token once (format
kb_…). The MCP server validates it against the database like the REST API.
Option 1: HTTP endpoint (recommended)
Section titled “Option 1: HTTP endpoint (recommended)”The HTTP endpoint is available at {APP_URL}/api/mcp (for example https://your-domain.com/api/mcp) and must be enabled per team in Settings → Developers → MCP Server (HTTP). Only team admins can toggle MCP.
Authentication uses Bearer tokens — the same API keys from Settings → Developers.
Quick start (Cursor + hosted Orimora)
Section titled “Quick start (Cursor + hosted Orimora)”- In the web app, go to Settings → Developers and turn on MCP Server (HTTP).
- Under API keys, create a key with the scopes you need (
writeif tools should create or edit documents). Copy the full token once — it starts withkb_. - Use the Copy endpoint URL and Copy Cursor MCP JSON buttons on the same page (they appear when MCP is enabled). In the JSON, replace
YOUR_API_KEYwith your realkb_…token. - Merge the
mcpServersentry into Cursor’s MCP config:- All projects (user):
~/.cursor/mcp.jsonon macOS/Linux, or the path shown in Cursor Settings → MCP for your OS. - Single repository:
.cursor/mcp.jsonat the repo root.
- All projects (user):
- Restart Cursor or use Reload MCP / restart the MCP host so the new server is picked up.
Cursor (HTTP) — config snippet
Section titled “Cursor (HTTP) — config snippet”{ "mcpServers": { "orimora": { "url": "https://your-domain.com/api/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } }}Replace https://your-domain.com with your instance URL and YOUR_API_KEY with the full kb_… key.
Claude Desktop (HTTP via mcp-remote bridge)
Section titled “Claude Desktop (HTTP via mcp-remote bridge)”Claude Desktop’s claude_desktop_config.json only speaks stdio. To use the Orimora HTTP endpoint from the config file, route through the mcp-remote bridge — a small Node helper that translates between stdio (what Claude speaks) and HTTP+Bearer (what Orimora speaks).
Open Claude Desktop → Settings → Developer → Edit Config (or edit the file directly — on macOS at ~/Library/Application Support/Claude/claude_desktop_config.json) and merge:
{ "mcpServers": { "orimora": { "command": "npx", "args": [ "-y", "mcp-remote", "https://your-domain.com/api/mcp", "--header", "Authorization:${AUTH}" ], "env": { "AUTH": "Bearer YOUR_API_KEY" } } }}Notes:
- Keep the token in
env.AUTHand reference it via${AUTH}in--header. That way the key is in the process environment, not in the command line (it won’t leak intops). - Do not put a space between
Authorization:and${AUTH}— somemcp-remoteversions drop everything after the first space in--header. - After editing, fully quit Claude Desktop (⌘Q on macOS) and relaunch. Closing only the window is not enough.
- On macOS, Claude Desktop does not always inherit your shell
PATH. Ifnpxis not found, use an absolute path (which npx).
You can sanity-check the bridge before touching Claude’s config:
npx -y mcp-remote https://your-domain.com/api/mcp \ --header "Authorization:Bearer kb_…"If that process starts cleanly (no 401), the same config will work in Claude Desktop.
Any MCP client
Section titled “Any MCP client”Send standard MCP Streamable HTTP requests:
- POST
/api/mcp— MCP JSON-RPC messages - GET
/api/mcp— SSE stream for server-initiated messages - DELETE
/api/mcp— session cleanup
All requests require the Authorization: Bearer kb_… header.
Claude Mobile / Custom Connector (OAuth)
Section titled “Claude Mobile / Custom Connector (OAuth)”The three flows above use a static kb_… API key — perfect for Cursor, Claude
Desktop and Obsidian. The Claude mobile app and claude.ai Custom
Connectors instead expect an OAuth 2.1 flow: you paste a URL, a browser
opens, you log in and approve, and the app receives its own token. Orimora
supports this out of the box — no API key to copy.
Setup:
- In Orimora, enable MCP for your team: Settings → Developers → MCP Server → toggle on.
- In the Claude app, add a custom connector and enter your Orimora MCP
endpoint URL — it must include the
/api/mcppath, e.g.https://wiki.example.com/api/mcp(the same endpoint as Option 1, just without an API key). Do not enter the bare domain (https://wiki.example.com) — that hits the homepage and returns405, so OAuth never starts. From the401at/api/mcpthe app discovers the OAuth flow automatically via/.well-known/oauth-protected-resource. - Your browser opens an Orimora login → consent screen. Approve the requested access (read, or read + write).
- Done. The app holds a short-lived access token (auto-refreshed) scoped to your team.
Managing access: every connected app appears under Settings → Developers → Authorized Apps with its granted scope and last-used date. Revoke there to immediately invalidate its tokens — the app must re-authorize to reconnect.
Endpoints & configuration (reference)
Section titled “Endpoints & configuration (reference)”Clients discover these automatically; listed here for operators and debugging. All are derived from APP_URL (the issuer):
| Endpoint | Purpose |
|---|---|
/.well-known/oauth-protected-resource | RFC 9728 protected-resource metadata |
/.well-known/oauth-authorization-server | RFC 8414 authorization-server metadata |
POST /oauth/register | Dynamic Client Registration (RFC 7591) |
GET/POST /oauth/authorize | Authorization + consent (PKCE S256) |
POST /oauth/token | Token issue + refresh (rotation) |
POST /oauth/revoke | Token revocation (RFC 7009) |
Full configuration (TTLs, CORS origins, allowed app schemes, rate limit, token secret) is in the Configuration reference → MCP OAuth.
Option 2: stdio server (local development)
Section titled “Option 2: stdio server (local development)”For local development with direct database access, you can use the stdio transport:
export ORIMORA_API_KEY="kb_your_full_token_here"yarn mcpThe process uses stdio — it is meant to be spawned by an MCP client, not browsed in a browser.
Under the hood this runs tsx with tsconfig.mcp.json and src/mcp-server/index.ts (see the mcp script in package.json).
Cursor (stdio)
Section titled “Cursor (stdio)”{ "mcpServers": { "orimora": { "command": "yarn", "args": ["mcp"], "cwd": "/absolute/path/to/orimora-repo", "env": { "ORIMORA_API_KEY": "kb_…", "DATABASE_URL": "postgresql://user:pass@host:5432/dbname" } } }}cwdmust be the Orimora repo root so dependencies and path aliases resolve.- If your client loads
.envfor you, you may omit duplicate variables — otherwise passDATABASE_URLexplicitly.
Claude Desktop and others (stdio)
Section titled “Claude Desktop and others (stdio)”Use the same pattern: command + args + working directory + environment with ORIMORA_API_KEY and DATABASE_URL.
Writing content: Markdown is canonical
Section titled “Writing content: Markdown is canonical”create_document, update_document, append_to_document, prepend_to_document, and insert_after_heading accept the document body as Markdown via the markdown parameter. The server converts it to the TipTap/ProseMirror JSON the editor needs (via markdown-it → HTML → the shared TipTap schema).
Do not send TipTap/ProseMirror JSON through MCP. The tools do not expose a content field. Sending raw editor JSON would require the client to know every extension and attribute in use, which drifts over time and silently strips unknown nodes.
get_document mirrors this and returns the body only as markdown (no TipTap JSON), keeping payloads small and forcing clients into the same format on both read and write.
Markdown support matrix
Section titled “Markdown support matrix”Round-tripped (write and read): headings (H1–H6) including heading IDs (### Title {#anchor}), paragraphs, bold, italic, inline code, ~~strike~~, <u>underline</u>, highlight (==x==), subscript (~x~), superscript (^x^), footnotes ([^1] references + [^1]: text definitions), emoji shortcodes (:joy: → 😂, stored as Unicode), ordered lists (including custom start index), unordered lists, nested lists, task lists (- [ ] / - [x]), block quotes, fenced code blocks with language hint, horizontal rules, GFM tables, links, and images via external URLs.
Not currently supported via MCP (tracked in the internal backlog):
- Definition lists (
term/: def) — not yet rendered by the editor; avoid them on write for now (backlog Tier 3). - Inline base64 images in Markdown — rejected. Use the
upload_imagetool to attach an image to a document, or reference an externally reachable URL.
If you need the absolute Markdown source at any time, call get_document — it returns a markdown string that is byte-for-byte representative of what the editor will display.
Incremental edits
Section titled “Incremental edits”For existing documents, prefer targeted edits over full rewrites:
append_to_document(id, markdown)— appends a Markdown fragment to the end of the body, separated by a blank line.prepend_to_document(id, markdown)— inserts a fragment at the top.insert_after_heading(id, heading, markdown)— inserts directly after the first heading whose text matchesheading(case-insensitive, trimmed). ReturnsHEADING_NOT_FOUNDif no match, in which case callers should fall back toappend_to_document.
All three preserve existing content, require write scope and write access to the document, and run the same Markdown → TipTap conversion as create_document.
Deleting documents
Section titled “Deleting documents”delete_document performs a soft delete (the document is moved to trash and can be restored from the app). It is a two-step action:
- Call without
confirm— the server returns{ status: "confirm_required", preview: { title, collectionName, bodyLength, … } }so the agent can present the user with what is about to be deleted. - Call again with
confirm: true— the server moves the document to trash and returns{ status: "deleted", id, deletedAt }.
Hard deletes are not exposed through MCP. Empty the trash from the app when permanent removal is required.
Permissions and restricted mode
Section titled “Permissions and restricted mode”API keys support:
- Scopes:
read,write,admin— MCP tools that mutate content requirewritewhere applicable. - Restricted mode: optional allowlists per collection and per-document overrides, configured in the app under Settings → Developers → Configure access. The same rules apply to MCP and to
/api/v1/.
Read-only “Second Brain” vs. writable collections
Section titled “Read-only “Second Brain” vs. writable collections”Typical agent setup: one collection acts as a read-only knowledge base for the AI, another collection is a scratchpad the AI is allowed to write into. A single API key is enough — restricted mode applies per-collection overrides, so one key can hold read on the knowledge base and read+write on the scratchpad.
Under Settings → Developers → Configure access for that key:
- Turn on Restricted mode.
- Set the knowledge-base collection to Read only.
- Set the working collection to Read + write.
- Set everything else to No access.
The MCP tools expose a canWrite flag on every collection returned from list_collections (and on each get_document response). Agents should inspect that flag before attempting create_document / update_document / append_to_document / delete_document rather than trial-and-erroring.
Related documentation
Section titled “Related documentation”- REST API overview — Bearer
kb_…authentication for HTTP clients - Configuration — environment variables
- OpenAPI reference — REST operations
For a short maintainer checklist (dev server URL, health check, internal notes), see internal/dev-and-mcp.md in the repository.