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
|
||||
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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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