UNPKG

orchestry-mcp

Version:

Orchestry MCP Server for multi-session task management

483 lines (428 loc) 14.5 kB
import { Server as SocketIOServer } from 'socket.io'; import { DatabaseManager } from '../database-manager.js'; import { Database } from '../database.js'; import { z } from 'zod'; import type { Project, Workspace, Goal, Task, Document, TaskStatus, Priority, Session, ProjectStats, } from '../../shared/types.js'; export class OrchestryTools { private currentProjectId: string | null = null; private currentDb: Database | null = null; constructor( private dbManager: DatabaseManager, private io: SocketIOServer ) {} private async getCurrentDatabase(): Promise<Database> { if (!this.currentProjectId) { // Get the most recent project or create a default one const currentProject = this.dbManager.getCurrentProject(); if (currentProject) { this.currentProjectId = currentProject; } else { // Create a default project this.currentProjectId = await this.dbManager.createProject( 'Default Project', 'Default project for initial tasks' ); } } if (!this.currentDb) { this.currentDb = await this.dbManager.getProjectDatabase(this.currentProjectId); } return this.currentDb; } private async switchToProject(projectId: string): Promise<Database> { this.currentProjectId = projectId; this.currentDb = await this.dbManager.switchProject(projectId); return this.currentDb; } getToolDefinitions() { return [ { name: 'create_project', description: `Create a new isolated project with its own database. Each project has completely separate data from other projects. Example: create_project("E-commerce Platform", "Build a modern e-commerce platform")`, inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Project name (will be used to generate project ID)' }, description: { type: 'string', description: 'Project description' }, }, required: ['name', 'description'], }, }, { name: 'list_all_projects', description: `List all available projects across the system. Shows project metadata including when they were last accessed. Example: list_all_projects()`, inputSchema: { type: 'object', properties: {}, }, }, { name: 'switch_project', description: `Switch to a different project database. All subsequent operations will work on the selected project. Example: switch_project("e-commerce-platform")`, inputSchema: { type: 'object', properties: { projectId: { type: 'string', description: 'Project ID to switch to' }, }, required: ['projectId'], }, }, { name: 'get_current_project', description: `Get the currently active project. Example: get_current_project()`, inputSchema: { type: 'object', properties: {}, }, }, { name: 'archive_project', description: `Archive a project (soft delete - can be restored). Example: archive_project("old-project")`, inputSchema: { type: 'object', properties: { projectId: { type: 'string', description: 'Project ID to archive' }, }, required: ['projectId'], }, }, { name: 'list_workspaces', description: `List all workspaces in the current project. Example: list_workspaces()`, inputSchema: { type: 'object', properties: {}, }, }, { name: 'create_workspace', description: `Create a new workspace in the current project. Example: create_workspace("Frontend Development", "React UI components and pages")`, inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Workspace name' }, description: { type: 'string', description: 'Workspace description' }, }, required: ['name'], }, }, { name: 'create_goal', description: `Create a new goal within a workspace. Example: create_goal("workspace-id", "Implement authentication", "User login and registration system")`, inputSchema: { type: 'object', properties: { workspaceId: { type: 'string', description: 'Workspace ID' }, title: { type: 'string', description: 'Goal title' }, description: { type: 'string', description: 'Goal description' }, }, required: ['workspaceId', 'title'], }, }, { name: 'create_task', description: `Create a new task within a goal. Example: create_task("goal-id", "Design login form", "Create responsive login form with validation", "high")`, inputSchema: { type: 'object', properties: { goalId: { type: 'string', description: 'Goal ID' }, title: { type: 'string', description: 'Task title' }, description: { type: 'string', description: 'Task description' }, priority: { type: 'string', enum: ['low', 'medium', 'high', 'critical'], description: 'Task priority' }, assigneeId: { type: 'string', description: 'Optional assignee ID' }, }, required: ['goalId', 'title'], }, }, { name: 'update_task_status', description: `Update the status of a task. Example: update_task_status("task-id", "in_progress")`, inputSchema: { type: 'object', properties: { taskId: { type: 'string', description: 'Task ID' }, status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'blocked', 'cancelled'], description: 'New task status', }, }, required: ['taskId', 'status'], }, }, { name: 'get_project_overview', description: `Get a comprehensive overview of the current project including all workspaces, goals, and tasks. Example: get_project_overview()`, inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_project_stats', description: `Get statistics about the current project. Example: get_project_stats()`, inputSchema: { type: 'object', properties: {}, }, }, { name: 'search_tasks', description: `Search for tasks across the current project. Example: search_tasks("authentication")`, inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'blocked', 'cancelled'], description: 'Filter by status', }, priority: { type: 'string', enum: ['low', 'medium', 'high', 'critical'], description: 'Filter by priority', }, }, required: ['query'], }, }, { name: 'create_session', description: `Create a new session for multi-agent collaboration. Example: create_session("Claude Agent 1", "Frontend development session")`, inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Session name/identifier' }, description: { type: 'string', description: 'Session description' }, }, required: ['name'], }, }, { name: 'get_active_sessions', description: `Get all currently active sessions in the current project. Example: get_active_sessions()`, inputSchema: { type: 'object', properties: {}, }, }, ]; } async executeTool(name: string, args: any) { try { // Project management tools (don't need current database) if (name === 'create_project') { const projectId = await this.dbManager.createProject(args.name, args.description); await this.switchToProject(projectId); return { success: true, projectId, message: `Created and switched to project: ${args.name}`, }; } if (name === 'list_all_projects') { const projects = this.dbManager.listProjects(); return { success: true, projects, currentProject: this.currentProjectId, }; } if (name === 'switch_project') { await this.switchToProject(args.projectId); return { success: true, message: `Switched to project: ${args.projectId}`, projectId: args.projectId, }; } if (name === 'get_current_project') { if (!this.currentProjectId) { return { success: false, message: 'No project currently selected', }; } const projects = this.dbManager.listProjects(); const currentProject = projects.find(p => p.id === this.currentProjectId); return { success: true, project: currentProject, projectId: this.currentProjectId, }; } if (name === 'archive_project') { this.dbManager.archiveProject(args.projectId); if (this.currentProjectId === args.projectId) { this.currentProjectId = null; this.currentDb = null; } return { success: true, message: `Archived project: ${args.projectId}`, }; } // All other tools need the current database const db = await this.getCurrentDatabase(); switch (name) { case 'list_workspaces': { const project = await db.getProjectWithDetails(await this.getProjectIdFromDb(db)); return { success: true, workspaces: project?.workspaces || [], projectId: this.currentProjectId, }; } case 'create_workspace': { const projectId = await this.getProjectIdFromDb(db); const workspace = await db.createWorkspace({ projectId, name: args.name, description: args.description || '', }); this.io.emit('workspace:created', workspace); return { success: true, workspace, message: `Created workspace: ${args.name}`, }; } case 'create_goal': { const goal = await db.createGoal({ workspaceId: args.workspaceId, title: args.title, description: args.description || '', }); this.io.emit('goal:created', goal); return { success: true, goal, message: `Created goal: ${args.title}`, }; } case 'create_task': { const task = await db.createTask({ goalId: args.goalId, title: args.title, description: args.description || '', priority: args.priority || 'medium', }); this.io.emit('task:created', task); return { success: true, task, message: `Created task: ${args.title}`, }; } case 'update_task_status': { const task = await db.updateTaskStatus(args.taskId, args.status); this.io.emit('task:updated', task); return { success: true, task, message: `Updated task status to: ${args.status}`, }; } case 'get_project_overview': { const projectId = await this.getProjectIdFromDb(db); const project = await db.getProjectWithDetails(projectId); return { success: true, project, currentProjectId: this.currentProjectId, }; } case 'get_project_stats': { const projectId = await this.getProjectIdFromDb(db); const stats = await db.getProjectStats(projectId); return { success: true, stats, projectId: this.currentProjectId, }; } case 'search_tasks': { const tasks = await db.searchTasks(args.query, { status: args.status, priority: args.priority, }); return { success: true, tasks, count: tasks.length, }; } case 'create_session': { const projectId = await this.getProjectIdFromDb(db); const session = await db.createSession({ projectId, name: args.name, }); return { success: true, session, message: `Created session: ${args.name}`, }; } case 'get_active_sessions': { const projectId = await this.getProjectIdFromDb(db); const sessions = await db.getActiveSessions(projectId); return { success: true, sessions, count: sessions.length, }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { console.error(`Error executing tool ${name}:`, error); return { success: false, error: error instanceof Error ? error.message : 'Unknown error', }; } } private async getProjectIdFromDb(db: Database): Promise<string> { // Get the first (and should be only) project from the database const projects = await db.getAllProjects(); if (projects.length === 0) { throw new Error('No project found in database'); } return projects[0].id; } }