@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.
217 lines (214 loc) • 11.1 kB
JavaScript
/**
* Edit Workflow Tool
* Edit an existing workflow's properties (name, description, required_servers, instructions, tags)
*/
import { ApiClient } from '../../utils/api-client.js';
export const editWorkflowTool = {
name: 'edit_workflow',
description: 'Edit an existing workflow\'s properties including name, description, required MCP servers, step-by-step instructions, and tags. Only organization members can edit workflows in their organization. Use this to refine workflows after creation, update server requirements, modify instructions, or adjust metadata. Supports partial updates - only provide the fields you want to change. The workflow retains all existing data for fields not specified in the update.',
inputSchema: {
type: 'object',
properties: {
workflow_id: {
type: 'string',
description: 'The ID of the workflow to edit. Get this from list_workflows results. Must be a workflow in your organization.'
},
name: {
type: 'string',
minLength: 2,
maxLength: 100,
description: "Updated clear, action-oriented name (e.g., 'Deploy Next.js to Vercel', 'Run Full Test Suite', 'Generate API Documentation'). Should be descriptive and unique within your organization."
},
description: {
type: 'string',
minLength: 5,
maxLength: 500,
description: 'Updated 2-3 sentence description of what this workflow accomplishes and when to use it. This helps others understand the workflow\'s purpose.'
},
required_servers: {
type: 'object',
description: 'Updated MCP server configurations needed for this workflow, in the same format as bundles. Each key is the server name, value is the config with command, args, env, and optional type. This completely replaces the existing server requirements.',
additionalProperties: {
type: 'object',
properties: {
command: { type: 'string' },
args: {
type: 'array',
items: { type: 'string' }
},
env: {
type: 'object',
additionalProperties: { type: 'string' }
},
type: {
type: 'string',
enum: ['stdio', 'http', 'sse']
}
},
required: ['command', 'args']
}
},
instructions: {
type: 'object',
description: 'Updated step-by-step instructions in structured format for AI execution. Should include clear actions, expected outcomes, and error handling. This completely replaces the existing instructions.',
properties: {
steps: {
type: 'array',
items: {
type: 'object',
properties: {
action: {
type: 'string',
description: 'What action to perform (e.g., check_git_status, run_tests, deploy)'
},
tool: {
type: 'string',
description: 'Which MCP server/tool to use for this step'
},
description: {
type: 'string',
description: 'Clear description of what this step does'
},
command: {
type: 'string',
description: 'Optional specific command to run (e.g., npm test)'
},
expected_outcome: {
type: 'string',
description: 'What success looks like for this step'
},
on_failure: {
type: 'string',
enum: ['stop_and_report', 'continue', 'skip'],
description: 'What to do if this step fails'
},
capture_output: {
type: 'array',
items: { type: 'string' },
description: 'Output variables to capture for use in later steps'
},
use_captured: {
type: 'array',
items: { type: 'string' },
description: 'Previously captured variables to use in this step'
}
},
required: ['action', 'description']
}
}
},
required: ['steps']
},
tags: {
type: 'array',
items: { type: 'string' },
description: "Updated tags for categorization (e.g., ['deployment', 'vercel', 'nextjs'], ['testing', 'jest'], ['documentation']). This completely replaces the existing tags."
},
install_commands: {
type: 'array',
items: { type: 'string' },
description: 'Updated array of shell commands to run before installing the required MCP servers. Examples: ["npm install -g @modelcontextprotocol/server-filesystem", "pip install mcp-server-git"]. This completely replaces the existing install commands.'
},
},
required: ['workflow_id']
},
async execute(args, context) {
try {
// Validate user context
if (!context.userId) {
throw new Error('User authentication required');
}
// API will handle all database operations
// Validate inputs before sending to API
if (args.required_servers !== undefined && Object.keys(args.required_servers).length === 0) {
throw new Error('Workflow must have at least one required server');
}
// Build update object only with provided fields
const updateData = {
updated_at: new Date().toISOString()
};
if (args.name !== undefined) {
updateData.name = args.name.trim();
}
if (args.description !== undefined) {
updateData.description = args.description.trim();
}
if (args.required_servers !== undefined) {
// Validate server configurations
for (const [serverName, serverConfig] of Object.entries(args.required_servers)) {
if (!serverConfig.command || typeof serverConfig.command !== 'string') {
throw new Error(`Server '${serverName}' must have a valid command`);
}
if (!Array.isArray(serverConfig.args)) {
throw new Error(`Server '${serverName}' must have valid args array`);
}
if (serverConfig.env && typeof serverConfig.env !== 'object') {
throw new Error(`Server '${serverName}' env must be an object`);
}
if (serverConfig.type && !['stdio', 'http', 'sse'].includes(serverConfig.type)) {
throw new Error(`Server '${serverName}' type must be 'stdio', 'http', or 'sse'`);
}
}
updateData.required_servers = args.required_servers;
}
if (args.instructions !== undefined) {
// Validate instructions have at least one step
if (!args.instructions.steps || args.instructions.steps.length === 0) {
throw new Error('Workflow must have at least one instruction step');
}
updateData.instructions = args.instructions;
}
if (args.tags !== undefined) {
updateData.tags = args.tags;
}
if (args.install_commands !== undefined) {
updateData.install_commands = args.install_commands;
}
// Update the workflow using API
const updatedWorkflow = await ApiClient.updateWorkflow(args.workflow_id, updateData, context);
if (!updatedWorkflow) {
throw new Error('Failed to update workflow');
}
const serverCount = Object.keys(updatedWorkflow.required_servers || {}).length;
const stepCount = updatedWorkflow.instructions?.steps?.length || 0;
return {
content: [
{
type: 'text',
text: `✅ Workflow updated successfully!
**Updated Workflow Details:**
- **Name**: ${updatedWorkflow.name}
- **Description**: ${updatedWorkflow.description}
- **Required Servers**: ${serverCount} MCP server${serverCount === 1 ? '' : 's'}
- **Instructions**: ${stepCount} step${stepCount === 1 ? '' : 's'}
- **Workflow ID**: ${updatedWorkflow.id}
- **Last Updated**: ${new Date(updatedWorkflow.updated_at).toLocaleString()}
${updatedWorkflow.tags && updatedWorkflow.tags.length > 0 ? `**Tags**: ${updatedWorkflow.tags.join(', ')}\n` : ''}${serverCount > 0 ? `**Server List**: ${Object.keys(updatedWorkflow.required_servers).join(', ')}\n` : ''}
**Changes Applied:**
${args.name !== undefined ? '- Name updated' : ''}
${args.description !== undefined ? '- Description updated' : ''}
${args.required_servers !== undefined ? '- Required servers updated' : ''}
${args.instructions !== undefined ? '- Instructions updated' : ''}
${args.tags !== undefined ? '- Tags updated' : ''}
${args.install_commands !== undefined ? '- Installation commands updated' : ''}
**Next Steps:**
- Use \`list_workflows\` to view your updated workflow
- Use \`perform_workflow\` to execute the workflow`
}
]
};
}
catch (error) {
console.error('Edit workflow error:', error);
return {
content: [
{
type: 'text',
text: `❌ Failed to edit workflow: ${error.message}`
}
]
};
}
}
};
//# sourceMappingURL=edit-workflow.js.map