Skip to main content
Glama
GreXLin85

NotNessie MCP Server

by GreXLin85
README.md
# 🪻 NotNessie

**Project memory for Claude Code, stored on your own machine.** NotNessie remembers a repo's decisions, constraints, preferences, verified commands, open questions, and bug notes across sessions — so Claude Code stops re-learning your project from scratch every single time.

[![npm version](https://img.shields.io/npm/v/@notnessie/cli?color=4a8a8a&label=%40notnessie%2Fcli)](https://www.npmjs.com/package/@notnessie/cli)
[![License: MIT](https://img.shields.io/badge/License-MIT-4a8a8a.svg)](LICENSE)
[![Node >=20](https://img.shields.io/badge/node-%3E%3D20-4a8a8a.svg)](https://nodejs.org)
[![Docs](https://img.shields.io/badge/docs-grexlin85.github.io-4a8a8a.svg)](https://grexlin85.github.io/notnessie/)

> šŸ“š **Full documentation lives at [grexlin85.github.io/notnessie](https://grexlin85.github.io/notnessie/).** This README is the tour; the docs site is the reference.

---

## The problem

Every Claude Code session starts cold. You explain that the project uses Fastify and not Express, that tests run with `pnpm test auth`, that the public API response shape is frozen, that nobody touches the generated Prisma files. Claude does great work — and then the session ends and all of that context evaporates. Next task, you say it all again.

CLAUDE.md helps, but it's a single hand-maintained file. It doesn't grow on its own, it doesn't rank what's relevant to the task at hand, and it has no idea which command you verified last Tuesday.

**NotNessie is the missing layer.** It captures the durable facts from your Claude Code work — decisions, rules, preferences, commands that actually worked, questions left open — and serves them back as a compact, task-aware context pack the next time you sit down. The memory lives in *your* Postgres. No account, no cloud, nothing leaves your machine.

## How it plugs into Claude Code

NotNessie integrates three ways, and `notnessie init` wires up all of them:

1. **An MCP server** (`.mcp.json`) exposing nine memory tools Claude can call mid-task — retrieve context, search memory, save a decision, and so on.
2. **Five lifecycle hooks** (`.claude/settings.json`). SessionStart injects a context pack as the session opens. UserPromptSubmit and PostToolUse quietly capture relevant context and verified commands as you work. Stop and PreCompact summarize the session back into memory before it's gone.
3. **A CLAUDE.md instruction block + a Claude Code skill** that teach Claude *when* to reach for those tools — pull context before non-trivial work, save the corrections and rules you hand it during work.

## Features

- 🧠 **Structured, typed memory** — decisions, constraints, preferences, verified commands, task summaries, bug notes, open questions. Not one big blob; each kind is retrieved and ranked on its own terms.
- šŸ” **Hybrid local-first search** — Postgres full-text *and* pgvector semantic search, re-ranked in JS by recency, confidence, memory type, pinned status, and overlap with the files you mentioned. Embeddings run locally via Transformers.js (`Xenova/all-MiniLM-L6-v2`, 384-dim) — no API calls. With embeddings off or unavailable, it degrades cleanly to keyword-only.
- šŸŖ **Automatic capture via hooks** — the good stuff gets saved without you remembering to ask, summarized by the Claude Agent SDK with a heuristic fallback when the SDK isn't reachable.
- šŸ›”ļø **Secret redaction** — private keys, credentialed connection strings, and `.env` dumps are refused outright; JWTs, API keys, and bearer tokens are masked inline before anything touches the database. Full transcripts and raw terminal output are never stored.
- šŸ“‹ **A local dashboard** for reviewing, editing, pinning, and deleting what the AI captured — because auto-captured memory is never perfect, and a human should get the last word.
- šŸ’» **Local-first, single-repo** — each repository gets its own memory space, keyed to its path. Your data, your Postgres, offline-friendly.

## Quick start

**Prerequisites:** Node >= 20 and Docker (or your own pgvector-enabled Postgres).

```bash
# 1. Install the CLI
npm i -g @notnessie/cli

# 2. Start NotNessie's database — ships its own Compose file, auto-picks a free port
notnessie pgup

# 3. Inside the repo you want Claude to remember:
notnessie init       # writes .mcp.json, the CLAUDE.md block, the skill, hooks, and config
notnessie status     # health check: database, schema, embeddings, Claude Code wiring
```

Then open Claude Code in the repo. The MCP tools load from `.mcp.json`, the hooks from `.claude/settings.json`, and Claude starts with a context pack injected at SessionStart. Before non-trivial work it pulls context; after, it saves what's worth keeping.

Already have a pgvector Postgres? Skip `notnessie pgup` and point NotNessie at it with `DATABASE_URL`:

```bash
export DATABASE_URL=postgres://user:pass@host:5432/dbname
```

### Try it without Claude Code

NotNessie is fully usable from the terminal, which is the fastest way to get a feel for it:

```bash
notnessie seed                                          # load a coherent demo dataset (an auth module)
notnessie context "add refresh token rotation to auth"  # see the context pack Claude would receive
notnessie search "refresh token" --types decision,constraint
notnessie dev                                           # open the dashboard at http://localhost:4319
```

## MCP tools

The MCP server exposes nine tools (stdio by default, with a Streamable HTTP transport on port `4320` for the curious):

| Tool | Purpose |
| --- | --- |
| `notnessie_get_project_context` | Compact, task-aware context pack for the current repo |
| `notnessie_search_memory` | Hybrid keyword + semantic search across stored memory |
| `notnessie_save_decision` | Record an architectural or implementation decision |
| `notnessie_save_preference` | Record a coding preference |
| `notnessie_save_constraint` | Record a hard rule, with a severity |
| `notnessie_save_verified_command` | Record a command confirmed to work in this repo |
| `notnessie_save_task_summary` | Record a post-task summary (files changed, commands run, TODOs) |
| `notnessie_get_open_questions` | List unresolved questions worth surfacing |
| `notnessie_get_verified_commands` | List commands previously verified here |

## How memory flows

```
   SessionStart hook              during the session                Stop / PreCompact hooks
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”       ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”       ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  context pack is   │  ──▶  │  Claude pulls context, │  ──▶  │  the session is          │
│  injected as the   │       │  you correct & decide, │       │  summarized back into    │
│  session opens     │       │  saves happen via MCP  │       │  memory before it's lost │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜       ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜       ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
         ā–²                                                                  │
         ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
                    next session starts warm, not cold
```

In short: context comes *in* at the start, useful facts are saved *during* the work (by Claude through the MCP tools, and by the hooks watching quietly), and a summary goes back *out* at the end. Each session leaves the repo a little smarter than it found it.

## CLI

The `notnessie` bin covers the whole loop:

```
notnessie init [path]                 Scaffold Claude Code integration + config
notnessie status [path]               Health of database, schema, embeddings, wiring
notnessie pgup                        Start the bundled Postgres (auto-picks a free port)
notnessie pgdown [--volume]           Stop the database (--volume also deletes all memory)
notnessie mcp [--http] [-p <port>]    Start the MCP server (stdio default; HTTP on 4320)
notnessie dev [-p <port>]             Launch the dashboard (default port 4319)
notnessie search <query>              Search memory (--types, --limit, --json)
notnessie context [task]              Build a context pack (Markdown, or --json)
notnessie seed                        Load the demo memory dataset
notnessie memory list|show|delete|restore|pin|unpin
notnessie hook <event>               Run a lifecycle hook (Claude Code calls these, not you)
```

## Configuration

NotNessie reads `DATABASE_URL` and embedding settings from your environment (and from a local `.env`, thanks to Node's built-in loader). The key variables:

| Variable | Default | Purpose |
| --- | --- | --- |
| `DATABASE_URL` | `postgres://notnessie:notnessie@localhost:5432/notnessie` | Postgres connection used by every package |
| `NOTNESSIE_EMBEDDINGS` | `1` | Set to `0` to disable local embeddings (keyword-only search) |
| `NOTNESSIE_EMBEDDING_MODEL` | `Xenova/all-MiniLM-L6-v2` | The Transformers.js model used for semantic search |
| `NOTNESSIE_DASHBOARD_PORT` | `4319` | Port for `notnessie dev` |

Per-repo behavior lives in `.notnessie/config.json` (written by `init`):

```json
{
  "projectName": "my-project",
  "projectId": "proj_…",
  "memoryMode": "conservative",
  "autoSaveTaskSummaries": true,
  "autoSaveCommands": true,
  "autoSaveFileChanges": false,
  "redactSecrets": true
}
```

See [`.env.example`](.env.example) for the full set, and the [documentation site](https://grexlin85.github.io/notnessie/) for the complete reference.

## Under the hood

A pnpm + TypeScript monorepo (ESM, Node >= 20), built with tsup, tested with Vitest, linted with Biome:

- **`@notnessie/core`** — types, Postgres storage, redaction, embeddings, hybrid search, context packs, the summarizer.
- **`@notnessie/mcp`** — the MCP server and the nine tools, over stdio and Streamable HTTP.
- **`@notnessie/cli`** — the `notnessie` bin (this is the package you install).
- **`@notnessie/dashboard`** — the Next.js review UI, launched via `notnessie dev`.
- **`examples/demo-repo`** — a pre-wired backend used to show the flow end to end.

Storage is PostgreSQL + pgvector, provisioned by the included `docker-compose.yml`. Defaults: Postgres on `localhost:5432`, dashboard on `4319`, MCP HTTP on `4320`.

## From source / contributing

Clone the repo, then:

```bash
pnpm install
pnpm build         # build @notnessie/core, @notnessie/mcp, @notnessie/cli
pnpm test          # unit tests (no database required)
node packages/cli/dist/index.js pgup      # start Postgres (pgvector) on a free port
node packages/cli/dist/index.js status
```

> šŸ’” Semantic search needs `onnxruntime-node`'s native binary, whose install pnpm defers until you run `pnpm approve-builds`. Until then (or offline, or with `NOTNESSIE_EMBEDDINGS=0`), retrieval runs keyword-only — and turning the build on later activates semantic ranking with no code change.

Issues and PRs welcome at [github.com/GreXLin85/notnessie](https://github.com/GreXLin85/notnessie).

## A nod to Nessie

The name is an affectionate homage to **[Nessie](https://nessielabs.com/)** (by Nessie Labs) — *"Make your thinking legible to every AI you use."* Nessie ingests and syncs your AI conversation history across ChatGPT, Claude, Gemini, Claude Code, Codex, and more, structures it into reusable context, and exposes it over an MCP server so your agents can query your history. It's cloud-synced, team-oriented, and permission-gated — a genuinely broader take on the idea.

NotNessie is a much narrower, local-first riff on that same north star: one project's memory, in your own local Postgres, scoped to a single repo. It's an independent, open-source experiment and is **not affiliated with, endorsed by, or connected to Nessie or Nessie Labs** in any way. The name is just a friendly tip of the hat. If you want the full, cross-tool, team-grade version of "make your context legible to AI," go look at the real thing. šŸ¦•

## Vibe-coded, human-reviewed

Honest disclosure: NotNessie was **vibe-coded** — built fast and largely with AI assistance (Claude Code itself, fittingly). It was then reviewed by a human for architecture, storage, security, privacy, and MCP behavior. AI wrote a lot of the lines; a person owns the scope, the security rules, and the decisions. That's rather the whole point of the project.

## License

MIT — see [LICENSE](LICENSE).