Skip to main content
Glama

workflows-mcp

by FiveOhhWon
index.tsโ€ข32.3 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; import { WorkflowStorage } from './services/storage.js'; import { WorkflowValidator } from './services/validator.js'; import { Workflow, WorkflowSession, Step } from './types/index.js'; import { zodToJsonSchema } from './utils/zod-to-json-schema.js'; import { v4 as uuidv4 } from 'uuid'; // Tool schemas const CreateWorkflowSchema = z.object({ workflow: z.object({ name: z.string().describe('Descriptive name for the workflow'), description: z.string().describe('What this workflow accomplishes'), goal: z.string().describe('The end result or desired outcome'), version: z.string().optional().describe('Semantic version (default: "1.0.0")'), tags: z.array(z.string()).optional().describe('Categories for organization'), inputs: z.record(z.object({ type: z.enum(['string', 'number', 'boolean', 'array', 'object']), description: z.string(), required: z.boolean().default(true), default: z.any().optional() })).optional().describe('Input parameters the workflow accepts'), outputs: z.array(z.string()).optional().describe('Names of output variables'), required_tools: z.array(z.string()).optional().describe('MCP tools needed'), steps: z.array(z.any()).describe('Workflow steps with actions'), }).describe('Complete workflow definition'), }); const ListWorkflowsSchema = z.object({ filter: z.object({ tags: z.array(z.string()).optional(), name_contains: z.string().optional(), created_after: z.string().optional(), created_before: z.string().optional(), min_success_rate: z.number().optional(), is_deleted: z.boolean().optional(), }).optional(), sort: z.object({ field: z.enum(['name', 'created_at', 'updated_at', 'times_run', 'success_rate']), order: z.enum(['asc', 'desc']), }).optional(), }); const GetWorkflowSchema = z.object({ id: z.string(), }); const UpdateWorkflowSchema = z.object({ id: z.string(), updates: z.record(z.any()), increment_version: z.boolean().optional(), }); const DeleteWorkflowSchema = z.object({ id: z.string(), }); const StartWorkflowSchema = z.object({ id: z.string(), inputs: z.record(z.any()).optional(), }); const RunWorkflowStepSchema = z.object({ execution_id: z.string(), step_result: z.any().optional(), next_step_needed: z.boolean(), }); const GetWorkflowVersionsSchema = z.object({ workflow_id: z.string(), }); const RollbackWorkflowSchema = z.object({ workflow_id: z.string(), target_version: z.string(), reason: z.string().optional(), }); // Server implementation export class WorkflowMCPServer { private server: Server; private storage: WorkflowStorage; private sessions: Map<string, WorkflowSession>; constructor() { this.server = new Server( { name: 'workflows-mcp', version: '0.3.0', }, { capabilities: { tools: {}, }, } ); this.storage = new WorkflowStorage(); this.sessions = new Map(); this.setupHandlers(); } private setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: this.getTools(), })); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'create_workflow': return await this.createWorkflow(args); case 'list_workflows': return await this.listWorkflows(args); case 'get_workflow': return await this.getWorkflow(args); case 'update_workflow': return await this.updateWorkflow(args); case 'delete_workflow': return await this.deleteWorkflow(args); case 'start_workflow': return await this.startWorkflow(args); case 'run_workflow_step': return await this.runWorkflowStep(args); case 'get_workflow_versions': return await this.getWorkflowVersions(args); case 'rollback_workflow': return await this.rollbackWorkflow(args); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], }; } }); } private getTools(): Tool[] { return [ { name: 'create_workflow', description: `Create a new workflow with specified steps and configuration. WORKFLOW STRUCTURE: - name: Descriptive workflow name - description: What the workflow accomplishes - goal: The end result or outcome - version: Semantic version (default: "1.0.0") - tags: Array of categorization tags - inputs: Object defining input parameters with type, description, required, and optional default - outputs: Array of expected output variable names - required_tools: Array of MCP tools this workflow needs - steps: Array of workflow steps (see below) - strict_dependencies: Boolean to enable strict dependency mode (default: false) * false: Steps without dependencies see all variables (backward compatible) * true: Steps without dependencies see NO variables (must explicitly declare dependencies) AVAILABLE ACTIONS: - tool_call: Execute an MCP tool (requires tool_name and parameters) - analyze: Analyze data and extract insights - consider: Evaluate options or possibilities - research: Gather information on a topic - validate: Check data quality or correctness - summarize: Create a summary of information - decide: Make a decision based on criteria - wait_for_input: Request user input (requires prompt) - transform: Transform data (requires transformation description) - extract: Extract specific information - compose: Create new content - branch: Conditional branching (requires conditions array) - checkpoint: Save progress checkpoint - notify: Send a notification (requires message) - assert: Verify a condition (requires condition) - retry: Retry a previous step (requires step_id) STEP STRUCTURE: { "id": 1, // Sequential number starting from 1 "action": "action_type", "description": "What this step does", "save_result_as": "variable_name", // Optional: save result "error_handling": "stop|continue|retry", // Default: "stop" "dependencies": [1, 3], // Optional: only show outputs from these step IDs "show_all_variables": true, // Optional: override to show all variables // For tool_call: "tool_name": "mcp_tool_name", "parameters": { "param": "value" }, // For cognitive actions (analyze, consider, research, etc): "input_from": ["variable1", "variable2"], // Input variables "criteria": "Specific criteria or focus", // Optional // For branch: "conditions": [ { "if": "variable.property > value", "goto_step": 5 } ], // For wait_for_input: "prompt": "Question for the user", "input_type": "text|number|boolean|json", // For transform: "transformation": "Description of transformation" } TEMPLATE VARIABLES: Use {{variable_name}} in any string field to reference: - Input parameters from workflow inputs - Results saved from previous steps via save_result_as - Any variables in the workflow state EXAMPLES: - "path": "output_{{format}}.txt" - "prompt": "Process {{count}} items?" - "description": "Analyzing {{filename}}" DEPENDENCY MANAGEMENT: - Use "dependencies" array to specify which previous steps' outputs are needed - In strict_dependencies mode, steps without dependencies see NO variables - Steps with dependencies only see outputs from those specific steps + workflow inputs - Use "show_all_variables": true to override and see all variables for a specific step PERFORMANCE FEATURES: - Only relevant variables are shown based on dependencies (reduces token usage) - Variable changes are highlighted (+ for new, ~ for modified) - Only next 3 steps are previewed (progressive loading) BEST PRACTICES: 1. Each step should have a single, clear responsibility 2. Use descriptive variable names for save_result_as 3. Consider error handling for each step (stop, continue, or retry) 4. Branch conditions should cover all cases 5. Order steps logically with proper dependencies 6. Use strict_dependencies for workflows with large/verbose outputs 7. Explicitly declare dependencies to minimize context and improve performance`, inputSchema: zodToJsonSchema(CreateWorkflowSchema), }, { name: 'list_workflows', description: 'List all workflows with optional filtering and sorting', inputSchema: zodToJsonSchema(ListWorkflowsSchema), }, { name: 'get_workflow', description: 'Get a specific workflow by ID', inputSchema: zodToJsonSchema(GetWorkflowSchema), }, { name: 'update_workflow', description: 'Update an existing workflow with optional version increment', inputSchema: zodToJsonSchema(UpdateWorkflowSchema), }, { name: 'delete_workflow', description: 'Soft delete a workflow (can be recovered)', inputSchema: zodToJsonSchema(DeleteWorkflowSchema), }, { name: 'start_workflow', description: 'Start a workflow execution session with step-by-step control', inputSchema: zodToJsonSchema(StartWorkflowSchema), }, { name: 'run_workflow_step', description: `Execute the next step in an active workflow session. IMPORTANT: When executing workflow steps, follow these rules: 1. DO NOT provide commentary between workflow steps unless explicitly requested 2. Simply execute each step according to the workflow instructions 3. Move immediately to the next step after completing the current one 4. Only provide output when the workflow specifically requires it (e.g., notify actions, final results) 5. Focus solely on executing the workflow actions as defined The tool will provide step-by-step instructions that should be followed exactly.`, inputSchema: zodToJsonSchema(RunWorkflowStepSchema), }, { name: 'get_workflow_versions', description: 'List all available versions of a workflow', inputSchema: zodToJsonSchema(GetWorkflowVersionsSchema), }, { name: 'rollback_workflow', description: 'Rollback a workflow to a previous version', inputSchema: zodToJsonSchema(RollbackWorkflowSchema), }, ]; } private async createWorkflow(args: unknown) { const parsed = CreateWorkflowSchema.parse(args); // Generate ID and add metadata const id = await this.storage.generateId(); const now = new Date().toISOString(); const workflow: Workflow = { id, name: parsed.workflow.name, description: parsed.workflow.description, goal: parsed.workflow.goal, version: parsed.workflow.version || '1.0.0', tags: parsed.workflow.tags || [], inputs: parsed.workflow.inputs || {}, outputs: parsed.workflow.outputs, required_tools: parsed.workflow.required_tools, steps: parsed.workflow.steps, metadata: { created_at: now, updated_at: now, times_run: 0, }, is_deleted: false, }; // Validate the complete workflow const validation = WorkflowValidator.validateWorkflow(workflow); if (!validation.success) { throw new Error(`Validation failed: ${validation.error}`); } // Save to storage await this.storage.save(workflow); return { content: [ { type: 'text', text: JSON.stringify({ success: true, workflow_id: id, message: `Workflow "${workflow.name}" created successfully`, }, null, 2), }, ], }; } private async listWorkflows(args: unknown) { const parsed = ListWorkflowsSchema.parse(args); const workflows = await this.storage.list(parsed.filter, parsed.sort); // Create summary for each workflow const summaries = workflows.map(w => ({ id: w.id, name: w.name, description: w.description, version: w.version, tags: w.tags, steps_count: w.steps.length, created_at: w.metadata?.created_at, times_run: w.metadata?.times_run || 0, success_rate: w.metadata?.success_rate, is_deleted: w.is_deleted, })); return { content: [ { type: 'text', text: JSON.stringify({ success: true, count: summaries.length, workflows: summaries, }, null, 2), }, ], }; } private async getWorkflow(args: unknown) { const parsed = GetWorkflowSchema.parse(args); const workflow = await this.storage.get(parsed.id); if (!workflow) { throw new Error(`Workflow not found: ${parsed.id}`); } return { content: [ { type: 'text', text: JSON.stringify({ success: true, workflow, }, null, 2), }, ], }; } private async updateWorkflow(args: unknown) { const parsed = UpdateWorkflowSchema.parse(args); const workflow = await this.storage.get(parsed.id); if (!workflow) { throw new Error(`Workflow not found: ${parsed.id}`); } // Validate partial update const validation = WorkflowValidator.validatePartialWorkflow(parsed.updates); if (!validation.success) { throw new Error(`Validation failed: ${validation.error}`); } // Apply updates const updatedWorkflow = { ...workflow, ...parsed.updates, id: workflow.id, // Prevent ID change metadata: { created_at: workflow.metadata?.created_at || new Date().toISOString(), updated_at: new Date().toISOString(), times_run: workflow.metadata?.times_run || 0, created_by: workflow.metadata?.created_by, average_duration_ms: workflow.metadata?.average_duration_ms, success_rate: workflow.metadata?.success_rate, last_run_at: workflow.metadata?.last_run_at, }, }; // Increment version if requested if (parsed.increment_version) { const [major, minor, patch] = updatedWorkflow.version.split('.').map(Number); updatedWorkflow.version = `${major}.${minor}.${patch + 1}`; } // Validate complete workflow const fullValidation = WorkflowValidator.validateWorkflow(updatedWorkflow); if (!fullValidation.success) { throw new Error(`Validation failed: ${fullValidation.error}`); } // Save await this.storage.save(updatedWorkflow); return { content: [ { type: 'text', text: JSON.stringify({ success: true, workflow_id: parsed.id, version: updatedWorkflow.version, message: `Workflow "${updatedWorkflow.name}" updated successfully`, }, null, 2), }, ], }; } private async deleteWorkflow(args: unknown) { const parsed = DeleteWorkflowSchema.parse(args); const success = await this.storage.delete(parsed.id); if (!success) { throw new Error(`Workflow not found: ${parsed.id}`); } return { content: [ { type: 'text', text: JSON.stringify({ success: true, workflow_id: parsed.id, message: 'Workflow deleted successfully (soft delete)', }, null, 2), }, ], }; } private async startWorkflow(args: unknown) { const parsed = StartWorkflowSchema.parse(args); const workflow = await this.storage.get(parsed.id); if (!workflow) { throw new Error(`Workflow not found: ${parsed.id}`); } if (workflow.is_deleted) { throw new Error('Cannot run deleted workflow'); } // Validate inputs const inputs = parsed.inputs || {}; const inputValidation = WorkflowValidator.validateInputs(workflow, inputs); if (!inputValidation.success) { throw new Error(`Input validation failed: ${inputValidation.error}`); } // Create new execution session const executionId = uuidv4(); const session: WorkflowSession = { workflow_id: workflow.id, execution_id: executionId, workflow_name: workflow.name, current_step_index: 0, total_steps: workflow.steps.length, variables: { ...inputs }, status: 'active', started_at: new Date().toISOString(), step_outputs: {}, previous_variables: {}, }; this.sessions.set(executionId, session); // Generate instructions for the first step const firstStep = workflow.steps[0]; const stepInstructions = this.generateStepInstructions(workflow, firstStep, session); return { content: [ { type: 'text', text: stepInstructions, }, ], }; } private async runWorkflowStep(args: unknown) { const parsed = RunWorkflowStepSchema.parse(args); const session = this.sessions.get(parsed.execution_id); if (!session) { throw new Error(`No active workflow session found: ${parsed.execution_id}`); } const workflow = await this.storage.get(session.workflow_id); if (!workflow) { throw new Error(`Workflow not found: ${session.workflow_id}`); } // Store result from previous step if provided if (parsed.step_result !== undefined && session.current_step_index > 0) { const previousStep = workflow.steps[session.current_step_index - 1]; if (previousStep.save_result_as) { // Store in step outputs for dependency tracking session.step_outputs[previousStep.id] = { variable_name: previousStep.save_result_as, value: parsed.step_result }; // Update previous_variables to track what was before this change session.previous_variables = { ...session.variables }; // Update current variables session.variables[previousStep.save_result_as] = parsed.step_result; } } // Check if workflow is complete if (!parsed.next_step_needed || session.current_step_index >= workflow.steps.length) { session.status = 'completed'; this.sessions.delete(parsed.execution_id); return { content: [ { type: 'text', text: JSON.stringify({ status: 'completed', execution_id: parsed.execution_id, message: `Workflow "${workflow.name}" completed successfully`, final_variables: session.variables, }, null, 2), }, ], }; } // Check if the current step was a branch step const currentStep = workflow.steps[session.current_step_index]; let nextStepIndex = session.current_step_index + 1; // Handle branching logic if (currentStep.action === 'branch' && parsed.step_result) { // Parse the branch result to find the target step const branchResult = parsed.step_result.toString(); const gotoMatch = branchResult.match(/step (\d+)/i); if (gotoMatch) { const targetStep = parseInt(gotoMatch[1]); // Find the index of the target step (steps are 1-indexed, array is 0-indexed) nextStepIndex = workflow.steps.findIndex(s => s.id === targetStep); if (nextStepIndex === -1) { throw new Error(`Branch target step ${targetStep} not found`); } } } // Update session with next step session.current_step_index = nextStepIndex; if (session.current_step_index >= workflow.steps.length) { session.status = 'completed'; this.sessions.delete(parsed.execution_id); return { content: [ { type: 'text', text: JSON.stringify({ status: 'completed', execution_id: parsed.execution_id, message: `Workflow "${workflow.name}" completed successfully`, final_variables: session.variables, }, null, 2), }, ], }; } // Generate instructions for the next step const nextStep = workflow.steps[session.current_step_index]; const stepInstructions = this.generateStepInstructions(workflow, nextStep, session); return { content: [ { type: 'text', text: stepInstructions, }, ], }; } private resolveTemplateVariables(template: string, variables: Record<string, any>): string { return template.replace(/\{\{(\w+)\}\}/g, (match, varName) => { if (variables.hasOwnProperty(varName)) { const value = variables[varName]; return typeof value === 'string' ? value : JSON.stringify(value); } return match; // Keep the original if variable not found }); } private resolveTemplateInObject(obj: any, variables: Record<string, any>): any { if (typeof obj === 'string') { return this.resolveTemplateVariables(obj, variables); } else if (Array.isArray(obj)) { return obj.map(item => this.resolveTemplateInObject(item, variables)); } else if (obj && typeof obj === 'object') { const resolved: any = {}; for (const [key, value] of Object.entries(obj)) { resolved[key] = this.resolveTemplateInObject(value, variables); } return resolved; } return obj; } private getVisibleVariables(workflow: Workflow, step: Step, session: WorkflowSession): Record<string, any> { // Check if step overrides with show_all_variables if ('show_all_variables' in step && step.show_all_variables) { return session.variables; } // Determine default behavior based on workflow settings const strictMode = workflow.strict_dependencies === true; const hasDependencies = 'dependencies' in step && step.dependencies && step.dependencies.length > 0; // If no dependencies specified if (!hasDependencies) { // In strict mode, show nothing. In normal mode, show everything return strictMode ? {} : session.variables; } // Dependencies are specified - only show variables from those steps const visibleVars: Record<string, any> = {}; // Always include input variables if (workflow.inputs) { for (const inputName of Object.keys(workflow.inputs)) { if (inputName in session.variables) { visibleVars[inputName] = session.variables[inputName]; } } } // Add variables from dependency steps for (const stepId of step.dependencies!) { const output = session.step_outputs[stepId]; if (output && output.variable_name in session.variables) { visibleVars[output.variable_name] = session.variables[output.variable_name]; } } return visibleVars; } private getChangedVariables(session: WorkflowSession): { added: string[], modified: string[], unchanged: string[] } { const added: string[] = []; const modified: string[] = []; const unchanged: string[] = []; for (const [key, value] of Object.entries(session.variables)) { if (!(key in session.previous_variables)) { added.push(key); } else if (JSON.stringify(value) !== JSON.stringify(session.previous_variables[key])) { modified.push(key); } else { unchanged.push(key); } } return { added, modified, unchanged }; } private generateStepInstructions(workflow: Workflow, step: Step, session: WorkflowSession): string { const lines: string[] = []; lines.push('=== WORKFLOW STEP EXECUTION ==='); lines.push(''); lines.push(`Workflow: ${workflow.name} (${session.execution_id})`); lines.push(`Step ${session.current_step_index + 1} of ${session.total_steps}`); lines.push(''); lines.push(`Action: ${step.action.toUpperCase()}`); lines.push(`Description: ${this.resolveTemplateVariables(step.description, session.variables)}`); lines.push(''); // Step-specific instructions if (step.action === 'tool_call' && 'tool_name' in step && 'parameters' in step) { lines.push('Execute the following tool:'); lines.push(`Tool: ${step.tool_name}`); // Resolve template variables in parameters const resolvedParams = this.resolveTemplateInObject(step.parameters, session.variables); lines.push(`Parameters: ${JSON.stringify(resolvedParams, null, 2)}`); } else if (step.action === 'analyze' || step.action === 'consider' || step.action === 'research' || step.action === 'validate' || step.action === 'summarize' || step.action === 'decide' || step.action === 'extract' || step.action === 'compose') { lines.push(`Perform the cognitive action: ${step.action}`); if ('input_from' in step && step.input_from) { lines.push('Using input from:'); for (const varName of step.input_from) { if (session.variables[varName] !== undefined) { lines.push(` ${varName}: ${JSON.stringify(session.variables[varName])}`); } } } if ('criteria' in step && step.criteria) { lines.push(`Criteria: ${this.resolveTemplateVariables(step.criteria, session.variables)}`); } } else if (step.action === 'wait_for_input' && 'prompt' in step) { lines.push('Request input from the user:'); lines.push(`Prompt: ${this.resolveTemplateVariables(step.prompt, session.variables)}`); if ('input_type' in step) { lines.push(`Expected type: ${step.input_type}`); } } else if (step.action === 'transform' && 'transformation' in step) { lines.push('Apply transformation:'); lines.push(this.resolveTemplateVariables(step.transformation, session.variables)); if ('input_from' in step && step.input_from) { lines.push('To variables:'); for (const varName of step.input_from) { if (session.variables[varName] !== undefined) { lines.push(` ${varName}: ${JSON.stringify(session.variables[varName])}`); } } } } else if (step.action === 'branch' && 'conditions' in step) { lines.push('Evaluate conditions and determine next step:'); lines.push(''); for (const condition of step.conditions) { lines.push(`IF ${condition.if} THEN GOTO step ${condition.goto_step}`); } lines.push(''); lines.push('Evaluate the conditions using the current variables and respond with:'); lines.push('- Which condition is true'); lines.push('- The step number to jump to (e.g., "Branching to step 8")'); } else if (step.action === 'notify' && 'message' in step) { lines.push('Send notification:'); lines.push(`Message: ${this.resolveTemplateVariables(step.message, session.variables)}`); if ('channel' in step && step.channel) { lines.push(`Channel: ${this.resolveTemplateVariables(step.channel, session.variables)}`); } } if (step.save_result_as) { lines.push(''); lines.push(`Save the result as: ${step.save_result_as}`); } // Get visible variables based on dependencies const visibleVariables = this.getVisibleVariables(workflow, step, session); // Show variable changes if we have previous state if (session.current_step_index > 0 && Object.keys(session.previous_variables).length > 0) { const changes = this.getChangedVariables(session); if (changes.added.length > 0 || changes.modified.length > 0) { lines.push(''); lines.push('Variable changes:'); for (const varName of changes.added) { if (varName in visibleVariables) { lines.push(` + ${varName}: ${JSON.stringify(session.variables[varName])}`); } } for (const varName of changes.modified) { if (varName in visibleVariables) { lines.push(` ~ ${varName}: ${JSON.stringify(session.variables[varName])}`); } } } } // Show current visible variables lines.push(''); lines.push('Available variables:'); const visibleVarNames = Object.keys(visibleVariables); if (visibleVarNames.length === 0) { lines.push(' (none - use dependencies to access previous step outputs)'); } else { for (const [key, value] of Object.entries(visibleVariables)) { lines.push(` ${key}: ${JSON.stringify(value)}`); } } // Show upcoming steps (progressive loading) if (session.current_step_index < workflow.steps.length - 1) { lines.push(''); lines.push('Upcoming steps:'); const maxPreview = Math.min(session.current_step_index + 3, workflow.steps.length); for (let i = session.current_step_index + 1; i < maxPreview; i++) { const upcomingStep = workflow.steps[i]; lines.push(` ${i + 1}. ${upcomingStep.action}: ${upcomingStep.description}`); } if (maxPreview < workflow.steps.length) { lines.push(` ... and ${workflow.steps.length - maxPreview} more steps`); } } lines.push(''); lines.push('After completing this step, call run_workflow_step with:'); lines.push('- execution_id: ' + session.execution_id); lines.push('- step_result: <the result of this step, if any>'); lines.push('- next_step_needed: true (or false if the workflow should end)'); return lines.join('\n'); } private async getWorkflowVersions(args: unknown) { const parsed = GetWorkflowVersionsSchema.parse(args); const workflow = await this.storage.get(parsed.workflow_id); if (!workflow) { throw new Error(`Workflow not found: ${parsed.workflow_id}`); } const versions = await this.storage.listVersions(parsed.workflow_id); return { content: [ { type: 'text', text: JSON.stringify({ success: true, workflow_id: parsed.workflow_id, workflow_name: workflow.name, current_version: workflow.version, available_versions: versions, version_count: versions.length, }, null, 2), }, ], }; } private async rollbackWorkflow(args: unknown) { const parsed = RollbackWorkflowSchema.parse(args); const workflow = await this.storage.get(parsed.workflow_id); if (!workflow) { throw new Error(`Workflow not found: ${parsed.workflow_id}`); } const targetWorkflow = await this.storage.getVersion(parsed.workflow_id, parsed.target_version); if (!targetWorkflow) { throw new Error(`Version ${parsed.target_version} not found for workflow ${parsed.workflow_id}`); } const success = await this.storage.rollback(parsed.workflow_id, parsed.target_version); if (!success) { throw new Error('Rollback failed'); } return { content: [ { type: 'text', text: JSON.stringify({ success: true, workflow_id: parsed.workflow_id, workflow_name: workflow.name, previous_version: workflow.version, rolled_back_to: parsed.target_version, reason: parsed.reason || 'No reason provided', message: `Successfully rolled back "${workflow.name}" from v${workflow.version} to v${parsed.target_version}`, }, null, 2), }, ], }; } async start() { await this.storage.initialize(); const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Workflow MCP Server started'); } } // Start the server const isMainModule = process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, '/')); if (isMainModule) { const server = new WorkflowMCPServer(); server.start().catch(console.error); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/FiveOhhWon/workflows-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server