From 7dc46ba5cbfaaa6925ba2ebec7cdcbd74486cb31 Mon Sep 17 00:00:00 2001 From: Niels Kaspers Date: Thu, 12 Mar 2026 09:07:06 +0200 Subject: [PATCH 1/2] fix(sequential-thinking): use z.coerce for number and boolean params LLM clients (Claude Code, Augment.AI, etc.) intermittently send thoughtNumber, totalThoughts, and nextThoughtNeeded as strings instead of native JSON types. Using z.coerce gracefully handles both string and native inputs without breaking existing behavior. Fixes #3428 --- src/sequentialthinking/index.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sequentialthinking/index.ts b/src/sequentialthinking/index.ts index 809086a94c..3c4415c667 100644 --- a/src/sequentialthinking/index.ts +++ b/src/sequentialthinking/index.ts @@ -72,14 +72,14 @@ You should: 11. Only set nextThoughtNeeded to false when truly done and a satisfactory answer is reached`, inputSchema: { thought: z.string().describe("Your current thinking step"), - nextThoughtNeeded: z.boolean().describe("Whether another thought step is needed"), - thoughtNumber: z.number().int().min(1).describe("Current thought number (numeric value, e.g., 1, 2, 3)"), - totalThoughts: z.number().int().min(1).describe("Estimated total thoughts needed (numeric value, e.g., 5, 10)"), - isRevision: z.boolean().optional().describe("Whether this revises previous thinking"), - revisesThought: z.number().int().min(1).optional().describe("Which thought is being reconsidered"), - branchFromThought: z.number().int().min(1).optional().describe("Branching point thought number"), + nextThoughtNeeded: z.coerce.boolean().describe("Whether another thought step is needed"), + thoughtNumber: z.coerce.number().int().min(1).describe("Current thought number (numeric value, e.g., 1, 2, 3)"), + totalThoughts: z.coerce.number().int().min(1).describe("Estimated total thoughts needed (numeric value, e.g., 5, 10)"), + isRevision: z.coerce.boolean().optional().describe("Whether this revises previous thinking"), + revisesThought: z.coerce.number().int().min(1).optional().describe("Which thought is being reconsidered"), + branchFromThought: z.coerce.number().int().min(1).optional().describe("Branching point thought number"), branchId: z.string().optional().describe("Branch identifier"), - needsMoreThoughts: z.boolean().optional().describe("If more thoughts are needed") + needsMoreThoughts: z.coerce.boolean().optional().describe("If more thoughts are needed") }, outputSchema: { thoughtNumber: z.number(), From e9340d3f01a76cfba2edc5abe81642497246930a Mon Sep 17 00:00:00 2001 From: Niels Kaspers Date: Sun, 15 Mar 2026 18:47:58 +0200 Subject: [PATCH 2/2] fix(sequential-thinking): replace z.coerce.boolean() with safe preprocess z.coerce.boolean() is unsafe because Boolean("false") === true in JavaScript. Replace with a preprocess transform that correctly handles string "true"/"false" values. Keeps z.coerce.number() as-is since Number("123") works correctly. --- src/sequentialthinking/index.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sequentialthinking/index.ts b/src/sequentialthinking/index.ts index 3c4415c667..038866b96f 100644 --- a/src/sequentialthinking/index.ts +++ b/src/sequentialthinking/index.ts @@ -5,6 +5,16 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" import { z } from "zod"; import { SequentialThinkingServer } from './lib.js'; +/** Safe boolean coercion that correctly handles string "false" */ +const coercedBoolean = z.preprocess((val) => { + if (typeof val === "boolean") return val; + if (typeof val === "string") { + if (val.toLowerCase() === "true") return true; + if (val.toLowerCase() === "false") return false; + } + return val; +}, z.boolean()); + const server = new McpServer({ name: "sequential-thinking-server", version: "0.2.0", @@ -72,14 +82,14 @@ You should: 11. Only set nextThoughtNeeded to false when truly done and a satisfactory answer is reached`, inputSchema: { thought: z.string().describe("Your current thinking step"), - nextThoughtNeeded: z.coerce.boolean().describe("Whether another thought step is needed"), + nextThoughtNeeded: coercedBoolean.describe("Whether another thought step is needed"), thoughtNumber: z.coerce.number().int().min(1).describe("Current thought number (numeric value, e.g., 1, 2, 3)"), totalThoughts: z.coerce.number().int().min(1).describe("Estimated total thoughts needed (numeric value, e.g., 5, 10)"), - isRevision: z.coerce.boolean().optional().describe("Whether this revises previous thinking"), + isRevision: coercedBoolean.optional().describe("Whether this revises previous thinking"), revisesThought: z.coerce.number().int().min(1).optional().describe("Which thought is being reconsidered"), branchFromThought: z.coerce.number().int().min(1).optional().describe("Branching point thought number"), branchId: z.string().optional().describe("Branch identifier"), - needsMoreThoughts: z.coerce.boolean().optional().describe("If more thoughts are needed") + needsMoreThoughts: coercedBoolean.optional().describe("If more thoughts are needed") }, outputSchema: { thoughtNumber: z.number(),