npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cchez/memory-mcp

v1.0.0

Published

AI memory MCP server with Qdrant vector storage and Ollama/OpenAI embeddings

Readme

Memory MCP

A self-contained AI memory store for agents and developers. Stores coding rules, architecture decisions, team discussions, and project facts as semantic vectors. Any MCP-compatible agent can search and save memories across sessions.

Stack: Qdrant (vector DB) + Ollama bge-m3 (embedding) + MCP stdio server — all containerised, zero cloud dependencies.


Architecture

memory-mcp/
├── db/
│   ├── docker-compose.yml   # Qdrant + Ollama containers
│   ├── .env.example         # Environment variable template
│   ├── qdrant_data/         # Persisted vector data (gitignored)
│   └── ollama_data/         # Persisted model cache (gitignored)
├── src/
│   ├── index.ts             # MCP stdio server
│   ├── server.ts            # HTTP bulk-ingest server
│   ├── embedding.ts         # Embedding provider abstraction (Ollama / OpenAI)
│   ├── qdrant.ts            # Qdrant client — dual-collection, auto-routing
│   └── tools/
│       ├── store.ts         # store_memory tool
│       ├── search.ts        # search_memory tool
│       ├── delete.ts        # delete_memory tool
│       ├── list.ts          # list_memories tool
│       ├── correct.ts       # correct_memory tool — supersession chain
│       └── episode.ts       # capture_episode tool — opt-in task summaries
├── scripts/
│   ├── migrate.ts           # One-time migration: old collection → coding/workspace
│   └── migrate-reembed.ts   # Re-embed all data when switching embedding model
└── skills/
    ├── memory-search/       # Agent skill: when and how to search memory
  ├── memory-save/         # Agent skill: when and how to save to memory
  └── memory-correct/      # Agent skill: when and how to correct memory

Collections

Memories are auto-routed to one of two Qdrant collections based on memory_type:

| Collection | memory_type values | Content | |---|---|---| | coding | rule, decision, preference | Coding constraints, architecture choices, tool preferences | | workspace | fact, summary | Team discussions, Slack summaries, project facts, config values |


Quick Start

Step 1 — Start the database and embedding model

cd db
docker compose up -d

This starts:

  • Qdrant on http://localhost:6333 (vector database)
  • Ollama on http://localhost:11434 (embedding server, auto-pulls bge-m3 on first run)

First run takes 1–2 minutes while bge-m3 (~1.1 GB) downloads. Check progress:

docker compose logs -f ollama

Verify both are ready:

curl http://localhost:6333/collections        # Qdrant
curl http://localhost:11434/api/tags          # Ollama — should list bge-m3

Note: If you have Ollama running locally on port 11434, stop it first to avoid port conflicts:

killall ollama 2>/dev/null || true

Step 2 — Configure environment

cp db/.env.example .env

The defaults work out of the box with the docker-compose setup. Edit .env only if you need to customise:

QDRANT_URL=http://localhost:6333
CODING_COLLECTION=coding
WORKSPACE_COLLECTION=workspace

EMBEDDING_PROVIDER=ollama          # or "openai"
EMBEDDING_DIMENSIONS=1024          # must match the model (bge-m3 = 1024)
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=bge-m3

# Only needed if EMBEDDING_PROVIDER=openai
OPENAI_API_KEY=sk-...

INGEST_API_KEY=your-secret-key     # for HTTP bulk-ingest auth
PORT=3000

Step 3 — Install dependencies

Only needed for local development from this repo:

npm install

Step 4 — Wire up the MCP server

For normal usage, run the MCP server via npm with npx. Add this to your Claude Code / Claude Desktop MCP config (.claude/settings.json or claude_desktop_config.json):

{
  "mcpServers": {
    "memory-mcp": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "@cchez/memory-mcp@latest"
      ],
      "env": {
        "QDRANT_URL": "http://localhost:6333",
        "EMBEDDING_PROVIDER": "ollama",
        "OLLAMA_BASE_URL": "http://localhost:11434",
        "OLLAMA_MODEL": "bge-m3",
        "EMBEDDING_DIMENSIONS": "1024",
        "CODING_COLLECTION": "coding",
        "WORKSPACE_COLLECTION": "workspace"
      }
    }
  }
}

For local development before publishing, point the MCP host at the source file instead:

{
  "mcpServers": {
    "memory-mcp": {
      "type": "stdio",
      "command": "node",
      "args": [
        "/absolute/path/to/memory-mcp/node_modules/tsx/dist/cli.mjs",
        "/absolute/path/to/memory-mcp/src/index.ts"
      ],
      "env": {
        "QDRANT_URL": "http://localhost:6333",
        "EMBEDDING_PROVIDER": "ollama",
        "OLLAMA_BASE_URL": "http://localhost:11434",
        "OLLAMA_MODEL": "bge-m3",
        "EMBEDDING_DIMENSIONS": "1024",
        "CODING_COLLECTION": "coding",
        "WORKSPACE_COLLECTION": "workspace"
      }
    }
  }
}

Why node instead of npm run dev for local development? Some MCP hosts don't reliably pass the cwd field to the spawned process, causing npm to look for package.json in the wrong directory. Invoking node with absolute paths bypasses npm entirely and works regardless of working directory.

Replace both /absolute/path/to/memory-mcp occurrences with the actual path to this repo (e.g. /Users/you/projects/memory-mcp).

Restart Claude Code / Claude Desktop. The MCP tools will be available immediately.

Step 5 — Install agent skills (optional but recommended)

Copy the skills into your Claude skills directory so agents automatically know when to search and save memory:

cp -r skills/memory-search ~/.claude/skills/
cp -r skills/memory-save ~/.claude/skills/
cp -r skills/memory-correct ~/.claude/skills/

Restart Claude Code to pick up the new skills.


Publishing to npmjs

The package is configured as @cchez/memory-mcp and exposes one executable bin, memory-mcp, backed by dist/index.js. The package uses a files allowlist so local Qdrant/Ollama data is not published.

Before publishing:

npm run build
npm run pack:dry-run

First-time publish for a scoped public package:

npm login --registry=https://registry.npmjs.org/
npm publish --access public --registry=https://registry.npmjs.org/

After publish, agent configs can use:

{
  "command": "npx",
  "args": ["-y", "@cchez/memory-mcp@latest"]
}

The npm package only runs the MCP server. Qdrant and Ollama still need to be running separately, for example with db/docker-compose.yml from this repo.

Step 6 - Global instruction

# Memory

知识库(memory mcp)是核心资产,通过 memory mcp 访问。

## 搜索 — 先查再做

开始任何任务前,先搜索知识库获取相关上下文:
- 开始写代码或修 bug → 搜索是否有相关编码规则或约束
- 讨论架构或技术选型 → 搜索是否有已有决策
- 遇到项目、系统、集成的名称 → 搜索相关背景和配置事实
- 用户问"我们之前怎么处理 X 的" → 搜索知识库,别靠猜

搜索时设 `score_threshold: 0.5` 过滤低相关结果。不确定搜哪个 collection 就两个都搜(默认行为)。每个话题最多搜两次(换个措辞重试一次),没结果就停,不要死循环。

## 保存 — 三问过滤

发现以下信息时主动保存,不需要用户提醒:
- 编码约束或规则("方法复杂度不能超过 15,否则 CI 挂")
- 架构或技术选型决策("选择了 Qdrant,原因是…")
- 配置事实("Airwallex RFI KYC template ID 是 xxx")
- 解决了一个非显而易见的歧义

用户说"记住这个"时,立即保存。

保存前过三问:① 耐久?3 个月后还成立吗 ② 非显而易见?新人不会的知道吗 ③ 可复用?会影响下次工作吗。三问全是才保存。不保存:任务状态、短暂信息、众所周知的事实。

## memory_type 分类

| memory_type | 保存什么 | 自动路由到 |
|---|---|---|
| `rule` | 编码约束、CI 规则、强制规范 | coding |
| `decision` | 技术选型、架构决策(含原因) | coding |
| `preference` | 团队偏好、软性约定 | coding |
| `fact` | 配置值、团队现状、集成细节 | workspace |
| `summary` | Slack/会议/文档的提炼摘要 | workspace |

collection 不需要手动指定,由 `memory_type` 自动路由。

## source 格式

`slack/<频道名>` / `agent/claude-code` / `confluence/<页面名>` / `manual/<主题>`

## 删除过时记忆

发现知识库中有错误或过期内容时,用 `correct_memory` 写入修订版并 supersede 旧记忆。只有敏感信息、重复垃圾、或确实不应保留审计历史的记录才用 `delete_memory(id, collection)`。

MCP Tools Reference

store_memory

Save a piece of knowledge. Content with identical text is automatically deduplicated (idempotent upsert via SHA-256 hash).

| Parameter | Type | Required | Description | |---|---|---|---| | content | string | yes | The knowledge to store (1–5 sentences, written for future retrieval) | | source | string | yes | Origin: slack/channel-name, agent/claude-code, confluence/page-title, manual/... | | memory_type | enum | yes | rule / decision / preference / fact / summary | | tags | string[] | no | 2–5 specific tags for filtered retrieval | | status | enum | active | active / superseded / deprecated | | confidence | number | no | Confidence score 0–1 | | episode_id | string | no | Task/debug episode that produced the memory | | related_ids | string[] | no | Related memory IDs | | supersedes | string | no | Older memory ID replaced by this memory | | valid_until | string | no | ISO timestamp for time-sensitive facts | | last_verified_at | string | no | ISO timestamp for last verification |

Returns: { id, collection }

search_memory

Hybrid search across one or both collections. The default hybrid mode combines vector similarity, exact keyword matching, light recency weighting, and MMR de-duplication. Superseded and deprecated memories are hidden unless explicitly requested.

| Parameter | Type | Default | Description | |---|---|---|---| | query | string | — | Natural language query | | collections | array | both | ["coding"], ["workspace"], or omit for both | | limit | number | 5 | Max results (1–20) | | score_threshold | number | — | Min similarity 0–1 (recommended: 0.5) | | memory_type | enum | — | Filter by type | | tags | string[] | — | Filter — all specified tags must match | | source | string | — | Filter by exact source string | | include_inactive | boolean | false | Include superseded / deprecated memories | | mode | enum | hybrid | vector, keyword, or hybrid | | use_recency | boolean | true | Apply recency weighting to time-sensitive memories | | use_mmr | boolean | true | Reduce duplicate-looking results |

Returns: array of { id, content, source, memory_type, collection, tags, score, vector_score, keyword_score, recency_score, status, created_at, updated_at, ...lifecycle_fields }

correct_memory

Create a corrected replacement for an existing memory. The old memory is marked superseded; the new memory is written as active with supersedes pointing at the old ID.

| Parameter | Type | Required | Description | |---|---|---|---| | id | string | yes | Old memory ID | | collection | enum | yes | Old memory collection: coding or workspace | | corrected_content | string | yes | Replacement memory content | | correction_reason | string | yes | Why the old memory is wrong/stale | | source | string | no | Defaults to old source | | memory_type | enum | no | Defaults to old type | | tags | string[] | no | Defaults to old tags | | confidence | number | no | Defaults to 0.9 |

capture_episode

Opt-in capture for task/debug episodes. This does not ingest raw conversation or tool traces; callers provide a compact summary plus durable observations that passed a quality filter.

| Parameter | Type | Required | Description | |---|---|---|---| | episode_id | string | yes | Task/debug episode identifier | | source | string | yes | Usually agent/claude-code or another agent source | | summary | string | yes | What happened, key attempts, final conclusion | | observations | array | yes | 1–10 structured memories with content, memory_type, optional tags and confidence | | related_ids | string[] | no | Existing memory IDs related to this episode |

delete_memory

Delete a specific memory entry by ID.

| Parameter | Type | Required | Description | |---|---|---|---| | id | string | yes | ID returned by store_memory | | collection | enum | yes | coding or workspace |

list_memories

Browse memory entries with pagination — useful for auditing or finding IDs.

| Parameter | Type | Default | Description | |---|---|---|---| | collection | enum | — | coding or workspace (required) | | limit | number | 20 | Max results (1–100) | | offset | number | 0 | Pagination offset | | source | string | — | Filter by source | | memory_type | enum | — | Filter by type | | include_inactive | boolean | false | Include superseded/deprecated memories for audit |


Example Prompts

Saving memories

Remember this rule: all service methods must have cyclomatic complexity ≤ 15,
otherwise the CI pipeline fails. Source is agent/claude-code, type is rule,
tags: ci, complexity, typescript.
Save to memory: we decided to use SHA-256 content hash as the Qdrant point ID
for deduplication. Same content written twice produces one record.
Source: agent/claude-code, type: decision, tags: qdrant, deduplication, architecture.
Note down: Airwallex RFI hosted flow KYC template ID is "kyc_rfi_v2_au".
Source: manual/airwallex-config, type: fact, tags: airwallex, rfi, configuration.

Searching memories

Search memory for any rules about code complexity limits.
What do we know about our Airwallex integration configuration?
Search the workspace collection.
Find all architecture decisions we've made. Search coding collection,
memory_type decision, score threshold 0.5.
List everything in the coding collection so I can audit what rules are stored.

Deleting stale memories

Delete memory id "a3f2b1c9..." from the workspace collection.

HTTP Bulk Ingest

For automated pipelines (e.g. daily Slack summarisation jobs):

npm run dev:server
curl -X POST http://localhost:3000/ingest \
  -H "Authorization: Bearer <INGEST_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "memories": [
      {
        "content": "Pod Pay Pilots weekly update 2026-04-22: document upload UI refactor completed. UAT sign-off from Katrina Li.",
        "source": "slack/pod-pay-pilots",
        "memory_type": "summary",
        "tags": ["weekly-update", "document-upload", "uat"]
      },
      {
        "content": "Decision: LaunchDarkly flag AU_discoverability_beanie_events controls Beanie event emission for AU bill pay. Default off in production.",
        "source": "slack/pod-pay-pilots-engineers",
        "memory_type": "fact",
        "tags": ["launchdarkly", "feature-flag", "au-bill-pay"]
      }
    ]
  }'

Response (all succeeded):

{ "stored": 2, "failed": 0, "succeeded": [{"index": 0, "id": "..."}, {"index": 1, "id": "..."}] }

Response (partial failure — HTTP 207):

{ "stored": 1, "failed": 1, "succeeded": [...], "errors": [{"index": 1, "error": "..."}] }

Health check:

curl http://localhost:3000/health
# {"status":"ok"}

Switching Embedding Models

The embedding model is a hard infrastructure decision — all stored vectors must use the same model. To switch models:

  1. Update .env: set OLLAMA_MODEL (or EMBEDDING_PROVIDER=openai) and EMBEDDING_DIMENSIONS
  2. Run the re-embed migration:
# Dry-run first
MIGRATE_DRY_RUN=true node --env-file=.env ./node_modules/tsx/dist/cli.mjs scripts/migrate-reembed.ts

# Execute
MIGRATE_DRY_RUN=false node --env-file=.env ./node_modules/tsx/dist/cli.mjs scripts/migrate-reembed.ts

This drops and recreates all collections at the new vector size, then re-embeds every stored record.


Stop Services

cd db
docker compose down          # stop containers, keep data
docker compose down -v       # stop containers and delete all data