tasq/node_modules/@claude-flow/hooks/dist/workers/mcp-tools.js

414 lines
14 KiB
JavaScript

/**
* MCP Tools for Worker System
*
* Exposes worker functionality via Model Context Protocol tools.
*/
// ============================================================================
// Input Validation
// ============================================================================
const ALLOWED_WORKERS = new Set([
'performance', 'health', 'security', 'adr', 'ddd',
'patterns', 'learning', 'cache', 'git', 'swarm'
]);
const ALLOWED_SEVERITIES = new Set(['info', 'warning', 'critical']);
const ALLOWED_FORMATS = new Set(['json', 'string']);
function validateWorkerName(name) {
if (typeof name !== 'string')
return null;
if (!ALLOWED_WORKERS.has(name))
return null;
return name;
}
function validateNumber(value, min = 0, max = 10000) {
if (typeof value !== 'number')
return null;
if (value < min || value > max)
return null;
return value;
}
function validateString(value, allowedSet) {
if (typeof value !== 'string')
return null;
if (allowedSet && !allowedSet.has(value))
return null;
return value;
}
function sanitizeErrorMessage(error) {
// Return generic message to avoid information disclosure
if (error instanceof Error) {
// Only expose safe error types
if (error.message.includes('not found') || error.message.includes('Invalid')) {
return error.message;
}
}
return 'An internal error occurred';
}
// ============================================================================
// Worker MCP Tools
// ============================================================================
export const workerRunTool = {
name: 'worker/run',
description: 'Run a specific background worker immediately. Available workers: performance, health, security, adr, ddd, patterns, learning, cache, git, swarm',
inputSchema: {
type: 'object',
properties: {
worker: {
type: 'string',
description: 'Name of the worker to run',
enum: ['performance', 'health', 'security', 'adr', 'ddd', 'patterns', 'learning', 'cache', 'git', 'swarm'],
},
},
required: ['worker'],
},
handler: async (input, manager) => {
// Validate input
const workerName = validateWorkerName(input.worker);
if (!workerName) {
return {
content: [{ type: 'text', text: 'Invalid worker name' }],
isError: true,
};
}
try {
const result = await manager.runWorker(workerName);
return {
content: [{
type: 'text',
text: JSON.stringify({
success: result.success,
worker: result.worker,
duration: result.duration,
data: result.data,
alerts: result.alerts,
error: result.error,
}, null, 2),
}],
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error running worker: ${sanitizeErrorMessage(error)}`,
}],
isError: true,
};
}
},
};
export const workerStatusTool = {
name: 'worker/status',
description: 'Get status of all background workers including run counts, errors, and last results',
inputSchema: {
type: 'object',
properties: {
worker: {
type: 'string',
description: 'Optional: Get status for a specific worker',
},
},
},
handler: async (input, manager) => {
const status = manager.getStatus();
// Validate optional worker name
if (input.worker !== undefined) {
const workerName = validateWorkerName(input.worker);
if (!workerName) {
return {
content: [{ type: 'text', text: 'Invalid worker name' }],
isError: true,
};
}
const worker = status.workers.find(w => w.name === workerName);
if (!worker) {
return {
content: [{ type: 'text', text: `Worker '${workerName}' not found` }],
isError: true,
};
}
return {
content: [{ type: 'text', text: JSON.stringify(worker, null, 2) }],
};
}
return {
content: [{
type: 'text',
text: JSON.stringify({
running: status.running,
platform: status.platform,
uptime: Math.round(status.uptime / 1000) + 's',
totalRuns: status.totalRuns,
workers: status.workers.map(w => ({
name: w.name,
status: w.status,
runCount: w.runCount,
errorCount: w.errorCount,
avgDuration: Math.round(w.avgDuration) + 'ms',
lastRun: w.lastRun?.toISOString(),
})),
}, null, 2),
}],
};
},
};
export const workerAlertsTool = {
name: 'worker/alerts',
description: 'Get recent alerts from worker runs (threshold violations)',
inputSchema: {
type: 'object',
properties: {
limit: {
type: 'number',
description: 'Maximum number of alerts to return (default: 20)',
},
severity: {
type: 'string',
description: 'Filter by severity level',
enum: ['info', 'warning', 'critical'],
},
},
},
handler: async (input, manager) => {
// Validate inputs
const limit = validateNumber(input.limit, 1, 100) ?? 20;
const severity = input.severity !== undefined
? validateString(input.severity, ALLOWED_SEVERITIES)
: undefined;
if (input.severity !== undefined && !severity) {
return {
content: [{ type: 'text', text: 'Invalid severity level' }],
isError: true,
};
}
let alerts = manager.getAlerts(limit);
if (severity) {
alerts = alerts.filter(a => a.severity === severity);
}
return {
content: [{
type: 'text',
text: alerts.length > 0
? JSON.stringify(alerts.map(a => ({
worker: a.worker,
severity: a.severity,
message: a.message,
metric: a.metric,
value: a.value,
threshold: a.threshold,
timestamp: a.timestamp.toISOString(),
})), null, 2)
: 'No alerts',
}],
};
},
};
export const workerHistoryTool = {
name: 'worker/history',
description: 'Get historical metrics from worker runs for trend analysis',
inputSchema: {
type: 'object',
properties: {
worker: {
type: 'string',
description: 'Filter by worker name',
},
limit: {
type: 'number',
description: 'Maximum number of history entries (default: 50)',
},
},
},
handler: async (input, manager) => {
// Validate inputs
const workerName = input.worker !== undefined
? validateWorkerName(input.worker)
: undefined;
if (input.worker !== undefined && !workerName) {
return {
content: [{ type: 'text', text: 'Invalid worker name' }],
isError: true,
};
}
const limit = validateNumber(input.limit, 1, 1000) ?? 50;
const history = manager.getHistory(workerName ?? undefined, limit);
return {
content: [{
type: 'text',
text: history.length > 0
? JSON.stringify(history, null, 2)
: 'No history available',
}],
};
},
};
export const workerStatuslineTool = {
name: 'worker/statusline',
description: 'Get formatted statusline data for display',
inputSchema: {
type: 'object',
properties: {
format: {
type: 'string',
description: 'Output format: json or string',
enum: ['json', 'string'],
},
},
},
handler: async (input, manager) => {
// Validate format
const format = validateString(input.format, ALLOWED_FORMATS) ?? 'json';
if (format === 'string') {
return {
content: [{ type: 'text', text: manager.getStatuslineString() }],
};
}
return {
content: [{
type: 'text',
text: JSON.stringify(manager.getStatuslineData(), null, 2),
}],
};
},
};
export const workerRunAllTool = {
name: 'worker/run-all',
description: 'Run all enabled workers immediately',
inputSchema: {
type: 'object',
properties: {
concurrency: {
type: 'number',
description: 'Maximum concurrent workers (default: 5)',
},
},
},
handler: async (input, manager) => {
// Validate concurrency (1-10 range)
const concurrency = validateNumber(input.concurrency, 1, 10) ?? 5;
try {
const results = await manager.runAll(concurrency);
const summary = {
total: results.length,
success: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
totalDuration: results.reduce((sum, r) => sum + r.duration, 0),
alerts: results.flatMap(r => r.alerts || []),
results: results.map(r => ({
worker: r.worker,
success: r.success,
duration: r.duration,
error: r.error,
})),
};
return {
content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }],
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error running workers: ${sanitizeErrorMessage(error)}`,
}],
isError: true,
};
}
},
};
export const workerStartTool = {
name: 'worker/start',
description: 'Start the worker manager with automatic scheduling',
inputSchema: {
type: 'object',
properties: {
autoSave: {
type: 'boolean',
description: 'Enable automatic state saving (default: true)',
},
statuslineUpdate: {
type: 'boolean',
description: 'Enable statusline file updates (default: true)',
},
},
},
handler: async (input, manager) => {
// Validate boolean inputs
const autoSave = typeof input.autoSave === 'boolean' ? input.autoSave : true;
const statuslineUpdate = typeof input.statuslineUpdate === 'boolean' ? input.statuslineUpdate : true;
try {
await manager.start({ autoSave, statuslineUpdate });
return {
content: [{
type: 'text',
text: 'Worker manager started with scheduling enabled',
}],
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error starting worker manager: ${sanitizeErrorMessage(error)}`,
}],
isError: true,
};
}
},
};
export const workerStopTool = {
name: 'worker/stop',
description: 'Stop the worker manager and save state',
inputSchema: {
type: 'object',
properties: {},
},
handler: async (_input, manager) => {
try {
await manager.stop();
return {
content: [{
type: 'text',
text: 'Worker manager stopped and state saved',
}],
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error stopping worker manager: ${sanitizeErrorMessage(error)}`,
}],
isError: true,
};
}
},
};
// ============================================================================
// Tool Registry
// ============================================================================
export const workerMCPTools = [
workerRunTool,
workerStatusTool,
workerAlertsTool,
workerHistoryTool,
workerStatuslineTool,
workerRunAllTool,
workerStartTool,
workerStopTool,
];
/**
* Create a tool handler function for MCP server integration
*/
export function createWorkerToolHandler(manager) {
return async (toolName, input) => {
const tool = workerMCPTools.find(t => t.name === toolName);
if (!tool) {
return {
content: [{ type: 'text', text: `Unknown tool: ${toolName}` }],
isError: true,
};
}
return tool.handler(input, manager);
};
}
//# sourceMappingURL=mcp-tools.js.map