{
  "markdown": "<!-- mcp-name: io.github.synpulse8-opensource/pulse8-ai-cortex-knowledge-vault -->\n\n<p align=\"center\">\n  <img src=\"assets/pulse8-banner.png\" alt=\"PULSE8.ai\" width=\"600\" />\n</p>\n\n<h1 align=\"center\">PULSE8.ai Cortex</h1>\n\n<p align=\"center\">\n  <strong>The open-source knowledge layer for AI agents</strong>\n  <br />\n  <em>Knowledge that compounds.</em>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/synpulse8-opensource/pulse8-ai-cortex-knowledge-vault/actions/workflows/pylint.yml\"><img src=\"https://github.com/synpulse8-opensource/pulse8-ai-cortex-knowledge-vault/actions/workflows/pylint.yml/badge.svg\" alt=\"Build\"></a>\n  <a href=\"https://github.com/synpulse8-opensource/pulse8-ai-cortex-knowledge-vault/releases/latest\"><img src=\"https://img.shields.io/github/v/release/synpulse8-opensource/pulse8-ai-cortex-knowledge-vault\" alt=\"Release\"></a>\n  <a href=\"LICENSE.md\"><img src=\"https://img.shields.io/badge/license-Apache%202.0-blue\" alt=\"License\"></a>\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/python-3.12+-3776AB?logo=python&logoColor=white\" alt=\"Python\">\n  <img src=\"https://img.shields.io/badge/FastAPI-009688?logo=fastapi&logoColor=white\" alt=\"FastAPI\">\n  <img src=\"https://img.shields.io/badge/MCP-Model%20Context%20Protocol-blueviolet\" alt=\"MCP\">\n  <img src=\"https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=white\" alt=\"Docker\">\n  <img src=\"https://img.shields.io/badge/NetworkX-graph%20engine-orange\" alt=\"NetworkX\">\n</p>\n\n\nPULSE8.ai Cortex is the **open-source knowledge layer for AI agents**: Git-native memory, a typed knowledge graph, and [MCP](https://modelcontextprotocol.io/)-powered retrieval on top of plain Markdown — so agents can build, evolve, and reuse persistent knowledge instead of re-deriving it on every query.\n\nUnder the hood it's a unified vault for AI agents and humans, backed by a typed knowledge graph, full-text + hybrid search, and a [MarkItDown](https://github.com/microsoft/markitdown)-powered file compiler. Drop files in (PDF, DOCX, PPTX, XLSX, HTML, images, and more), let agents read, write, search, link, and compile knowledge — no database required.\n\n> Inspired by [Andrej Karpathy](https://github.com/karpathy)'s [LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) pattern — a persistent, compounding knowledge base maintained by LLMs instead of re-derived on every query. Search powered by [Tobi Lütke](https://github.com/tobi)'s [QMD](https://github.com/tobi/qmd).\n\n---\n\n## Why PULSE8.ai Cortex?\n\nMost AI agents can access tools, but they cannot accumulate knowledge.\n\nTraditional RAG systems retrieve documents. PULSE8.ai Cortex builds a **persistent, evolving knowledge layer** that grows over time and becomes more valuable the more agents and humans interact with it.\n\nWith PULSE8.ai Cortex, agents can:\n\n- **Learn from interactions** — every read, write, ingest, and compile event is logged and graph-linked\n- **Build and traverse knowledge graphs** — wikilinks, tags, and typed edges, maintained automatically\n- **Store structured insights** — Markdown notes with typed nodes (`note`, `agent_def`, `session`, `daily`, `feedback`)\n- **Retrieve context across projects** — `vault_context` builds a ranked subgraph from any seed query\n- **Share knowledge through MCP** — one vault, every MCP-compatible client\n- **Maintain long-term memory** — files survive sessions, deployments, and model upgrades\n- **Version knowledge through Git** — the vault is a plain directory of Markdown, diff-friendly out of the box\n\n| Aspect | Traditional RAG | PULSE8.ai Cortex |\n| ------ | --------------- | ---------------- |\n| Focus | Documents | Knowledge |\n| Memory | Session-based | Persistent |\n| Structure | Chunks | Markdown + typed graph |\n| Evolution | Static index | Continuous, file-watched |\n| Versioning | None | Git-native |\n| Agent collaboration | Limited | First-class (MCP) |\n\n\n\n## When to use PULSE8.ai Cortex\n\n### Ideal use cases\n\n- ✅ Persistent memory for AI agents\n- ✅ Shared knowledge across multiple agents\n- ✅ Git-versioned organisational knowledge\n- ✅ MCP-compatible knowledge retrieval\n- ✅ Knowledge graphs without a dedicated graph database\n- ✅ Long-term accumulation of institutional knowledge\n- ✅ Human + AI collaborative knowledge management\n\n### Not ideal for\n\n- ❌ Simple full-text document search (use a search engine)\n- ❌ Pure vector-only retrieval with no graph (use a vector DB)\n- ❌ Short-lived, stateless conversations\n- ❌ Workflows that don't need persistent knowledge evolution\n\n\n\n## PULSE8.ai Cortex vs alternatives\n\n\n| Capability                     | PULSE8.ai Cortex | Traditional RAG | GraphRAG |\n| ------------------------------ | :--------------: | :-------------: | :------: |\n| Persistent knowledge           |        ✅        |       ❌        |    ⚠️    |\n| Markdown-native storage        |        ✅        |       ❌        |    ❌    |\n| MCP-compatible out of the box  |        ✅        |       ❌        |    ❌    |\n| Knowledge graph                |        ✅        |       ❌        |    ✅    |\n| Git versioning                 |        ✅        |       ❌        |    ❌    |\n| Agent memory layer             |        ✅        |       ❌        |    ⚠️    |\n| Human + AI collaboration       |        ✅        |       ❌        |    ⚠️    |\n| Continuous knowledge evolution |        ✅        |       ❌        |    ⚠️    |\n| Zero database required         |        ✅        |       ❌        |    ❌    |\n\n\n\n## Works with\n\nPULSE8.ai Cortex speaks [MCP](https://modelcontextprotocol.io/) — so it plugs into any AI client that does. The same vault is reachable over streamable HTTP or stdio, and mirrored 1:1 by a REST API at `/api/v1/`.\n\n\n| Category              | Compatible with                                                                |\n| --------------------- | ------------------------------------------------------------------------------ |\n| **AI agents**         | Claude Desktop, Claude Code, OpenAI Agents, Gemini, custom agent frameworks    |\n| **Development tools** | Cursor, VS Code, JetBrains IDEs                                                |\n| **Agent frameworks**  | LangGraph, LangChain, CrewAI, AutoGen                                          |\n| **MCP ecosystem**     | MCP clients, MCP servers, MCP tool registries                                  |\n| **Human tools**       | Obsidian, any Markdown editor, any Git client                                  |\n\n\nBecause the vault is just files, humans and agents collaborate on the same knowledge — no proprietary format, no lock-in.\n\n\n\n## Get started\n\n> [!NOTE]\n> PULSE8.ai Cortex requires Docker. An [OpenRouter API key](https://openrouter.ai/keys) is optional — needed only for LLM-powered cross-referencing between wiki articles. File conversion works out of the box without any API key.\n\n1. Clone the repository:\n  ```bash\n    git clone https://github.com/synpulse8-opensource/pulse8-ai-cortex-knowledge-vault.git\n    cd cortex-knowledge-vault\n  ```\n2. Launch PULSE8.ai Cortex:\n  ```bash\n    ./scripts/start.sh\n  ```\n    This builds and starts both **PULSE8.ai Cortex** (API + MCP on `:8420`) and **QMD** (search on `:3100`), waits for health checks, and you're ready to go.\n3. Connect your MCP client (e.g. Claude Desktop) to `http://localhost:8420/mcp/`.\n\nTo stop: `./scripts/stop.sh`\n\n### Native QMD mode (macOS / Metal GPU)\n\nDocker Desktop on macOS cannot expose the Metal GPU to containers, so containerized QMD embeds on CPU only — over an order of magnitude slower on non-trivial vaults. Run QMD natively instead; the `qmd` binary uses Metal automatically:\n\n```bash\n# One-time: install the qmd binary\nbrew install tobi/tap/qmd   # or: npm install -g @tobilu/qmd\n\n# Start native QMD (background daemon) + Cortex in Docker\n./scripts/start.sh --native-qmd\n```\n\nThe QMD daemon's pid and log are kept in `.qmd-native.pid` / `.qmd-native.log`. To stop both: `./scripts/stop.sh --native-qmd` (a plain `./scripts/stop.sh` also cleans up a native QMD if one is running).\n\n### Cortex-only mode (external QMD)\n\nIf you manage QMD yourself (already running elsewhere), start only the Cortex container:\n\n```bash\n./scripts/start.sh --cortex-only   # set QMD_URL in .env if not http://host.docker.internal:3100\n```\n\nTo stop: `./scripts/stop.sh --cortex-only`\n\n### GPU-accelerated QMD (EC2 / Linux with NVIDIA GPU)\n\nFor production deployments with NVIDIA GPU acceleration:\n\n```bash\ndocker compose -f docker-compose.yml -f docker-compose.gpu.yml up --build -d\n```\n\nSee [docs/ec2-gpu-setup.md](docs/ec2-gpu-setup.md) for a full guide on instance selection, NVIDIA toolkit installation, and cost estimates.\n\n\n\n## Features\n\n\n|                              |                                                                                                                                                                              |\n| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Knowledge Graph**          | Typed graph engine (NetworkX) — wikilinks, tags, and custom edges, auto-maintained on every file change                                                                      |\n| **Full-Text Search**         | QMD search with hybrid (BM25 + vector + re-ranking) by default; keyword and semantic modes selectable. Results cached with a configurable TTL.                               |\n| **File Compiler**            | Converts raw sources (PDF, DOCX, PPTX, XLSX, HTML, images, etc.) to Markdown via [MarkItDown](https://github.com/microsoft/markitdown). LLM used only for cross-referencing. |\n| **MCP Server**               | Streamable HTTP + stdio transport — works with Claude Desktop, Cursor, and any MCP client                                                                                    |\n| **Feedback & Notifications** | `vault_feedback` captures quality feedback as notes; optional Microsoft Teams webhook posts an adaptive card per submission                                                  |\n| **Daily Activity Log**       | Every write/ingest/compile is mirrored into `daily/<date>.md` as a greppable, wikilinked timeline                                                                            |\n| **Bulk Ingest**              | Ingest dozens or hundreds of files at once from a local directory with SHA-256 dedup and bounded concurrency                                                                 |\n| **REST API**                 | FastAPI endpoints mirroring all MCP tools at `/api/v1/`, including multipart file upload and bulk ingest                                                                     |\n| **Vault Watcher**            | Real-time filesystem monitoring — graph stays in sync automatically                                                                                                          |\n| **Lineage & Audit**          | Every edge labeled `extracted` / `inferred` / `manual`; `vault_trace` answers \"why does the vault say X\" back to the source document                                         |\n| **Graph Queries**            | `vault_path` (what connects X to Y), `vault_impact` (what's downstream of this note), `vault_explain` (entity summary with provenance)                                       |\n| **Curation Report**          | Read counters + outcome feedback (`useful` / `dead-end` / `corrected`) surface stale, contradicted, and never-read notes at `GET /api/v1/curation/report`                    |\n| **Zero Database**            | Everything persists as Markdown + JSON on your filesystem                                                                                                                    |\n\n## Benchmarks\n\n**45.0% overall accuracy on [LongMemEval-S](https://github.com/xiaowu0162/LongMemEval)** (500 questions, full haystacks, hybrid search) with 65.6% evidence recall@8 and zero judge errors — measured end-to-end through the public REST API: ingest → compile → graph → search → answer.\n\n| Category | Accuracy | Recall@8 |\n|---|---|---|\n| single-session-assistant | **96.4%** | 98.2% |\n| single-session-user | **71.4%** | 78.6% |\n| knowledge-update | **60.3%** | 75.6% |\n| temporal-reasoning | 25.6% | 51.1% |\n| multi-session | 24.8% | 54.1% |\n| single-session-preference | 23.3% | 63.3% |\n\nEvery number is reproducible from a pinned config (dataset SHA-256, models, seed) with one command:\n\n```bash\nuv run python -m evals.run_longmemeval --config evals/configs/longmemeval-s-hybrid.yaml\n```\n\nThe harness ([`evals/`](evals/)) publishes per-question JSONL traces, separates the judge model from the answer model, uses the official LongMemEval per-type grading prompts, and includes blind human validation of the judge. Full methodology, caveats, and raw results: [docs/benchmarks/](docs/benchmarks/README.md).\n\n## Runs without an LLM\n\nCortex is deterministic-first: ingestion (MarkItDown conversion), the knowledge graph (wikilinks, tags, `derived_from` edges), and QMD search all work with **zero LLM calls**. The LLM is an optional enrichment pass — cross-referencing, tagging, image captioning — not a dependency.\n\nPick a backend with `LLM_BACKEND` (env) / `CORTEX_LLM_BACKEND` (Python):\n\n| Backend | What it covers |\n|---------|----------------|\n| `openai-compatible` (default) | OpenRouter, Azure OpenAI, Ollama, vLLM, LM Studio — anything speaking the OpenAI protocol. Point `LLM_BASE_URL` at your endpoint. |\n| `bedrock` | AWS Bedrock via the standard AWS credential chain (no API key). Requires `boto3`. |\n| `none` | Explicit zero-LLM mode. Guaranteed to construct no LLM client and make no model calls — suitable for air-gapped deployments. |\n\nAir-gapped example with a local Ollama:\n\n```bash\nLLM_BACKEND=openai-compatible \\\nLLM_BASE_URL=http://localhost:11434/v1 \\\nLLM_API_KEY=ollama \\\nCOMPILER_MODEL=llama3.1 \\\n./scripts/start.sh\n```\n\nOr fully deterministic: `LLM_BACKEND=none ./scripts/start.sh` (no API key needed).\n\n## MCP resources (token-light large payloads)\n\nPULSE8.ai Cortex implements the [resources-as-tool-inputs pattern](https://microsoft.github.io/mcscatblog/posts/mcp-resources-as-tool-inputs/) recommended by the Microsoft Copilot Studio CAT team: token-heavy tool outputs (large search result sets, full context windows) can be kept server-side and passed between tools as lightweight handles, so the LLM context window stays small.\n\n**How it works.** Pass `as_resource: true` to `vault_search` or `vault_context` (or `?as_resource=true` on `GET /api/v1/search`). Instead of inlining the full payload, you get a handle:\n\n```json\n{\n  \"resource_id\": \"7f8a3c...\",\n  \"resource_uri\": \"cortex://resource/7f8a3c...\",\n  \"summary\": { \"query\": \"...\", \"count\": 8, \"paths\": [\"...\"] }\n}\n```\n\nRead it back through any of the three transports:\n\n- **MCP resources protocol** — `resources/read` with the `cortex://resource/{id}` URI (Claude Desktop, Cursor, Copilot Studio MCP).\n- **Fallback tool** — `vault_resource_read` for clients that only expose tools to the planning layer (some Copilot Studio configurations).\n- **REST** — `GET /api/v1/resources/{resource_id}` (accepts the bare ID or the full URI).\n\nThe store is in-memory, asyncio-safe, TTL-evicted, and LRU-bounded:\n\n| Env var | Default | Purpose |\n|---------|---------|---------|\n| `CORTEX_RESOURCE_TTL_SECONDS` | `3600` | Max age before a stored resource is evicted lazily on read |\n| `CORTEX_RESOURCE_MAX_ITEMS` | `1000` | LRU cap before oldest entry is dropped |\n\nThe same `ResourceStore` is shared between MCP and REST — produce a handle via MCP, read it back via REST (or vice versa).\n\nMicrosoft Copilot Studio setup — agent instructions, tool selection, and the Custom Connector fallback — is documented in [docs/copilot-studio.md](docs/copilot-studio.md). No Cortex code change required.\n\n\n\n## MCP tools\n\n\n| Tool                   | Description                                                                                |\n| ---------------------- | ------------------------------------------------------------------------------------------ |\n| `vault_read`           | Read a note by path                                                                        |\n| `vault_write`          | Create or update a note                                                                    |\n| `vault_search`         | Search the vault (keyword / semantic / hybrid). Supports `as_resource=true`                |\n| `vault_link`           | Create, query, or delete graph edges                                                       |\n| `vault_context`        | Build a context window: search → graph traversal → ranked subgraph. Supports `as_resource=true` |\n| `vault_ingest`         | Ingest raw content or binary files (supports `content_base64` for binary)                  |\n| `vault_compile`        | Compile unprocessed raw sources into wiki Markdown via MarkItDown                          |\n| `vault_feedback`       | Submit feedback on vault quality (`status: OPEN`; optional `related_paths` and `outcome`: useful / dead-end / corrected) |\n| `vault_list_feedbacks` | List feedback note metadata (paths, tags, status; not full body)                           |\n| `vault_resource_read`  | Read a server-stored MCP resource by ID (fallback for clients without `resources/read`)    |\n| `vault_trace`          | Trace a note's lineage: provenance, raw sources, and edges labeled extracted / inferred / manual |\n| `vault_path`           | Shortest paths between two notes — \"what connects X to Y\", every hop typed and origin-labeled |\n| `vault_impact`         | Walk everything downstream of a note (change-impact analysis)                              |\n| `vault_explain`        | Explain a note: summary, provenance, sources, links in/out, contradictions                 |\n\n\n\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────┐\n│  MCP Client (Claude Desktop, Cursor, etc.)   │\n└──────────┬───────────────────────────────────┘\n           │  MCP (HTTP or stdio)\n┌──────────▼───────────────────────────────────┐\n│  PULSE8.ai Cortex  :8420                     │\n│  ┌──────────────────────────────────────┐     │\n│  │ Auth (API Key or Microsoft Entra ID) │     │\n│  └──────────────┬───────────────────────┘     │\n│  ┌─────────┐ ┌──┴───────┐ ┌──────────────┐   │\n│  │ MCP     │ │ REST API │ │ Vault Watcher│   │\n│  │ /mcp/   │ │ /api/v1/ │ │ (watchfiles) │   │\n│  └────┬────┘ └────┬─────┘ └──────┬───────┘   │\n│       └───────────┼──────────────┘           │\n│            ┌──────▼──────┐                   │\n│            │ Graph Engine│                   │\n│            │ + Compiler  │                   │\n│            └─────────────┘                   │\n└──────────┬───────────────────────────────────┘\n           │\n┌──────────▼───────────────────────────────────┐\n│  QMD  :3100                                  │\n│  BM25 + vector search, auto-indexes on start │\n└──────────┬───────────────────────────────────┘\n           │\n┌──────────▼───────────────────────────────────┐\n│  Vault (bind-mounted volume)                 │\n│  wiki/ raw/ agents/ sessions/ daily/ feedback/ │\n│  .cortex/ (graph.json, index.md, log.md)     │\n└──────────────────────────────────────────────┘\n```\n\n\n\n## Vault layout\n\nThe vault is a plain directory of Markdown files organised by purpose. Cortex classifies each file into a typed node (`NodeType`) used by the graph engine and exposed in REST and MCP responses.\n\n\n| Folder      | `NodeType`   | Purpose                                                          |\n| ----------- | ------------ | ---------------------------------------------------------------- |\n| `wiki/`     | `note`       | Compiled, interlinked knowledge articles                         |\n| `raw/`      | `raw_source` | Unprocessed sources (PDF, DOCX, TXT, …) the compiler reads from  |\n| `agents/`   | `agent_def`  | Agent definitions                                                |\n| `sessions/` | `session`    | Per-session notes / conversation transcripts                     |\n| `daily/`    | `daily`      | Daily notes (Obsidian Daily Notes convention)                    |\n| `feedback/` | `feedback`   | Feedback on vault quality (`status`, `related_paths`)            |\n| `.cortex/`  | *(skipped)*  | Cortex internals — `graph.json`, `index.md`, `log.md`, manifests |\n\n\n### How classification works\n\nOrder of precedence (first match wins):\n\n1. **Frontmatter `type:*`* — explicit override always wins (e.g. `type: note` in `agents/foo.md` resolves to `NodeType.NOTE`)\n2. **Folder prefix** — files under `raw/ agents/ sessions/ daily/ feedback/` inherit the folder's type with no filename suffix needed (e.g. `daily/2026-06-10.md` → `daily`)\n3. **Filename suffix** (backward-compatible) — `.agent.md`, `.session.md`, `.memory.md` are still honored anywhere (e.g. `wiki/legacy.agent.md` → `agent_def`)\n4. **Default** — `NodeType.NOTE`\n\nIn practice this means you can drop `YYYY-MM-DD.md` straight into `daily/`, or an unsuffixed `planner.md` into `agents/`, and the graph and API will classify them correctly without any renaming.\n\n### Daily activity log\n\nEvery `vault_write`, `vault_ingest`, and successful `compile` event (MCP **and** REST paths) is automatically mirrored into today's UTC daily note at `daily/YYYY-MM-DD.md`. The file is created on first event of the day and each subsequent event appends a `## [HH:MM] event | summary` block plus a `[[wiki-stem]]` wikilink (so the watcher draws a `LINKS_TO` edge to the affected note). The format follows the Karpathy log.md greppable-prefix pattern — `grep \"^## \\[\" daily/2026-06-10.md` gives a clean timeline of the day.\n\nWrites targeting `daily/`, `feedback/`, or `.cortex/` are deliberately **not** mirrored (would be self-referential noise). The hidden `.cortex/log.md` audit log is unaffected and continues to receive every operation.\n\n\n\n## Bulk ingest\n\nFor ingesting many files at once (dozens or hundreds of PDFs, papers, docs), use the one-click shell script instead of feeding them one at a time through MCP. It reads directly from a local directory (recursively, including subfolders) — no wire overhead, no running server required — deduplicates via SHA-256 hashing, compiles with bounded concurrency, and rebuilds the index once at the end. Subpaths are preserved under the vault raw folder (e.g. `source/abcde/doc.html` → `raw/abcde/doc.html`).\n\n### One-click script (recommended)\n\n```bash\n# Ingest all files from a directory\n./scripts/bulk_ingest.sh ./my-papers/\n\n# Dry-run to preview what would be ingested\n./scripts/bulk_ingest.sh ./my-papers/ --dry-run\n\n# Force re-ingest (bypass dedup manifest)\n./scripts/bulk_ingest.sh ./my-papers/ --force\n\n# Control LLM concurrency (default: 4)\n./scripts/bulk_ingest.sh ./my-papers/ --concurrency 8\n```\n\nThe script automatically loads your `.env` for the LLM key and vault path, prints a summary, then runs the full pipeline (copy, compile, reindex). No running Cortex server needed.\n\n### Python CLI (direct)\n\n```bash\nCORTEX_VAULT_PATH=./example_vault uv run cortex-bulk-ingest --source ./my-papers/\n```\n\n### Inside Docker\n\n```bash\n# Set INGEST_DIR in .env or export it, then restart\nexport INGEST_DIR=/path/to/your/papers\ndocker compose up -d\n\n# Run bulk ingest inside the container\ndocker exec pulse8-ai-cortex uv run cortex-bulk-ingest --source /ingest\n```\n\n### Via REST API\n\nFor programmatic use without MCP (requires running Cortex server):\n\n```bash\ncurl -X POST http://localhost:8420/api/v1/bulk-ingest \\\n  -H \"Content-Type: application/json\" \\\n  -H \"x-api-key: your-secret-api-key\" \\\n  -d '{\"source_dir\": \"/ingest\", \"concurrency\": 4}'\n```\n\n### Deduplication\n\nThe dedup manifest is stored at `.cortex/ingest-manifest.json`. Files are matched by content hash, not filename — renaming a file won't cause re-ingestion, and the same content under a different name will be skipped.\n\n\n\n## Configuration\n\nCopy the example and fill in your values:\n\n```bash\ncp .env.example .env\n```\n\n\n| Variable                       | Required | Default                        | Description                                                                                        |\n| ------------------------------ | -------- | ------------------------------ | -------------------------------------------------------------------------------------------------- |\n| `LLM_BACKEND`                  | No       | `openai-compatible`            | LLM backend: `openai-compatible`, `bedrock` (AWS credential chain), or `none` (zero LLM calls)     |\n| `LLM_API_KEY`                  | No       | —                              | OpenRouter (or compatible) API key (for cross-referencing only)                                    |\n| `COMPILER_MODEL`               | No       | `anthropic/claude-sonnet-4`    | Model for cross-reference detection                                                                |\n| `LLM_BASE_URL`                 | No       | `https://openrouter.ai/api/v1` | LLM API base URL                                                                                   |\n| `VAULT_DIR`                    | No       | `./example_vault`              | Path to your vault directory                                                                       |\n| `INGEST_DIR`                   | No       | `./ingest`                     | Path to bulk-ingest source directory (mounted as `/ingest` in Docker)                              |\n| `QMD_REFRESH_INTERVAL_SECONDS` | No       | `900`                          | Periodic re-index interval (seconds; `0` to disable)                                               |\n| `QMD_SEARCH_MODE`              | No       | `hybrid`                       | Default search mode when unspecified: `hybrid` (BM25 + vector + re-rank), `semantic`, or `keyword` |\n| `QMD_CACHE_TTL_SECONDS`        | No       | `30`                           | TTL for the search-result cache; raise it on read-heavy vaults to skip repeat QMD calls            |\n| `QMD_SEARCH_TIMEOUT_SECONDS`   | No       | `120`                          | Per-request search timeout (increase for hybrid on CPU-only hosts)                                 |\n| `QMD_EMBED_TIMEOUT_MS`         | No       | `600000`                       | Embed timeout in ms (increase for CPU-only deployments)                                            |\n| `QMD_URL`                      | No       | —                              | External QMD URL for cortex-only mode (e.g. `http://host.docker.internal:3100`)                    |\n| `AUTH_METHOD`                  | No       | `none`                         | Authentication method: `none`, `apikey`, or `oidc` (see [Authentication](#authentication))         |\n| `API_KEY`                      | No       | —                              | Static API key for `x-api-key` header (used when `AUTH_METHOD=apikey`)                             |\n| `OIDC_TENANT_ID`               | No       | —                              | Microsoft Entra ID tenant ID (used when `AUTH_METHOD=oidc`)                                        |\n| `OIDC_CLIENT_ID`               | No       | —                              | Microsoft Entra ID app (client) ID                                                                 |\n| `OIDC_CLIENT_SECRET`           | No       | —                              | Microsoft Entra ID client secret                                                                   |\n| `OIDC_BASE_URL`                | No       | `http://localhost:8420`        | Public base URL of the Cortex server (used for OAuth callbacks)                                    |\n| `TEAMS_WEBHOOK_URL`            | No       | —                              | Incoming webhook / Power Automate URL; posts an adaptive card on each new feedback note            |\n| `TEAMS_APP_BASE_URL`           | No       | —                              | Optional public Cortex base URL for a \"View in Cortex\" link on the Teams card                      |\n\n\n`OPENROUTER_API_KEY` and `CORTEX_LLM_API_KEY` are accepted as aliases for `LLM_API_KEY`. Variables above are set in `.env` (Docker reads them via Compose) and map to the `CORTEX_*` settings used by the app.\n\n\n\n## Authentication\n\nCortex supports two authentication methods that protect both the REST API (`/api/v1/`) and the MCP endpoint (`/mcp/`). Set `AUTH_METHOD` in `.env` to choose:\n\n\n| `AUTH_METHOD` | Description                                                   |\n| ------------- | ------------------------------------------------------------- |\n| `none`        | Default. All endpoints are open — no authentication required. |\n| `apikey`      | Static API key. Clients pass `x-api-key` header.              |\n| `oidc`        | Microsoft Entra ID (Azure AD) with OAuth 2.0 + MFA support.   |\n\n\n### API Key (`AUTH_METHOD=apikey`)\n\nThe simplest option. Set the method and key in `.env`:\n\n```\nAUTH_METHOD=apikey\nAPI_KEY=your-secret-api-key\n```\n\nClients pass it via the `x-api-key` header:\n\n```bash\n# REST API\ncurl http://localhost:8420/api/v1/health \\\n  -H \"x-api-key: your-secret-api-key\"\n\n# MCP (via curl)\ncurl -X POST http://localhost:8420/mcp/ \\\n  -H \"x-api-key: your-secret-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{...}}'\n```\n\nNo OAuth discovery endpoints are served — no login popups. Requests without a valid key receive a `401`.\n\n### Microsoft Entra ID (`AUTH_METHOD=oidc`)\n\nFor enterprise environments that require interactive login with MFA support:\n\n```\nAUTH_METHOD=oidc\nOIDC_TENANT_ID=your-tenant-id\nOIDC_CLIENT_ID=your-client-id\nOIDC_CLIENT_SECRET=your-client-secret\nOIDC_BASE_URL=http://localhost:8420\n```\n\nThis enables:\n\n- **REST API**: OAuth 2.0 Authorization Code Flow via `GET /api/v1/login`. After login, pass the access token as `Authorization: Bearer <token>`. A valid `x-api-key` header is also accepted as a fallback when `API_KEY` is set.\n- **MCP endpoint**: FastMCP's built-in OIDCProxy handles interactive browser-based login.\n\n### Azure AD app registration\n\nTo use OIDC, register an app in the [Azure Portal](https://portal.azure.com):\n\n1. Go to **Azure Active Directory → App registrations → New registration**\n2. Set the redirect URI to `http://localhost:8420/api/v1/auth/callback` (Web platform)\n3. Under **Certificates & secrets**, create a client secret\n4. Under **API permissions**, add `openid`, `profile`, and `email` (Microsoft Graph → Delegated)\n5. Copy the Tenant ID, Client ID, and Client Secret into `.env`\n\n\n\n## MCP client setup\n\n### Claude Desktop\n\nAn example config is included at `[claude_desktop_config.example.json](claude_desktop_config.example.json)`.\n\n**HTTP with API key (recommended)** — PULSE8.ai Cortex runs as a persistent server:\n\n```json\n{\n  \"mcpServers\": {\n    \"cortex\": {\n      \"url\": \"http://localhost:8420/mcp/\",\n      \"headers\": {\n        \"x-api-key\": \"your-secret-api-key\"\n      }\n    }\n  }\n}\n```\n\n**HTTP without auth** — when no authentication is configured:\n\n```json\n{\n  \"mcpServers\": {\n    \"cortex\": {\n      \"url\": \"http://localhost:8420/mcp/\"\n    }\n  }\n}\n```\n\n**Stdio** — Claude Desktop launches the server on demand (no auth needed):\n\n```json\n{\n  \"mcpServers\": {\n    \"cortex\": {\n      \"command\": \"uv\",\n      \"args\": [\"run\", \"--project\", \"/path/to/cortex\", \"python\", \"-m\", \"cortex.mcp\"],\n      \"env\": {\n        \"CORTEX_VAULT_PATH\": \"/path/to/your/vault\"\n      }\n    }\n  }\n}\n```\n\n### Cursor\n\nAdd to your `.cursor/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"cortex\": {\n      \"url\": \"http://localhost:8420/mcp/\",\n      \"headers\": {\n        \"x-api-key\": \"your-secret-api-key\"\n      }\n    }\n  }\n}\n```\n\n\n\n## How it works\n\n**Watcher** and **Compiler** are independent components:\n\n- The **Watcher** maintains the graph. Any `.md` file added, modified, or deleted triggers automatic node/edge updates.\n- The **Compiler** converts raw source files to Markdown using [MarkItDown](https://github.com/microsoft/markitdown) and writes them to `wiki/`. Supported formats include PDF, DOCX, PPTX, XLSX, HTML, CSV, JSON, XML, images (EXIF/OCR), and plain text. The LLM is only used for optional cross-reference detection between articles.\n\nThey connect indirectly: the compiler writes to `wiki/`, the watcher picks those up and updates the graph.\n\n### Supported file formats\n\n\n| Format               | Extensions                             |\n| -------------------- | -------------------------------------- |\n| PDF                  | `.pdf`                                 |\n| Microsoft Word       | `.docx`                                |\n| Microsoft PowerPoint | `.pptx`                                |\n| Microsoft Excel      | `.xlsx`, `.xls`                        |\n| HTML                 | `.html`, `.htm`                        |\n| Text-based           | `.csv`, `.json`, `.xml`, `.txt`, `.md` |\n| Images               | `.jpg`, `.png`, etc. (EXIF metadata)   |\n\n\n**Search** uses a two-stage pipeline:\n\n1. **QMD** performs keyword/semantic search on file contents\n2. **PULSE8.ai Cortex** enriches results with graph edges (wikilinks, tags, relationships between matched notes)\n\nQMD answers *\"what's relevant?\"* — the graph answers *\"how are these results connected?\"*\n\n\n\n## Real-world use cases\n\n### Software engineering knowledge base\n\n- Architecture Decision Records (ADRs)\n- Coding standards and conventions\n- Engineering handbooks and runbooks\n- Platform and service documentation\n- Domain-driven design models\n\n### Banking & financial services\n\n- Product documentation\n- Regulatory and compliance knowledge\n- Business domain models\n- Wealth management expertise\n- Institutional process know-how\n\n### Enterprise knowledge management\n\n- Internal wikis\n- Project documentation and post-mortems\n- Lessons learned and retrospectives\n- Organisational memory\n- Best practices and playbooks\n\n### AI agent memory layer\n\n- Shared memory across agents\n- Long-term reasoning context\n- Knowledge accumulation across sessions\n- Multi-agent collaboration on a single vault\n\n\n\n## Roadmap\n\n### Near-term\n\n- Enhanced knowledge-graph visualisation\n- Agent activity timeline\n- Knowledge quality scoring\n- Additional MCP integrations\n\n### Mid-term\n\n- Multi-vault federation\n- Enterprise governance and access policies\n- Knowledge lineage tracking\n- Cross-agent learning signals\n\n### Long-term\n\n- Self-improving organisational memory\n- Autonomous knowledge curation\n- Knowledge-driven agent ecosystems\n\n\n\n## Development\n\n```bash\n# Install dependencies\nuv sync --all-extras\n\n# Run tests\nuv run pytest tests/ -v\n\n# Run shell tests (requires bats-core)\nbats tests/test_start_sh.bats\n\n# Start PULSE8.ai Cortex locally (without Docker)\nCORTEX_MCP_TRANSPORT=http CORTEX_VAULT_PATH=./example_vault uv run python scripts/serve.py\n```\n\n### Utility scripts\n\n\n| Script                   | Description                                                |\n| ------------------------ | ---------------------------------------------------------- |\n| `scripts/serve.py`       | Dev server (HTTP or stdio based on `CORTEX_MCP_TRANSPORT`) |\n| `scripts/compile.py`     | Batch-compile all raw sources                              |\n| `scripts/reindex.py`     | Full reindex + graph rebuild                               |\n| `scripts/bulk_ingest.sh` | One-click bulk ingest from a local directory               |\n| `scripts/bulk_ingest.py` | Python CLI for bulk ingest (called by `bulk_ingest.sh`)    |\n| `scripts/lint.py`        | Lint vault structure                                       |\n\n\n\n\n## Data persistence\n\nThe vault directory is bind-mounted from your host into the containers. All data lives on your local disk and survives container restarts.\n\nThe QMD search index is stored in a Docker volume (`qmd-cache`). To force a full re-index:\n\n```bash\ndocker compose down -v\n./scripts/start.sh\n```\n\n\n\n## Releasing\n\nReleases are automated through GitHub Actions. Publishing a GitHub Release triggers three workflows that build and publish everything:\n\n| Workflow | Publishes to |\n| -------- | ------------ |\n| `publish-pypi.yml` | [PyPI](https://pypi.org/project/pulse8-ai-cortex-knowledge-vault/) |\n| `publish-docker.yml` | GitHub Container Registry (`ghcr.io`) |\n| `publish-mcp.yml` | [MCP Registry](https://registry.modelcontextprotocol.io) (GitHub OIDC auth) |\n\nTo cut a release:\n\n1. **Bump the version** in `pyproject.toml` and `server.json` (keep them in sync), update `CHANGELOG.md`, and commit to `main`.\n\n   ```bash\n   # optional: validate the registry manifest locally before tagging\n   mcp-publisher validate\n   ```\n\n2. **Create the GitHub Release** — via the UI (Releases → \"Draft a new release\" → new tag `vX.Y.Z`) or the CLI:\n\n   ```bash\n   git tag vX.Y.Z && git push origin vX.Y.Z\n   gh release create vX.Y.Z --title \"vX.Y.Z\" --notes-file docs/releases/vX.Y.Z.md\n   ```\n\n3. **That's it** — the release event fires all three workflows. `publish-mcp.yml` waits for PyPI to serve the new version (so the `mcp-name` ownership marker in this README is verifiable), then publishes the server via GitHub OIDC under `io.github.synpulse8-opensource/*` (no token or local `mcp-publisher` needed).\n\n4. **Verify** the registry entry once the workflow finishes:\n\n   ```bash\n   curl \"https://registry.modelcontextprotocol.io/v0.1/servers?search=pulse8-ai-cortex-knowledge-vault\"\n   ```\n\n> [!IMPORTANT]\n> PyPI versions are immutable — a version number can never be reused, even after deletion. Always increment to a new version; never re-release an existing one.\n\n\n\n## Community\n\nWe believe AI agents need a dedicated knowledge layer — not another document store. If you share that vision:\n\n- Star the [repository](https://github.com/synpulse8-opensource/pulse8-ai-cortex-knowledge-vault)\n- Open a [discussion](https://github.com/synpulse8-opensource/pulse8-ai-cortex-knowledge-vault/discussions) or [issue](https://github.com/synpulse8-opensource/pulse8-ai-cortex-knowledge-vault/issues)\n- Submit a pull request (see [Contributing](#contributing))\n- Share your use cases — banking, engineering, research, or anything else\n\nTogether we can build the knowledge layer for agentic AI.\n\n\n\n## Contributing\n\nWe welcome contributions! Please open an issue to discuss your idea before submitting a pull request.\n\n```bash\n# Fork and clone the repo\ngit clone https://github.com/<your-username>/cortex-knowledge-vault.git\ncd cortex-knowledge-vault\n\n# Create a branch\ngit checkout -b feat/my-feature\n\n# Install dev dependencies\nuv sync --all-extras\n\n# Make changes, then run tests\nuv run pytest tests/ -v\n\n# Submit a pull request\n```\n\n\n\n## Reporting issues\n\nUse [GitHub Issues](https://github.com/pulse8-ai/cortex-knowledge-vault/issues) to report bugs or request features.\n\n\n\n## Acknowledgements\n\nPULSE8.ai Cortex builds on ideas and tools from the open-source community:\n\n- **[LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f)** by [Andrej Karpathy](https://github.com/karpathy) — the core pattern of an LLM-maintained, persistent knowledge base that compiles and interlinks knowledge incrementally rather than re-discovering it from raw documents on every query. This gist is the direct inspiration for Cortex's architecture.\n- **[QMD](https://github.com/tobi/qmd)** by [Tobi Lütke](https://github.com/tobi) — the on-device search engine powering all full-text and hybrid search in Cortex. QMD combines BM25, vector search, and LLM re-ranking, all running locally.\n- **[MarkItDown](https://github.com/microsoft/markitdown)** by [Microsoft](https://github.com/microsoft) — the file-to-Markdown converter powering the Cortex compiler. Converts PDF, Office documents, HTML, images, and more into structured Markdown for ingestion into the vault.\n\n\n\n## License\n\nThis project is licensed under the [PULSE8.ai Cortex Open Source License](LICENSE.md) (Apache License 2.0 with additional terms).",
  "bytes": 39723,
  "sha": "0f0d478368df537eef1e26bf873b870f8e80273970db687cb7a0257bc6f01ab9",
  "repo_slug": "synpulse8-opensource/pulse8-ai-cortex-knowledge-vault",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_synpulse8_opensource_pulse8_ai_4940285a/readme"
}