Summary
A project that declares toolcraft@0.0.109 as an optional dependency and already has AJV 6 through development tooling can receive an invalid clean npm install. npm dedupes Toolcraft's required ajv@^8.20.0 to the incompatible root AJV 6 instead of installing AJV 8 beneath the optional Toolcraft package.
The Toolcraft CLI/MCP entrypoint then fails at startup when bundled tiny-stdio-mcp-server imports AJV.
Reproduction
Use npm with a package containing:
{
"devDependencies": {
"eslint": "9.39.4"
},
"optionalDependencies": {
"toolcraft": "0.0.109"
}
}
Then run:
npm install --package-lock-only
rm -rf node_modules
npm ci --ignore-scripts
npm ls ajv --all
node -e 'import("toolcraft/mcp")'
Observed dependency result:
toolcraft@0.0.109
└── ajv@6.15.0 deduped invalid: "^8.20.0"
Observed startup error:
Cannot find package '.../node_modules/toolcraft/node_modules/ajv/index.js'
imported from .../toolcraft/node_modules/tiny-stdio-mcp-server/dist/server.js
The lockfile contains an intended node_modules/toolcraft/node_modules/ajv@8.20.0 entry marked optional, but npm ci does not materialize it and reuses the incompatible root AJV 6.
Workaround
Declaring ajv@8.20.0 as another direct optional dependency makes AJV 8 the root selection and moves ESLint's AJV 6 under ESLint, after which Toolcraft CLI/MCP starts correctly. This leaks another Toolcraft implementation dependency into downstream manifests.
Expected behavior
Installing optional Toolcraft must provide a compatible AJV 8 runtime even when the consuming project already uses AJV 6.
A package-side fix could bundle the AJV runtime needed by the bundled stdio server, avoid resolving it through the optional parent dependency graph, or otherwise ensure npm cannot dedupe it to an incompatible major.
Acceptance criteria
The reproduction installs cleanly with only Toolcraft declared by the consumer, npm ls ajv --all reports no invalid dependency, and importing/running toolcraft/mcp succeeds.
Summary
A project that declares
toolcraft@0.0.109as an optional dependency and already has AJV 6 through development tooling can receive an invalid clean npm install. npm dedupes Toolcraft's requiredajv@^8.20.0to the incompatible root AJV 6 instead of installing AJV 8 beneath the optional Toolcraft package.The Toolcraft CLI/MCP entrypoint then fails at startup when bundled
tiny-stdio-mcp-serverimports AJV.Reproduction
Use npm with a package containing:
{ "devDependencies": { "eslint": "9.39.4" }, "optionalDependencies": { "toolcraft": "0.0.109" } }Then run:
npm install --package-lock-only rm -rf node_modules npm ci --ignore-scripts npm ls ajv --all node -e 'import("toolcraft/mcp")'Observed dependency result:
Observed startup error:
The lockfile contains an intended
node_modules/toolcraft/node_modules/ajv@8.20.0entry marked optional, butnpm cidoes not materialize it and reuses the incompatible root AJV 6.Workaround
Declaring
ajv@8.20.0as another direct optional dependency makes AJV 8 the root selection and moves ESLint's AJV 6 under ESLint, after which Toolcraft CLI/MCP starts correctly. This leaks another Toolcraft implementation dependency into downstream manifests.Expected behavior
Installing optional Toolcraft must provide a compatible AJV 8 runtime even when the consuming project already uses AJV 6.
A package-side fix could bundle the AJV runtime needed by the bundled stdio server, avoid resolving it through the optional parent dependency graph, or otherwise ensure npm cannot dedupe it to an incompatible major.
Acceptance criteria
The reproduction installs cleanly with only Toolcraft declared by the consumer,
npm ls ajv --allreports no invalid dependency, and importing/runningtoolcraft/mcpsucceeds.