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

@muninhq/mcp

v0.2.0

Published

Local stdio MCP server over the Munin engine — your machine, your keys, your choice of provider. Part of the open-core local product; the hosted/managed product and the verticals are separate and closed.

Downloads

223

Readme

@muninhq/mcp — local MCP server over your Munin memory

A stdio MCP server that lets your own LLM client (Claude Desktop, Cursor, anything MCP-capable) retrieve cited, permission-filtered context from a Munin memory. Your machine, your keys, your choice of provider — the server runs as a child process of your client, opens no network listener, and adds no auth of its own (the OS process boundary is the trust boundary).

One-command setup

The server reads its configuration from a per-user MUNIN_HOME (default ~/.munin) — one munin.env file plus its pgdata/ and encrypted blobs/. Nothing is hand-edited in the common case.

The fastest way in is the guided wizard — it provisions your home, helps you choose a model, adds files, wires your AI client, and verifies the connection:

munin setup

munin setup defaults to "local store · cloud AI": your files, blobs and memory stay on this machine, and only the passages a question needs go to the cloud provider you choose, under your own key (munin set-key openai is the smooth one-key path — it does extraction and embeddings, so no Ollama is needed). Prefer zero egress? Choose Local (Ollama) in the wizard (or munin init --local) and nothing leaves your machine — install Ollama and pull its two models once: ollama pull qwen2.5:7b && ollama pull bge-m3.

The examples below use the installed munin command (npm install -g munin-mcp). From a dev checkout, prefix each with pnpm --filter munin-mcp (e.g. pnpm --filter munin-mcp munin setup).

Or step by step

The wizard just orchestrates these — run them directly if you prefer:

# 1. bootstrap a portable home (~/.munin): munin.env (mode 0600), data dirs, tenant
munin init

# 2. choose your model: a cloud key (smooth), or zero-egress local
munin set-key openai          # or `munin set-key anthropic`, or `munin use-local`

# 3. put documents in, then build the local knowledge graph (both run in-process)
munin ingest /path/to/your/docs
munin extract

# 4. wire your AI client — prints the block by default; --write merges it safely
munin mcp connect --client claude-desktop --write
#    (Cursor: --client cursor; project-local: add --project <dir>)

# 5. confirm it is wired, then restart your client
munin mcp doctor

munin ingest / munin extract default --tenant to the home's tenant and --tags to personal, so the common case needs no extra flags. The local store is a single-process database, but you never need to stop anything: when your AI client (or Studio) has the memory open through the shared munin serve host, the data verbs detect it and route through that host automatically; with nothing running they open the store directly.

Choosing (or switching) your model

munin setup sets this up interactively; you can also point the home at a provider key — or back at fully-local — in one command at any time:

# Claude for extraction + answers; embeddings stay local (Ollama bge-m3):
ANTHROPIC_API_KEY=sk-ant-… munin set-key anthropic
# OpenAI for extraction + answers AND cloud embeddings (re-ingest to re-embed):
OPENAI_API_KEY=sk-… munin set-key openai
# Or go fully local (zero egress) — Ollama does extraction + embeddings:
munin use-local

The two postures are: local store · cloud AI (files stay local; the text a question needs goes to your chosen provider under your key) and fully local (Ollama, zero egress — heavier on the machine, best on ~16 GB+ RAM). set-key writes the key to munin.env at mode 0600 (never printed), picks sensible models, and sets the posture to MUNIN_ALLOW_CLOUD_PROVIDERS=true (local store + cloud AI; egress acknowledged); use-local re-engages MUNIN_LOCAL_MODE=true. Prefer the provider env var over --key <value> so the key stays out of your shell history. Run munin mcp doctor afterwards to confirm the posture.

Seeing and managing what's in the memory

munin docs            # list documents (newest first)

munin docs lists the documents in the memory with the ids you can read via munin_get_document. The munin_status tool also surfaces the most-recent few from inside your AI client. To remove a document and everything derived from it (paragraphs, embeddings, extracted entities/edges, the raw blob), run the CLI:

munin forget <documentId> --commit --confirm-title "<title>"

munin forget is a dry run by default; --commit --confirm-title performs the hard delete in a single transaction and removes the blob, leaving no orphans.

Like ingest/extract, munin docs and munin forget are doorman-aware: with a shared munin serve host running they read (and erase) through it; otherwise they open the local store directly. If the store is ever locked by something that is not a reachable host, munin serve --stop frees it (a shared host restarts automatically the next time your AI client uses it).

munin mcp connect

Prints the mcpServers block (and the target path) by default — paste it yourself, or re-run with --write. --write is safe: it refuses symlinks and unparseable configs, backs up the existing file, writes atomically, and merges — it sets only mcpServers.munin and never touches your other servers or top-level keys. Re-running is a no-op. The emitted block carries no secrets — only a MUNIN_HOME pointer and a hardened PATH; the settings (including the blob key) stay in munin.env.

For an installed Munin, the emitted block runs the published munin-mcp bin directly (no pnpm, no --dir, no checkout path):

{
  "mcpServers": {
    "munin": {
      "command": "/Users/you/.nvm/versions/node/v22.22.0/bin/node",
      "args": [
        "/Users/you/project/node_modules/@muninhq/mcp/dist/main.js"
      ],
      "env": {
        "MUNIN_HOME": "/Users/you/.munin",
        "PATH": "/Users/you/.nvm/versions/node/v22.22.0/bin:/usr/bin:/bin"
      }
    }
  }
}

(From a dev repository checkout the block instead runs pnpm --dir <checkout>/packages/mcp --silent start — same shape, repo launch target.)

Node-version pinning (the nvm fix). connect pins the exact Node it is running under (Node 22, by construction) and leads PATH with that Node's bin dir. A bare command: "pnpm"/"node" would let your client resolve the binary — and under nvm pick up an old Node (v18): the launcher then crashes (File is not defined, undici) and the client just shows "Server disconnected". So run connect under Node 22 (the repo's pinned version) and the emitted block can't grab a broken Node. (In the checkout form --silent matters too: pnpm's script banner would otherwise corrupt the stdio JSON-RPC channel.)

Honest v1 boundaries

  • connect emits the installed-bin launcher. munin-mcp ships as a compiled Node bin (dist/main.js, #!/usr/bin/env node — no tsx, no checkout needed to run the server), and the package carries its own engine + migrations + WASM-backed PGlite as real dependencies, so an installed munin-mcp runs standalone. When run from an install, munin mcp connect resolves that installed bin and emits a <node> <…>/@muninhq/mcp/dist/main.js launcher (the clean-install acceptance test asserts exactly this — no --dir, no repo path); from a dev checkout it emits the pnpm --dir <repo>/packages/mcp form. What remains is the actual npm publish (and then a one-line npx munin-mcp).
  • Blob encryption is mandatory only under the local/prod posture, and the AES key is co-located with the data. Raw original bytes are encrypted at rest with AES-256-GCM, and under MUNIN_LOCAL_MODE (and in production) plaintext blob storage is refused — but a non-local configuration can store plaintext by design, so the encryption guarantee is the local/prod posture's, not an unconditional one. The AES key lives in the same 0600 munin.env as the store, so at-rest encryption protects a stolen disk only if that key file is separately protected, and is weakened on a synced/cloud-backed folder (Dropbox / iCloud) where the key travels with the data. Keep MUNIN_HOME on local disk for the encryption to mean what it says.
  • MCP reads are blob-free, so your memory relocates to any path. All five tools read paragraph text from the database (pgdata/), never from blobs — so copying/pointing MUNIN_HOME keeps reading and chatting at any path. Raw blobs (original bytes, used only by ingest/erase) still store absolute file:// URIs, so full blob fidelity — erase / original-byte re-read — survives only a same-path move or a re-ingest until tenant-relative blob keys land (a deferred engine change). munin mcp doctor and the launcher surface this posture.
  • Don't point two engines at one home. PGlite is single-process: a synced home or two clients opening the same pgdata risks corruption. Sync/share for transport only.

The five tools

| tool | what it does | | --- | --- | | munin_retrieve_context | ranked, cited context for a question — the calling LLM synthesises (one embedding call, no server-side answer model) | | munin_ask | full grounded answer with [n] citations, or an honest no_evidence | | munin_gather_entity | everything about one subject by identity; same-name collisions return candidates + pick tokens | | munin_get_document | the full source behind a citation (title + ordered paragraphs) | | munin_status | document/paragraph/entity counts, pending extraction, active tenant + configuration, and a sample of the most-recent documents |

Verifiable citations. Every source the retrieval tools return carries a sourceId (e.g. P11), and each result includes a short, stable citationGuidance field instructing the calling model to cite the sourceId inline after each claim (e.g. "…rooted in doughnut economics [P11].") and to say so when the sources don't support something. munin_ask returns [n] markers mapped to a citations[] array (each with documentId, paragraphId and the quoted text) and guidance to preserve those markers when relaying the answer. The grounding shows up in the answer, not by reading the logs.

Advanced / dev

  • Hosted-Postgres store (instead of the local PGlite home): point the server at a Postgres connection string and supply the config/tenant directly — set MUNIN_CONFIG_PACKAGE=@muninhq/config-generic-baseline (or @muninhq/config-personal) + MUNIN_TENANT_ID=<id> + DATABASE_URL=<conn> in the client env block (no MUNIN_HOME needed for a hosted store).
  • Inspector: npx @modelcontextprotocol/inspector pnpm --filter @muninhq/mcp --silent start (set MUNIN_HOME or the config/tenant env first).
  • Without a home: the launcher fails fast and friendly ("run munin init") unless you supply MUNIN_CONFIG_PACKAGE (and the rest) another way — e.g. the hosted demo above.

Troubleshooting

  • Logs are on stderr (stdout belongs to JSON-RPC). Claude Desktop: ~/Library/Logs/Claude/mcp-server-munin.log. Verbosity: MUNIN_MCP_LOG_LEVEL=debug.
  • "No Munin home … run munin init" — the launcher found no munin.env at MUNIN_HOME and no MUNIN_CONFIG_PACKAGE in its env. Run munin init, then munin mcp connect.
  • "Multiple tenants exist" — set MUNIN_TENANT_ID (or let munin init pin one). munin mcp doctor shows what resolved.
  • Empty results — check munin_status (pending extraction?) and that the embedding provider matches the one the corpus was embedded with.
  • munin.env is authoritative — it is loaded with override-on, so a value set only in the client env block is intentionally overridden by the home file.

Open core

Part of the Munin open-core local product, released under AGPL-3.0-only (see LICENSE and the repository NOTICE). The hosted / managed product, team and device-sync features, brokered LLM access, and the vertical configurations (e.g. MAT / HR) are a separate, closed commercial product and are not licensed under the AGPL.