# Cortex

> Memory and knowledge service, MCP and TUI for AI agent workflows.

- **Type:** MCP server
- **Install:** `agentstack add mcp-petal-labs-cortex`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [petal-labs](https://agentstack.voostack.com/s/petal-labs)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [petal-labs](https://github.com/petal-labs)
- **Source:** https://github.com/petal-labs/cortex
- **Website:** https://docs.petallabs.io/cortex/overview/

## Install

```sh
agentstack add mcp-petal-labs-cortex
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

Cortex

  

  
    &nbsp;
    &nbsp;
    &nbsp;
    &nbsp;
    &nbsp;
 

Cortex is a memory and knowledge service for AI agents. It provides persistent context, vector-backed knowledge retrieval, and conversation memory through the Model Context Protocol (MCP).

## Features

**Four Memory Primitives:**

- **Conversation Memory** — Agent dialogue history with semantic search and auto-summarization
- **Knowledge Store** — Vector-indexed documents with hybrid search (semantic + full-text)
- **Workflow Context** — Key-value state that persists across tasks and runs
- **Entity Memory** — Auto-extracted knowledge graph of people, organizations, and concepts

**Production Ready:**

- SQLite + vec0 for single-node deployments (zero infrastructure)
- PostgreSQL + pgvector for production scale
- Prometheus metrics and structured logging
- Backup, export, and garbage collection

**Developer Experience:**

- MCP-native — works with any MCP-compatible client
- Web dashboard for browsing data
- Terminal UI for quick inspection
- Comprehensive CLI for all operations

## Installation

### Download Binary

Prebuilt binaries are published for Linux (amd64, arm64), macOS (amd64,
arm64), and Windows (amd64) on the
[releases page](https://github.com/petal-labs/cortex/releases/latest).

On Linux and macOS, this resolves the current release and your platform
automatically:

```bash
VERSION=$(curl -sI https://github.com/petal-labs/cortex/releases/latest | awk -F'/v' '/^[Ll]ocation:/ {print $2}' | tr -d '\r')
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
[ "$ARCH" = "x86_64" ] && ARCH=amd64
[ "$ARCH" = "aarch64" ] && ARCH=arm64

curl -LO "https://github.com/petal-labs/cortex/releases/latest/download/cortex_${VERSION}_${OS}_${ARCH}.tar.gz"
tar -xzf "cortex_${VERSION}_${OS}_${ARCH}.tar.gz"
sudo mv "cortex_${VERSION}_${OS}_${ARCH}/cortex" /usr/local/bin/
```

`/releases/latest` redirects to the current tag, so `VERSION` tracks whatever
is newest without this page needing an edit each release.

**Windows:**

Download the `cortex__windows_amd64.zip` asset from the
[latest release](https://github.com/petal-labs/cortex/releases/latest),
extract it, and add the extracted folder to your PATH.

### Build from Source

Requires Go 1.24+ and CGO enabled.

```bash
git clone https://github.com/petal-labs/cortex.git
cd cortex
go build -o cortex ./cmd/cortex
```

### Verify Installation

```bash
cortex --version
# cortex version 1.0.1 (a1b2c3d, 2026-08-16T22:00:00Z)
```

Release binaries report the version, commit, and build date. A binary built
from source without ldflags reports `cortex version dev`.

## Quick Start

### Start the MCP Server

```bash
# Start with stdio transport (default)
cortex serve

# Start with SSE transport for web clients
cortex serve --transport sse --port 9810

# Restrict to a specific namespace
cortex serve --namespace my-project
```

### Use the CLI

```bash
# Ingest a document (namespace defaults to "default")
cortex knowledge ingest --collection docs --title "README" --file README.md

# Search knowledge
cortex knowledge search "how to install"

# View conversation history
cortex conversation history --thread-id my-thread

# List entities
cortex entity list --namespace default
```

### Launch the TUI

```bash
cortex tui
```

Navigate with `1-5` to switch sections, `j/k` to move, `Enter` to select, `q` to quit.

## Configuration

Create `~/.cortex/config.yaml`:

```yaml
storage:
  backend: sqlite  # or "pgvector"
  data_dir: ~/.cortex/data

embedding:
  provider: openai  # openai, voyageai, ollama
  model: text-embedding-3-small
  dimensions: 1536
  cache_size: 10000
  timeout: 120s  # 0 disables the timeout

summarization:
  provider: anthropic  # anthropic, openai, gemini, ollama
  model: claude-sonnet-4-6
  timeout: 120s  # 0 disables the timeout

conversation:
  auto_summarize_threshold: 50
  semantic_search_enabled: true

knowledge:
  default_chunk_strategy: sentence  # fixed, sentence, paragraph, semantic
  default_chunk_max_tokens: 512
  default_chunk_overlap: 50

entity:
  extraction_mode: full  # off, sampled, whitelist, full
  extraction_max_attempts: 5
  extraction_backoff: exponential  # fixed, exponential
  extraction_dead_letter_policy: retain  # retain, drop
  extraction_timeout: 120s  # 0 disables the timeout

server:
  metrics_enabled: true
  metrics_port: 9811
  structured_logging: true
  shutdown_timeout: 30s
```

### Provider API Keys

API keys are read from the environment. They are never read from
`config.yaml`, so the config file stays safe to commit.

| Provider | Environment variable | Summarization / extraction | Embeddings |
|----------|---------------------|:--------------------------:|:----------:|
| `openai` | `OPENAI_API_KEY` | ✅ | ✅ |
| `anthropic` | `ANTHROPIC_API_KEY` | ✅ | — |
| `gemini` | `GEMINI_API_KEY` or `GOOGLE_API_KEY` | ✅ | — |
| `voyageai` | `VOYAGEAI_API_KEY` | — | ✅ |
| `ollama` | *(none — runs locally)* | ✅ | ✅ |

A missing key fails at startup, naming the variable:

```
Error: failed to create embedding client: failed to create embedding provider: OPENAI_API_KEY environment variable is required for openai provider
```

**Not every provider does both.** `anthropic` and `gemini` expose no embedding
API through Iris; `voyageai` is embeddings-only and cannot summarize. Cortex
checks this at startup and names the alternatives:

```
invalid config:
  - embedding.provider "gemini" does not support embeddings (supported: ollama, openai, voyageai)
  - summarization.provider "voyageai" does not support summarization (supported: anthropic, gemini, ollama, openai)
```

A typical setup mixes providers — Anthropic for summarization, OpenAI for
embeddings:

```bash
export ANTHROPIC_API_KEY=sk-ant-...
export OPENAI_API_KEY=sk-...
```

```yaml
embedding:
  provider: openai
  model: text-embedding-3-small
  dimensions: 1536

summarization:
  provider: anthropic
  model: claude-sonnet-4-6
```

**Entity extraction has no provider setting of its own.** It reuses
`summarization.provider` with `entity.extraction_model`, so that model must be
one the summarization provider serves. If you switch `summarization.provider`,
change `entity.extraction_model` to match — the default,
`claude-haiku-4-5`, is an Anthropic model.

```yaml
summarization:
  provider: openai
  model: gpt-4o
entity:
  extraction_model: gpt-4o-mini  # must be an OpenAI model too
```

### Using Ollama

Ollama needs no API key. Pull the models you intend to use, then point both
settings at them:

```bash
ollama pull nomic-embed-text
ollama pull llama3.1
```

```yaml
embedding:
  provider: ollama
  model: nomic-embed-text
  dimensions: 768        # must match the model — nomic-embed-text is 768

summarization:
  provider: ollama
  model: llama3.1

entity:
  extraction_model: llama3.1  # reuses summarization.provider
```

Set `dimensions` to whatever your embedding model actually returns. A mismatch
is caught on the first call rather than silently corrupting the index:

```
embedding 0 has 768 dimensions, expected 1536 (check embedding model and dimensions config)
```

On pgvector the dimension is baked into the schema at creation, so decide
before first run — see [PostgreSQL Setup](#postgresql-setup).

Cortex talks to `http://localhost:11434` by default. To reach Ollama
elsewhere, set `OLLAMA_BASE_URL` (Cortex reads this first) or `OLLAMA_HOST`
(Iris's fallback):

```bash
export OLLAMA_BASE_URL=http://gpu-box:11434
```

### Config Validation

Cortex validates the configuration at startup and refuses to start on bad
values, reporting every violation at once with its config key:

```
invalid config:
  - storage.database_url is required when storage.backend is "pgvector"
  - embedding.dimensions must be > 0 (got 0)
  - entity.extraction_backoff "linear" is not supported (supported: "fixed", "exponential")
```

This replaces late, opaque runtime failures — a non-positive
`retention.gc_interval`, for example, used to panic the background ticker
after startup.

### Timeouts

Provider calls are bounded so a hung upstream fails fast instead of blocking
indefinitely:

| Option | Default | Bounds |
|--------|---------|--------|
| `embedding.timeout` | `120s` | Embedding provider calls |
| `summarization.timeout` | `120s` | Conversation summarization calls |
| `entity.extraction_timeout` | `120s` | Entity extraction calls |
| `server.shutdown_timeout` | `30s` | Graceful drain of background workers |

Setting any of the provider timeouts to `0` disables it (unbounded). A timeout
is only applied when the caller supplied no deadline of its own.

### PostgreSQL Setup

For production deployments:

```yaml
storage:
  backend: pgvector
  database_url: postgres://user:pass@localhost:5432/cortex

  # Optional explicit pool sizing. 0 (the default) defers to the
  # pool_max_conns/pool_min_conns query params on database_url, then to
  # pgx's built-in defaults. Explicit values take precedence over the URL.
  pool_max_conns: 0
  pool_min_conns: 0
```

Ensure pgvector extension is installed:

```sql
CREATE EXTENSION IF NOT EXISTS vector;
```

**Embedding dimensions.** The pgvector schema is generated from
`embedding.dimensions`, so a non-1536 model works without patching DDL. The
dimension is baked into the `vector(N)` columns when the schema is first
created. If you later change `embedding.dimensions` against an existing
database, Cortex fails at startup rather than writing ragged vectors:

```
existing embedding columns are vector(1536) but embedding.dimensions is 768;
run an ALTER TABLE ... ALTER COLUMN embedding TYPE vector(768) migration
or start with a fresh database
```

Changing dimensions requires re-embedding your data — the stored vectors are
not convertible.

**Migrations.** Both backends run versioned migrations at startup and record
the applied version in `cortex_metadata.schema_version`. Upgrades apply only
the pending migrations; no manual DDL step is needed.

## MCP Tools

Cortex exposes 19 MCP tools across the four memory primitives:

### Conversation

| Tool | Description |
|------|-------------|
| `conversation_append` | Add a message to a conversation thread |
| `conversation_history` | Retrieve conversation history |
| `conversation_search` | Semantic search across messages |
| `conversation_summarize` | Summarize and compress history |

### Knowledge

| Tool | Description |
|------|-------------|
| `knowledge_ingest` | Ingest a document with chunking |
| `knowledge_bulk_ingest` | Batch ingest multiple documents |
| `knowledge_search` | Hybrid search (vector + full-text) |
| `knowledge_collections` | Create, list, delete collections |

### Context

| Tool | Description |
|------|-------------|
| `context_get` | Retrieve a value by key |
| `context_set` | Store a value with optional TTL |
| `context_merge` | Merge values with strategy |
| `context_list` | List keys with prefix filter |
| `context_history` | View version history for a key |

### Entity

| Tool | Description |
|------|-------------|
| `entity_query` | Look up entity by name or alias |
| `entity_search` | Semantic search across entities |
| `entity_relationships` | Get entity relationships |
| `entity_update` | Modify entity attributes |
| `entity_merge` | Combine duplicate entities |
| `entity_list` | List entities with filters |

Entity types are `person`, `organization`, `product`, `location`, `concept`,
`event`, and `other`. Any other value is rejected — an unrecognized type used
to be silently remapped to an unrelated one.

### Response Contract

Every tool response is a declared, versioned JSON shape. Each top-level
response carries `schema_version`, so clients can feature-detect contract
changes instead of guessing:

```json
{
  "schema_version": 1,
  "results": [
    {
      "chunk": {
        "id": "chunk-1",
        "document_id": "doc-1",
        "namespace": "ns",
        "collection_id": "col-1",
        "content": "chunk content",
        "index": 0,
        "token_count": 2
      },
      "score": 0.91,
      "rank": 1,
      "document_title": "Doc",
      "source": "test://src"
    }
  ],
  "query": "chunk",
  "total_found": 1
}
```

The contract guarantees:

- **`schema_version`** is bumped only on a breaking change to a response shape.
- **Empty collections serialize as `[]` and `{}`**, never `null`, so clients can
  iterate without a nil check.
- **Embedding vectors are stripped from search results.** They were a large,
  unusable payload for MCP clients; use the Go API if you need raw vectors.
- **Zero-value timestamps are omitted** rather than rendered as the misleading
  `0001-01-01T00:00:00Z`.

### Argument Handling

Tool arguments are validated rather than silently defaulted:

- An argument that is **present but of the wrong type** is rejected with a
  message naming the parameter and the type it got — e.g.
  `parameter "limit" must be a number, got string`. Previously a wrong-typed
  argument fell back to the default, so a client bug looked like a working call.
- **Metadata, filters, and attributes are string-valued.** Non-string values are
  rejected with an actionable message —
  `parameter "metadata" values must be strings; key "count" has a number value (send "3" as a string)`.
  Cortex used to stringify them silently, which made filter matching ambiguous.
- **Large integers keep their precision** through the JSON argument path;
  a value that would be corrupted by float64 decoding is rejected instead.

### Errors

Tool errors are classified before they reach the client. Known client-facing
conditions (not found, invalid input, version conflict, batch too large) return
their own message. Anything else returns `internal server error` and is logged
server-side with full detail, so SQL fragments, connection URLs, and other
internals never leak to MCP clients.

## Architecture

```
┌─────────────────────────────────────────────────────────┐
│                    MCP Clients                          │
│           (AI Agents, IDEs, Tools)                      │
└─────────────────────┬───────────────────────────────────┘
                      │ MCP (stdio/SSE)
                      ▼
┌─────────────────────────────────────────────────────────┐
│                     Cortex                              │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐   │
│  │Conversation │ │ Knowledge   │ │ Workflow Context│   │
│  │   Memory    │ │   Store     │ │                 │   │
│  └──────┬──────┘ └──────┬──────┘ └────────┬────────┘   │
│         │               │                  │            │
│  ┌──────┴───────────────┴──────────────────┴──────┐    │
│  │              Entity Memory                      │    │
│  │         (Auto-extracted Knowledge Graph)        │    │
│  └─────────────────────┬───────────────────────────┘    │
│                        │                                │
│  ┌─────────────────────┴───────────────────────────┐   │
│  │            Storage Backend                       │   │
│  │      (SQLite+vec0 or PostgreSQL+pgvector)       │   │
│  └─────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────────────┐
│                     Iris                                │
│           (Embedding Generation)                        │
└─────────────────────────────────────────────────────────┘
```

## CLI Reference

### Server

```bash
cortex serve [flags]
  --transport string   Transport mode: stdio or sse (default "stdio")
  --port int           Port for SSE transport (default 9810)
  --namespace string   Restrict to a single namespace
```

### Knowledge Store

```bash
cortex knowledge ingest --collection  --title  --file 
cortex knowledge ingest-dir --collection  --dir  --pattern "*.md"
cortex knowledge search  [--collection ] [--mode hybrid]
cortex knowledge collections [--namespace ]
cortex knowledge create-collection --name  [--description ]
cortex k

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [petal-labs](https://github.com/petal-labs)
- **Source:** [petal-labs/cortex](https://github.com/petal-labs/cortex)
- **License:** MIT
- **Homepage:** https://docs.petallabs.io/cortex/overview/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-petal-labs-cortex
- Seller: https://agentstack.voostack.com/s/petal-labs
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
