MCP
Corveil exposes all of its first-party tools as a remote MCP (Model Context Protocol) server at one endpoint: /mcp. Any MCP-aware client — Claude Code, Codex CLI, Claude Desktop, ChatGPT Desktop, Cursor — can point at it, authenticate with a virtual API key or a browser OAuth flow, and call Corveil's tools alongside its own.
One connection carries three families of tools, delivered as namespaces:
ontology_*— read your organizational knowledge graph: the entities, meetings, decisions, and people the gateway has already indexed for your org.insights_*— read generated intelligence: rolling summaries, activity-derived recommendations, and auto-built user profiles.control_*— operate Corveil itself: models, keys, guardrails, prompts, teams, sensors, org settings, billing. Mutating, and destructive operations require an explicitconfirm: true.
Which namespaces you receive depends on how you authenticate — see The three namespaces below. The same ontology_* primitives are also available inside Corveil chat as built-in tools with no connect step; this page is for wiring an external client to the endpoint.
Endpoint & authentication
Streamable HTTP transport. Org scoping is by construction: the credential's organization is the only data the server can return, and a cross-org probe comes back empty rather than erroring.
POST https://your-corveil-host/mcp
X-Corveil-Key: sk-citadel-your-key-here
Content-Type: application/json
Accept: application/json, text/event-streamThere are two ways to authenticate the same endpoint:
| Method | Header | Best for |
|---|---|---|
| Virtual API key | X-Corveil-Key: sk-citadel-… (or x-api-key:) | Headless clients, CI, scripts. Create one under People & Access → API Keys. |
| OAuth 2.0 + DCR | Authorization: Bearer crv_at_… | Interactive clients with native OAuth (Claude Desktop, ChatGPT Desktop, Codex CLI ≥ 0.4). No key ever leaves Corveil — see Connect without an API key. |
The two paths are additive: OAuth is purely a keyless alternative for clients that support it, and the API-key path stays as-is for everything else.
The three namespaces
A single connection can carry well over a hundred tools. Which of the three namespaces you actually receive is decided by your credential class, so the same endpoint is safe to hand to a browser connector and to a CI key alike:
| Credential | ontology_* | insights_* | control_* |
|---|---|---|---|
| Virtual API key / dashboard session | ✅ | ✅ | ✅ † |
| OAuth / DCR bearer | ✅ | — | — |
| Unauthenticated / in-process | ✅ | ✅ | — |
† The control_* namespace is offered to a control-capable credential only when the deployment has the control plane enabled.
The rule for OAuth is deliberate: the scopes issued to a DCR client are read-only (mcp.read, mcp.tools) and carry no control access, so a browser-consented connector can never mutate your configuration. Tools you are not entitled to are absent from tools/list and refused on tools/call — knowing a tool's name is not enough to invoke it.
Narrowing the tool list
Because one connection can expose the whole first-party surface, you can narrow it to just the namespaces a given agent needs — smaller tool lists keep a model focused and cheaper. Append ?ns= to the URL:
https://your-corveil-host/mcp?ns=ontology,insights…or send an X-Corveil-MCP-Namespaces: ontology header. Narrowing only ever shrinks the list — a selector can never grant a namespace your credential lacks, and an unrecognized selector yields an empty list rather than everything.
Tool reference
Ontology (ontology_*) — read the knowledge graph
Three ergonomic primitives cover most work:
| Tool | Purpose |
|---|---|
ontology_lookup | "Tell me about X." Resolve an entity by name (+ optional entity_type) or entity_id; returns the synthesized context bundle — properties, 1-hop relationships, identity records, source-event count. |
ontology_search | Open-ended search. query plus optional entity_types[], a time_range (since/until), and conversation_id/thread_id to scope to one chat. |
ontology_related | The 1..N-hop neighborhood of an entity. entity_id (preferred) or name+entity_type, optional depth (default 1, max 5). |
Lower-level reads for drilling into a specific entity or source:
| Tool | Purpose |
|---|---|
ontology_entity | The entity's own fields (no edges). |
ontology_context | Full synthesized page + relationships + identity records. |
ontology_graph | Explore connected entities and relationships within N hops. |
ontology_evidence | The source events that produced or mention an entity (newest first). |
ontology_timeline | Those source events in chronological order. |
ontology_history | Per-entity change history: one attributed, newest-first feed merging the decision/audit log (edits, with a field-level diff) and the source-event timeline. Identify the entity by id, or by type + name. Optional limit (default 50) and offset. |
ontology_event | The full original content of one source event, by id. |
ontology_source_fetch | The full session transcript behind a session-derived entity. |
ontology_stats | Scalar counts + extraction status for the graph. |
ontology_decisions | The decision / guardrail audit trail. |
ontology_policies | Active organizational policies governing AI usage. |
Write tools — present only for callers whose org has the ontology-write feature and whose grant permits it, and concealed from tools/list otherwise:
| Tool | Purpose |
|---|---|
ontology_create_entity | Create (or merge into) an entity. Idempotent on entity_type + name. |
ontology_update_entity | Update a known entity's properties, name, or aliases. |
ontology_link | Create a directional relationship between two existing entities. |
ontology_unlink | Remove a relationship (destructive — pass confirm: true). |
ontology_propose_merge | File a reviewable proposal to merge duplicate entities (does not merge). |
ontology_set_content | Write the entity's synthesized knowledge page (markdown). Replaces any prior page wholesale — fold existing facts in rather than dropping them. Distinct from ontology_update_entity, which sets structured attributes. |
Insights (insights_*) — read generated intelligence
| Tool | Purpose |
|---|---|
insights_summaries | Organizational summaries (type: hourly / daily / weekly) — markdown digests of what teams are working on. Optional user_id, limit, offset. |
insights_recommendations | Activity-derived, actionable recommendations as markdown. Optional limit, offset. |
insights_profiles | Auto-built user profiles — expertise, projects, focus areas. Optional user_id; omit for all. |
Control (control_*) — operate Corveil itself
The control namespace is one tool per control-plane operation — around a hundred of them — named control_<area>_<verb>, e.g. control_keys_list, control_keys_create, control_teams_delete, control_guardrails_update. They span:
| Area | What it manages |
|---|---|
| Keys & credentials | Virtual API keys, provider credentials |
| Models & pricing | The model registry and pricing |
| Guardrails & prompts | Content guardrails and saved prompts |
| Teams, users & org | Teams, members, invitations, org settings |
| Sensors & jobs | Sensor configuration and scheduled jobs |
| Billing, usage & logs | Billing, usage rollups, request logs |
| Sessions & export | Sessions and data export |
Because the set is large and evolves, treat tools/list as the source of truth for the exact names your build and credential expose. Four conventions apply across the namespace:
- Destructive operations return a
requires_confirmationpreview unless you passconfirm: true— the mutation never runs on the first, un-confirmed call. - Privilege / enable–disable flips require the same
confirm: true, but only when the call actually sets the gated field. - Idempotent operations accept an optional retry key so a re-sent call returns the original result instead of acting twice.
- Optimistic concurrency: operations that support it accept an
If-Matchprecondition (a resource'supdated_at, or*for "must exist").
Client setup
Point the client at /mcp over the HTTP (Streamable HTTP) transport and set the X-Corveil-Key header.
Claude Code (~/.config/claude-code/mcp.json)
{
"mcpServers": {
"corveil": {
"transport": {
"type": "http",
"url": "https://your-corveil-host/mcp",
"headers": {
"X-Corveil-Key": "sk-citadel-your-key-here"
}
}
}
}
}Codex CLI (~/.codex/config.toml)
[mcp_servers.corveil]
url = "https://your-corveil-host/mcp"
http_headers = { "X-Corveil-Key" = "sk-citadel-your-key-here" }Verify the connection
# tools/list returns the namespaces your credential is entitled to.
curl -sS https://your-corveil-host/mcp \
-H "X-Corveil-Key: sk-citadel-your-key-here" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Connect without an API key (OAuth + DCR)
Corveil's MCP endpoint also speaks OAuth 2.0 + RFC 7591 Dynamic Client Registration, so clients with native OAuth support connect without ever seeing an sk-citadel-… key. The user clicks Connect in their MCP client, a browser opens to a Corveil consent screen, they approve, and the client is authorized.
- In the client's "Add remote server" dialog, paste your Corveil host URL —
https://your-corveil-host/mcp— and leave the API-key field empty. - The client fetches the endpoint's protected-resource metadata (
/mcp/.well-known/oauth-protected-resource, or the equivalent/.well-known/oauth-protected-resource/mcp), discovers the authorization server, registers itself viaPOST /mcp/oauth/register, and opens the consent screen. - If you're not already signed in, you'll bounce through WorkOS login first. The consent screen shows
mcp.readandmcp.tools— the only scopes Corveil grants to DCR clients — so a browser connector is read-only and lands theontology_*namespace. Approve, and the client exchanges the returned code for a bearer token automatically. - Manage every authorized client from People & Access → Organization → OAuth Clients (page title Clients). Revoking immediately kills all outstanding bearer tokens for that client.
Discovery + token endpoints, for reference:
# RFC 8414 authorization-server metadata (issuer-global)
curl -sS https://your-corveil-host/.well-known/oauth-authorization-server | jq .
# RFC 9728 protected-resource metadata for the /mcp resource
curl -sS https://your-corveil-host/mcp/.well-known/oauth-protected-resource | jq .
# Subsequent calls use the issued bearer in place of X-Corveil-Key
curl -sS https://your-corveil-host/mcp \
-H "Authorization: Bearer crv_at_..." \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'PKCE (S256) is mandatory; public clients receive a token without a client secret (PKCE-only). Access tokens last 1 hour; refresh tokens last 30 days and rotate on every use.
Rate limit & observability
Requests share the per-key RPM/TPM rate limit applied to every /v1/* and /mcp surface — bursting from a desktop client will eventually hit the same 429 your other API-key traffic would, under the same global auth + key throttle as /v1/messages and /v1/responses.
The MCP per-tool rate limit (mcp_rate_limits) and per-call audit rows (mcp_audit_logs) fire on the in-app chat built-in path, which goes through the gateway's MCP dispatch layer. They do not currently fire on the remote Streamable HTTP transport — that path is direct to the in-process MCP server. Prefer the in-app chat surface when you need per-tool granularity or per-call audit attribution; reach for this endpoint when you want an external MCP client to read the graph and operate Corveil alongside its other tools.
Also available in Corveil chat
The three ontology primitives (ontology_lookup / ontology_search / ontology_related) are registered as built-in tools inside Corveil chat, with no connect step and per-call audit attribution. Use chat when a person is already in the product; use this endpoint when an external agent needs the same reads in its own context.
Troubleshooting
- 401: the
X-Corveil-Key(orAuthorization: Bearer) is missing, malformed, or revoked. Re-issue under People & Access → API Keys, or re-run the OAuth connect flow. - Empty results for a known entity: the entity belongs to another org. Cross-org probes return empty by row-level security — verify the credential's org owns the data.
- A tool you expected is absent from
tools/list: you are not entitled to its namespace with this credential (an OAuth bearer seesontology_*only), or — for theontology_*write tools — your org lacks the write feature. Re-check the namespace table. control_*tools missing entirely: either you authenticated with an OAuth bearer (read-only by design) or the deployment has the control plane disabled.