# How the MCP Memory Server Stores Entities, Relations, and Observations

> Discover how the MCP Memory server stores entities, relations, and observations using JSON Lines files and an in-memory KnowledgeGraph for efficient data management and persistence.

- Repository: [Model Context Protocol/servers](https://github.com/modelcontextprotocol/servers)
- Tags: internals
- Published: 2026-03-01

---

**The MCP Memory server persists entities, relations, and observations as discrete JSON objects in a JSON Lines (JSONL) file, loading them into an in-memory `KnowledgeGraph` structure that is atomically rewritten to disk after every mutation.**

The `modelcontextprotocol/servers` repository implements a lightweight, file-based knowledge graph for AI agent memory. This server stores structured data—entities with their observations and the relations between them—using a simple JSONL format that eliminates external database dependencies while maintaining data integrity through atomic write operations.

## Storage Architecture and File Format

The MCP Memory server adopts a flat-file persistence strategy centered on the JSON Lines (JSONL) format. This approach treats each line of the storage file as an independent, valid JSON object, enabling efficient append operations and line-by-line streaming without requiring complex parsing logic.

### The JSON Lines Persistence Layer

According to the implementation in [`src/memory/index.ts`](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts), the server defaults to a file named `memory.jsonl` in the working directory. Developers can override this location by setting the `MEMORY_FILE_PATH` environment variable. The `ensureMemoryFilePath()` function (lines 10-25) resolves the final path and automatically migrates legacy [`memory.json`](https://github.com/modelcontextprotocol/servers/blob/main/memory.json) files to the current JSONL format, ensuring backward compatibility.

### Data Models for Entities and Relations

The storage schema distinguishes between two primary record types, defined by TypeScript interfaces at lines 49-65:

**Entity records** store nodes in the graph with the following fields:
- `type: "entity"` (type discriminator)
- `name`: Unique identifier string
- `entityType`: Categorical classification (e.g., "person", "organization")
- `observations`: Array of textual notes or timestamped observations

**Relation records** store directed edges between entities:
- `type: "relation"` (type discriminator)
- `from`: Name of the source entity
- `to`: Name of the target entity
- `relationType`: Semantic descriptor defining the connection (e.g., "employee_of")

## Loading and Persistence Mechanisms

The `KnowledgeGraphManager` class encapsulates all storage I/O, maintaining the working graph state in memory while ensuring durable, consistent disk writes.

### Initializing the Knowledge Graph

The `loadGraph()` method (lines 71-92) reads the JSONL file line-by-line using Node.js streaming interfaces. It parses each JSON object and populates the internal `KnowledgeGraph` structure, which maintains separate arrays for `entities` and `relations`. This separation enables O(1) entity lookup by name and efficient relation traversal without full file rescans during operations.

### Atomic Write Operations

When the graph mutates, `saveGraph()` (lines 101-117) serializes the current `entities` and `relations` arrays to JSONL format—converting each object to a string line—and writes the entire file atomically. This write-replace strategy ensures that the on-disk representation is always transactionally consistent; readers never encounter partially written JSON or corrupted intermediate states.

## CRUD Operations on the Knowledge Graph

The manager exposes specific methods for manipulating the graph, all following the pattern: validate and modify the in-memory arrays, then invoke `saveGraph()` to persist.

### Creating and Managing Entities

The `createEntities()` method accepts an array of `Entity` objects, validates uniqueness against existing names to prevent collisions, appends valid entities to the internal array, and triggers persistence. The `deleteEntities()` method removes entities by name and automatically cascades deletions to any relations referencing those entities, maintaining referential integrity.

### Defining Relations Between Entities

Relations are created via `createRelations()`, which validates that both `from` and `to` entities exist in the current graph before adding the directed edge. The `deleteRelations()` method filters the relations array by source entity, target entity, or relation type criteria, removing matching edges before saving.

### Adding and Removing Observations

Observations attach contextual text data to entities. The `addObservations()` method accepts an array of objects containing `entityName` and `contents` (a string array), appending these strings to the target entity's `observations` property. Deletion supports removing specific observation strings by exact match or clearing all observations from a specified entity.

## MCP Tool Integration

These storage primitives are exposed as Model Context Protocol (MCP) tools registered in [`src/memory/index.ts`](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts), enabling AI agents to manipulate the graph through standardized interfaces:

- **`create_entities`**: Wraps `createEntities()` to add new nodes
- **`create_relations`**: Wraps `createRelations()` to establish connections
- **`add_observations`**: Wraps `addObservations()` to append entity metadata
- **`read_graph`**: Returns the complete `KnowledgeGraph` object
- **`search_nodes`**: Filters entities by name or observation content using substring matching
- **`open_nodes`**: Retrieves specific entities by exact name lookup

## Summary

- The MCP Memory server uses a **JSON Lines (JSONL)** file format, defaulting to `memory.jsonl` or using a custom path specified via `MEMORY_FILE_PATH`.
- Storage records are polymorphic JSON objects distinguished by a `type` field: **entities** contain `name`, `entityType`, and `observations[]`, while **relations** contain `from`, `to`, and `relationType`.
- The `KnowledgeGraphManager` class loads data via `loadGraph()` (lines 71-92) and persists changes atomically via `saveGraph()` (lines 101-117).
- All write operations modify an in-memory copy of the graph before rewriting the entire JSONL file to ensure consistency.
- Legacy storage files are automatically detected and migrated from [`memory.json`](https://github.com/modelcontextprotocol/servers/blob/main/memory.json) to `memory.jsonl` during server initialization.

## Frequently Asked Questions

### What file format does the MCP Memory server use?

The server uses **JSON Lines (JSONL)**, where each line is a self-contained JSON object representing either an entity or a relation. As implemented in [`src/memory/index.ts`](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts), this format supports simple line-by-line parsing and atomic file replacement, ensuring the storage layer remains portable and free of external database dependencies.

### How does the MCP Memory server handle concurrent writes?

The server handles concurrency through **atomic file replacement** in the `saveGraph()` method. Rather than appending incrementally, the method serializes the entire current state to a buffer and writes it to disk atomically, ensuring that any process reading the file always sees a complete, valid JSONL structure rather than a partial write.

### Can I change the storage location for the memory file?

Yes. Set the `MEMORY_FILE_PATH` environment variable to specify an absolute or relative path. The `ensureMemoryFilePath()` function in [`src/memory/index.ts`](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts) (lines 10-25) resolves this path, creates parent directories if necessary, and handles migration of legacy files to the new location.

### What is the difference between an entity and a relation in MCP Memory?

An **entity** represents a node in the knowledge graph—such as a person, place, or concept—and maintains an array of **observations** (textual data). A **relation** represents a directed edge connecting two entities, defined by `from` (source name), `to` (target name), and `relationType` (semantic label). Both are stored as JSON objects in the same file, distinguished by their `type` property.