Content
# ESIM — External Structured Intent Memory
A graph-based MCP server that gives AI assistants persistent, structured memory via Neo4j.
ESIM turns any AI client that supports [MCP](https://modelcontextprotocol.io/) into a system with long-term memory — structured as a typed knowledge graph with semantic search, relationship traversal, and automated diagnostics.
> **Upgrading from `esm`?** This release renames the project (ESM → ESIM) and makes two breaking schema changes. See **[MIGRATING.md](MIGRATING.md)**.
## How It Works
AI clients connect to ESIM via MCP (stdio transport). Every piece of knowledge is stored as a typed node in a Neo4j graph with vector embeddings for semantic search. Nodes are connected by typed relationships that encode how things relate.
```
AI Client (Claude, etc.)
↕ MCP (stdio)
ESIM Server (Deno + TypeScript)
↕
Neo4j (graph storage + vector search)
↕
LLM API (embeddings + metadata extraction)
```
## Data Model
### Node Types
| Type | Purpose |
|------|---------|
| **Agent** | A person, team, org, or AI that acts with intent |
| **Need** | A gap, requirement, or want — has a lifecycle |
| **Resource** | A capability or asset — skill, knowledge, tool, budget |
| **Constraint** | A governing force — priority, understanding, approach, mechanics |
| **Output** | Something produced by an agent |
| **Role** | A named function or position |
| **Signal** | An observation — concrete data + interpretation |
| **Session** | A bounded interaction or working session |
| **Discrepancy** | A gap between intent and output |
| **Stock** | A measurable accumulation (trust, knowledge, capacity) |
### Relationship Types
23 typed relationships including `PURPOSE`, `CONTAINS`, `FILLS`, `GOVERNS`, `OWNS`, `SERVES`, `SIGNALS`, `SCOPED_TO`, `TRIGGERED_BY`, `AFFECTS`, `GOVERNED_BY`, and more. See `src/types.ts` for the full list.
## The Structured-Intent Model
ESIM's node and relationship types aren't generic — they encode a specific model of how intent is structured. Understanding the model is what makes the graph useful; without it, you just have typed boxes.
The core idea: **intent has structure, and that structure can be made explicit, stored, and diagnosed.** Most intent lives implicitly — in someone's head, scattered across documents, assumed but never stated. Structured intent pulls it into the open as a graph you can reason about.
### The intent frame — six layers
The central object is the **intent frame**: the full configuration of a single entity — a person, team, project, tool, or role. A complete frame answers six questions, cascading from *why* down to *how*. Each layer is expressed by an ESIM primitive you already have, and each is judged a different way:
| Layer | Question it answers | ESIM primitive | Well-formed when… |
|-------|---------------------|---------------|-------------------|
| **1. Purpose** | Why does it exist? | `PURPOSE` edge (+ `purpose_type`) | it points outward, to who or what it serves |
| **2. Understanding** | What do we understand to be true? | `Constraint` (`understanding`) | the picture is *complete* — no unstated assumptions |
| **3. Priorities** | What wins when things conflict? | `Constraint` (`priority`) | each takes a clear *position* on a real tension |
| **4. Approaches** | How do we go about it? | `Constraint` (`approach`) | the method is *compatible* with the priorities above |
| **5. Composition** | What is it made of? | `CONTAINS` edge (with `order`) | the parts *cohere* — they fit together |
| **6. Mechanics** | What concrete machinery runs it? | `Constraint` (`mechanics`) | it *integrates* with everything above |
The layers **cascade**: purpose shapes what you need to understand, understanding shapes which priorities are in play, priorities shape approaches, and so on down. That's why a problem at the bottom (a tool, a routine) so often traces to something unstated near the top (an unexamined understanding, an undeclared purpose) — and why you diagnose by walking the cascade from the symptom upward.
Two of the six are edges rather than nodes: purpose and composition describe how an entity *relates* — to what it serves, and to its own parts. The other four are `Constraint` nodes, distinguished by `constraint_type`. None of this is a feature you switch on; the six layers are a lens over primitives ESIM already stores.
> **Naming note:** the four constraint layers are stored as `Constraint` nodes with `constraint_type` `understanding`, `priority`, `approach`, or `mechanics`. Purpose and Composition aren't constraint types — they're the `PURPOSE` and `CONTAINS` edges.
### Purpose lives on edges, not in nodes
An entity's purpose isn't a property it holds — it's the set of `PURPOSE` edges pointing from it to what it serves. A person, team, or tool *exists to do something for someone*, and that "for someone" is directional, so it's an edge. An entity with no purpose edges doesn't act with intent — it reacts.
Every `PURPOSE` edge carries a type:
| Purpose type | Meaning |
|--------------|---------|
| **Create** | New value enters the system |
| **Sustain** | Existing value held against entropy |
| **Transform** | Value changes form through processing |
| **Enable** | Value reaches a destination it couldn't reach alone |
### The intent formula
Structured intent reads every output through one relationship:
```
(Needs + Resources) / Constraints = Output
```
- **Needs** — what is required.
- **Resources** — what is available (skill, knowledge, tool, budget, capacity).
- **Constraints** — what shapes how potential becomes output.
- **Output** — what actually gets produced.
Constraints are the lever. Aligned with purpose, a constraint *multiplies* output (leverage); misaligned, it *consumes* potential (a tax). The formula turns a vague "this isn't working" into a locatable question: *which constraint is taxing the output?*
### Purpose vs. function
**Purpose** is what an entity exists to do (declared). **Function** is what it's actually being used for (observed). Conflating the two is at the root of most misalignment — a butter knife used as a screwdriver partly works, but the knife gets damaged and the screw never seats. ESIM keeps declared purpose and observed function as separate, comparable things; the gap between them is a first-class node (a `Discrepancy`).
### Framing an intent — the workflow
"Intent framing" is the act of building a frame for a specific entity until it's complete and coherent. In ESIM that's a concrete sequence of tool calls:
1. **Create the entity.** `create_entity` an `Agent` (or `Role`) — the thing whose intent you're framing.
2. **Declare purpose.** Wire `PURPOSE` edges from it to what it serves, each with a `purpose_type`. No purpose edge means no intent — just reaction.
3. **Add the four constraint layers.** `create_entity` `Constraint` nodes for Understanding, Priorities, Approaches, and Mechanics (`constraint_type` `understanding`, `priority`, `approach`, `mechanics`); wire them with `GOVERNS`.
4. **Compose it.** If the entity has parts, `create_entity` them and wire `CONTAINS` (with `order`) — that's the composition layer.
5. **Attach needs and resources.** `create_entity` `Need` and `Resource` nodes for what's required and what's available.
6. **Capture evidence as it arrives.** As reality produces observations, `create_signal` to record them against the relevant entity — declared intent on one side, observed signal on the other.
7. **Calibrate.** Compare the declared frame against accumulated signals. Where they diverge, surface a `Discrepancy`. Closing that gap — by updating the frame or changing behavior — is the point of the whole exercise.
Steps 1–5 are *declaration* (what you intend); steps 6–7 are *calibration* (reconciling intent with reality). A frame is never "done" — it gets sharper every time you calibrate.
### Where the skills fit
The [`skills/`](skills/) directory turns this model into guided practice:
- [`purpose-discovery`](skills/purpose-discovery/SKILL.md) — walks a person through building their first frame end to end (steps 1–5).
- [`session-protocol`](skills/session-protocol/SKILL.md) — governs how the assistant captures signals and keeps the graph calibrated during ongoing work (steps 6–7).
- [`signal-processing`](skills/signal-processing/SKILL.md) — systematically processes captured signals into graph updates (the engine behind calibration).
## Setup
### Prerequisites
- [Deno 2+](https://deno.land/)
- Neo4j (cloud or local)
- An OpenAI-compatible API for embeddings and completions
### Quick Start (Docker + OpenAI)
```bash
# Start local Neo4j
docker compose up -d
# Configure environment (recommended: system-wide config directory)
mkdir -p ~/.config/env
cp .env.example ~/.config/env/esim.env
# Edit ~/.config/env/esim.env — uncomment Docker lines, add your OpenAI key
# Initialize schema (creates vector indexes + constraints)
deno task setup
# Register with Claude Code (use absolute path to your clone)
claude mcp add esim -- deno run --allow-net --allow-env --allow-read --allow-sys \
/absolute/path/to/esim/src/main.ts
# Verify
claude mcp list # should show "esim"
```
### Fully Local Setup (Docker + Ollama)
For a fully self-hosted setup with no external API calls:
1. Install [Ollama](https://ollama.com/) and pull models:
```bash
ollama pull qwen3-embedding:0.6b # embeddings — see "Choosing an Embedding Model" below
ollama pull llama3.2 # metadata extraction / classification
```
2. Start Neo4j:
```bash
docker compose up -d
```
3. Configure environment:
```bash
mkdir -p ~/.config/env
cp .env.example ~/.config/env/esim.env
```
Edit `~/.config/env/esim.env` — uncomment the Docker and Ollama sections:
```env
NEO4J_DB_CONNECTION_URI=bolt://localhost:7687
NEO4J_DB_USERNAME=neo4j
NEO4J_DB_PASSWORD=password
LLM_BASE_URL=http://localhost:11434/v1
LLM_API_KEY=ollama
LLM_EMBEDDING_MODEL=qwen3-embedding:0.6b
LLM_EMBEDDING_DIMENSIONS=1024
LLM_COMPLETION_MODEL=llama3.2
```
4. Initialize and register:
```bash
deno task setup
claude mcp add esim -- deno run --allow-net --allow-env --allow-read --allow-sys \
/absolute/path/to/esim/src/main.ts
```
### Cloud Setup (Neo4j Aura + OpenAI/OpenRouter)
1. Create a free [Neo4j Aura](https://neo4j.com/cloud/aura/) instance
2. Configure environment:
```bash
mkdir -p ~/.config/env
cp .env.example ~/.config/env/esim.env
```
Edit `~/.config/env/esim.env`:
```env
NEO4J_DB_CONNECTION_URI=neo4j+s://xxxx.databases.neo4j.io
NEO4J_DB_USERNAME=neo4j
NEO4J_DB_PASSWORD=your-password
LLM_BASE_URL=https://api.openai.com/v1
LLM_API_KEY=sk-...
```
3. Initialize and register:
```bash
deno task setup
claude mcp add esim -- deno run --allow-net --allow-env --allow-read --allow-sys \
/absolute/path/to/esim/src/main.ts
```
## Environment Variables
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `NEO4J_DB_CONNECTION_URI` | — | Yes | Neo4j connection string |
| `NEO4J_DB_USERNAME` | — | Yes | Neo4j username |
| `NEO4J_DB_PASSWORD` | — | Yes | Neo4j password |
| `LLM_API_KEY` | — | Yes | API key for your LLM provider |
| `LLM_BASE_URL` | `https://api.openai.com/v1` | No | Any OpenAI-compatible endpoint |
| `LLM_EMBEDDING_MODEL` | `text-embedding-3-small` | No | Embedding model name |
| `LLM_EMBEDDING_DIMENSIONS` | `1536` | No | Must match your embedding model's output |
| `LLM_COMPLETION_MODEL` | `gpt-4o-mini` | No | Model for classification and metadata extraction |
Environment is loaded in this order (first found wins):
1. `ESIM_ENV_FILE` environment variable (explicit override — useful for multi-instance setups)
2. `~/.config/env/esim.env` (recommended — works regardless of working directory)
3. `.env` in repo root (fallback for development)
**Important:** `LLM_EMBEDDING_DIMENSIONS` must exactly match your embedding model's output. Common values: `qwen3-embedding:0.6b` (Ollama) = 1024, `nomic-embed-text` (Ollama) = 768, `text-embedding-3-small` (OpenAI) = 1536. If you change models after setup, drop and recreate indexes — see **Choosing an Embedding Model** below.
## Choosing an Embedding Model
Semantic search quality is mostly set by the embedding model, and for ESIM one factor matters more than benchmark scores: **the context window.**
ESIM embeds whole nodes, not short labels — a single node can hold several paragraphs (a signal stores its full reasoning, not just a title). If the model's context window is smaller than the node, it embeds only the beginning and silently drops the rest, quietly degrading search. **Choose a model whose context window comfortably exceeds your longest nodes.** This is why `mxbai-embed-large`, despite strong English recall, is a poor fit here: its 512-token window truncates longer entries.
Dimension count matters much less. An ESIM graph is small — hundreds to low thousands of nodes — so the usual "more dimensions = more storage and slower search" tradeoff is negligible. Use the model's native dimension and move on.
### Options (Ollama / local)
| Model | Native dims | Context | Fit for ESIM |
|-------|-------------|---------|--------------|
| **`qwen3-embedding:0.6b`** — recommended | 1024 | 32K | Best 2026 local quality, multilingual (100+ languages), context so large no node ever truncates. ~640 MB. |
| `bge-m3` | 1024 | 8K | Proven, multilingual, also emits sparse vectors (a path to hybrid search later). ~1.2 GB. |
| `embeddinggemma` | 768 | 2K | Best quality-per-byte when RAM is tight. ~300 MB. |
| `nomic-embed-text` | 768 | ~2K | Lightweight baseline — fine, but bettered by the above. ~274 MB. |
| `mxbai-embed-large` | 1024 | **512** ⚠️ | Strong English recall, but the 512-token window truncates rich `context` fields — a poor fit for ESIM despite its reputation. |
For OpenAI/cloud, `text-embedding-3-small` (1536) and `text-embedding-3-large` (3072) both have ample context and need no special handling.
### Changing models after setup
Embeddings from different models are **not comparable** — switching models means re-embedding every node, even if the dimension count is identical:
```bash
# 1. Set LLM_EMBEDDING_MODEL + LLM_EMBEDDING_DIMENSIONS in your env file
# 2. Drop the old vector indexes
deno run --allow-net --allow-env --allow-read --allow-sys scripts/drop-indexes.ts
# 3. Recreate indexes at the new dimension
deno task setup
# 4. Re-embed every node with the new model
deno run --allow-net --allow-env --allow-read --allow-sys scripts/reembed.ts
```
> **Note:** `scripts/reembed.ts` only re-embeds nodes whose vector length differs from the configured dimension. If you switch models but keep the same dimension, it won't notice the change — clear the old embeddings first, or change the dimension to force a full re-embed.
## MCP Connection
### Claude Code
```bash
claude mcp add esim -- deno run --allow-net --allow-env --allow-read --allow-sys /path/to/esim/src/main.ts
```
Verify: `claude mcp list` should show `esim` as available.
### Claude Desktop
Add to your MCP config (location varies by platform — see Claude Desktop docs):
```json
{
"mcpServers": {
"esim": {
"command": "deno",
"args": ["run", "--allow-net", "--allow-env", "--allow-read", "--allow-sys", "/path/to/esim/src/main.ts"]
}
}
}
```
Restart Claude Desktop after editing. ESIM tools appear in the tools menu.
### Verifying the Connection
Once registered, test from Claude Code by asking it to run `stats`. You should get back node/edge counts (all zeros on a fresh install). If you get a connection error, see Troubleshooting below.
## MCP Tools
| Tool | Description |
|------|-------------|
| `setup_schema` | Create vector indexes and constraints (idempotent) |
| `capture` | Unified intake — auto-classifies and stores any content |
| `create_entity` | Create typed entity nodes with auto-embedding |
| `create_signal` | Capture observations with data + interpretation |
| `create_session` | Start sessions with participants and scope |
| `create_relationship` | Wire nodes together with typed edges |
| `search` | Semantic search across vector indexes |
| `get_node` | Fetch a node with all its relationships |
| `get_context` | Reconstruct context around a topic or entity |
| `traverse` | Multi-hop graph traversal with filters |
| `list` | Browse nodes by type, recency, status |
| `stats` | Summary statistics and attention items |
| `run_diagnostic` | Structural health checks on the graph |
| `update_node` | Update properties on an existing node |
| `delete_node` | Remove a node and its relationships |
## Tool Reference
### `setup_schema`
Create vector indexes and uniqueness constraints in Neo4j. Idempotent — safe to run multiple times.
**Parameters:** None
```json
{}
```
---
### `capture`
Unified intake — send any content and it gets classified, embedded, and stored as the appropriate node type. This is the simplest way to add data. The classifier determines the node type automatically, or you can provide hints to skip classification.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `content` | string | Yes | The content to capture — any text |
| `hints` | object | No | `{ node_type?: string, name?: string }` — guide or skip classification |
```json
// Minimal — let the classifier decide the type
{ "content": "We need to migrate the auth service to OAuth 2.1 before Q3" }
// With hints — skip classification
{ "content": "Kurt is the engineering manager for the platform team", "hints": { "node_type": "Agent", "name": "Kurt" } }
```
---
### `create_entity`
Create a typed entity node (Agent, Need, Resource, Constraint, Output, Role). Auto-generates embedding and extracts metadata via LLM. Explicit properties override LLM-extracted values.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `entity_type` | string | Yes | One of: `Agent`, `Need`, `Resource`, `Constraint`, `Output`, `Role` |
| `name` | string | Yes | Display name for the entity |
| `content` | string | No | Description/context — used for embedding generation |
| `properties` | object | No | Additional properties — explicit values override LLM-extracted metadata |
```json
{ "entity_type": "Agent", "name": "Ada", "content": "Engineering manager focused on platform infrastructure" }
{ "entity_type": "Need", "name": "Auth Migration", "content": "Migrate auth service to OAuth 2.1", "properties": { "lifecycle_state": "open", "priority": "high" } }
```
---
### `create_signal`
Capture an observation with optional context. Auto-creates `OBSERVED_BY`, `SIGNALS`, and `PRODUCED_IN` edges when IDs are provided. Observer-authored fields (`observation`, `context`, `how_observed`, `confidence`, `perceived_impact`) are sacred — never overwritten by the system.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `observation` | string | Yes | What happened, from the observer's vantage point — factual, verifiable |
| `context` | string | No | Observer-authored situational context, circumstances, or hypotheses |
| `observed_by_agent_id` | string | No | ID of the agent who captured this signal |
| `signals_entity_id` | string | No | ID of the entity this signal is about |
| `produced_in_session_id` | string | No | ID of the session where this was captured |
| `properties` | object | No | Additional properties (how_observed, confidence, perceived_impact, disposition, disposition_note, etc.) |
```json
{
"observation": "API latency p99 increased from 200ms to 850ms after deploy",
"context": "The new auth middleware was deployed 2 hours ago, latency started climbing immediately",
"signals_entity_id": "uuid-of-auth-service"
}
```
---
### `create_session`
Start a session with participants, scope, and triggers. Creates `PARTICIPATES_IN`, `SCOPED_TO`, and `TRIGGERED_BY` edges.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `name` | string | Yes | Session name |
| `content` | string | No | Session description / summary |
| `participant_ids` | string[] | No | Agent IDs of participants |
| `scoped_to_id` | string | No | Entity ID this session is scoped to |
| `triggered_by_signal_ids` | string[] | No | Signal IDs that triggered this session |
| `properties` | object | No | Additional properties (session_type, trigger_type, etc.) |
```json
{
"name": "Auth Migration Planning",
"content": "Planning session for OAuth 2.1 migration",
"participant_ids": ["agent-uuid-1", "agent-uuid-2"],
"scoped_to_id": "need-uuid",
"properties": { "session_type": "planning" }
}
```
---
### `create_relationship`
Create any of the 23 relationship types between two nodes with optional edge properties.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `from_id` | string | Yes | Source node ID |
| `to_id` | string | Yes | Target node ID |
| `relationship_type` | string | Yes | One of: `PURPOSE`, `CONTAINS`, `FILLS`, `GOVERNS`, `OWNS`, `SERVES`, `GENERATED_BY`, `REQUIRES`, `PRODUCES`, `EVALUATED_AGAINST`, `HAS_STOCK`, `SIGNALS`, `OBSERVED_BY`, `FLAGGED_AT`, `PRODUCED_IN`, `PARTICIPATES_IN`, `SCOPED_TO`, `TRIGGERED_BY`, `DEFINED_BY`, `ESCALATED_TO`, `RELATED_TO` |
| `properties` | object | No | Edge properties |
```json
{ "from_id": "agent-uuid", "to_id": "need-uuid", "relationship_type": "OWNS" }
```
---
### `search`
Semantic search across vector indexes. Returns results ranked by cosine similarity.
**Parameters:**
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `query` | string | Yes | — | Natural language search query |
| `index` | string | No | `"all"` | Which index: `all`, `entity`, `signal`, `session`, `discrepancy` |
| `limit` | number | No | `10` | Max results per index |
| `threshold` | number | No | `0.5` | Minimum similarity score (0–1) |
```json
{ "query": "authentication and authorization" }
{ "query": "latency issues", "index": "signal", "limit": 5, "threshold": 0.7 }
```
---
### `get_node`
Fetch a node by ID with all its relationships and connected nodes.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | string | Yes | Node ID (UUID) |
```json
{ "id": "550e8400-e29b-41d4-a716-446655440000" }
```
---
### `get_context`
Reconstruct context around a topic or entity. Returns semantic anchors, active threads, structural neighbors, attention items, and optionally discovery suggestions.
**Parameters:**
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `query` | string | Yes | — | Natural language query to find relevant context |
| `entity_id` | string | No | — | Anchor to a known entity ID instead of searching |
| `include_discoveries` | boolean | No | `true` | Include discovery suggestions |
```json
{ "query": "what's happening with the auth migration" }
{ "query": "platform team priorities", "entity_id": "agent-uuid", "include_discoveries": false }
```
---
### `traverse`
Multi-hop graph traversal from a starting node. Filter by relationship type(s), control depth, and direction.
**Parameters:**
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `id` | string | Yes | — | Starting node ID |
| `relationship_types` | string[] | No | all | Filter to specific relationship types |
| `max_depth` | number | No | `3` | Maximum traversal depth (1–10) |
| `direction` | string | No | `"both"` | `both`, `outgoing`, or `incoming` |
```json
{ "id": "agent-uuid", "relationship_types": ["OWNS", "PRODUCES"], "max_depth": 2, "direction": "outgoing" }
```
---
### `list`
Browse captured nodes with optional filters.
**Parameters:**
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `days` | number | No | — | Only nodes from the last N days |
| `type` | string | No | — | Filter by node label (e.g. `Signal`, `Agent`, `Need`) |
| `status` | string | No | — | Filter by status field |
| `limit` | number | No | `20` | Max results |
```json
{ "type": "Signal", "days": 7, "limit": 10 }
{ "type": "Need", "status": "open" }
```
---
### `stats`
Summary statistics: node counts by type, edge counts, 7-day activity, and attention items (unprocessed signals, open needs).
**Parameters:** None
```json
{}
```
---
### `run_diagnostic`
Structural health checks on the graph.
**Parameters:**
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `checks` | string[] | No | `["all"]` | Diagnostics to run: `unattached_needs`, `missing_purpose`, `overloaded_agents`, `phantom_sessions`, `entities_without_purpose`, `unprocessed_signals`, `ego_drift_check`, `constraint_role_analysis`, `all` |
```json
{}
{ "checks": ["unprocessed_signals", "unattached_needs"] }
```
---
### `update_node`
Update properties on an existing node by ID. Merges with existing properties — only specified fields change, unmentioned fields are preserved. Re-generates embedding if `content` or `name` changes. Cannot overwrite `id` or `created_at`.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | string | Yes | Node ID (UUID) to update |
| `properties` | object | Yes | Properties to set or update — merged with existing |
```json
{ "id": "550e8400-e29b-41d4-a716-446655440000", "properties": { "status": "resolved_into_update" } }
{ "id": "550e8400-e29b-41d4-a716-446655440000", "properties": { "name": "Updated Name", "content": "New description triggers re-embedding" } }
```
---
### `delete_node`
Delete a node and all its relationships. Irreversible.
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | string | Yes | Node ID (UUID) to delete |
```json
{ "id": "550e8400-e29b-41d4-a716-446655440000" }
```
## Security & Privacy
**ESIM is designed for local, single-user environments.** It runs as a stdio MCP server — the AI client and ESIM communicate over standard input/output on your local machine. There is no network authentication layer.
Do not expose ESIM over a network without adding your own authentication. Anyone with access to the MCP transport can read and write all data in the graph.
**Data flow awareness:**
- By default, node content is sent to your configured LLM API (OpenAI, OpenRouter, etc.) for embedding generation and metadata extraction.
- If you need zero external data sharing, use the fully local setup with Ollama — all processing stays on your machine.
- All graph data is stored in your Neo4j instance. You are responsible for securing it.
## Project Structure
```
src/
main.ts — Entry point
server.ts — MCP tool registrations
db.ts — Neo4j connection and query runner
queries.ts — Cypher query builders
types.ts — TypeScript types and constants
llm.ts — LLM integration (embeddings + metadata)
classify.ts — Content classification
context.ts — Context reconstruction
env.ts — Environment variable loading
schema.ts — Schema setup script
```
## Troubleshooting
**"Missing NEO4J_DB_CONNECTION_URI"** — Environment file not found. Check that `~/.config/env/esim.env` exists, or set `ESIM_ENV_FILE` explicitly:
```bash
ESIM_ENV_FILE=/path/to/your/.env deno task setup
```
**"Neo4j connection failed"** — Docker not running or wrong URI. Verify Neo4j is up:
```bash
docker compose ps # should show neo4j running
docker compose up -d # restart if needed
```
**"Embedding request failed (401)"** — Invalid `LLM_API_KEY` or wrong `LLM_BASE_URL`. Verify your API credentials with your provider.
**"Vector index created with wrong dimensions"** — `LLM_EMBEDDING_DIMENSIONS` doesn't match your model. Drop and recreate:
```bash
deno run --allow-net --allow-env --allow-read --allow-sys scripts/drop-indexes.ts
deno task setup
```
**Claude Code doesn't see ESIM tools** — Re-register and verify:
```bash
claude mcp remove esim
claude mcp add esim -- deno run --allow-net --allow-env --allow-read --allow-sys /absolute/path/to/esim/src/main.ts
claude mcp list
```
## Skills
ESIM ships with [Claude Code](https://claude.com/claude-code) skills in [`skills/`](skills/) that turn the raw graph into a guided, structured-intent practice:
- **[`purpose-discovery`](skills/purpose-discovery/SKILL.md)** — facilitates a first structured-intent session: discover an entity's core purpose, constraint stack, and foundational graph.
- **[`session-protocol`](skills/session-protocol/SKILL.md)** — an operating protocol that governs how the assistant loads context, captures signals, and keeps the graph synchronized during any ESIM session.
- **[`signal-processing`](skills/signal-processing/SKILL.md)** — processes captured signals into graph-state updates so the graph stays coherent and complete.
See [`skills/README.md`](skills/README.md) for how to load them.
## License
Apache 2.0 — see [LICENSE](LICENSE).
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available