Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cortex

Project memory for AI agents. When an agent pushes to git, it registers what it did and why — not the diff, the reasoning. Any other agent on the project reads that context via MCP before touching the same code.

No external model API required. The agent writing the change is the model.

Architecture

Two processes, split by what each can own:

  • core/ — Go (Fiber v3 + pgx). One long-lived daemon per machine. Owns PostgreSQL and the full-text search. Plain HTTP+SQL — it never runs git, never speaks MCP, never calls a model. Auth via static Bearer token.
  • mcp/ — Node (MCP SDK). The per-agent shell, spawned over stdio by Claude Code, one process per conversation. Speaks MCP, runs git locally in the repo (the part Go can't do), forwards tool calls to the Core. Holds no state.

In register_change: the agent writes the semantic fields (what/why/decisions/ risks); the MCP layer adds the mechanical ones from git (sha/branch/files/diff); the Core upserts by (repository_id, sha) and regenerates the FTS vector.

Quickstart

Everything is wired through the Makefile (make lists all targets):

make up      # Postgres + Redis + Ollama (pulls the embed model on first run)
make build   # Core + CLI -> bin/, and the MCP server -> mcp/dist
make dev     # up + run the Core in the foreground on 127.0.0.1:8742

make dev is the one-liner for local work: it brings the stack up and runs the Core with Redis + embeddings enabled. In another terminal:

make smoke   # end-to-end MCP cycle against the running Core
make test    # go vet + integration tests + MCP typecheck

Wire another repo into Cortex with the CLI:

./bin/cortex init --project myproj --token <token>
# writes .cortex.yml and merges .mcp.json without clobbering other MCP servers
Manual equivalent (no make)
docker compose up -d
cd core && CORTEX_REDIS=redis://127.0.0.1:6380 CORTEX_OLLAMA=http://127.0.0.1:11434 go run ./cmd/server
cd mcp  && npm install && npm run build

Multi-tenancy & tokens

Cortex is multi-tenant: an org is the tenant boundary, owns its projects, and issues tokens with a role. One org never sees another's data — even with the same project name.

  • Roles: read (GET search/context/recent/patterns/check), write (+ the POST endpoints), admin (everything; today used only from the CLI).
  • Auth: every API token is an api_keys row; the bearer is sha256-hashed and looked up per request. Revoke a token and it's 401 immediately.
  • Admin is a CLI that talks straight to Postgres via CORTEX_DSN (no HTTP), so bootstrap has no chicken-and-egg:
cortex admin org create acme
cortex admin token create --org acme --role write --label ci-bot
#   -> ctx_live_…   (shown ONCE — store it now)
cortex admin token list  --org acme
cortex admin token revoke <id-prefix>

Wire the issued token into an agent with cortex init --token <ctx_live_…> (it goes into .mcp.json as CORTEX_TOKEN). The token determines the org; the project field of each MCP tool is a name within that org.

In dev, the Core seeds an org cortex + token dev-token on startup when CORTEX_DEV=1 (set by make dev/make core), so local flows just work.

Production

One command brings up the full stack including the Core in Docker (built from core/Dockerfile as a static binary). The Core talks to Postgres/Redis/Ollama by service name; a real token is required.

make prod CORTEX_TOKEN=<your-secret>      # build images, recreate containers, wait healthy
make prod-ps                              # status
make prod-logs                            # tail Core logs
make prod-down                            # stop (data volumes preserved)

make prod recreates containers with fresh images but preserves data volumes (it never wipes the project memory). It refuses to start with the default dev-token. The dev-only Core runs on the host (make dev); the core service is gated behind the compose prod profile so it never starts in dev.

CORTEX_TOKEN in prod is the bootstrap admin secret — there's no dev seed. Once up, mint real per-agent tokens with cortex admin token create (see Multi-tenancy & tokens) rather than sharing the admin token.

Transports

  • stdio (default): node mcp/dist/index.js — spawned per-agent by Claude Code.
  • Streamable HTTP (remote / CI): npm run start:http (port CORTEX_MCP_HTTP_PORT, default 9000, at /mcp; optional CORTEX_MCP_TOKEN bearer gate). SSE is not used — it was removed server-side from the MCP SDK.

Language convention: English

All semantic fields (what/why/decisions/risks, knowledge, features, ADRs) are stored in English, regardless of the conversation language. Cortex is a tool for agents, so a single language keeps the FTS (and any future embedding model) on one English-tuned index — mixing languages degrades search. This is enforced where the agent actually reads it: the MCP tool schema descriptions instruct "write in English". (A Claude Code hook can't force a tool call, so the schema is the right enforcement point, not a hook.)

Multi-agent notifications

When CORTEX_REDIS is set, Core publishes a JSON ChangeEvent to cortex:{project}:changes after every registered change, so other agents/services on the project can react. No Redis configured → publishing is a no-op.

Hybrid search (embeddings)

Search is FTS by default. When CORTEX_OLLAMA is set (e.g. http://127.0.0.1:11434), Core also embeds each change/knowledge entry with a local model (nomic-embed-text, 768-dim, via Ollama — no external API) and fuses keyword + vector rankings with Reciprocal Rank Fusion. This surfaces relevant prior work even when the query shares no keywords with it. docker compose up provisions Ollama and pulls the model automatically; embeddings are generated off the write path and search degrades to FTS if Ollama is down.

CORTEX_OLLAMA=http://127.0.0.1:11434 CORTEX_REDIS=redis://127.0.0.1:6380 \
  go run ./core/cmd/server

Claude Code skills

.claude/skills/ ships two skills so agents use Cortex without being told:

  • cortex-recall — consult context (get_project_context / search_knowledge / check_feature_exists) before starting work.
  • cortex-log — record what & why (register_change / add_decision / upsert_feature / add_knowledge) after committing.

Tools, resources & prompts exposed to the agent

Write: register_change, add_knowledge, add_decision, upsert_feature. Read: search_knowledge (commits + knowledge), get_recent_changes, get_project_context, get_patterns, check_feature_exists.

Resource: cortex://context/{project} (mountable project summary). Prompts: analyze-impact, whats-left-for-feature.

Smoke tests of the full cycle live in mcp/scripts/e2e*.mjs. The Go store has an integration test gated on CORTEX_TEST_DSN:

CORTEX_TEST_DSN="postgres://cortex:cortex@127.0.0.1:5433/cortex?sslmode=disable" \
  go test ./core/internal/store -run TestStore -v

About

Project memory server for AI agents. Agents write semantic context on git push; any agent on the team reads it via MCP. Go API core + Node.js MCP server. No external model required.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages