fix(sequential-thinking): convert to modern TypeScript SDK APIs (#3014)

* fix(sequential-thinking): convert to modern TypeScript SDK APIs

Convert the sequential-thinking server to use the modern McpServer API
instead of the low-level Server API.

Key changes:
- Replace Server with McpServer from @modelcontextprotocol/sdk/server/mcp.js
- Use registerTool() method instead of manual request handlers
- Use Zod schemas directly in inputSchema/outputSchema
- Add structuredContent to tool responses
- Fix type literals to use 'as const' assertions

The modern API provides:
- Less boilerplate code
- Better type safety with Zod
- More declarative tool registration
- Cleaner, more maintainable code

* fix: exclude test files from TypeScript build

Add exclude for test files and vitest.config.ts to tsconfig

* refactor: remove redundant validation now handled by Zod

Zod schema already validates all required fields and types. Removed
validateThoughtData() method and kept only business logic validation
(adjusting totalThoughts if needed).

* fix(sequentialthinking): add Zod validation to processThought method

The modern API migration removed manual validation from processThought(),
but tests call this method directly, bypassing the Zod validation in the
tool registration layer. This commit adds Zod validation directly in the
processThought() method to ensure validation works both when called via
MCP and when called directly (e.g., in tests).

Also improves error message formatting to match the expected error
messages in the tests.

* refactor: simplify by removing redundant validation

Since processThought() is only called through the tool registration in
production, validation always happens via Zod schemas at that layer.
Removed redundant validation logic from processThought() and updated
tests to reflect this architectural decision.

Changes:
- Remove Zod validation from processThought() method
- Accept ThoughtData type instead of unknown
- Remove 10 validation tests that are now handled at tool registration
- Add comment explaining validation approach
This commit is contained in:
adam jones
2025-11-20 19:05:30 +00:00
committed by GitHub
parent 4dc24cf349
commit 88a2ac4360
4 changed files with 78 additions and 273 deletions

View File

@@ -1,17 +1,22 @@
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { SequentialThinkingServer } from './lib.js';
const SEQUENTIAL_THINKING_TOOL: Tool = {
name: "sequentialthinking",
description: `A detailed tool for dynamic and reflective problem-solving through thoughts.
const server = new McpServer({
name: "sequential-thinking-server",
version: "0.2.0",
});
const thinkingServer = new SequentialThinkingServer();
server.registerTool(
"sequentialthinking",
{
title: "Sequential Thinking",
description: `A detailed tool for dynamic and reflective problem-solving through thoughts.
This tool helps analyze problems through a flexible thinking process that can adapt and evolve.
Each thought can build on, question, or revise previous insights as understanding deepens.
@@ -37,13 +42,13 @@ Key features:
Parameters explained:
- thought: Your current thinking step, which can include:
* Regular analytical steps
* Revisions of previous thoughts
* Questions about previous decisions
* Realizations about needing more analysis
* Changes in approach
* Hypothesis generation
* Hypothesis verification
* Regular analytical steps
* Revisions of previous thoughts
* Questions about previous decisions
* Realizations about needing more analysis
* Changes in approach
* Hypothesis generation
* Hypothesis verification
- nextThoughtNeeded: True if you need more thinking, even if at what seemed like the end
- thoughtNumber: Current number in sequence (can go beyond initial total if needed)
- totalThoughts: Current estimate of thoughts needed (can be adjusted up/down)
@@ -65,86 +70,42 @@ You should:
9. Repeat the process until satisfied with the solution
10. Provide a single, ideally correct answer as the final output
11. Only set next_thought_needed to false when truly done and a satisfactory answer is reached`,
inputSchema: {
type: "object",
properties: {
thought: {
type: "string",
description: "Your current thinking step"
},
nextThoughtNeeded: {
type: "boolean",
description: "Whether another thought step is needed"
},
thoughtNumber: {
type: "integer",
description: "Current thought number (numeric value, e.g., 1, 2, 3)",
minimum: 1
},
totalThoughts: {
type: "integer",
description: "Estimated total thoughts needed (numeric value, e.g., 5, 10)",
minimum: 1
},
isRevision: {
type: "boolean",
description: "Whether this revises previous thinking"
},
revisesThought: {
type: "integer",
description: "Which thought is being reconsidered",
minimum: 1
},
branchFromThought: {
type: "integer",
description: "Branching point thought number",
minimum: 1
},
branchId: {
type: "string",
description: "Branch identifier"
},
needsMoreThoughts: {
type: "boolean",
description: "If more thoughts are needed"
}
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"),
branchId: z.string().optional().describe("Branch identifier"),
needsMoreThoughts: z.boolean().optional().describe("If more thoughts are needed")
},
outputSchema: {
thoughtNumber: z.number(),
totalThoughts: z.number(),
nextThoughtNeeded: z.boolean(),
branches: z.array(z.string()),
thoughtHistoryLength: z.number()
},
required: ["thought", "nextThoughtNeeded", "thoughtNumber", "totalThoughts"]
}
};
const server = new Server(
{
name: "sequential-thinking-server",
version: "0.2.0",
},
{
capabilities: {
tools: {},
},
async (args) => {
const result = thinkingServer.processThought(args);
if (result.isError) {
return result;
}
// Parse the JSON response to get structured content
const parsedContent = JSON.parse(result.content[0].text);
return {
content: result.content,
structuredContent: parsedContent
};
}
);
const thinkingServer = new SequentialThinkingServer();
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [SEQUENTIAL_THINKING_TOOL],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "sequentialthinking") {
return thinkingServer.processThought(request.params.arguments);
}
return {
content: [{
type: "text",
text: `Unknown tool: ${request.params.name}`
}],
isError: true
};
});
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);