mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-15 05:09:32 +00:00
Integration with TaskMaster AI
This commit is contained in:
198
server/utils/mcp-detector.js
Normal file
198
server/utils/mcp-detector.js
Normal file
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* MCP SERVER DETECTION UTILITY
|
||||
* ============================
|
||||
*
|
||||
* Centralized utility for detecting MCP server configurations.
|
||||
* Used across TaskMaster integration and other MCP-dependent features.
|
||||
*/
|
||||
|
||||
import { promises as fsPromises } from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
|
||||
/**
|
||||
* Check if task-master-ai MCP server is configured
|
||||
* Reads directly from Claude configuration files like claude-cli.js does
|
||||
* @returns {Promise<Object>} MCP detection result
|
||||
*/
|
||||
export async function detectTaskMasterMCPServer() {
|
||||
try {
|
||||
// Read Claude configuration files directly (same logic as mcp.js)
|
||||
const homeDir = os.homedir();
|
||||
const configPaths = [
|
||||
path.join(homeDir, '.claude.json'),
|
||||
path.join(homeDir, '.claude', 'settings.json')
|
||||
];
|
||||
|
||||
let configData = null;
|
||||
let configPath = null;
|
||||
|
||||
// Try to read from either config file
|
||||
for (const filepath of configPaths) {
|
||||
try {
|
||||
const fileContent = await fsPromises.readFile(filepath, 'utf8');
|
||||
configData = JSON.parse(fileContent);
|
||||
configPath = filepath;
|
||||
break;
|
||||
} catch (error) {
|
||||
// File doesn't exist or is not valid JSON, try next
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!configData) {
|
||||
return {
|
||||
hasMCPServer: false,
|
||||
reason: 'No Claude configuration file found',
|
||||
hasConfig: false
|
||||
};
|
||||
}
|
||||
|
||||
// Look for task-master-ai in user-scoped MCP servers
|
||||
let taskMasterServer = null;
|
||||
if (configData.mcpServers && typeof configData.mcpServers === 'object') {
|
||||
const serverEntry = Object.entries(configData.mcpServers).find(([name, config]) =>
|
||||
name === 'task-master-ai' ||
|
||||
name.includes('task-master') ||
|
||||
(config && config.command && config.command.includes('task-master'))
|
||||
);
|
||||
|
||||
if (serverEntry) {
|
||||
const [name, config] = serverEntry;
|
||||
taskMasterServer = {
|
||||
name,
|
||||
scope: 'user',
|
||||
config,
|
||||
type: config.command ? 'stdio' : (config.url ? 'http' : 'unknown')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Also check project-specific MCP servers if not found globally
|
||||
if (!taskMasterServer && configData.projects) {
|
||||
for (const [projectPath, projectConfig] of Object.entries(configData.projects)) {
|
||||
if (projectConfig.mcpServers && typeof projectConfig.mcpServers === 'object') {
|
||||
const serverEntry = Object.entries(projectConfig.mcpServers).find(([name, config]) =>
|
||||
name === 'task-master-ai' ||
|
||||
name.includes('task-master') ||
|
||||
(config && config.command && config.command.includes('task-master'))
|
||||
);
|
||||
|
||||
if (serverEntry) {
|
||||
const [name, config] = serverEntry;
|
||||
taskMasterServer = {
|
||||
name,
|
||||
scope: 'local',
|
||||
projectPath,
|
||||
config,
|
||||
type: config.command ? 'stdio' : (config.url ? 'http' : 'unknown')
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (taskMasterServer) {
|
||||
const isValid = !!(taskMasterServer.config &&
|
||||
(taskMasterServer.config.command || taskMasterServer.config.url));
|
||||
const hasEnvVars = !!(taskMasterServer.config &&
|
||||
taskMasterServer.config.env &&
|
||||
Object.keys(taskMasterServer.config.env).length > 0);
|
||||
|
||||
return {
|
||||
hasMCPServer: true,
|
||||
isConfigured: isValid,
|
||||
hasApiKeys: hasEnvVars,
|
||||
scope: taskMasterServer.scope,
|
||||
config: {
|
||||
command: taskMasterServer.config?.command,
|
||||
args: taskMasterServer.config?.args || [],
|
||||
url: taskMasterServer.config?.url,
|
||||
envVars: hasEnvVars ? Object.keys(taskMasterServer.config.env) : [],
|
||||
type: taskMasterServer.type
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// Get list of available servers for debugging
|
||||
const availableServers = [];
|
||||
if (configData.mcpServers) {
|
||||
availableServers.push(...Object.keys(configData.mcpServers));
|
||||
}
|
||||
if (configData.projects) {
|
||||
for (const projectConfig of Object.values(configData.projects)) {
|
||||
if (projectConfig.mcpServers) {
|
||||
availableServers.push(...Object.keys(projectConfig.mcpServers).map(name => `local:${name}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hasMCPServer: false,
|
||||
reason: 'task-master-ai not found in configured MCP servers',
|
||||
hasConfig: true,
|
||||
configPath,
|
||||
availableServers
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error detecting MCP server config:', error);
|
||||
return {
|
||||
hasMCPServer: false,
|
||||
reason: `Error checking MCP config: ${error.message}`,
|
||||
hasConfig: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all configured MCP servers (not just TaskMaster)
|
||||
* @returns {Promise<Object>} All MCP servers configuration
|
||||
*/
|
||||
export async function getAllMCPServers() {
|
||||
try {
|
||||
const homeDir = os.homedir();
|
||||
const configPaths = [
|
||||
path.join(homeDir, '.claude.json'),
|
||||
path.join(homeDir, '.claude', 'settings.json')
|
||||
];
|
||||
|
||||
let configData = null;
|
||||
let configPath = null;
|
||||
|
||||
// Try to read from either config file
|
||||
for (const filepath of configPaths) {
|
||||
try {
|
||||
const fileContent = await fsPromises.readFile(filepath, 'utf8');
|
||||
configData = JSON.parse(fileContent);
|
||||
configPath = filepath;
|
||||
break;
|
||||
} catch (error) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!configData) {
|
||||
return {
|
||||
hasConfig: false,
|
||||
servers: {},
|
||||
projectServers: {}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
hasConfig: true,
|
||||
configPath,
|
||||
servers: configData.mcpServers || {},
|
||||
projectServers: configData.projects || {}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error getting all MCP servers:', error);
|
||||
return {
|
||||
hasConfig: false,
|
||||
error: error.message,
|
||||
servers: {},
|
||||
projectServers: {}
|
||||
};
|
||||
}
|
||||
}
|
||||
129
server/utils/taskmaster-websocket.js
Normal file
129
server/utils/taskmaster-websocket.js
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* TASKMASTER WEBSOCKET UTILITIES
|
||||
* ==============================
|
||||
*
|
||||
* Utilities for broadcasting TaskMaster state changes via WebSocket.
|
||||
* Integrates with the existing WebSocket system to provide real-time updates.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Broadcast TaskMaster project update to all connected clients
|
||||
* @param {WebSocket.Server} wss - WebSocket server instance
|
||||
* @param {string} projectName - Name of the updated project
|
||||
* @param {Object} taskMasterData - Updated TaskMaster data
|
||||
*/
|
||||
export function broadcastTaskMasterProjectUpdate(wss, projectName, taskMasterData) {
|
||||
if (!wss || !projectName) {
|
||||
console.warn('TaskMaster WebSocket broadcast: Missing wss or projectName');
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
type: 'taskmaster-project-updated',
|
||||
projectName,
|
||||
taskMasterData,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
|
||||
wss.clients.forEach((client) => {
|
||||
if (client.readyState === 1) { // WebSocket.OPEN
|
||||
try {
|
||||
client.send(JSON.stringify(message));
|
||||
} catch (error) {
|
||||
console.error('Error sending TaskMaster project update:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast TaskMaster tasks update for a specific project
|
||||
* @param {WebSocket.Server} wss - WebSocket server instance
|
||||
* @param {string} projectName - Name of the project with updated tasks
|
||||
* @param {Object} tasksData - Updated tasks data
|
||||
*/
|
||||
export function broadcastTaskMasterTasksUpdate(wss, projectName, tasksData) {
|
||||
if (!wss || !projectName) {
|
||||
console.warn('TaskMaster WebSocket broadcast: Missing wss or projectName');
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
type: 'taskmaster-tasks-updated',
|
||||
projectName,
|
||||
tasksData,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
|
||||
wss.clients.forEach((client) => {
|
||||
if (client.readyState === 1) { // WebSocket.OPEN
|
||||
try {
|
||||
client.send(JSON.stringify(message));
|
||||
} catch (error) {
|
||||
console.error('Error sending TaskMaster tasks update:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast MCP server status change
|
||||
* @param {WebSocket.Server} wss - WebSocket server instance
|
||||
* @param {Object} mcpStatus - Updated MCP server status
|
||||
*/
|
||||
export function broadcastMCPStatusChange(wss, mcpStatus) {
|
||||
if (!wss) {
|
||||
console.warn('TaskMaster WebSocket broadcast: Missing wss');
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
type: 'taskmaster-mcp-status-changed',
|
||||
mcpStatus,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
|
||||
wss.clients.forEach((client) => {
|
||||
if (client.readyState === 1) { // WebSocket.OPEN
|
||||
try {
|
||||
client.send(JSON.stringify(message));
|
||||
} catch (error) {
|
||||
console.error('Error sending TaskMaster MCP status update:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast general TaskMaster update notification
|
||||
* @param {WebSocket.Server} wss - WebSocket server instance
|
||||
* @param {string} updateType - Type of update (e.g., 'initialization', 'configuration')
|
||||
* @param {Object} data - Additional data about the update
|
||||
*/
|
||||
export function broadcastTaskMasterUpdate(wss, updateType, data = {}) {
|
||||
if (!wss || !updateType) {
|
||||
console.warn('TaskMaster WebSocket broadcast: Missing wss or updateType');
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
type: 'taskmaster-update',
|
||||
updateType,
|
||||
data,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
|
||||
wss.clients.forEach((client) => {
|
||||
if (client.readyState === 1) { // WebSocket.OPEN
|
||||
try {
|
||||
client.send(JSON.stringify(message));
|
||||
} catch (error) {
|
||||
console.error('Error sending TaskMaster update:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user