Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions npm/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ module.exports = {
isLockContention,
openResponse,
releaseAssetLock,
REPO,
sha256File,
verifyFile,
};
4 changes: 2 additions & 2 deletions npm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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": [
Expand Down
60 changes: 60 additions & 0 deletions npm/registry-identity.test.js
Original file line number Diff line number Diff line change
@@ -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",
);
});
2 changes: 1 addition & 1 deletion npm/server.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading