Featured image of post Server Memory for MCP: Knowledge Graph Memory in VSCode Copilot

Server Memory for MCP: Knowledge Graph Memory in VSCode Copilot

The MCP Memory Server gives VSCode Copilot a persistent knowledge graph. This post walks through a real workflow: seed facts in one session, retrieve them in a new one, and inspect memory.jsonl on disk.

Every time you open a new Copilot chat, the model starts fresh. It does not know that yesterday you picked PostgreSQL, that you prefer Minimal APIs, or that your project is called MemorySample. Large language models are stateless — they only see what fits in the current context window, and a new session starts with an empty one.

The MCP Memory Server changes this. It is the official reference implementation of persistent memory for MCP clients. It stores facts as a local knowledge graph of entities, relations, and observations in a plain-text memory.jsonl file, and exposes tools that Copilot can call to save and retrieve those facts.

In this post, we will walk through a real VSCode workflow: we teach Copilot a few facts about ourselves and our project in one session, close the chat, open a brand-new session, and ask a question that only makes sense if Copilot still remembers the previous context. Then we will inspect the underlying file and look at what makes memory actually work well.

What You Will Build

You will teach Copilot a few facts about yourself and your project in Session 1, close the chat, open a Session 2, and ask a question that only makes sense if Copilot still remembers the previous context. Then you will inspect the underlying memory.jsonl file to see how the data is stored.

1
2
3
Session 1: "I work on a .NET microservice called MemorySample. I use .NET 10, EF Core with PostgreSQL, and I prefer WolverineFx over MassTransit."
Session 2: "Suggest an architecture for the next feature in MemorySample."
            → Copilot answers using .NET 10, EF Core, PostgreSQL, WolverineFx, and Minimal APIs.

Everything in this post was verified against the real server — the tool calls, the JSON responses, and the memory.jsonl content below come from running @modelcontextprotocol/server-memory and driving it over MCP.

How the Memory Server Works

Before the step-by-step walkthrough, it helps to understand what the server actually does under the hood. The MCP Memory Server is a small Node.js process that speaks the MCP protocol over standard input/output. Copilot launches it through the mcp.json configuration and then calls its tools like any other MCP tool. The same server works with any MCP client — Claude Desktop, VSCode Copilot, Cursor — because they all speak the same protocol.

The Knowledge Graph Model

Memory is stored as a knowledge graph with three building blocks:

  • Entity — a node. It has a unique name, an entityType (for example person, project, technology_stack), and a list of observations.
  • Relation — a directed edge between two entities. It has from, to, and a relationType written in active voice (for example works_on).
  • Observation — an atomic fact attached to an entity (one fact per observation).

This is why the memory server can answer questions like “what tech stack do I prefer?” — the facts live on the John_Doe entity, and the relations tell Copilot how that entity connects to your projects and tools.

graph LR A["Session 1
You share facts"] -->|"create_entities
create_relations"| B["memory.jsonl
entities + relations"] B -->|"read_graph
search_nodes
open_nodes"| C["Session 2
New chat"] C --> D["Contextual answer
without repeating facts"]

Storage: A File, Not a Database

The most common question is: does the memory server use a database? No. There is no database server, no connection string, and no schema migration. You cannot plug in PostgreSQL, SQLite, or MongoDB — the official server is deliberately file-based. Storage is a single plain-text JSONL file — one JSON object per line. By default the server writes memory.jsonl next to its own package files, which is why you must set MEMORY_FILE_PATH to point at a stable location inside your project.

The file has two advantages:

  • Portable — copy the file and the memory moves with it. You can commit it to your repository or back it up like any text file.
  • Human-readable — you can open it in any editor and see exactly what the model knows about you.

Every time Copilot calls a tool, the server reads the file, applies the change in memory, and writes the whole file back. The file is the database.

Alternatives With Database Storage

If you need more than a single JSONL file, several MCP-compatible memory servers store data in a real database or vector store:

  • mem0-mcp — Mem0 is an open-source memory layer for AI apps. Its MCP server can persist facts in PostgreSQL, SQLite, or a vector store, and it supports semantic search over past conversations.
  • AgentMemory — Built on Upstash Redis and vector indexes. It is designed for agent-style workflows where memory is queried by semantic similarity rather than by exact entity name.
  • Context-Mode — A hosted memory service with a database backend. It keeps user facts, preferences, and project context in a managed store and exposes them to any MCP client.

The official MCP Memory Server keeps storage simple: it uses a plain JSONL file, not a database.

Why Memory Survives Across Sessions

A chat session is ephemeral — it is a window into a conversation and disappears when you close it. The memory file is not. This is the key to the whole system:

  • All sessions share one file. Every Copilot chat starts the server with the same MEMORY_FILE_PATH, so every session reads and writes the same memory.jsonl.
  • The file outlives the chat. When you close Session 1, the chat history is gone, but the file stays on disk with everything the model saved.
  • Nothing is lost between sessions. Each tool call performs a read-modify-write: the server reads the whole file, applies your change, and writes the file back. Facts added in Session 1 are still there when Session 3 or Session 10 starts.
  • Retrieval is on demand. In a new session the model does not automatically know the old facts. It pulls them back with read_graph, search_nodes, or open_nodes — that is how a new session “remembers” what an old session learned.

So the model is still stateless — but the memory is not. The file is the bridge between sessions, and the tools are how Copilot crosses it.

Prerequisites

  • VSCode with the GitHub Copilot extension.
  • Node.js installed so npx works.
  • The MCP Memory Server package: @modelcontextprotocol/server-memory.

Step 1: Configure the Memory Server in VSCode

Open the Command Palette with Ctrl + Shift + P and run MCP: Open User Configuration (or add a .vscode/mcp.json file to your project). Add the memory server:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "servers": {
    "memory": {
      "command": "cmd",
      "args": [
        "/c",
        "npx",
        "-y",
        "@modelcontextprotocol/server-memory"
      ],
      "env": {
        "MEMORY_FILE_PATH": "C:\\Users\\YourName\\projects\\my-project\\memory.jsonl"
      }
    }
  }
}

On macOS or Linux, replace the command with npx directly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "servers": {
    "memory": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-memory"
      ],
      "env": {
        "MEMORY_FILE_PATH": "/home/yourname/projects/my-project/memory.jsonl"
      }
    }
  }
}

Replace the path with the real absolute path where you want the memory file to live, and put it somewhere stable that will not be deleted or cleaned up.

Here is what each part of the configuration means:

  • command and args — how VSCode launches the server process. npx runs the npm package; on Windows it must be wrapped with cmd /c.
  • env.MEMORY_FILE_PATH — the absolute path of the memory.jsonl file where all memory is stored. This is the most important line in the whole file.

MCP memory server in VSCode

Most important detail #1: Always set MEMORY_FILE_PATH to a stable, absolute path. If you skip this, the server writes memory.jsonl next to the package cache and your memory can disappear on the next npx install.

After saving, open the Copilot Chat side panel. You should see the memory server listed as an available tool. If it is missing, reload VSCode with Developer: Reload Window.

Memory server available in Copilot Chat

Step 2: Seed Memory in Session 1

Start a new Copilot Chat and tell it about your project. Be explicit so the model extracts the right entities.

You:

1
2
3
4
5
6
Remember these facts for later:
- My name is John Doe.
- I work on a .NET microservice project called MemorySample.
- MemorySample uses a Vertical Slice Architecture with feature folders.
- My preferred stack is .NET 10, EF Core with PostgreSQL, Minimal APIs, and WolverineFx for messaging.
- I prefer xunit.v3 for tests.

Copilot will call create_entities and create_relations on your behalf. The resulting knowledge graph looks like this:

Entity Type Observations
John_Doe person Prefers vertical slice architecture; likes WolverineFx over MassTransit; uses PostgreSQL; favorite IDE is VSCode
MemorySample_Project project .NET 10 microservice; Vertical Slice Architecture with feature folders; tested with xunit.v3
DotNet_TechStack technology_stack .NET 10; EF Core + PostgreSQL; Minimal APIs; xunit.v3

Relations:

  • John_Doeworks_onMemorySample_Project
  • John_DoeprefersDotNet_TechStack

Under the hood, Copilot sends a tools/call request to the memory server. This is the real request and response from memory-server v0.6.3:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
  "name": "create_entities",
  "arguments": {
    "entities": [
      { "name": "John_Doe", "entityType": "person", "observations": [
        "Prefers vertical slice architecture in .NET",
        "Likes WolverineFx over MassTransit for transactional messaging",
        "Uses PostgreSQL as default database",
        "Favorite IDE is VSCode with Copilot"
      ]},
      { "name": "MemorySample_Project", "entityType": "project", "observations": [
        "Is a .NET 10 microservice",
        "Uses Vertical Slice Architecture with feature folders",
        "Is tested with xunit.v3"
      ]},
      { "name": "DotNet_TechStack", "entityType": "technology_stack", "observations": [
        "Primary framework is .NET 10",
        "Uses Entity Framework Core with PostgreSQL",
        "Prefers Minimal APIs over controllers",
        "Uses xunit.v3 for testing"
      ]}
    ]
  }
}

Session 1: seeding memory with create_entities

After the chat, close the panel. The data is already on disk.

Step 3: Inspect the Memory File

Open the file at MEMORY_FILE_PATH. Each line is a JSON object. Entities are stored with a type field of entity, relations with type of relation — this is the JSONL format used by the current version of the server:

{"type":"entity","name":"John_Doe","entityType":"person","observations":["Prefers vertical slice architecture in .NET","Likes WolverineFx over MassTransit for transactional messaging","Uses PostgreSQL as default database","Favorite IDE is VSCode with Copilot"]}
{"type":"entity","name":"MemorySample_Project","entityType":"project","observations":["Is a .NET 10 microservice","Uses Vertical Slice Architecture with feature folders","Is tested with xunit.v3"]}
{"type":"entity","name":"DotNet_TechStack","entityType":"technology_stack","observations":["Primary framework is .NET 10","Uses Entity Framework Core with PostgreSQL","Prefers Minimal APIs over controllers","Uses xunit.v3 for testing"]}
{"type":"relation","from":"John_Doe","to":"MemorySample_Project","relationType":"works_on"}
{"type":"relation","from":"John_Doe","to":"DotNet_TechStack","relationType":"prefers"}

This file is the entire secret. It is plain text, local, and portable. You can back it up, version it, or move it to another machine — the memory comes with it.

Most important detail #2: Memory is only as good as the file path. Treat memory.jsonl like a small database. Back it up.

Step 4: Use Memory in Session 2

Open a new Copilot Chat. Do not repeat the project facts. Ask something that depends on them.

You:

1
I want to add a new feature to MemorySample. Suggest the architecture and tech stack.

Copilot starts by calling read_graph or search_nodes. Here is the real search_nodes call and response when we searched for WolverineFx — notice how the server returns the matching entity together with its relations:

1
2
3
4
{
  "name": "search_nodes",
  "arguments": { "query": "WolverineFx" }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "entities": [
    {
      "name": "John_Doe",
      "entityType": "person",
      "observations": [
        "Prefers vertical slice architecture in .NET",
        "Likes WolverineFx over MassTransit for transactional messaging",
        "Uses PostgreSQL as default database",
        "Favorite IDE is VSCode with Copilot"
      ]
    }
  ],
  "relations": [
    { "from": "John_Doe", "to": "MemorySample_Project", "relationType": "works_on" },
    { "from": "John_Doe", "to": "DotNet_TechStack", "relationType": "prefers" }
  ]
}

It finds John_Doe, MemorySample_Project, and DotNet_TechStack. The answer is now contextual instead of generic.

Copilot (example response):

1
2
3
4
5
6
7
8
For the new feature in MemorySample, I would suggest:
- .NET 10 Minimal API as the host.
- Vertical Slice Architecture with feature folders.
- EF Core 10 with PostgreSQL for persistence.
- WolverineFx for messaging if the feature needs durable handlers or outbox.
- xunit.v3 with TestContainers for integration tests.

This matches your existing DotNet_TechStack and the style you already use in MemorySample_Project.

Session 2: Copilot retrieving memory before answering

Notice that the model did not ask “What is your stack?” It answered directly because the memory server bridged the two sessions.

Step 5: Add More Facts Across Sessions

Memory gets more useful over time. In a third session, you add a new observation.

You:

1
Also remember: I am currently learning about agent memory systems and writing documentation about the MCP Memory Server.

Copilot calls add_observations on John_Doe. Again, the real request and response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "name": "add_observations",
  "arguments": {
    "observations": [
      {
        "entityName": "John_Doe",
        "contents": [
          "Currently learning about MCP memory systems",
          "Writing documentation about the MCP Memory Server"
        ]
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
[
  {
    "entityName": "John_Doe",
    "addedObservations": [
      "Currently learning about MCP memory systems",
      "Writing documentation about the MCP Memory Server"
    ]
  }
]

In a fourth session, you can ask:

1
What am I currently learning about?

Copilot searches the graph, finds the observation, and answers without you repeating the topic.

What Makes Memory Work Well

The memory server is simple, but a few patterns decide whether it helps or gets in the way. These are the most important parts of using memory.

1. Atomic Observations

One fact per observation. Good:

1
2
"Uses PostgreSQL as default database"
"Prefers Minimal APIs over controllers"

Bad:

1
"Uses PostgreSQL and Minimal APIs and likes WolverineFx"

Atomic facts make search precise and deletions safe.

2. Stable, Unique Entity Names

Use John_Doe instead of John. If a second John appears, the graph still works.

3. Active Voice Relations

Relations are directed. Use works_on, prefers, subscribed_to. Avoid passive forms like is_used_by where the direction is unclear.

4. A System Prompt That Triggers Memory

Without guidance, the model may not call read_graph at the start of a chat. Add custom instructions in VSCode:

1
2
3
Before answering, retrieve relevant facts from the memory knowledge graph.
When you learn new facts about the user, their projects, or their preferences,
create entities and relations, and store atomic observations.

Custom instructions to trigger memory usage

Most important detail #3: Memory does nothing unless the model is reminded to use it. A system prompt or custom instruction is required.

Common Pitfalls

Problem Cause Fix
Memory is lost after restart MEMORY_FILE_PATH not set or points to a temp directory Use an absolute path in mcp.json
Copilot ignores memory No custom instruction triggers retrieval Add a system prompt
Graph grows with noise Observations are too long or duplicated Keep facts atomic, one per line
Search returns wrong nodes Entity names are too generic or change between sessions Use stable, unique names

Conclusion

The MCP Memory Server turns Copilot from a stateless chat into an assistant that remembers your project across days. The workflow is simple: configure the server with a stable MEMORY_FILE_PATH, seed facts in one session, and let read_graph, search_nodes, and open_nodes bring that context into every new session.

The three most important details to remember:

  1. The file path is everything — point MEMORY_FILE_PATH at a stable absolute location and back it up like a database.
  2. Atomic, well-named data — one fact per observation, stable entity names, active-voice relations.
  3. A prompt that triggers memory — the model only uses the tools if you tell it to.

Reference

Built with Hugo
Theme Stack designed by Jimmy