Skip to main content
Glama
README.md
# edumem-mcp

**Directory-scoped agent memory using markdown files** — an MCP server that gives LLM agents persistent, per-project memory without cluttering their context.

---

## Why edumem-mcp?

Agents forget everything between sessions. Dumping all memories into the context is messy and doesn't scale. `edumem-mcp` solves this by:

- **Scoping memory by directory** — The agent only sees memories relevant to the workspace it's working in.
- **Using plain Markdown files** — Human-readable, git-friendly, easy to edit or browse manually.
- **Supporting shared, read-only memories** — Configure `--self-mem` directories with global notes, coding conventions, or project knowledge that appear alongside workspace memories.
- **Zero enforced structure** — Write any markdown you want; no schemas, no templates, no restrictions.

---

## Installation

> **Note:** This package is not yet published to npm. Install directly from the git repository:

### Install globally from git

```bash
npm install -g github:SkillfulElectro/edumem-mcp
```

### Or run with npx (no install)

```bash
npx -y github:SkillfulElectro/edumem-mcp
```

### Clone and build locally

```bash
git clone https://github.com/SkillfulElectro/edumem-mcp.git
cd edumem-mcp
npm install
npm run build
```

---

## Configuration

Add to your MCP client config (Claude Desktop, Cursor, etc.):

```json
{
  "mcpServers": {
    "edumem-mcp": {
      "command": "npx",
      "args": ["-y", "github:SkillfulElectro/edumem-mcp"]
    }
  }
}
```

### ⚠️ Important: Do NOT use `npm start`

When running via an MCP client config, the server communicates over **stdin/stdout** using JSON-RPC. **`npm start` prints lifecycle banners to stdout** (like `> edumem-mcp@1.0.0 start`), which break the JSON-RPC protocol. Always launch the server directly:

```json
{
  "mcpServers": {
    "edumem-mcp": {
      "command": "npx",
      "args": ["-y", "github:SkillfulElectro/edumem-mcp"]
    }
  }
}
```

Or if running from a local clone:
```json
{
  "mcpServers": {
    "edumem-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/edumem-mcp/dist/index.js"]
    }
  }
}
```

### CLI Options

| Option | Default | Description |
|---|---|---|
| `--wname` | `.edumem-mcp` | Name of the per-workspace memory directory |
| `--self-mem` | *(none)* | Comma-separated list of absolute paths to directories whose `.md` files act as extra (read-only) memory sources |
| `--workspace`, `--prefix` | *(none)* | Default workspace directory. When set, the `workspace_path` tool parameter becomes **optional** and defaults to this path |

**Example with custom options:**

```json
{
  "mcpServers": {
    "edumem-mcp": {
      "command": "npx",
      "args": [
        "-y", "github:SkillfulElectro/edumem-mcp",
        "--wname", ".myproject-memory",
        "--self-mem", "/Users/alex/global-notes,/Users/alex/team-conventions",
        "--workspace", "/Users/alex/projects/myapp"
      ]
    }
  }
}
```

---

## How It Works

For any `workspace_path` the agent is working in, `edumem-mcp` looks for memory files in:

1. **Workspace memory:** `<workspace_path>/.edumem-mcp/*.md` (read/write by the agent)
2. **Self-mem directories:** Any directories passed via `--self-mem` (read-only by the agent)

The agent can list, read, write, delete, and search across all sources seamlessly.

---

## Tools

### `list_memories`
List all `.md` memory files visible from a workspace.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `workspace_path` | string | *(optional if --workspace set)* | The directory to list memories for |
| `recursive` | boolean | `false` | Include subdirectories of each memory source |

**Returns** an array of `{ name, source, size }` objects.

---

### `read_memory`
Read the full content of a memory file by name.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `workspace_path` | string | *(optional if --workspace set)* | Directory context for the read |
| `memory_name` | string | *(required)* | File name (e.g. `"setup"` or `"decisions/why-rust"`) |

Looks in workspace memory first, then self-mem directories. Returns the raw markdown content.

---

### `search_memories`
Search across all memory files for a query string (case-insensitive).

| Parameter | Type | Default | Description |
|---|---|---|---|
| `workspace_path` | string | *(optional if --workspace set)* | Directory context for the search |
| `query` | string | *(required)* | Substring to search for |
| `recursive` | boolean | `false` | Search in subdirectories too |
| `source_filter` | `"workspace"` \| `"all"` | `"all"` | Limit search to a specific source |

**Returns** an array of `{ name, source, snippet, line }` objects.

---

### `write_memory`
Create or update a memory file. Only writes to the workspace's own memory directory; cannot modify self-mem files.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `workspace_path` | string | *(optional if --workspace set)* | Where to write the memory |
| `memory_name` | string | *(required)* | File name (`.md` appended automatically if missing) |
| `content` | string | *(required)* | Markdown content to write |
| `mode` | `"overwrite"` \| `"append"` | `"overwrite"` | Write strategy |

---

### `delete_memory`
Delete a memory file from the workspace. Cannot delete self-mem files.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `workspace_path` | string | *(optional if --workspace set)* | Where to delete from |
| `memory_name` | string | *(required)* | Name of the file to delete |

---

## Usage Examples

### Agent writes a project decision
```
Tool: write_memory
Args: {
  "workspace_path": "/home/alex/projects/myapp",
  "memory_name": "decisions/why-postgres",
  "content": "## Why PostgreSQL\n\nChose PostgreSQL over MySQL for:\n- Better JSON support\n- Stronger ACID compliance\n- Familiar team experience"
}
Result: Memory "decisions/why-postgres.md" written successfully.
```

### Agent recalls past decisions
```
Tool: list_memories
Args: { "workspace_path": "/home/alex/projects/myapp" }
Result: [
  { "name": "decisions/why-postgres.md", "source": "workspace", "size": 156 },
  { "name": "setup-notes.md", "source": "workspace", "size": 89 }
]

Tool: read_memory
Args: { "workspace_path": "/home/alex/projects/myapp", "memory_name": "decisions/why-postgres.md" }
Result: ## Why PostgreSQL ...
```

### Agent searches for anything about "auth"
```
Tool: search_memories
Args: { "workspace_path": "/home/alex/projects/myapp", "query": "auth" }
Result: [
  "[workspace] setup-notes.md:12 — Configured OAuth2 with Google and GitHub",
  "[/Users/alex/global-notes] conventions.md:5 — All services must use the auth-gateway"
]
```

---

## Architecture

```
edumem-mcp/
├── src/
│   ├── index.ts           # Entry point: CLI parsing, MCP server, stdio transport
│   ├── memory-service.ts  # Core logic: file I/O, path security, source resolution
│   └── tools.ts           # MCP tool registrations with Zod schemas
├── dist/                  # Compiled JavaScript (published to npm)
├── package.json
├── tsconfig.json
└── README.md
```

---

## Security

- All file operations are restricted to allowed memory directories.
- Path traversal attacks (e.g. `../../../etc/passwd`) are blocked.
- Self-mem directories are read-only — the agent cannot modify or delete them.
- Only `.md` files are returned by list/search; other files are ignored.

---

## Development

```bash
git clone https://github.com/SkillfulElectro/edumem-mcp.git
cd edumem-mcp
npm install
npm run build          # compile TypeScript
npm start              # run the compiled server
# or for dev:
npm run dev            # run directly with ts-node
```

### Testing

```bash
# Manual stdio test
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | node dist/index.js
```

---

## License

MIT