feat: implement sequential thinking MCP server with tool handling and logging
Build And Push Image / docker (push) Successful in 2m53s Details

This commit is contained in:
Matt 2025-08-07 14:58:08 +02:00
parent 5c8bf15956
commit 99772ab62c
6 changed files with 378 additions and 8 deletions

View File

@ -1,17 +1,29 @@
export default defineNuxtRouteMiddleware((to) => {
export default defineNuxtRouteMiddleware(async (to) => {
// Skip auth for public pages
if (to.meta.auth === false) {
return;
}
// Check if user is authenticated
const authState = useState('auth.state', () => ({
authenticated: false,
user: null,
groups: [],
}));
// Use the same auth system as the rest of the app
const { isAuthenticated, checkAuth, user } = useAuth();
console.log('🛡️ Auth middleware running for:', to.path);
// Ensure auth is checked if user isn't loaded
if (!user.value) {
console.log('🔄 User not loaded, checking auth...');
await checkAuth();
}
if (!authState.value.authenticated) {
console.log('✅ Auth middleware check:', {
isAuthenticated: isAuthenticated.value,
user: user.value?.email
});
if (!isAuthenticated.value) {
console.log('❌ User not authenticated, redirecting to login');
return navigateTo('/login');
}
console.log('✅ Auth middleware passed, allowing access to:', to.path);
});

4
sequential-thinking/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules/
build/
*.log
.env*

View File

@ -0,0 +1,70 @@
# sequential-thinking MCP Server
A Model Context Protocol server
This is a TypeScript-based MCP server that implements a simple notes system. It demonstrates core MCP concepts by providing:
- Resources representing text notes with URIs and metadata
- Tools for creating new notes
- Prompts for generating summaries of notes
## Features
### Resources
- List and access notes via `note://` URIs
- Each note has a title, content and metadata
- Plain text mime type for simple content access
### Tools
- `create_note` - Create new text notes
- Takes title and content as required parameters
- Stores note in server state
### Prompts
- `summarize_notes` - Generate a summary of all stored notes
- Includes all note contents as embedded resources
- Returns structured prompt for LLM summarization
## Development
Install dependencies:
```bash
npm install
```
Build the server:
```bash
npm run build
```
For development with auto-rebuild:
```bash
npm run watch
```
## Installation
To use with Claude Desktop, add the server config:
On MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"sequential-thinking": {
"command": "/path/to/sequential-thinking/build/index.js"
}
}
}
```
### Debugging
Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), which is available as a package script:
```bash
npm run inspector
```
The Inspector will provide a URL to access debugging tools in your browser.

View File

@ -0,0 +1,26 @@
{
"name": "sequential-thinking",
"version": "0.1.0",
"description": "A Model Context Protocol server",
"private": true,
"type": "module",
"bin": {
"sequential-thinking": "./build/index.js"
},
"files": [
"build"
],
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
"prepare": "npm run build",
"watch": "tsc --watch",
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "0.6.0"
},
"devDependencies": {
"@types/node": "^20.11.24",
"typescript": "^5.3.3"
}
}

View File

@ -0,0 +1,243 @@
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from '@modelcontextprotocol/sdk/types.js';
interface ThoughtStep {
thoughtNumber: number;
thought: string;
isRevision?: boolean;
revisesThought?: number;
branchFromThought?: number;
branchId?: string;
timestamp: string;
}
interface SequentialThinkingArgs {
thought: string;
nextThoughtNeeded: boolean;
thoughtNumber: number;
totalThoughts: number;
isRevision?: boolean;
revisesThought?: number;
branchFromThought?: number;
branchId?: string;
needsMoreThoughts?: boolean;
}
const isValidSequentialThinkingArgs = (
args: any
): args is SequentialThinkingArgs =>
typeof args === 'object' &&
args !== null &&
typeof args.thought === 'string' &&
typeof args.nextThoughtNeeded === 'boolean' &&
typeof args.thoughtNumber === 'number' &&
typeof args.totalThoughts === 'number' &&
(args.isRevision === undefined || typeof args.isRevision === 'boolean') &&
(args.revisesThought === undefined || typeof args.revisesThought === 'number') &&
(args.branchFromThought === undefined || typeof args.branchFromThought === 'number') &&
(args.branchId === undefined || typeof args.branchId === 'string') &&
(args.needsMoreThoughts === undefined || typeof args.needsMoreThoughts === 'boolean');
class SequentialThinkingServer {
private server: Server;
private thoughtHistory: ThoughtStep[] = [];
private disableLogging: boolean;
constructor() {
this.disableLogging = process.env.DISABLE_THOUGHT_LOGGING === 'true';
this.server = new Server(
{
name: 'sequential-thinking-server',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
this.setupToolHandlers();
// Error handling
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
private setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'sequential_thinking',
description: 'Facilitates a detailed, step-by-step thinking process for problem-solving and analysis. Supports dynamic revision, branching, and iterative refinement of thoughts.',
inputSchema: {
type: 'object',
properties: {
thought: {
type: 'string',
description: 'The current thinking step or analysis',
},
nextThoughtNeeded: {
type: 'boolean',
description: 'Whether another thought step is needed to continue the analysis',
},
thoughtNumber: {
type: 'integer',
description: 'Current thought number in the sequence',
minimum: 1,
},
totalThoughts: {
type: 'integer',
description: 'Estimated total number of thoughts needed (can be adjusted dynamically)',
minimum: 1,
},
isRevision: {
type: 'boolean',
description: 'Whether this thought revises or refines previous thinking',
},
revisesThought: {
type: 'integer',
description: 'Which thought number is being reconsidered (if isRevision is true)',
minimum: 1,
},
branchFromThought: {
type: 'integer',
description: 'Thought number from which this branches into alternative reasoning',
minimum: 1,
},
branchId: {
type: 'string',
description: 'Identifier for the branch (e.g., "alternative_approach", "edge_case")',
},
needsMoreThoughts: {
type: 'boolean',
description: 'Whether the total number of thoughts should be increased',
},
},
required: ['thought', 'nextThoughtNeeded', 'thoughtNumber', 'totalThoughts'],
},
},
],
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== 'sequential_thinking') {
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
if (!isValidSequentialThinkingArgs(request.params.arguments)) {
throw new McpError(
ErrorCode.InvalidParams,
'Invalid sequential thinking arguments'
);
}
const args = request.params.arguments;
try {
// Create thought step
const thoughtStep: ThoughtStep = {
thoughtNumber: args.thoughtNumber,
thought: args.thought,
isRevision: args.isRevision,
revisesThought: args.revisesThought,
branchFromThought: args.branchFromThought,
branchId: args.branchId,
timestamp: new Date().toISOString(),
};
// Add to history
this.thoughtHistory.push(thoughtStep);
// Log thought if logging is enabled
if (!this.disableLogging) {
console.error(`[Thought ${args.thoughtNumber}/${args.totalThoughts}]${args.isRevision ? ' (Revision)' : ''}${args.branchId ? ` [Branch: ${args.branchId}]` : ''}: ${args.thought}`);
}
// Prepare response
let responseText = `**Thought ${args.thoughtNumber}/${args.totalThoughts}**`;
if (args.isRevision && args.revisesThought) {
responseText += ` *(Revising thought ${args.revisesThought})*`;
}
if (args.branchId) {
responseText += ` *[Branch: ${args.branchId}]*`;
if (args.branchFromThought) {
responseText += ` *(from thought ${args.branchFromThought})*`;
}
}
responseText += `\n\n${args.thought}`;
if (args.needsMoreThoughts) {
responseText += `\n\n*Note: Analysis indicates more thoughts may be needed beyond the initial estimate of ${args.totalThoughts}.*`;
}
if (args.nextThoughtNeeded) {
responseText += `\n\n*Continuing to next thought...*`;
} else {
responseText += `\n\n*Analysis complete.*`;
// Provide summary if this is the final thought
if (this.thoughtHistory.length > 1) {
responseText += `\n\n**Summary**: Completed sequential thinking process with ${this.thoughtHistory.length} thought steps.`;
const revisions = this.thoughtHistory.filter(t => t.isRevision).length;
const branches = new Set(this.thoughtHistory.filter(t => t.branchId).map(t => t.branchId)).size;
if (revisions > 0) {
responseText += ` Included ${revisions} revision${revisions > 1 ? 's' : ''}.`;
}
if (branches > 0) {
responseText += ` Explored ${branches} alternative branch${branches > 1 ? 'es' : ''}.`;
}
}
}
return {
content: [
{
type: 'text',
text: responseText,
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error in sequential thinking process: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
isError: true,
};
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Sequential Thinking MCP server running on stdio');
}
}
const server = new SequentialThinkingServer();
server.run().catch(console.error);

View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}