Metadata-Version: 2.5
Name: cortexdb-mcp
Version: 0.9.0
Summary: MCP Server for CortexDB — expose memory operations to AI agents
License-Expression: MIT
Requires-Python: >=3.10
Requires-Dist: fastapi>=0.110
Requires-Dist: httpx>=0.27
Requires-Dist: mcp<2.0,>=1.0
Requires-Dist: pydantic>=2.0
Requires-Dist: uvicorn>=0.29
Provides-Extra: test
Requires-Dist: anyio<5,>=4.12; extra == 'test'
Requires-Dist: pytest-asyncio<2,>=1.3; extra == 'test'
Requires-Dist: pytest<10,>=9.0; extra == 'test'
Description-Content-Type: text/markdown

# CortexDB MCP Server

MCP (Model Context Protocol) server that gives AI tools persistent long-term memory via CortexDB. Works with Claude Desktop, Cursor, Windsurf, VS Code Copilot, and any MCP-compatible client.

> **0.3.0 rewrites the server against the v1 CortexDB API.** Default surface is now `https://api-v1.cortexdb.ai`. The server **auto-signs-up anonymously** on first launch — no API key required.

## What's new in 0.9.0 for CortexDB 0.14.0

0.9.0 is the release for CortexDB 0.14.0, the shared-memory release: per-person and agent tokens, org trees with
private regions, rollups, publishing, notices and a directory of names. It still works against an older server, where
the tools it adds answer 404 (which their error says). `COVERAGE.md` has the audit of every tool against 0.14.0 and
what each finding changed.

### Breaking changes

- **A tool refuses an argument it does not take.** Until 0.8.0 an unknown argument was dropped:
  `memory_forget(reason=..., memory_ids=[...])` dropped the ids and forgot every derived record in the scope, and
  `memory_store(content=..., scope=...)` wrote to the default scope. Now the call fails, names the argument and lists
  the ones the tool takes; every tool's schema says `additionalProperties: false`. CortexDB 0.14.0 refuses the same
  mistake on its forget and erasure routes, whose selectors are strict.
- **The default profile is no longer every tool.** `--tool-profile all` (the default) lists today's tools and the new
  read tools; the new write tools, `publish` and `forget_by_key`, are opt-in with `--tool-profile write`, as the
  server's own HTTP `/mcp` serves its writes only under `CORTEX_MCP_PROFILE=write`. A new `read` profile lists only
  the tools that write no memory.
- **The recall tools send `view` only when `CORTEXDB_VIEW` is set.** The server's default is holistic, but at a shared
  org-tree scope (an org, a team, a group, a compartment) a recall naming no view reads granular, and one naming
  holistic is refused with 422 `VIEW_NOT_ALLOWED_AT_SCOPE`: every search at a team scope used to fail.
- **`X-Cortex-Actor` comes from the token** when `CORTEXDB_ACTOR` is unset: the `sub` of a PASETO or JWT bearer, when
  it names an actor (`user:`, `agent:`, `service:`, `system:`). A minted token used to need `CORTEXDB_ACTOR` set by
  hand, else every call was 401. Set `CORTEXDB_ACTOR=-` for a token whose issuer maps the subject itself (an IdP's
  `sub`), which the server takes alone.
- **Errors read differently.** A refusal is `CortexDB returned HTTP <status> <code> (retriable | not retriable) on
  <route>: <message>`, then `What to do:` for each code a caller can act on, the details and the request id. It used to
  be the raw JSON body.
- **The forget tools say what they delete.** Under the server's default cascade (`derived_only`), `memory_forget` and
  `memory_bulk_delete` delete derived records (facts, beliefs, episodes, understanding) and keep the memories
  themselves, which their descriptions used to deny. Every forget answer shows its derived counts, how many named
  memories matched, its audit id and the server's warnings. To delete memories, use `memory_delete` (by id) or
  `forget_by_key` (by the keys they were written with).
- **`memory_status` shows the server's advice** (`resend` and `note`). By event id, "not found" means erased,
  forgotten or not visible to you; it no longer says re-storing is safe. A failed write is stored and excluded from
  recall: it says never to re-send it, at any age, under any key (its key replays the failure, and a copy under a new
  key would store the memory twice), since the operator re-includes the stored event in place. It used to suggest a
  new key.
- **`memory_get` sends `tenant_id` as `?scope=`**, the event's exact scope (an event elsewhere answers the same 404 as
  a missing one), which a cluster routes the read by.
- **`memory_list` pages by `cursor`, not `offset`.** CortexDB 0.14.0 pages `GET /v1/events` by an opaque, newest-first
  cursor bound to its listing, and refuses a number (422 `INVALID_CURSOR`). Each page ends with
  `More: cursor=<next>`; pass it back with the same `tenant_id` and `episode_type`, which the server now filters.

### New tools

| Tool | Profile | What it does |
|---|---|---|
| `access_check` | all, read, write | Can a principal read, write or own a scope or a memory? ALLOWED or DENIED, with the link that decided (capability, confinement, tenant, person, app, label) and each link's path. Your own token by default, or another chain (`principal`, `agent`, `app`, `chain_scopes`), which takes an Owner of the scope |
| `access_explain` | all, read, write | Why: the home's decision link by link, one decision per atom of an agent-written or derived memory, a derived memory's supports, the label against the cap |
| `rollups` | all, read, write | A team's rollups at its `aud:roll` home (name the team, the home is completed): the typed totals and the synthesis items, one by id, or the merged totals over a month range |
| `notices` | all, read, write | Your own unacknowledged notices: who read what of yours, when and why (a break-glass read, a read under a grant, a sign-in account change) |
| `acknowledge_notices` | all, read, write | Acknowledges them; it changes no memory |
| `directory_lookup` | all, read, write | People and named scopes by a name or email prefix, or one entry by id, with only the fields you may read |
| `publish` | write | A person copies a memory they are served (an event, or a rollup) into a wider home they write; erasing the original erases the copy |
| `forget_by_key` | write | Forgets memories by their idempotency keys or capture labels (`cap:<capture>`), reading nothing first, with a preview |

New fields on existing tools, each sent only when used, so a call without them sends the 0.8.0 request (but for `belief_declare`, which sends a fresh `idempotency_key` when none is given, as `memory_store` does):

| Tool | Fields |
|---|---|
| `memory_search`, `get_context` | `extra_scopes` (at most 40 scopes read in the same pass), `coverage` (what was searched and each gap, with its reason), `at_least` (a consistency token to wait for), `purpose` (what the read is for: a roster path whose `when` names it holds only then) |
| `get_context` | also `merged_beliefs` (beliefs rebuilt across the read set) and `app_mounts` (your application's mounted sources) |
| `advanced_search` | `coverage`, `at_least`, `purpose` |
| `access_check`, `access_explain` | `purpose`: decide for a read declaring it |
| `memory_list` | `cursor` (replaces `offset`), `at_least` |
| `memory_get` | `at_least` |
| `memory_status`, `resolve_conflict` | `tenant_id`, sent as `?scope=`: the row's scope, which a cluster routes a by-id call by |
| `memory_store` | `idempotency_key`, `provenance` (the events it was made from: erasing one erases it), `consistency` (returns a token to read the write back with) |
| `belief_declare` | `provenance`, `idempotency_key` (a retry answers the first declaration's belief) |
| `publish` | `consistency` |

Every forget answer and preview says what a legal hold kept (`events_under_legal_hold`, `derived_under_legal_hold`),
which no deleted count includes. A retriable refusal of `memory_store` or `belief_declare` names the idempotency key the
attempt sent, so a write whose outcome is unknown (`WAL_OUTCOME_UNKNOWN`) is retried with it and never made twice.

Each answer of a write names where a redirected write landed (`redirected_to`: CortexDB 0.14.0 stores an agent's
write in its person's private space when it cannot show what its run read), and `health_check` and `get_usage` name an
agent token's chain. Every tool carries MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`), so a
client asks before a destructive call.

### Credentials

- **`CORTEXDB_API_KEY` takes any bearer**: a person's own token, an agent's chain token (`act`, `run`), a token from an
  issuers-file entry (an enterprise IdP's), or the deployment's operator key (`CORTEX_API_KEY` on the server).
- **`CORTEXDB_SUBJECT_TOKEN`**: with the operator key, a person's or a worker's token, sent as
  `X-Cortex-Subject-Token`. The server evaluates each decision for that person too and counts what it would refuse
  (`GET /v1/admin/access/report`, check `subject_shadow`), without changing any answer.
- **`CORTEXDB_BREAK_GLASS_REASON`, `CORTEXDB_BREAK_GLASS_TICKET`, `CORTEXDB_BREAK_GLASS_NOTE`**: on a deployment that
  refuses the operator key's data reads (`CORTEX_OPERATOR_DATA_READS=deny`, the default on `cloud_shared_saas`), every
  request carries `X-Cortex-Break-Glass: reason=<reason>; ticket=<ticket>[; note=<note>]`. The reason is one of
  `incident`, `legal`, `support`, `security`, `migration`, `restore` or `other`; the ticket is your case reference
  (1 to 64 characters of `[A-Za-z0-9._:-]`, no personal data); the note, at most 256 bytes, goes to the decision log
  only. Each such request is recorded, and a read of someone's private region tells them first (see `notices`).
  Without them such a read answers `BREAK_GLASS_REQUIRED`, whose error says what to set. A setting the server would
  refuse fails the call before anything is sent, naming the variable.
- **Confined tokens.** A token that carries `scopes` holds only the capabilities its entries name. A refusal names the
  capability (`details.capability`, `details.link: confinement`) and the entry to mint (`<capability>:<scope>`); for a
  recall, `CORTEXDB_VIEW=granular` reads with `scope.read.local` alone.
- **`CORTEXDB_PURPOSE`**: the purpose reads declare unless a call names its own (`[a-z0-9_-]`, 1 to 64 characters):
  a roster path whose `when` names it holds only for such a read, and every decision line records it.

### Configuration examples

**Claude Desktop**, a person's own token, with the write tools (`claude_desktop_config.json`: on macOS
`~/Library/Application Support/Claude/`, on Windows `%APPDATA%\Claude\`; Settings > Developer > Edit Config opens it,
and Claude Desktop reads it only after a full quit and restart). Give literal values here, not `${VAR}` references:

```json
{
  "mcpServers": {
    "cortexdb": {
      "command": "cortexdb-mcp",
      "args": ["--tool-profile", "write"],
      "env": {
        "CORTEXDB_URL": "https://memory.example.com",
        "CORTEXDB_API_KEY": "<PASETO_TOKEN>",
        "CORTEXDB_SCOPE": "org:acme/user:alice"
      }
    }
  }
}
```

An operator's read-only session with break-glass, telling the people whose private regions it reads:

```json
{
  "mcpServers": {
    "cortexdb-operator": {
      "command": "cortexdb-mcp",
      "args": ["--tool-profile", "read"],
      "env": {
        "CORTEXDB_URL": "https://memory.example.com",
        "CORTEXDB_API_KEY": "<the deployment's CORTEX_API_KEY>",
        "CORTEXDB_ACTOR": "service:support",
        "CORTEXDB_SCOPE": "org:acme",
        "CORTEXDB_VIEW": "granular",
        "CORTEXDB_BREAK_GLASS_REASON": "support",
        "CORTEXDB_BREAK_GLASS_TICKET": "SUP-1142"
      }
    }
  }
}
```

**Claude Code**, for every project of this user (`--scope user`, stored in `~/.claude.json`; `local`, the default, is
this project only and private to you; `project` writes `.mcp.json` for the team). The server's name comes first:
`-e` takes several values, so a name written after it would be read as one more variable. Everything after `--` is
the server's own command line:

```bash
claude mcp add cortexdb --scope user \
  -e CORTEXDB_URL=https://memory.example.com \
  -e CORTEXDB_API_KEY="$CORTEXDB_API_KEY" \
  -e CORTEXDB_SCOPE=org:acme/user:alice \
  -- cortexdb-mcp --tool-profile write
```

Or a project's `.mcp.json`, checked in without the secret. Claude Code expands `${CORTEXDB_API_KEY}` from the
environment it was started in, so export the variable first: an unset one is passed as the literal text, with a
warning in `claude mcp list`. Claude Code asks before it starts a project's server the first time:

```json
{
  "mcpServers": {
    "cortexdb": {
      "command": "cortexdb-mcp",
      "args": ["--tool-profile", "write"],
      "env": {
        "CORTEXDB_URL": "https://memory.example.com",
        "CORTEXDB_API_KEY": "${CORTEXDB_API_KEY}",
        "CORTEXDB_SCOPE": "org:acme/team:eng"
      }
    }
  }
}
```

## Quick Start

```bash
# Install (the PyPI name is `cortexdb-mcp`)
pip install cortexdb-mcp

# Then run with zero config:
cortexdb-mcp
```

For a coding agent that only needs repository intelligence, select the compact
read-only surface (four tools: explore, impact, inventory, graph):

```bash
cortexdb-mcp --tool-profile code
```

This materially reduces tool-schema context while retaining natural-language
retrieval, typed traversal, prefix symbol discovery, and directory inventory.

The profiles (`--tool-profile`, or `CORTEXDB_TOOL_PROFILE`):

| Profile | Tools |
|---|---|
| `all` (default) | every memory, access and code tool but the opt-in writes |
| `write` | `all`, plus `publish` and `forget_by_key` |
| `read` | only the tools that write no memory (`acknowledge_notices` included: it changes only your notices) |
| `code` | `code_explore`, `code_impact`, `code_inventory`, `code_graph` |

That's it. On first launch the server hits `POST /v1/auth/signup`, mints a free-tier PASETO token + scope for itself, and caches them under `~/.config/cortexdb-mcp/state.json` (Linux/macOS) or `%APPDATA%\cortexdb-mcp\state.json` (Windows). Re-launches reuse the cached identity until the token expires (7 days); the cached `expires_at` is checked at load, and a token the server rejects with 401 is discarded and re-minted automatically, so no hand-deletion of `state.json` is needed. Signup runs against any configured `CORTEXDB_URL`, self-hosted included; a deployment with no minter answers 503 and the server carries on unauthenticated.

To target a custom deployment or pre-existing identity, set any of:

```bash
export CORTEXDB_URL="https://api-v1.cortexdb.ai"   # base URL
export CORTEXDB_API_KEY="v4.public..."             # bearer: a token, or the operator key
export CORTEXDB_ACTOR="user:alice"                 # X-Cortex-Actor (default: the token's sub)
export CORTEXDB_SCOPE="org:acme/user:alice"        # default scope path
```

## IDE Setup

Most clients just need a single line — the auto-signup handles the rest.

### Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "cortexdb": {
      "command": "cortexdb-mcp"
    }
  }
}
```

> **Windows note (audit POL-3):** if Claude Desktop / Cursor / Windsurf
> can't find `cortexdb-mcp` on PATH, give it the full path to the
> installed executable:
>
> ```json
> {
>   "mcpServers": {
>     "cortexdb": {
>       "command": "C:\\Python313\\Scripts\\cortexdb-mcp.exe"
>     }
>   }
> }
> ```
>
> Locate it with `where cortexdb-mcp` in `cmd.exe` or
> `(Get-Command cortexdb-mcp).Source` in PowerShell.
>
> Some Windows terminals don't have UTF-8 enabled by default, which can
> garble emoji in `--help` output (they show as `?`). The server itself
> isn't affected — only the CLI banner. Run
> `chcp 65001` once per terminal session to fix.

### Claude Code (CLI)

```bash
claude mcp add cortexdb -- cortexdb-mcp
```

Add `--scope user` for every project of yours, or `--scope project` to write the project's `.mcp.json`; the
configuration examples above set a token and a profile. Claude Code does not read `~/.claude/mcp.json`.

### Cursor

Edit `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "cortexdb": {
      "command": "cortexdb-mcp"
    }
  }
}
```

Or in Cursor Settings > MCP Servers > Add Server.

### Windsurf

Edit `~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "cortexdb": {
      "command": "cortexdb-mcp"
    }
  }
}
```

### VS Code (GitHub Copilot)

Add to `.vscode/mcp.json` in your project or `~/.vscode/mcp.json` globally:

```json
{
  "servers": {
    "cortexdb": {
      "command": "cortexdb-mcp"
    }
  }
}
```

### Windows tip

On Windows, MCP clients sometimes need the absolute path:

```json
{
  "mcpServers": {
    "cortexdb": {
      "command": "C:\\Python310\\Scripts\\cortexdb-mcp.exe"
    }
  }
}
```

## Tools

### Memory Operations

| Tool | Maps to | Description |
|------|---------|-------------|
| `memory_store` | `POST /v1/experience` | Store a new memory. Source/tags/type become labels; optional `idempotency_key`, `provenance` and `consistency` (a token to read it back with). Names where a redirected write landed. |
| `memory_status` | `GET /v1/experience/status` | Did a store land? Check by event ID or idempotency key after a timeout; the answer carries the server's re-send advice; `tenant_id` is the write's scope. |
| `memory_search` | `POST /v1/recall` | Search memories using natural language; `extra_scopes`, `coverage`, `at_least`, `purpose`. |
| `memory_forget` | `POST /v1/forget` | Forget derived knowledge (facts, beliefs, episodes, understanding): about a subject with `query`, else the whole scope's. Keeps the memories themselves. Accepts `from_preview_id` from `forget_preview`. |
| `forget_preview` | `POST /v1/forget/preview` | Non-destructive dry run of a forget: per-layer estimated deletion counts, a `preview_id`, and the tool that executes exactly that selector. |
| `get_context` | `POST /v1/recall` (the server's view unless `CORTEXDB_VIEW` is set) | Deep context across the requested scope, authorized ancestors, and authorized descendants—never siblings; `extra_scopes`, `coverage`, `at_least`, `merged_beliefs`, `app_mounts`, `purpose`. |
| `advanced_search` | `POST /v1/recall` + temporal | Search with structured filters (time / source / type); `coverage`, `at_least`, `purpose`. |

### Event CRUD

| Tool | Maps to | Description |
|------|---------|-------------|
| `memory_list` | `GET /v1/events` | List events in scope, newest first, a page at a time (`cursor`). |
| `memory_get` | `GET /v1/events/{id}` | Fetch a single event (used for citations); `tenant_id` is its exact scope. |
| `memory_delete` | `POST /v1/forget` (memory_ids) | Delete one memory by id, with what was derived from it. |
| `memory_bulk_delete` | `POST /v1/forget` or `/v1/forget/preview` | Forget the derived knowledge about one subject, with a dry run. Keeps the memories. |

### Access and shared memory (CortexDB 0.14.0)

| Tool | Maps to | Description |
|------|---------|-------------|
| `access_check` | `POST /v1/access/check` | Can a principal read, write or own a scope or memory (for a `purpose`), and which link decided. |
| `access_explain` | `POST /v1/access/explain` | Why, link by link, with atoms, supports and the label. |
| `rollups` | `GET /v1/rollups`, `/v1/rollups/{id}`, `/v1/rollups/totals` | A team's rollups and typed totals. |
| `notices` | `GET /v1/access/notices?mine=true` | Your own unacknowledged notices. |
| `acknowledge_notices` | `POST /v1/access/notices/ack` | Acknowledge them. |
| `directory_lookup` | `GET /v1/directory`, `/v1/directory/{id}` | Names, as you may read them. |
| `publish` (write profile) | `POST /v1/publish` | A person (directly, or through an admitted app whose token says `client: person`) widens a memory by publishing a copy. |
| `forget_by_key` (write profile) | `POST /v1/forget` (`idempotency_keys`, `capture_keys`) | The read-free forget, with a preview. |

### Knowledge Graph (facts-backed in v1)

| Tool | Maps to | Description |
|------|---------|-------------|
| `entity_list` | `GET /v1/facts` | List fact subjects, ranked by count. |
| `entity_get` | `GET /v1/facts?subject=…` | Fact lineage for a subject. |
| `entity_edges` | `GET /v1/facts?subject=…` | Predicate/object pairs for an entity. |
| `entity_link` | `POST /v1/experience` | Store a sentence the extractor will turn into a fact. |
| `belief_declare` | `POST /v1/beliefs` | Declare a belief directly, with optional evidence refs (`evt_...` / `fact_...`) and an `idempotency_key`. |

### Bi-temporal Conflicts & Claim History

| Tool | Maps to | Description |
|------|---------|-------------|
| `list_conflicts` | `GET /v1/conflicts` | Queue of contradicting values detected for the same claim. |
| `resolve_conflict` | `POST /v1/conflicts/{id}/resolve` | Resolve: `pick` / `split` / `new_information` / `dismiss`. |
| `claim_history` | `GET /v1/claims/history` | Full bi-temporal change-log of one claim (+ its conflicts). |

Validity boundaries carry a basis: `stated` boundaries render as "since/until \<date\>"; `observed` boundaries are knowledge bounds and render as "by \<date\>" — never "on \<date\>".

### Admin & Observability

| Tool | Maps to | Description |
|------|---------|-------------|
| `health_check` | `GET /v1/auth/whoami` | Verify the bearer + reach the deployment policy; names an agent token's chain. |
| `get_usage` | `GET /v1/auth/whoami` + headers | Tier, rate limit, token expiry, chain. |
| `get_insights` | (in-process) | Proactive insights derived from stored episodes; names a layer it could not read. |
| `cortexdb_check_call` | (static) | Check a CortexDB call before writing it, against 0.14.0's rules. |

### Removed from 0.3.0

`memory_update`, `export_data`, `import_data`, `get_ontology` — v1 has no direct equivalents. Updates: store a new event; bulk import: see `/v1/import` directly with the v1 envelope shape; ontology: not part of the v1 surface.

## Resources

Resources provide read-only data that AI tools can access:

| Resource URI | Description |
|---|---|
| `cortexdb://health` | Server health status |
| `cortexdb://episodes` | Recent 50 events in the default scope |
| `cortexdb://insights` | Proactive insights |

## Prompts

Pre-built prompt templates:

| Prompt | Description |
|---|---|
| `investigate_incident` | Investigate an incident using stored memories |
| `summarize_knowledge` | Summarize everything known about a topic |
| `deployment_review` | Pre-deployment safety review |
| `onboard_to_codebase` | Onboard to a codebase using stored knowledge |
| `weekly_digest` | Generate a weekly activity summary |

## Configuration

| Environment Variable | Default | Description |
|---|---|---|
| `CORTEXDB_URL` | `https://api-v1.cortexdb.ai` | CortexDB server URL |
| `CORTEXDB_API_KEY` | (none: anonymous signup) | The bearer: a person's token, an agent's chain token, an issuers-file token, or the deployment's operator key |
| `CORTEXDB_ACTOR` | the token's `sub`, else the signup state | `X-Cortex-Actor`; must match a minted token's subject. `-` sends none (a token whose issuer maps the subject) |
| `CORTEXDB_SCOPE` | (from signup state) | Default scope for tool calls |
| `CORTEXDB_VIEW` | (unset: the server's) | Recall reach: `holistic` = self + authorized ancestors + authorized descendants (no siblings); `descend` = self + authorized descendants; `granular` = exact scope. Unset, the server reads holistic, and granular at a shared org-tree scope |
| `CORTEXDB_TOOL_PROFILE` | `all` | `all`, `write`, `read` or `code`; `--tool-profile` overrides it |
| `CORTEXDB_SUBJECT_TOKEN` | (none) | With the operator key: a person's token, sent as `X-Cortex-Subject-Token` (evaluated in shadow, never changes an answer) |
| `CORTEXDB_BREAK_GLASS_REASON` | (none) | `incident`, `legal`, `support`, `security`, `migration`, `restore` or `other`; with the ticket, sends `X-Cortex-Break-Glass` |
| `CORTEXDB_BREAK_GLASS_TICKET` | (none) | Your case reference, 1 to 64 characters of `[A-Za-z0-9._:-]` |
| `CORTEXDB_BREAK_GLASS_NOTE` | (none) | Optional, at most 256 bytes, kept in the decision log only |
| `CORTEXDB_PURPOSE` | (none) | The purpose the recall and access tools declare unless a call names one (`[a-z0-9_-]`, 1 to 64) |
| `CORTEXDB_TENANT_ID` | (none) | Legacy v0 tenant identifier |
| `CORTEXDB_TIMEOUT` | `30.0` | HTTP request timeout (seconds) |

## Errors

A tool that fails returns the error as its result (`isError: true`):

```text
CortexDB returned HTTP 403 BREAK_GLASS_REQUIRED (not retriable) on POST /v1/recall: the operator key's
private_region action on /v1/recall needs X-Cortex-Break-Glass under the tenant's operator mode deny
What to do: ... set CORTEXDB_BREAK_GLASS_REASON ... and CORTEXDB_BREAK_GLASS_TICKET ...
Details: class=private_region, mode=deny, route=/v1/recall
Request id: req_01a17af6-b6aa-7811-a96a-2563471d4586
```

`retriable` is the server's own flag (with `Retry-After` when it sends one): retry such a call unchanged. `What to do`
covers the codes a caller can act on, among them `BREAK_GLASS_REQUIRED`, a confinement refusal
(`POLICY_DENIED` with `details.link: confinement`, naming `details.capability`), `VIEW_NOT_ALLOWED_AT_SCOPE`,
`INVALID_HEADER` (naming the setting behind the header: `CORTEXDB_ACTOR`, the break-glass settings),
`ACCESS_RESTORE_PENDING`, `NOTICE_UNAVAILABLE`, `FORMAT_UPGRADE_PENDING`, `WAIT_TIMEOUT`, `CONSISTENCY_TOKEN_INVALID`,
`WAL_OUTCOME_UNKNOWN` (with the idempotency key to retry with), `IDEMPOTENCY_CONFLICT`, `DB_TIMEOUT`,
`ROLLUP_SUPERSEDED` (naming the current build), `INVALID_CURSOR`, `CLUSTER_ROUTE_UNSUPPORTED`, `ACCESS_LIMIT_REACHED`,
`SUBJECT_UNMAPPED`, `PUBLISH_PERSON_REQUIRED`, `FRESH_AUTH_REQUIRED`, `SCOPE_MOVING` and the token codes
(`act_not_delegable` among them). The client keeps no table of statuses or retriable flags: a code it does not know is
read by its status and the envelope's `retriable`.

## Examples

### Store a memory from Cursor

Ask your AI assistant:
> "Remember that the payments service was migrated to Stripe v3 on March 15th"

The assistant will call `memory_store` with the content.

### Search memories

> "What do we know about the payments service?"

The assistant calls `memory_search` and gets relevant context from CortexDB.

### Explore the knowledge graph

> "Show me all entities related to the auth service"

The assistant calls `entity_get` or `entity_edges` to traverse relationships.

### Pre-deployment review

> "Run a deployment review for the user-service"

Uses the `deployment_review` prompt to check for recent incidents, dependencies, and risks.

## Architecture

```
┌─────────────┐     stdio/SSE     ┌──────────────┐     HTTP     ┌──────────┐
│  AI Client  │ ◄──────────────► │  MCP Server  │ ◄──────────► │ CortexDB │
│  (Cursor,   │     MCP JSON-RPC  │  (this pkg)  │   REST API   │  Server  │
│   Claude,   │                   │              │              │          │
│   VS Code)  │                   └──────────────┘              └──────────┘
└─────────────┘
```

The MCP server is a thin translation layer:
1. Receives MCP tool calls from the AI client
2. Translates them to CortexDB REST API calls
3. Formats responses for the AI to consume

## Tests

```bash
pip install -e ".[test]"
python -m pytest -q        # unit tests, and the stdio protocol against a local stub
python tests/live/live_stdio.py --binary /path/to/cortexdb --port 3430
```

The live test starts a real CortexDB release binary on a fresh data directory, registers a small org tree, mints
person, confined and agent tokens, and drives `cortexdb-mcp` over stdio with the official MCP client, calling every
tool of every profile. It stops the server when it is done.

## License

MIT
