@grebyn/toolflow-mcp-server
Version:
MCP server for managing other MCP servers - discover, install, organize into bundles, and automate with workflows. Uses StreamableHTTP transport with dual OAuth/API key authentication.
186 lines (177 loc) • 8.18 kB
JavaScript
import { ApiClient } from '../../utils/api-client.js';
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
export const performWorkflowTool = {
name: 'perform_workflow',
description: `Check if a workflow's required MCP servers are installed and return either the workflow instructions or a list of missing servers. This tool performs a quick name-based check to see if all required servers are present in the client's configuration.
If servers are missing, use list_missing_workflow_servers to get the full installation configurations.
If all servers are present, you'll receive the workflow instructions to execute.`,
inputSchema: {
type: 'object',
properties: {
workflow_id: {
type: 'string',
description: 'The workflow ID from list_workflows results'
},
config_path: {
type: 'string',
description: 'Optional custom configuration file path. If not provided, uses CLIENT environment variable.'
}
},
required: ['workflow_id']
},
async execute(args, context) {
try {
// Fetch the workflow from API
const workflow = await ApiClient.getWorkflow(args.workflow_id, context);
if (!workflow) {
return {
content: [{
type: 'text',
text: 'Workflow not found or access denied.'
}]
};
}
// Get the current MCP configuration using the CLI
let command;
if (args.config_path) {
command = `npx @grebyn/toolflow-cli@latest read --path "${args.config_path}"`;
}
else {
const client = process.env.CLIENT;
if (!client) {
throw new Error('CLIENT environment variable not set. Please configure your MCP client connection.');
}
command = `npx @grebyn/toolflow-cli@latest read ${client}`;
}
let currentConfig = {};
try {
const { stdout } = await execAsync(command, {
timeout: 30000,
maxBuffer: 1024 * 1024
});
currentConfig = JSON.parse(stdout);
}
catch (error) {
console.error('Failed to read current configuration:', error);
// Continue with empty config to show all servers as missing
}
// Extract installed servers from the config
const installedServers = new Set();
// Check different config formats
if (currentConfig.mcpServers) {
Object.keys(currentConfig.mcpServers).forEach(name => installedServers.add(name));
}
else if (currentConfig.servers) {
Object.keys(currentConfig.servers).forEach(name => installedServers.add(name));
}
else if (currentConfig.context_servers) {
Object.keys(currentConfig.context_servers).forEach(name => installedServers.add(name));
}
// Check for missing servers
const requiredServers = Object.keys(workflow.required_servers || {});
const missingServers = requiredServers.filter(server => !installedServers.has(server));
if (missingServers.length > 0) {
// Servers are missing
const result = {
status: 'missing_servers',
workflow_id: workflow.id,
workflow_name: workflow.name,
can_perform: false,
missing_servers: missingServers,
instructions: undefined,
required_servers: workflow.required_servers,
install_commands: workflow.install_commands
};
return {
content: [{
type: 'text',
text: formatWorkflowResult(result, workflow)
}]
};
}
// All servers are installed
const result = {
status: 'ready',
workflow_id: workflow.id,
workflow_name: workflow.name,
can_perform: true,
missing_servers: [],
instructions: workflow.instructions,
required_servers: workflow.required_servers,
install_commands: workflow.install_commands
};
return {
content: [{
type: 'text',
text: formatWorkflowResult(result, workflow)
}]
};
}
catch (error) {
console.error('Error performing workflow:', error);
return {
content: [{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : 'An unexpected error occurred'}`
}]
};
}
}
};
function formatWorkflowResult(result, workflow) {
if (!result.can_perform) {
// Missing servers - format installation instructions
const hasInstallCommands = result.install_commands && result.install_commands.length > 0;
return `**Cannot Perform Workflow: Missing MCP Servers**
**Workflow:** ${result.workflow_name}
**Missing Servers:** ${result.missing_servers?.join(', ') || 'None'}
${hasInstallCommands ? `
**Installation Commands Required:**
Before configuring the servers, run these commands:
${result.install_commands.map(cmd => `- \`${cmd}\``).join('\n')}
` : ''}
**Required Actions:**
${hasInstallCommands ? '1. Run the installation commands listed above\n' : ''}${hasInstallCommands ? '2' : '1'}. Use \`list_missing_workflow_servers\` with these parameters:
- workflow_id: "${result.workflow_id}"
- missing_servers: ${JSON.stringify(result.missing_servers || [])}
${hasInstallCommands ? '3' : '2'}. Use \`write_client_config\` to install the servers
${hasInstallCommands ? '4' : '3'}. Call \`perform_workflow\` again after installation
**Required Servers:**
${result.required_servers ? Object.entries(result.required_servers)
.map(([name, config]) => {
const isMissing = result.missing_servers?.includes(name) || false;
const status = isMissing ? '❌ Missing' : '✅ Installed';
return `- **${name}** (${status}): ${config.command} ${config.args.join(' ')}`;
})
.join('\n') : 'None'}`;
}
// All servers installed - format workflow instructions
return `**Ready to Perform Workflow**
**Workflow:** ${result.workflow_name}
**Description:** ${workflow.description}
**All Required Servers Installed:** ✅
${result.required_servers ? Object.keys(result.required_servers).map(name => `- ${name}`).join('\n') : 'None'}
**Workflow Instructions:**
${workflow.user_provided_instructions ? `\n**User Instructions:**\n${workflow.user_provided_instructions}\n` : ''}
**Steps to Execute:**
${result.instructions?.steps.map((step, index) => {
let stepText = `${index + 1}. **${step.action}**`;
if (step.description)
stepText += `\n ${step.description}`;
if (step.tool)
stepText += `\n Tool: ${step.tool}`;
if (step.command)
stepText += `\n Command: \`${step.command}\``;
if (step.expected_outcome)
stepText += `\n Expected: ${step.expected_outcome}`;
if (step.on_failure)
stepText += `\n On Failure: ${step.on_failure}`;
return stepText;
}).join('\n\n')}
**Tags:** ${workflow.tags ? workflow.tags.join(', ') : 'None'}
**Next Steps:**
Execute the workflow steps in order. Each step should be performed carefully, checking for the expected outcomes before proceeding to the next step.`;
}
//# sourceMappingURL=performWorkflow.js.map