Package Exports
- @danielzfliu/memory
Readme
Memory
A fully local MCP server and Node.js library for storing, semantically searching, and querying tagged/titled text with ChromaDB (vector storage) and Ollama (embeddings and generation).
Three ways to use Memory:
- MCP Server — Run Memory as a Model Context Protocol server over stdio and expose memory tools to MCP-compatible clients.
- npm Package — Install
@danielzfliu/memoryin your own project and use the classes directly (store, embeddings, RAG, and MCP server class). - REST API Server — Run the standalone HTTP server with CRUD, semantic search, and RAG endpoints.
Prerequisites
- Node.js ≥ 18
- Ollama running locally
- ChromaDB server running locally
Setting up Ollama
Install Ollama (install) and pull the default models:
ollama pull nomic-embed-text-v2-moe:latest
ollama pull gemma3:latestThen run:
npm run ollama # start Ollama on default port 11434
npm run ollama:port -- 11435 # start Ollama on a custom portSetting up ChromaDB
Option 1: Docker
The repo includes a Docker Compose file that runs ChromaDB and stores its data in ./chroma/.
npm run docker:up # start ChromaDB on port 8000
npm run docker:logs # view logs
npm run docker:down # stop ChromaDBOption 2: pip
pip install chromadb
chroma run --port 8000 # start ChromaDB on port 8000Note: You may need to add Python's Scripts folder to your PATH after installing.
Option A: MCP Server
Use this option to run Memory as a standalone MCP server.
1. Setup
git clone https://github.com/DanielZFLiu/memory.git
cd memory
npm install2. Build and run the MCP server
npm run build
node ./dist/main.jsMemory MCP communicates over stdio, so it does not bind an HTTP port.
MCP Client Configuration
Claude Desktop (example)
{
"mcpServers": {
"memory": {
"command": "npx",
"args": ["-y", "@danielzfliu/memory"]
}
}
}If you are running from a local clone instead of npm:
{
"mcpServers": {
"memory": {
"command": "node",
"args": ["c:/path/to/memory/dist/main.js"]
}
}
}MCP Tools
| Tool | Description |
|---|---|
add_piece |
Add a new piece with optional title and tags |
get_piece |
Retrieve a piece by id |
update_piece |
Update piece content, title, and/or tags (title: null clears title) |
delete_piece |
Delete a piece by id |
query_pieces |
Semantic search over content, plus title when present. Supports hybrid search (vector + keyword via RRF). |
rag_query |
Retrieve + generate answer with citations using content and title context. Supports hybrid search. |
list_collections |
List all collection names in the memory store |
delete_collection |
Delete an entire collection and all its pieces |
All piece-level tools accept an optional collection parameter to target a specific collection instead of the default. This allows multiple agents to use isolated memory stores.
Option B: npm Package
Use this option to integrate Memory into your own Node.js/TypeScript project.
1. Install
npm install @danielzfliu/memory2. Programmatic usage
Using PieceStore and RagPipeline directly
import { PieceStore, RagPipeline, MemoryConfig } from "@danielzfliu/memory";
async function main() {
const config: MemoryConfig = {
chromaUrl: "http://localhost:8000",
ollamaUrl: "http://localhost:11434",
embeddingModel: "nomic-embed-text-v2-moe:latest",
};
// Store: CRUD + semantic search
const store = new PieceStore(config);
await store.init();
await store.addPiece(
"TypeScript is a typed superset of JavaScript.",
["typescript", "programming"],
"TypeScript overview",
);
await store.addPiece("Python is great for data science.", [
"python",
"data-science",
]);
const results = await store.queryPieces("typed languages", { topK: 5 });
console.log("results", results);
const filtered = await store.queryPieces("typed languages", {
tags: ["typescript"],
topK: 5,
});
console.log("filtered", filtered);
// Hybrid search: combines vector similarity with keyword matching via RRF
const hybrid = await store.queryPieces("typed languages", {
topK: 5,
useHybridSearch: true,
});
console.log("hybrid", hybrid);
// RAG: retrieve relevant pieces → generate an answer via Ollama
const rag = new RagPipeline(store, config.ollamaUrl!, "gemma3:latest");
const answer = await rag.query("What is TypeScript?", {
tags: ["programming"],
});
console.log("answer", answer);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});Embedding the REST API in your own Express app
createServer returns a configured Express app you can mount or extend:
import { createServer } from "@danielzfliu/memory";
const app = createServer({
chromaUrl: "http://localhost:8000",
ollamaUrl: "http://localhost:11434",
});
app.listen(4000, () => console.log("Running on :4000"));Option C: REST API Server
Use this option to run Memory as a standalone HTTP service.
1. Setup
git clone https://github.com/DanielZFLiu/memory.git
cd memory
npm install2. Start the REST server
npm run dev:httpServer starts on http://localhost:3000 by default (set PORT env var to change).
API Endpoints
Add a piece
curl -X POST http://localhost:3000/pieces \
-H "Content-Type: application/json" \
-d '{"title": "TypeScript overview", "content": "TypeScript is a typed superset of JavaScript.", "tags": ["typescript", "programming"]}'With a specific collection:
curl -X POST http://localhost:3000/pieces \
-H "Content-Type: application/json" \
-d '{"content": "Agent-specific memory.", "tags": ["agent"], "collection": "agent-alice"}'Get a piece by ID
curl http://localhost:3000/pieces/<id>
curl http://localhost:3000/pieces/<id>?collection=agent-aliceUpdate a piece
curl -X PUT http://localhost:3000/pieces/<id> \
-H "Content-Type: application/json" \
-d '{"title": "Updated title", "content": "Updated content.", "tags": ["new-tag"]}'Set title to null to clear it.
Delete a piece
curl -X DELETE http://localhost:3000/pieces/<id>
curl -X DELETE http://localhost:3000/pieces/<id>?collection=agent-aliceSemantic search
curl -X POST http://localhost:3000/query \
-H "Content-Type: application/json" \
-d '{"query": "What is TypeScript?", "topK": 5}'With tag filtering:
curl -X POST http://localhost:3000/query \
-H "Content-Type: application/json" \
-d '{"query": "What is TypeScript?", "tags": ["programming"], "topK": 5}'With hybrid search (vector + keyword via Reciprocal Rank Fusion):
curl -X POST http://localhost:3000/query \
-H "Content-Type: application/json" \
-d '{"query": "What is TypeScript?", "topK": 5, "useHybridSearch": true}'RAG query (retrieve + generate)
curl -X POST http://localhost:3000/rag \
-H "Content-Type: application/json" \
-d '{"query": "Explain TypeScript", "tags": ["programming"], "topK": 5}'Returns:
{
"answer": "Generated answer based on retrieved context...",
"sources": [
{
"piece": { "id": "...", "title": "...", "content": "...", "tags": ["..."] },
"score": 0.87
}
]
}List collections
curl http://localhost:3000/collectionsDelete a collection
curl -X DELETE http://localhost:3000/collections/agent-aliceMulti-collection: All piece and query endpoints accept an optional
collectionparameter (in the request body for POST/PUT, as a query string for GET/DELETE) to target a specific collection. Omitting it uses the default collection.
Exports
| Export | Description |
|---|---|
PieceStore |
CRUD + semantic search over tagged text pieces |
RagPipeline |
Retrieve-then-generate pipeline using PieceStore + Ollama |
EmbeddingClient |
Low-level Ollama embedding wrapper |
MemoryMcpServer |
MCP server class (stdio transport) exposing memory tools |
createServer |
Express app factory with all REST endpoints pre-configured |
MemoryConfig |
Configuration interface (all fields optional with defaults) |
DEFAULT_MEMORY_CONFIG |
The default values for MemoryConfig |
Piece |
{ id, content, title?, tags } |
QueryOptions |
{ tags?, topK?, useHybridSearch? } |
QueryResult |
{ piece, score } |
RagResult |
{ answer, sources } |
Configuration (MemoryConfig)
All fields are optional. Defaults are applied automatically.
| Option | Default | Description |
|---|---|---|
chromaUrl |
http://localhost:8000 |
ChromaDB server URL |
ollamaUrl |
http://localhost:11434 |
Ollama server URL |
embeddingModel |
nomic-embed-text-v2-moe:latest |
Ollama model for embeddings |
generationModel |
gemma3:latest |
Ollama model for RAG generation |
collectionName |
pieces |
ChromaDB collection name |
Note:
generationModelis used bycreateServerandMemoryMcpServer. When constructingRagPipelinedirectly, you pass the model name to its constructor.
Environment variables with the names above can override these defaults at runtime.
Testing
npm test # run all tests
npm run test:watch # watch mode
npm run test:coverage # with coverage report