# How KnowledgeGraphManager Loads and Saves the Graph in the MCP Memory Server

> Learn how KnowledgeGraphManager loads and saves graphs in the MCP Memory Server using JSON Lines for efficient, on-demand data management and atomic saves.

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

---

**The KnowledgeGraphManager class persists knowledge graphs as JSON Lines in a local file, loading entities and relations on demand and atomically saving mutations back to disk.**

The MCP Memory server from the `modelcontextprotocol/servers` repository maintains persistent knowledge using a graph structure of interconnected entities. At the core of this persistence layer lies the `KnowledgeGraphManager` class implemented in [`src/memory/index.ts`](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts), which handles all disk I/O operations for the knowledge graph. Understanding how this manager loads and saves data is essential for developers building tools that interact with the memory system or extending the server's capabilities.

## Determining the Memory File Path

Before any loading or saving occurs, the server must resolve where to store the graph data. The `ensureMemoryFilePath()` function handles this initialization logic at startup.

- If the `MEMORY_FILE_PATH` environment variable is set, the function uses that value directly, resolving relative paths against the script location
- If unset, it defaults to `memory.jsonl` in the working directory and migrates any legacy [`memory.json`](https://github.com/modelcontextprotocol/servers/blob/main/memory.json) file if present
- The resolved path is stored in the global `MEMORY_FILE_PATH` variable and passed to the `KnowledgeGraphManager` instance during initialization

This resolution happens during server startup (lines 14-44 of [`src/memory/index.ts`](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts)), ensuring the manager knows exactly which file to monitor before handling any tool requests.

## Loading the Knowledge Graph

When MCP tools like `read_graph` or `search_nodes` request data, they invoke `knowledgeGraphManager.loadGraph()`. This method implements a streaming JSON Lines parser that reconstructs the graph object from disk.

The method reads the entire file as UTF-8 text and splits it into non-empty lines. Each line represents a single JSON record containing either an entity or relation, distinguished by a `type` field. Entities are identified when `item.type === "entity"` and pushed to `graph.entities`, while relations are identified when `item.type === "relation"` and pushed to `graph.relations`.

If the file does not exist, the method catches the `ENOENT` error and gracefully returns an empty graph structure (`{ entities: [], relations: [] }`), allowing the server to start fresh without crashing.

### Load Implementation Details

According to the source code at lines 71-93 of [`src/memory/index.ts`](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts), the `loadGraph` method operates as follows:

```typescript
private async loadGraph(): Promise<KnowledgeGraph> {
  try {
    const data = await fs.readFile(this.memoryFilePath, "utf-8");
    const lines = data.split("\n").filter(l => l.trim() !== "");
    return lines.reduce((graph, line) => {
      const item = JSON.parse(line);
      if (item.type === "entity") {
        graph.entities.push({ name: item.name, entityType: item.entityType, observations: item.observations });
      }
      if (item.type === "relation") {
        graph.relations.push({ from: item.from, to: r.to, relationType: item.relationType });
      }
      return graph;
    }, { entities: [], relations: [] });
  } catch (error) {
    if ((error as any).code === "ENOENT") return { entities: [], relations: [] };
    throw error;
  }
}

```

## Saving the Knowledge Graph

Mutations such as `create_entities` or `delete_relations` trigger `saveGraph(graph)`, which serializes the entire in-memory graph back to disk. This method ensures that every entity and relation is written with its type discriminator to maintain the JSON Lines format.

The method maps every entity to a JSON string prefixed with `type: "entity"` and every relation with `type: "relation"`, joining all records with newline characters. This write pattern replaces the entire file content, ensuring that deletions are properly persisted and the file remains valid even if the process encounters errors during the operation.

### Save Implementation Details

The `saveGraph` implementation from lines 101-117 of [`src/memory/index.ts`](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts) demonstrates the serialization logic:

```typescript
private async saveGraph(graph: KnowledgeGraph): Promise<void> {
  const lines = [
    ...graph.entities.map(e => JSON.stringify({ type: "entity", name: e.name, entityType: e.entityType, observations: e.observations })),
    ...graph.relations.map(r => JSON.stringify({ type: "relation", from: r.from, to: r.to, relationType: r.relationType })),
  ];
  await fs.writeFile(this.memoryFilePath, lines.join("\n"));
}

```

## Integration with MCP Tools

The persistence layer integrates seamlessly with the Model Context Protocol tool system through a consistent load-modify-save pattern. Read-only tools simply call `loadGraph()` and return the result, while mutation tools follow a specific transaction flow.

When handling a mutation request:

1. The tool handler calls `loadGraph()` to retrieve the current state from `memory.jsonl`
2. It modifies the in-memory graph by adding or removing entities and relations
3. It invokes `saveGraph(updatedGraph)` to persist the changes back to disk

This pattern guarantees that the in-memory representation always remains synchronized with the on-disk JSON Lines file, preventing data loss across server restarts.

## Summary

- **File resolution**: The `ensureMemoryFilePath()` function determines the storage location via the `MEMORY_FILE_PATH` environment variable or defaults to `memory.jsonl`
- **JSON Lines format**: The graph stores entities and relations as newline-delimited JSON records with `type` discriminators for efficient streaming
- **Graceful initialization**: `loadGraph()` returns empty graphs for missing files by catching `ENOENT` errors, allowing clean server starts
- **Atomic persistence**: `saveGraph()` writes the complete graph as a single string operation, ensuring the file is replaced atomically
- **Synchronous consistency**: All tool operations follow load-modify-save cycles to maintain alignment between disk and memory states

## Frequently Asked Questions

### What file format does the Memory server use to store knowledge graphs?

The Memory server uses **JSON Lines (JSONL)** format in a file named `memory.jsonl`. Each line represents a single JSON object—either an entity with `type: "entity"` or a relation with `type: "relation"`—which allows the `KnowledgeGraphManager` to stream data efficiently without parsing an entire large JSON object into memory.

### How does the KnowledgeGraphManager handle missing memory files?

When `loadGraph()` encounters an `ENOENT` error indicating the file does not exist, it returns an empty graph structure containing empty arrays for entities and relations rather than throwing an exception. This allows the server to start cleanly without existing data and create the file automatically upon the first save operation.

### Can I change the location of the memory file?

Yes. Set the `MEMORY_FILE_PATH` environment variable before starting the server. The `ensureMemoryFilePath()` function in [`src/memory/index.ts`](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts) checks this variable first and resolves relative paths against the script location, giving you full control over where the persistence file resides.

### Is the save operation atomic?

The `saveGraph()` method writes the entire graph content as a single string joined by newlines using `fs.writeFile()`, which typically replaces the file atomically at the filesystem level. This prevents file corruption during write operations, ensuring that the `memory.jsonl` file remains valid even if the process crashes during the save.