# AIcortex

> A persistent, self-hosted brain for LLMs on your own NAS — memory, skills, tools, devices, sessions, secrets & scheduling as one MCP custom connector.

- **Type:** MCP server
- **Install:** `agentstack add mcp-ikarusmk-aicortex`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [IkarusMK](https://agentstack.voostack.com/s/ikarusmk)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [IkarusMK](https://github.com/IkarusMK)
- **Source:** https://github.com/IkarusMK/AIcortex

## Install

```sh
agentstack add mcp-ikarusmk-aicortex
```

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

## About

A private, self-hosted brain for your LLM — on your own NAS.

AICortex is a self-hosted [MCP](https://modelcontextprotocol.io) server that turns your NAS into a **personal LLM connector**: a persistent "brain" your assistant loads at the start of every session. Any MCP-capable LLM — Claude, ChatGPT, or a fully local model — gains a durable identity and real reach into your own services and devices, while running inside **your** network. And non-MCP tools (n8n, LangChain, your own scripts) reach the *same* brain through a **native REST API** with scoped per-user keys.

The model stays in its provider's cloud (or runs locally). **Your memory, skills and secrets stay on your NAS.** The assistant talks to AICortex over an HTTPS connector; the server uses your local credentials internally and never hands them to the model. New capabilities are added as **data** — a skill, a service config, a secret — with no redeploy.

## Highlights

**🧠 A brain that travels with you**
- One-call `bootstrap` — loads the guide *and* a live catalog of the whole brain in a single round-trip
- Self-learning, **typed** memory with a review queue, so it grows without polluting itself
- A **skill router** — search your skills, load only what's needed, author new ones at runtime
- The *same* brain across models, desktop **and** mobile — one account, one state

**🔌 Real reach — every integration is just data**
- **Email** (SMTP send **+ IMAP read**), **calendars** (**CalDAV**), HTTP services, **MQTT** & **FTP/FTPS** devices, **WebDAV** cloud, **SSH/SFTP**
- **IPP printing** & **eSCL scanning** (straight into Paperless-ngx) on LAN multifunction devices
- **Event-driven:** inbound **webhooks** (`POST /hooks/`, secret/HMAC-verified) turn external events into inbox items; outbound webhooks notify
- An **MCP gateway** to use other MCP servers' tools — register any integration with one call, **no code, no redeploy**

**🌐 Beyond MCP — drive it over plain HTTP**
- A **native REST API**: every tool as `POST /api/v1/tools/` with an **auto-generated OpenAPI 3.1** spec — plug AICortex into n8n, LangChain, an OpenAI-compatible client or a shell script, no MCP client required
- **Per-user API keys**: scoped (default-deny), hashed at rest, rate-limited, optional expiry — and a key runs the *same* per-user areas as an OIDC session, never admin by default

**👥 Team, autonomy & continuity**
- A **presence-aware multi-agent board**: live presence, capability-routed task *pull*, context-preserving handoff
- **Cross-LLM session handoff** — resume exactly where another model or device left off
- **Cron-as-data scheduling** with per-user **act-as**: a scheduled job runs confined to *its owner's* area, via a short-lived per-job token — never a standing god-token

**🔒 Secure & multi-tenant**
- Your own **OIDC** login and **roles** (admin / user / viewer) — one switch, `AUTH_ENFORCE`
- **Per-user isolation *and* capability areas:** private memory + vault per person, plus **default-deny** service/skill allow-lists an admin assigns — so not everyone may use every tool
- An **encrypted secret vault**, an **SSRF egress guard**, and capability checks that **fail closed** — end-to-end on your hardware, including a **local model** (Ollama)

## How it works

```
LLM app — any MCP client (desktop / mobile)  ·  one or many agents
        │  custom connector / MCP server (HTTPS, from the model's cloud)
        ▼
Reverse proxy (Zoraxy / Caddy / nginx / Traefik …)
        │
        ▼
AICortex  (this container, on your NAS)
        │  uses local files & secrets
        ▼
Memory · Skills · HTTP services · MQTT & FTP devices · WebDAV cloud · Workspace files
   · SSH/SFTP · SMTP + IMAP email · IPP printing · eSCL scanning · MCP gateway · Inbox/Tasks
   · Sessions · Cron · Secret vault   (every outbound call passes the SSRF egress guard)
```

## How AICortex differs

The "LLM brain" space has some great projects — AICortex deliberately sits in a different spot:

- **[GBrain](https://github.com/garrytan/gbrain)** is a *memory engine* — markdown-first, Postgres/PGLite-backed, with a self-wiring knowledge graph and hybrid/vector search. Deep retrieval, one concern.
- **[CortexPrism](https://cortexprism.io/)** is a full *agent operating system* — its own runtime, ~30 LLM providers, a plugin marketplace, sandboxed code execution and multi-user teams. Batteries included, heavyweight.
- **AICortex** is a single, lightweight *MCP connector*: one container that gives any client a persistent brain — memory **and** skills, devices, sessions, secrets and scheduling — where new integrations are added as **data, no redeploy**. And unlike most brain projects it isn't MCP-only: the same tools are exposed over a **native REST API** with an auto-generated OpenAPI spec, so non-MCP clients (n8n, LangChain, a script) plug in too. No database, no graph, no marketplace; you bring your own client.

Rule of thumb: want a dedicated memory/graph engine → GBrain; want a batteries-included agent OS → CortexPrism; want a minimal self-hosted connector that plugs your existing LLM app into your own stuff → AICortex.

## Requirements

- A NAS or server running **Docker** (Compose v2).
- A **reverse proxy** serving the container over public HTTPS (cloud-hosted LLM clients connect from their provider's cloud, so the endpoint must be reachable from the internet; a purely local client can reach it on the LAN).
- A domain/subdomain pointing at your proxy.
- An **MCP-capable client** that supports custom connectors / MCP servers (Claude, ChatGPT, or any MCP client).

## Quick start

> 🤖 **Installing with an AI coding agent?** Point it at
> **[INSTALL_FOR_AGENTS.md](INSTALL_FOR_AGENTS.md)** — it walks the agent through the
> whole install and verification, handing the human-only steps (DNS, proxy, OIDC) back
> to you.

```bash
git clone git@github.com:IkarusMK/AICortex.git
cd AICortex
cp .env.example .env        # adjust PUID / PGID / HOST_PORT / TZ
docker compose up -d --build
```

The MCP endpoint is served at `http://:8787/mcp`.

> **Prebuilt image (no local build):** a multi-arch image is published to GHCR by CI.
> In `docker-compose.yml`, comment out `build: .`, uncomment
> `image: ghcr.io/ikarusmk/aicortex:latest`, then `docker compose pull && docker compose up -d`.

**Expose it & add the connector:**

1. Point a subdomain (e.g. `agent.example.com`) at your reverse proxy.
2. Proxy that host to `http://:8787` over HTTPS — the upstream is **plain HTTP**, so do *not* enable "TLS to upstream". If your proxy geo-blocks, allow your LLM provider's egress region. The published port binds to **`127.0.0.1`** by default (only this host's proxy reaches it, not the LAN); a host-network proxy uses `http://127.0.0.1:8787`, a **bridge-network** proxy needs `BIND_ADDR=0.0.0.0` (or a shared Docker network).
3. In your MCP client: **add a custom connector / MCP server** → URL `https://agent.example.com/mcp`.
4. Test: ask the assistant to call the `ping` tool.

## Authentication & authorization

AICortex separates **authentication** (who may connect) from **authorization** (what they may do). Set up the login once, then pick your mode with a single line in `.env`.

### 1. Authenticate with your own OIDC provider

Exposing the connector publicly requires OAuth. AICortex uses **your own identity provider** as the login backend — Pocket ID, Authentik, Keycloak, Auth0, anything with standard OIDC discovery. FastMCP's OIDC proxy handles the MCP-side OAuth 2.1 flow (Dynamic Client Registration + PKCE); your provider does the actual login.

| Variable | Example |
|----------|---------|
| `OIDC_CONFIG_URL` | `https://id.example.com/.well-known/openid-configuration` |
| `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` | from a client you register in your provider |
| `BASE_URL` | `https://agent.example.com` (this server's public URL) |
| `JWT_SIGNING_KEY` | `openssl rand -hex 32` |

Register the OAuth client in your provider with redirect URI **`/auth/callback`**, then (re-)add the connector in your client to be sent through the login.

> 🟣 **Using Pocket ID?** Follow the click-by-click **[Pocket ID setup guide](docs/pocketid-setup.md)** — creating the OIDC client, the exact `.env`, and optional groups → roles.

> ℹ️ **Don't** put reverse-proxy SSO / forward-auth in front of `/mcp` — an MCP connector is a *machine* client and can't follow an interactive login redirect. Authentication belongs at the MCP layer, which is exactly what this does. When the OIDC variables are unset, the server binds to `127.0.0.1` only (local testing).

### 2. Pick your mode — one line in `.env`

|  | 🏠 **Homelab** | 🏢 **Enterprise** |
|---|---|---|
| **For** | one trusted person (you) | several people on one brain |
| **Tool access** | everyone gets every tool | roles: admin / user / viewer |
| **Data** | one shared brain | per-user memory **+ private vault** |
| **The line** | `AUTH_ENFORCE=0` | `AUTH_ENFORCE=1` *(default)* |

**🏠 Homelab — keep it simple**
```env
AUTH_ENFORCE=0
```
Every authenticated caller gets every tool — one shared brain, zero friction. Authentication (OIDC) still guards the front door; you've just turned off the *internal* role gate.

**🏢 Enterprise — several people, one brain**
```env
AUTH_ENFORCE=1                          # default — the single switch (see below)
OIDC_SCOPE=openid profile email groups  # request the groups claim
AUTH_ROLE_CLAIM=groups                  # map Pocket ID groups → roles
```
`AUTH_ENFORCE=1` is the **one switch** — "enforce means enforce". It turns on roles (admin / user / viewer), per-user isolation, and per-user capability areas together. Roles are driven by your IdP groups (mapped in `data/auth/policy.json`, or managed live with the `tenancy_*` admin tools). Each non-admin is confined to their **own memory scope and private vault namespace** — two people never read or overwrite each other's data — and reaches only the **services & skills an admin assigns** (default-deny); an admin provisions per-user secrets (`secret_set owner=…`) and areas (`tenancy_set services=… skills=…`). Admins keep full access.

Both modes run the same image. **Private data** (memory, vault) fails **open** so a glitch never strands the operator; **shared capabilities** (services, skills) fail **closed** under enforce. **Full guides: [docs/authorization.md](docs/authorization.md) · [docs/per-user-areas.md](docs/per-user-areas.md).**

## Capabilities

| Group | Tools | What it does |
|-------|-------|--------------|
| Onboarding | `bootstrap` | **Start here** — one call returns the guide + a live catalog of the whole brain |
| Health | `ping` | Connectivity check |
| Memory | `memory_write` (typed) · `memory_read`/`list`/`search`/`delete` · `memory_note` · `memory_candidates` · `memory_promote`/`memory_reject` | Self-learning, **typed** facts with dedup + a candidate review queue |
| Skills | `skill_search` · `skill_list` · `skill_load` · `skill_resource` · `skill_write` | Searchable know-how; learn new skills at runtime |
| Services (HTTP) | `service_add` · `service_list` · `call_service` | Register & call any HTTP API as data |
| Devices (MQTT) | `mqtt_add` · `mqtt_list` · `mqtt_publish` · `mqtt_get` | Talk to MQTT devices (e.g. a LAN printer or sensor) as data |
| Files (FTP/FTPS) | `ftp_add` · `ftp_list_endpoints` · `ftp_list` · `ftp_upload` | Up/list files over FTP/FTPS |
| Cloud (WebDAV) | `webdav_add` · `webdav_list` · `webdav_upload` · `webdav_download` · `webdav_mkdir` · `webdav_delete` | Move large files NAS↔cloud (Nextcloud/ownCloud), app-password auth |
| Calendar (CalDAV) | `caldav_add` · `caldav_list_calendars` · `caldav_list_events` · `caldav_add_event` · `caldav_delete_event` | Read/create/delete calendar events (Nextcloud/Radicale), app-password auth |
| Workspace files | `fs_list` · `fs_read` · `fs_write` · `fs_move` · `fs_delete` · `fs_info` | See & tidy the `/data/work` file hub (sandboxed) |
| SSH / SFTP | `ssh_add` · `ssh_run` · `ssh_upload` · `ssh_download` · `ssh_list_dir` | Run remote commands & transfer files (hosts as data, vault creds) |
| Email (SMTP) | `mail_add` · `mail_list` · `mail_send` | Send mail/notifications with optional attachment |
| Email (IMAP) | `imap_add` · `imap_list` · `imap_search` · `imap_fetch` | Read incoming mail (read-only, PEEK); pull attachments → `/data/work` |
| Printing (IPP) | `print_add` · `print_list` · `print_delete` · `print_document` | Print PDFs/images to a LAN printer via IPP/AirPrint |
| Scanning (eSCL) | `scan_add` · `scan_list` · `scan_delete` · `scan_document` | Scan on a LAN device → `/data/work`, optionally into Paperless |
| MCP gateway | `mcp_add` · `mcp_list` · `mcp_tools` · `mcp_call` | Use other MCP servers' tools as data |
| Webhooks | `webhook_add` · `webhook_list` · `webhook_delete` · `webhook_send` | Inbound `POST /hooks/` (secret/HMAC) → inbox (event-driven); outbound POST notify |
| Multi-agent | `agent_register`/`list` · `task_add`/`list`/`claim`/`update` · `task_next` · `task_handoff` · `inbox_*` | Presence-aware team: capability-routed pull & context-preserving handoff |
| Sessions | `session_save` · `session_list` · `session_load` · `session_delete` · `session_prune` | Cross-LLM handoff log; auto-expires |
| Scheduling | `cron_add` · `cron_list` · `cron_delete` · `cron_due` · `cron_mark_run` | Cron jobs as data; a NAS runner triggers them |
| Secrets | `secret_set` · `secret_list` · `secret_delete` | Encrypted vault; values never returned |
| Tenancy (admin) | `tenancy_set` · `tenancy_show` · `tenancy_list` · `tenancy_unset` · `tenancy_status` | Configure per-user data areas — the multi-user control plane, as data |
| Guide | `guide` | Self-description (also sent as server `instructions` on connect) |

Every register has a matching delete (`skill_delete`, `service_delete`, `mqtt_delete`, `memory_delete`, `secret_delete`, …), so anything you register you can also remove via the connector.

## Memory, skills & the skill router

This is the heart of the project — making the assistant *itself* portable, not just the chat.

- **Memory** lives as plain files under `data/memory`; the `memory_*` tools let the LLM recall and update what it knows about you, the same on every device. The catalog groups it by **tier** — 🧭 Core (identity & preferences) → 📂 Projects → 🛠 Working style → 🔗 References, derived from each memory's `type`, with short-term/current state in the sessions layer — so a fresh LLM sees who you are before the ephemera.
- **Skills** live as folders under `data/skills` (`/SKILL.md` + resources). The router (`skill_search` / `skill_load` / `skill_resource`) finds the right skill and pulls in **only what it needs** (progressive disclosure).
- **Categories keep it cheap — and are mandatory.** `skill_write` refuses an uncategorized skill and snaps near-duplicate spellings onto the existing category, so a 300-skill library stays a dozen lines in `bootstrap`, not a token dump. Starter skills live in [`examples/skills/`](examples/skills/README.md).
- **Call `bootstrap` first.** Its description tells any LLM to call it at the start of every session. For clients that don't call tools on their own, add a one-line instruction to your client's project/system prompt — see [`docs/client-project-instructions.md`](docs/client-project-instructions.md).

## Auto-memory — a brain that learns by itself

Most assistants forget the moment a chat ends. AICortex closes that loop — and stays tidy while doing it.

- **Typed & tiered.** Every memory is `user`, `feedback`, `project` or `reference`; `memory_write` refuses an untyped memory, and `bootstrap` groups them into tiers (Core → Projects → Working style → References) so the catalog reads long-term → ephemeral — the brain stays sorted by intent.
- **Learns in-session, at zero extra cost.** The model already talking to you distills the durable facts and writes them back — no second model, no background loop.
- **Dedup-first.** A write flags overlapping entries so related facts merge into one file

…

## Source & license

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

- **Author:** [IkarusMK](https://github.com/IkarusMK)
- **Source:** [IkarusMK/AIcortex](https://github.com/IkarusMK/AIcortex)
- **License:** Apache-2.0

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:** yes
- **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-ikarusmk-aicortex
- Seller: https://agentstack.voostack.com/s/ikarusmk
- 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%.
