Contributing to mcp-memory-server

April 12, 2026 · View on GitHub

This package is the public face of Mnemoverse across every AI tool marketplace. A single typo in an install snippet breaks every Cursor / VS Code / Claude Desktop / Smithery / Official MCP Registry / GitHub README copy of it. To make that impossible, we use a single source of truth with mechanical drift detection.

Read this before editing any install snippet, config, or README install section. The rules are short, but ignoring them produces silent breakage that we only notice when a user reports it.


The one rule

src/configs/source.json is the only file you may edit by hand for distribution metadata. Everything else is generated and any manual edit will be overwritten — or, worse, will silently drift until CI catches it.

Files that are generated (never edit by hand):

FileWhat it powers
docs/configs/cursor.jsonCursor .cursor/mcp.json snippet
docs/configs/claude-desktop.jsonClaude Desktop claude_desktop_config.json snippet
docs/configs/windsurf.jsonWindsurf mcp_config.json snippet
docs/configs/vscode.jsonVS Code .vscode/mcp.json snippet (uses servers, not mcpServers)
docs/configs/cursor-deep-link.txtBase64-encoded cursor://... install URL
docs/configs/vscode-deep-link.txtURL-encoded vscode:mcp/install?... URL
docs/configs/claude-code-cli.shclaude mcp add ... shell command
server.jsonOfficial MCP Registry manifest
docs/snippets/claude-code.mdMarkdown partial — Claude Code install (consumed by README + docs site)
docs/snippets/cursor.mdMarkdown partial — Cursor install
docs/snippets/claude-desktop.mdMarkdown partial — Claude Desktop install
docs/snippets/vscode.mdMarkdown partial — VS Code install
docs/snippets/windsurf.mdMarkdown partial — Windsurf install
README.md (only the section between <!-- INSTALL_SNIPPETS_START --> and <!-- INSTALL_SNIPPETS_END -->)Top-level install section, in-place rewritten by the generator

If your editor pops up a diff in any of these files and you didn't change src/configs/source.json, the diff is wrong. Discard it.

How to change a distribution config

# 1. Edit the source
$EDITOR src/configs/source.json

# 2. Regenerate everything
npm run generate:configs

# 3. Verify nothing else drifted
npm run verify:configs

# 4. Commit BOTH the source change AND every regenerated file
git add src/configs/source.json docs/ server.json README.md
git commit -m "..."

If you forget step 2 and push only the source change, CI will fail on the drift check before the PR can merge. This is intentional — mechanical enforcement is the whole point.

CI and the optional pre-push hook

There are two layers of drift detection — they catch the same problem at different points and you should not skip either.

Layer 1: GitHub Actions (mandatory)

Every PR runs .github/workflows/verify-configs.yml, which executes node scripts/generate-configs.mjs --check. The job fails the build if any of the 15 generated artifacts (or the README install block) does not match what would be re-emitted from src/configs/source.json. The job is required for merge into main. This is the authoritative gate — it works for forks, blocks PRs, can't be bypassed by --no-verify, and protects you from your own typos.

Faster feedback (~50 ms vs ~30-60 s for CI). Catches the drift before the commit even hits GitHub. Install once per clone:

npm run install-hooks

This writes .git/hooks/pre-push (per-clone, not committed) that runs npm run verify:configs before every push. If it fails, your push is aborted and you get the same ✗ Drift detected: ... message you would have seen in CI — just locally and 1000× faster.

If you ever need to bypass it for a one-off (e.g., pushing a temporary branch you don't care about), use git push --no-verify. Do not bypass for branches that target main — CI will reject them anyway.

We deliberately avoid husky and pre-commit — those would add a dependency and force the hook on everyone, including casual contributors who just want to fix a typo. The opt-in script is one command and zero deps.

How npm run generate:configs works

scripts/generate-configs.mjs reads src/configs/source.json and emits 14 artifacts:

  1. 8 distribution configs in docs/configs/ (Cursor, Claude Desktop, Windsurf, VS Code JSON + 2 deep-link strings + Claude Code CLI shell script), plus server.json at the repo root for the Official MCP Registry. (Smithery's current publish model requires a framework rewrite or a hosted HTTPS endpoint — see the note in scripts/generate-configs.mjs where genSmitheryYaml used to live.)
  2. 5 Markdown partials in docs/snippets/ — these are the same install snippets, formatted for inclusion in any Markdown context (README, mnemoverse-docs site pages, llms.txt, etc.).
  3. 1 in-place rewrite of the install section in README.md, between the <!-- INSTALL_SNIPPETS_START --> and <!-- INSTALL_SNIPPETS_END --> HTML comment markers. The rest of the README is human-prose and is left untouched.

The generator is idempotent: running it twice in a row produces zero changes the second time. CI relies on this property — it runs the generator with --check, which verifies every output matches what is committed and fails the build otherwise.

How to add a new distribution channel

  1. Add a new generator function in scripts/generate-configs.mjs that produces the channel's config from the data already in source.json (do NOT introduce a parallel data source — extend source.json instead if you need new fields).
  2. Add the new artifact to the OUTPUTS array.
  3. If the channel needs a Markdown install snippet, also add a snippet*() helper and a docs/snippets/{channel}.md entry. If the snippet should also appear in README, append it to readmeInstallBlock().
  4. Update this CONTRIBUTING.md table above with the new file.
  5. Run npm run generate:configs && npm run verify:configs. Both must succeed.
  6. Commit everything together — the source change, the new generator function, the new generated files, and the updated CONTRIBUTING.md.

Things you must not do

  • Edit a generated file directly. Even a one-character fix will be reverted on the next generate:configs and CI will fail in the meantime.
  • Bypass the drift check. Do not pass --no-verify or skip CI. The check exists because we got bitten by 8 copies of the same install snippet drifting in different directions.
  • Hard-code distribution metadata in src/index.ts. The MCP server source code only knows about tools, transport, and the API URL. Channel-specific stuff lives in source.json.
  • Create a parallel "config" file alongside source.json. If source.json is missing a field you need, add it to source.json and update the generator. One source.

Versioning and releases

package.json#versionsrc/index.ts#version (server name reported to MCP clients) → server.json#version (Official MCP Registry).

These are bumped manually in the first two files and propagated by the generator to the third. If they drift, the drift check on server.json catches it on every PR.

Automated release pipeline

Releases are automated by .github/workflows/release.yml. You bump the version on main, push a semver tag, and the workflow does the rest: npm publish, MCP Registry publish (via GitHub OIDC — no stored PAT), GitHub release.

The workflow is triggered by any tag matching v* pushed to the repo. To release:

# 1. Bump version in package.json
$EDITOR package.json

# 2. Match it in src/index.ts (search for version: "...")
$EDITOR src/index.ts

# 3. Regenerate (this updates server.json to match)
npm run generate:configs

# 4. Build and run any local checks you want before tagging
npm run build
npm run verify:configs

# 5. PR + squash-merge to main (drift CI runs on the PR)
git checkout -b release/vX.Y.Z
git add -A && git commit -m "release: vX.Y.Z"
git push -u origin release/vX.Y.Z
gh pr create --fill && gh pr merge --squash --delete-branch
git checkout main && git pull --ff-only

# 6. Tag main and push the tag
git tag -a vX.Y.Z -m "vX.Y.Z"
git push origin vX.Y.Z

That push fires .github/workflows/release.yml, which:

  1. Verifies the tag matches package.json#version (belt and suspenders).
  2. Runs npm ci && npm run build (which also runs generate:configs via prebuild).
  3. Runs npm run verify:configs for drift.
  4. npm publish using the NPM_TOKEN secret.
  5. Installs mcp-publisher from its Linux amd64 release bottle.
  6. Authenticates to the Registry via mcp-publisher login github-oidc — this uses GitHub Actions' OIDC identity token. No long-lived PAT is stored anywhere. The prerequisite is that the authenticating GitHub account's membership in the mnemoverse org be public (already the case for izgorodin).
  7. mcp-publisher publish uploads the freshly-generated server.json.
  8. Verifies the registry entry via a direct curl against the public API.
  9. Creates a GitHub release with auto-generated notes.

If any step fails, the release is aborted — nothing partial gets published. You can re-push the same tag after fixing the issue.

One-time setup for the workflow

A single secret must be added at Settings → Secrets → Actions:

SecretHow to get it
NPM_TOKENnpmjs.com → Settings → Access Tokens → Generate New TokenAutomation token, scope: publish to @mnemoverse/mcp-memory-server.

Nothing else. GitHub OIDC provides the MCP Registry credential at run time, so we do not store an MCP_GITHUB_TOKEN secret at all.

Manual trigger

If you need to re-run the pipeline for a tag that was already pushed (e.g. the workflow was added after the tag), use workflow_dispatch:

gh workflow run release.yml -f tag=v0.3.1

Or click Run workflow on the Actions tab and enter the tag.

Testing changes locally

The fastest feedback loop is:

# Build
npm run build

# Pack into an installable tarball
npm pack

# Smoke test in an isolated dir
mkdir /tmp/mcp-smoke && cd /tmp/mcp-smoke
npm init -y >/dev/null
npm install /path/to/mcp-memory-server/mnemoverse-mcp-memory-server-X.Y.Z.tgz
MNEMOVERSE_API_KEY=mk_test_fake ./node_modules/.bin/mcp-memory-server
# → should print "Error: MNEMOVERSE_API_KEY environment variable is required" if unset, or start a stdio MCP server if set

For end-to-end testing against the live API, set a real mk_live_* key and pipe a few JSON-RPC messages on stdin (initialize, notifications/initialized, tools/call).

Who to ask

  • For the design rationale behind single-source-of-truth: see PR #6.
  • For the README rewriter design: see PR #11.
  • For everything else, open an issue.