From e7fdc03f76074d80965546e49ccfc129eb829f9a Mon Sep 17 00:00:00 2001 From: waterbro-8 <318569545+waterbro-8@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:00:04 +0000 Subject: [PATCH] fix(npm): follow the registry identifier to the bytefolk namespace The repository moved to `bytefolk`, and the official MCP Registry derives an entry's namespace from the repository owner. `mcpName` still named the organization this repository used to belong to, so a submission carrying it either resolves to a namespace we do not control or is rejected outright. That is the whole of why the registry listing has never appeared: nothing else about the server was missing. `mcpName` becomes `io.github.bytefolk/mem-mcp` in both manifests that carry it (`npm/server.json`, `npm/package.json`). Deliberately unchanged: - The npm package stays `@fullstack-ai-infra/mem-mcp`. A registry identifier and an npm package identifier are different namespaces, and moving the package would break every existing `"args": ["-y", "@fullstack-ai-infra/mem-mcp"]` client config and every cached install path for no registry-side gain. - The installer's cache directory, which is also keyed on the old name, keeps its path. Renaming it would discard working caches to change a folder name. - The version stays 0.1.1 here. `scripts/validate_release_version.sh` pins `npm/server.json` to the tagged version, so the bump belongs to the release that carries it, not to this change. `npm/registry-identity.test.js` is added because the failure was a stale string in a manifest, and a rename is exactly the event that makes a manifest stale. It derives the expected namespace from the repository coordinate the installer itself downloads from, so it follows a future rename instead of asserting one particular spelling, and it pins the two manifests against each other. Verified by mutation: leaving `mcpName` on the old organization fails two of the three cases, and renaming only the repository coordinate fails the third. Refs #153 for the registry half only. That issue's title and scope ask for the npm package to move to `@bytefolk/mem-mcp` as well, and the 2026-09-03 decision is to keep the published scope. #153 should be rewritten to say so: as long as it reads "migrate mem-mcp to @bytefolk/mem-mcp", the next person who picks it up will move the scope and undo the part that is not up for grabs. --- CHANGELOG.md | 11 ++++++- npm/install.js | 1 + npm/package.json | 4 +-- npm/registry-identity.test.js | 60 +++++++++++++++++++++++++++++++++++ npm/server.json | 2 +- 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 npm/registry-identity.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 104b02f..3e6107e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,16 @@ The project publishes 0.x prerelease versions; a stable release line is not yet - Migrate GitHub repository, Release, issue, badge, and raw-content coordinates to the canonical `bytefolk` organization while retaining the published npm - scope, MCP identity, and existing cache paths. + scope and the existing cache paths. +- Follow the registry identifier after that rename: `mcpName` becomes + `io.github.bytefolk/mem-mcp`, because the official MCP Registry namespace is + derived from the repository owner and the previous value, naming the + organization this repository used to belong to, cannot resolve. The npm + package name and the installer's cache directory are deliberately unchanged, + so an existing installation keeps working and keeps its cache. + `npm/registry-identity.test.js` now asserts the identifier against the + repository coordinate the installer itself uses, so the next rename cannot + leave a stale identifier behind unnoticed. ### Security diff --git a/npm/install.js b/npm/install.js index 55dcf41..4215cc7 100644 --- a/npm/install.js +++ b/npm/install.js @@ -792,6 +792,7 @@ module.exports = { isLockContention, openResponse, releaseAssetLock, + REPO, sha256File, verifyFile, }; diff --git a/npm/package.json b/npm/package.json index 9481628..235e2ee 100644 --- a/npm/package.json +++ b/npm/package.json @@ -2,7 +2,7 @@ "name": "@fullstack-ai-infra/mem-mcp", "version": "0.1.1", "description": "MCP server for mem — a portable, self-hosted memory plane for AI agents", - "mcpName": "io.github.fullstack-ai-infra/mem-mcp", + "mcpName": "io.github.bytefolk/mem-mcp", "keywords": [ "mcp", "model-context-protocol", @@ -27,7 +27,7 @@ "os": ["linux", "darwin", "win32"], "cpu": ["x64", "arm64"], "scripts": { - "test": "node --test install.test.js mem-mcp.test.js windows-shim.test.js", + "test": "node --test install.test.js mem-mcp.test.js registry-identity.test.js windows-shim.test.js", "test:tarball": "node --test clean-tarball.test.js" }, "files": [ diff --git a/npm/registry-identity.test.js b/npm/registry-identity.test.js new file mode 100644 index 0000000..298e161 --- /dev/null +++ b/npm/registry-identity.test.js @@ -0,0 +1,60 @@ +"use strict"; + +const assert = require("node:assert/strict"); +const { readFileSync } = require("node:fs"); +const { join } = require("node:path"); +const test = require("node:test"); +const { REPO } = require("./install"); + +const serverManifest = JSON.parse( + readFileSync(join(__dirname, "server.json"), "utf8"), +); +const packageManifest = JSON.parse( + readFileSync(join(__dirname, "package.json"), "utf8"), +); + +// The registry derives its namespace from the repository owner, so every rename +// leaves the published identifier pointing at an organization that no longer +// exists, and the submission is rejected with no hint that a stale string in a +// manifest caused it. The repository coordinate is therefore asserted here +// against the identifier the submission carries, rather than repeated in a +// third file. +test("the registry namespace follows the repository owner", () => { + const [owner] = REPO.split("/"); + assert.match(REPO, /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\/[a-z0-9._-]+$/i); + for (const [label, manifest] of [ + ["server.json", serverManifest], + ["package.json", packageManifest], + ]) { + assert.equal( + manifest.mcpName, + `io.github.${owner}/${serverManifest.name}`, + `${label} mcpName must carry the owner of the repository the installer downloads from (${REPO})`, + ); + } +}); + +// Both manifests are published and read by different tools, so a disagreement +// between them decides which one the validator saw rather than which one is right. +test("both manifests name the same server", () => { + assert.equal(serverManifest.mcpName, packageManifest.mcpName); + assert.equal(serverManifest.version, packageManifest.version); +}); + +// A registry identifier is a primary key, so the npm scope is allowed to differ +// from it while the package name is not allowed to drift from it: `mcpName` +// ends in the unscoped package name by construction, and moving the package +// without moving the identifier would silently fork the registry record. +test("the registry name is the unscoped package name", () => { + const [, packageName] = packageManifest.name.split("/"); + assert.equal( + serverManifest.mcpName.split("/").pop(), + packageName, + "the trailing segment of mcpName must be the published package name without its scope", + ); + assert.equal( + serverManifest.name, + packageName, + "server.json name must match the published package name without its scope", + ); +}); diff --git a/npm/server.json b/npm/server.json index f214f7a..aa3fd6e 100644 --- a/npm/server.json +++ b/npm/server.json @@ -1,5 +1,5 @@ { - "mcpName": "io.github.fullstack-ai-infra/mem-mcp", + "mcpName": "io.github.bytefolk/mem-mcp", "name": "mem-mcp", "version": "0.1.1", "description": "MCP server for mem — a portable, self-hosted memory plane for AI agents",