Skip to content

fix(memory, sequentialthinking, filesystem): declare zod as a dependency (#4330)#4539

Open
gianghungtien wants to merge 1 commit into
modelcontextprotocol:mainfrom
gianghungtien:fix/declare-zod-dependency
Open

fix(memory, sequentialthinking, filesystem): declare zod as a dependency (#4330)#4539
gianghungtien wants to merge 1 commit into
modelcontextprotocol:mainfrom
gianghungtien:fix/declare-zod-dependency

Conversation

@gianghungtien

Copy link
Copy Markdown

Description

memory, sequentialthinking and filesystem all import { z } from "zod" but none of them declared zod in package.json. This PR declares it, and adds a regression test so a phantom dependency cannot silently return.

Fixes #4330.

Server Details

  • Server: memory, sequentialthinking, filesystem
  • Changes to: package.json dependencies (+ tests). No tool, resource, or prompt behaviour changes.

Motivation and Context

Each of the three servers imports zod directly:

Server Import
memory src/memory/index.ts:6
sequentialthinking src/sequentialthinking/index.ts:5
filesystem src/filesystem/index.ts:13

None declared it. The import resolves only by accident: @modelcontextprotocol/sdk depends on zod, and npm's hoisted node_modules layout happens to place it somewhere these packages can reach. That is a phantom dependency — it works until the layout changes.

Under a strict layout (pnpm without hoisting, yarn PnP) it is not reachable, and the servers die on startup. Reproduced against the current published packages:

$ cat .npmrc
node-linker=isolated
hoist=false

$ pnpm add @modelcontextprotocol/server-memory \
           @modelcontextprotocol/server-sequential-thinking \
           @modelcontextprotocol/server-filesystem

$ node node_modules/@modelcontextprotocol/server-memory/dist/index.js
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod' imported from
  .../@modelcontextprotocol/server-memory/dist/index.js

All three fail identically. The issue reports memory and sequentialthinking; filesystem has the same defect and is included here.

Note this is not only a strict-layout concern. The SDK's zod range is ^3.25 || ^4.0, so even under npm the servers silently bind to whichever major the SDK happens to hoist — a future SDK release could flip that under them.

Why ^4.0.0

  • It matches the everything server, the one TS server that already declared zod, so the repo stays consistent.
  • It sits inside the SDK's ^3.25 || ^4.0 peer range, so zod still dedupes to one instance shared with the SDK. That matters: two zod copies would make schemas built here unrecognisable to the SDK's instanceof checks. Verified after the fix — a single zod@4.4.3 is installed and linked.

How Has This Been Tested?

Reproduction, before and after. Packed all three servers from this branch and installed the tarballs into a clean project using the same strict config that fails above (node-linker=isolated, hoist=false):

$ node node_modules/@modelcontextprotocol/server-memory/dist/index.js
Knowledge Graph MCP Server running on stdio
$ node node_modules/@modelcontextprotocol/server-sequential-thinking/dist/index.js
Sequential Thinking MCP Server running on stdio
$ node node_modules/@modelcontextprotocol/server-filesystem/dist/index.js .
Secure MCP Filesystem Server running on stdio

All three now start over stdio where all three previously aborted.

Regression test. Each package gets __tests__/dependencies.test.ts, which scans the sources that compile into dist/ for bare import specifiers and asserts each is declared in dependencies (relative paths and Node builtins are skipped, deep/scoped specifiers reduce to the package name). Confirmed it actually catches the bug by removing zod from src/memory/package.json and re-running:

AssertionError: expected [ '@modelcontextprotocol/sdk' ] to deeply equal ArrayContaining{…}
- ArrayContaining [
+ [
    "@modelcontextprotocol/sdk",
-   "zod",
  ]

It is written with vitest, per CONTRIBUTING.md.

Suites and build. npm ci from a clean tree (so the lockfile is verified the way CI installs it), npm run build, then per-package npm test:

Server Result
memory 4 files, 52 tests passed
sequentialthinking 2 files, 17 tests passed
filesystem 8 files, 158 tests passed

tsc --noEmit is clean for all three.

Not tested with an LLM client. This change adds no tools and alters no schemas or behaviour — the servers either start or they do not, which is what the reproduction above covers end to end. Happy to run a client check if you would like one anyway.

Breaking Changes

None. No client configuration changes. Existing installs on hoisted npm layouts are unaffected, since they already resolve to the same zod v4 — the declaration just makes that guaranteed instead of incidental.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Protocol Documentation
  • My changes follows MCP security best practices
  • I have updated the server's README accordingly — not applicable, no user-facing configuration or behaviour change
  • I have tested this with an LLM client — see the note above; verified by launching each server over stdio under the layout that reproduces the bug
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have documented all environment variables and configuration options — not applicable, none added

Additional context

package-lock.json is regenerated to record the new declarations for the three workspaces; validated with a clean npm ci.

The regression test is duplicated per package rather than shared from a common helper, since each server is published standalone and its tsconfig.json sets rootDir to the package directory — reaching outside it would break the build. Tests are already excluded from compilation via **/*.test.ts, so nothing new ships in dist/.

Worth noting the same class of bug is easy to reintroduce elsewhere; the test is deliberately generic, so dropping it into another server would guard that one too.

The memory, sequentialthinking and filesystem servers all `import { z } from
"zod"` but never declared zod in their package.json. The import resolved only
by accident: the MCP SDK depends on zod, and npm's hoisted node_modules layout
happens to place it where these packages can reach it.

Under a strict node_modules layout (pnpm without hoisting, yarn PnP) the
phantom dependency is not reachable and every one of these servers dies on
startup:

    Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod' imported from
      .../@modelcontextprotocol/server-memory/dist/index.js

Declare zod explicitly in all three packages. The range `^4.0.0` matches the
everything server, which already declared it, and sits inside the SDK's
`^3.25 || ^4.0` peer range, so zod still dedupes to a single instance shared
with the SDK — schemas built here stay recognisable to the SDK.

Add a per-package regression test that scans the shipped sources for bare
import specifiers and asserts each one is declared, so a phantom dependency
cannot silently return.

Fixes modelcontextprotocol#4330
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

server-memory and server-sequential-thinking fail at startup due to missing zod dependency

1 participant