feat: implement sequential thinking MCP server with tool handling and logging
Build And Push Image / docker (push) Successful in 2m53s
Details
Build And Push Image / docker (push) Successful in 2m53s
Details
This commit is contained in:
parent
5c8bf15956
commit
99772ab62c
|
|
@ -1,17 +1,29 @@
|
||||||
export default defineNuxtRouteMiddleware((to) => {
|
export default defineNuxtRouteMiddleware(async (to) => {
|
||||||
// Skip auth for public pages
|
// Skip auth for public pages
|
||||||
if (to.meta.auth === false) {
|
if (to.meta.auth === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user is authenticated
|
// Use the same auth system as the rest of the app
|
||||||
const authState = useState('auth.state', () => ({
|
const { isAuthenticated, checkAuth, user } = useAuth();
|
||||||
authenticated: false,
|
|
||||||
user: null,
|
console.log('🛡️ Auth middleware running for:', to.path);
|
||||||
groups: [],
|
|
||||||
}));
|
// 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');
|
return navigateTo('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('✅ Auth middleware passed, allowing access to:', to.path);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/
|
||||||
|
build/
|
||||||
|
*.log
|
||||||
|
.env*
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -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"]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue