Skip to content

Latest commit

 

History

History
284 lines (205 loc) · 9.76 KB

File metadata and controls

284 lines (205 loc) · 9.76 KB
layout default
title Chapter 4: Memory Server
nav_order 4
parent MCP Servers Tutorial

Chapter 4: Memory Server

Welcome to Chapter 4: Memory Server. In this part of MCP Servers Tutorial: Reference Implementations and Patterns, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.

The memory server is a clean reference for persistent, structured memory using a local knowledge graph.

Data Model

The official model uses three primitives:

  • Entities: named nodes with types and observations
  • Relations: directed edges between entities
  • Observations: atomic facts attached to entities

This encourages explicit memory structure instead of opaque long-context accumulation.

Tool Groups

The server groups operations into:

  • create: entities and relations
  • update: add observations
  • delete: entities/relations/observations
  • query: search nodes, open nodes, read entire graph

This surface is small but expressive enough for many memory patterns.

Design Advantages

  • easy to inspect and debug state
  • selective deletion and correction
  • relation-based retrieval beyond plain text search
  • portable JSON-like structures for external storage

Operational Caveats

Memory quality degrades quickly without governance.

Add controls for:

  • duplicate entities and alias handling
  • contradictory observations
  • stale relations
  • source attribution and confidence labels

Prompting Pattern

A practical pattern is:

  1. retrieve relevant nodes first
  2. reason with retrieved memory
  3. apply bounded updates only when confidence is high

Avoid writing memory for every interaction. Quality beats quantity.

Summary

You now understand how graph-based memory differs from ad-hoc conversation history and why it can be productionized more safely.

Next: Chapter 5: Multi-Language Servers

What Problem Does This Solve?

Most teams struggle here because the hard part is not writing more code, but deciding clear boundaries for core abstractions in this chapter so behavior stays predictable as complexity grows.

In practical terms, this chapter helps you avoid three common failures:

  • coupling core logic too tightly to one implementation path
  • missing the handoff boundaries between setup, execution, and validation
  • shipping changes without clear rollback or observability strategy

After working through this chapter, you should be able to reason about Chapter 4: Memory Server as an operating subsystem inside MCP Servers Tutorial: Reference Implementations and Patterns, with explicit contracts for inputs, state transitions, and outputs.

Use the implementation notes around execution and reliability details as your checklist when adapting these patterns to your own repository.

How it Works Under the Hood

Under the hood, Chapter 4: Memory Server usually follows a repeatable control path:

  1. Context bootstrap: initialize runtime config and prerequisites for core component.
  2. Input normalization: shape incoming data so execution layer receives stable contracts.
  3. Core execution: run the main logic branch and propagate intermediate state through state model.
  4. Policy and safety checks: enforce limits, auth scopes, and failure boundaries.
  5. Output composition: return canonical result payloads for downstream consumers.
  6. Operational telemetry: emit logs/metrics needed for debugging and performance tuning.

When debugging, walk this sequence in order and confirm each stage has explicit success/failure conditions.

Source Walkthrough

Use the following upstream sources to verify implementation details while reading this chapter:

  • MCP servers repository Why it matters: authoritative reference on MCP servers repository (github.com).

Suggested trace strategy:

  • search upstream code for Memory and Server to map concrete implementation paths
  • compare docs claims against actual runtime/config code before reusing patterns in production

Chapter Connections

Source Code Walkthrough

src/memory/index.ts

The ensureMemoryFilePath function in src/memory/index.ts handles a key part of this chapter's functionality:

// Handle backward compatibility: migrate memory.json to memory.jsonl if needed
export async function ensureMemoryFilePath(): Promise<string> {
  if (process.env.MEMORY_FILE_PATH) {
    // Custom path provided, use it as-is (with absolute path resolution)
    return path.isAbsolute(process.env.MEMORY_FILE_PATH)
      ? process.env.MEMORY_FILE_PATH
      : path.join(path.dirname(fileURLToPath(import.meta.url)), process.env.MEMORY_FILE_PATH);
  }
  
  // No custom path set, check for backward compatibility migration
  const oldMemoryPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'memory.json');
  const newMemoryPath = defaultMemoryPath;
  
  try {
    // Check if old file exists and new file doesn't
    await fs.access(oldMemoryPath);
    try {
      await fs.access(newMemoryPath);
      // Both files exist, use new one (no migration needed)
      return newMemoryPath;
    } catch {
      // Old file exists, new file doesn't - migrate
      console.error('DETECTED: Found legacy memory.json file, migrating to memory.jsonl for JSONL format compatibility');
      await fs.rename(oldMemoryPath, newMemoryPath);
      console.error('COMPLETED: Successfully migrated memory.json to memory.jsonl');
      return newMemoryPath;
    }
  } catch {
    // Old file doesn't exist, use new path
    return newMemoryPath;
  }

This function is important because it defines how MCP Servers Tutorial: Reference Implementations and Patterns implements the patterns covered in this chapter.

src/memory/index.ts

The main function in src/memory/index.ts handles a key part of this chapter's functionality:

);

async function main() {
  // Initialize memory file path with backward compatibility
  MEMORY_FILE_PATH = await ensureMemoryFilePath();

  // Initialize knowledge graph manager with the memory file path
  knowledgeGraphManager = new KnowledgeGraphManager(MEMORY_FILE_PATH);

  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Knowledge Graph MCP Server running on stdio");
}

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
});

This function is important because it defines how MCP Servers Tutorial: Reference Implementations and Patterns implements the patterns covered in this chapter.

src/memory/index.ts

The Entity interface in src/memory/index.ts handles a key part of this chapter's functionality:

// We are storing our memory using entities, relations, and observations in a graph structure
export interface Entity {
  name: string;
  entityType: string;
  observations: string[];
}

export interface Relation {
  from: string;
  to: string;
  relationType: string;
}

export interface KnowledgeGraph {
  entities: Entity[];
  relations: Relation[];
}

// The KnowledgeGraphManager class contains all operations to interact with the knowledge graph
export class KnowledgeGraphManager {
  constructor(private memoryFilePath: string) {}

  private async loadGraph(): Promise<KnowledgeGraph> {
    try {
      const data = await fs.readFile(this.memoryFilePath, "utf-8");
      const lines = data.split("\n").filter(line => line.trim() !== "");
      return lines.reduce((graph: KnowledgeGraph, line) => {
        const item = JSON.parse(line);
        if (item.type === "entity") {
          graph.entities.push({
            name: item.name,

This interface is important because it defines how MCP Servers Tutorial: Reference Implementations and Patterns implements the patterns covered in this chapter.

src/memory/index.ts

The Relation interface in src/memory/index.ts handles a key part of this chapter's functionality:

}

export interface Relation {
  from: string;
  to: string;
  relationType: string;
}

export interface KnowledgeGraph {
  entities: Entity[];
  relations: Relation[];
}

// The KnowledgeGraphManager class contains all operations to interact with the knowledge graph
export class KnowledgeGraphManager {
  constructor(private memoryFilePath: string) {}

  private async loadGraph(): Promise<KnowledgeGraph> {
    try {
      const data = await fs.readFile(this.memoryFilePath, "utf-8");
      const lines = data.split("\n").filter(line => line.trim() !== "");
      return lines.reduce((graph: KnowledgeGraph, 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({

This interface is important because it defines how MCP Servers Tutorial: Reference Implementations and Patterns implements the patterns covered in this chapter.

How These Components Connect

flowchart TD
    A[ensureMemoryFilePath]
    B[main]
    C[Entity]
    D[Relation]
    E[KnowledgeGraph]
    A --> B
    B --> C
    C --> D
    D --> E
Loading