Skip to content

[pull] main from modelcontextprotocol:main - #335

Merged
pull[bot] merged 7 commits into
threatcode:mainfrom
modelcontextprotocol:main
Aug 28, 2026
Merged

[pull] main from modelcontextprotocol:main#335
pull[bot] merged 7 commits into
threatcode:mainfrom
modelcontextprotocol:main

Conversation

@pull

@pull pull Bot commented Aug 28, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

eeshsaxena and others added 7 commits August 28, 2026 09:58
…nation (#4630)

The move_file tool description and README both state the operation fails if
the destination already exists, but the handler called fs.rename directly,
which silently overwrites the destination. Since this server ships no delete
tool, move_file effectively provided unadvertised, irreversible file
deletion: moving any file onto an existing target destroyed the target.

Add a moveFile helper in lib.ts that rejects when the destination already
exists (checked with lstat, so an existing symlink is detected rather than
followed) and only renames when the target is free, and use it from the
move_file handler. Behaviour now matches the documented contract.

Fixes #4628
* fix(memory): write the knowledge graph atomically

`saveGraph()` wrote the graph straight to the memory file:

    await fs.writeFile(this.memoryFilePath, lines.join("\n"));

`fs.writeFile` opens the target with `'w'`, which truncates it before any
new bytes are written. If the process is interrupted between truncation
and completion — SIGKILL, container or host stop, OOM kill, power loss —
the memory file is left empty or half-written.

That file is the only persistence layer for the knowledge graph, so the
window is small but the loss is total and unrecoverable.

The fix writes to a temporary file in the same directory and renames it
over the target. `rename(2)` is atomic on POSIX filesystems: a reader
sees either the complete old file or the complete new one, never an
intermediate state. Keeping the temp file in the same directory ensures
the rename stays on one filesystem, since cross-device renames fail with
EXDEV. On failure the temp file is removed so no strays accumulate.

Tests added in `__tests__/atomic-save.test.ts`:

- the live memory file is never opened for truncating writes
- a committed graph survives a write that fails midway
- no temporary files remain after a successful write
- the temporary file is cleaned up when the rename fails
- graph contents still round-trip correctly across reloads

The first and fourth fail against the previous implementation.

Full suite: 55 passed (50 existing, 5 new). `tsc --noEmit` clean.

Fixes #4614

* fix(memory): use randomBytes for temp filename to avoid concurrent collision

---------

Co-authored-by: teddiesloco <teddiesloco@users.noreply.github.com>
Signed-off-by: KXH <shepherdlaurie238@gmail.com>
* fix(sequentialthinking): read server version from package.json

Replace the hardcoded 0.2.0 so initialize serverInfo tracks the
package version (0.6.2 in-tree; CalVer after release), matching the
approach used for memory server version sync.

Skip the dist-layout assertion when dist/ is absent so CI's
test-before-build job (and local runs without prepare) still pass.

* test(sequentialthinking): smoke stdio initialize serverInfo.version

Spawn the built server over stdio and assert getServerVersion() matches
package.json (and is not the old hardcoded 0.2.0). Skip when dist/ is
absent so test-before-build CI still passes.
…inputSchema required array (#4652)

* fix(sequentialthinking): restore nextThoughtNeeded in the advertised inputSchema required array

commit 1cdf806 (#3533) wrapped nextThoughtNeeded in a z.preprocess-based
coercedBoolean to fix a real footgun (string "false" coercing to true).
zod's toJSONSchema(..., { io: "input" }) treats a z.preprocess()-wrapped
field's input type as unknown, so it silently drops that field from the
emitted required array, even though it carries no .optional(). A client
that builds its call arguments from the advertised schema then omits
nextThoughtNeeded and gets a -32602 Invalid params from the runtime
validator, which still requires it.

Rebuild coercedBoolean as a transform on an explicit z.union([z.boolean(),
z.string()]) instead of z.preprocess. The union gives toJSONSchema a
concrete input type to report, so nextThoughtNeeded stays in required,
while parse behavior (including the case-insensitive string coercion
#3533 added) is unchanged.

Fixes #4651

* test(sequentialthinking): pin nextThoughtNeeded in required and string coercion

Runs against the built server so it checks the schema the SDK emits.
Skips when dist/ is absent, matching server-version.test.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Wo28CHPXyM3DHoLWvKnCK

---------

Co-authored-by: olaservo <olahungerford@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…4631)

validatePath()'s ENOENT fallback checked only the immediate parent
directory before allowing a new path through. Creating a path with
more than one missing level (e.g. a/b/c when none of a, b, c exist)
made the immediate parent check fail with "Parent directory does not
exist", even though fs.mkdir was already called with recursive: true
one level up in the create_directory handler and an existing allowed
ancestor was available further up the tree.

Walk up through missing ancestors until an existing one is found (or
the filesystem root is reached), verifying that ancestor still
resolves inside an allowed directory via realpath. The top-level
lexical containment check already ran against the full target path,
so this only extends how far the existence/symlink check climbs.

Fixes #4629

Co-authored-by: Riley <313779891+rileybuilds@users.noreply.github.com>
Co-authored-by: olaservo <olahungerford@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@pull pull Bot locked and limited conversation to collaborators Aug 28, 2026
@pull pull Bot added the ⤵️ pull label Aug 28, 2026
@pull
pull Bot merged commit cda92bd into threatcode:main Aug 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants