From 99772ab62cee050561003fa56e42c46791e9a8da Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 7 Aug 2025 14:58:08 +0200 Subject: [PATCH] feat: implement sequential thinking MCP server with tool handling and logging --- middleware/auth.ts | 28 +++- sequential-thinking/.gitignore | 4 + sequential-thinking/README.md | 70 +++++++++ sequential-thinking/package.json | 26 ++++ sequential-thinking/src/index.ts | 243 ++++++++++++++++++++++++++++++ sequential-thinking/tsconfig.json | 15 ++ 6 files changed, 378 insertions(+), 8 deletions(-) create mode 100644 sequential-thinking/.gitignore create mode 100644 sequential-thinking/README.md create mode 100644 sequential-thinking/package.json create mode 100644 sequential-thinking/src/index.ts create mode 100644 sequential-thinking/tsconfig.json diff --git a/middleware/auth.ts b/middleware/auth.ts index b8cef7c..1d7d392 100644 --- a/middleware/auth.ts +++ b/middleware/auth.ts @@ -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); }); diff --git a/sequential-thinking/.gitignore b/sequential-thinking/.gitignore new file mode 100644 index 0000000..7f106e9 --- /dev/null +++ b/sequential-thinking/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +build/ +*.log +.env* \ No newline at end of file diff --git a/sequential-thinking/README.md b/sequential-thinking/README.md new file mode 100644 index 0000000..fba72f7 --- /dev/null +++ b/sequential-thinking/README.md @@ -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. diff --git a/sequential-thinking/package.json b/sequential-thinking/package.json new file mode 100644 index 0000000..cd1ab6e --- /dev/null +++ b/sequential-thinking/package.json @@ -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" + } +} diff --git a/sequential-thinking/src/index.ts b/sequential-thinking/src/index.ts new file mode 100644 index 0000000..efd871e --- /dev/null +++ b/sequential-thinking/src/index.ts @@ -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); diff --git a/sequential-thinking/tsconfig.json b/sequential-thinking/tsconfig.json new file mode 100644 index 0000000..a14bee0 --- /dev/null +++ b/sequential-thinking/tsconfig.json @@ -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"] +}