diff --git a/apps/sim/app/api/mcp/workflow-servers/[id]/tools/route.ts b/apps/sim/app/api/mcp/workflow-servers/[id]/tools/route.ts index 2a668a99c2..858797db64 100644 --- a/apps/sim/app/api/mcp/workflow-servers/[id]/tools/route.ts +++ b/apps/sim/app/api/mcp/workflow-servers/[id]/tools/route.ts @@ -6,7 +6,7 @@ import { createLogger } from '@/lib/logs/console/logger' import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware' import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils' import { sanitizeToolName } from '@/lib/mcp/workflow-tool-schema' -import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' +import { hasValidStartBlock } from '@/lib/workflows/triggers/trigger-utils' const logger = createLogger('WorkflowMcpToolsAPI') @@ -16,36 +16,6 @@ interface RouteParams { id: string } -/** - * Check if a workflow has a valid start block that can accept inputs - */ -async function hasValidStartBlock(workflowId: string): Promise { - try { - const normalizedData = await loadWorkflowFromNormalizedTables(workflowId) - if (!normalizedData?.blocks) { - return false - } - - // Look for a start block - const startBlock = Object.values(normalizedData.blocks).find((block: any) => { - const blockType = block?.type - return ( - blockType === 'starter' || - blockType === 'start' || - blockType === 'start_trigger' || - blockType === 'api' || - blockType === 'api_trigger' || - blockType === 'input_trigger' - ) - }) - - return !!startBlock - } catch (error) { - logger.warn('Error checking for start block:', error) - return false - } -} - /** * GET - List all tools for a workflow MCP server */ diff --git a/apps/sim/app/api/workflows/[id]/deploy/route.ts b/apps/sim/app/api/workflows/[id]/deploy/route.ts index be390f5904..b17b240635 100644 --- a/apps/sim/app/api/workflows/[id]/deploy/route.ts +++ b/apps/sim/app/api/workflows/[id]/deploy/route.ts @@ -5,6 +5,10 @@ import { generateRequestId } from '@/lib/core/utils/request' import { createLogger } from '@/lib/logs/console/logger' import { deployWorkflow, loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' import { validateWorkflowPermissions } from '@/lib/workflows/utils' +import { + hasValidStartBlock, + isValidStartBlockType, +} from '@/lib/workflows/triggers/trigger-utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' const logger = createLogger('WorkflowDeployAPI') @@ -24,15 +28,7 @@ async function generateMcpToolSchema(workflowId: string): Promise { - const blockType = block?.type - return ( - blockType === 'starter' || - blockType === 'start' || - blockType === 'start_trigger' || - blockType === 'api' || - blockType === 'api_trigger' || - blockType === 'input_trigger' - ) + return isValidStartBlockType(block?.type) }) as any if (!startBlock?.subBlocks?.inputFormat?.value) { @@ -88,35 +84,6 @@ async function generateMcpToolSchema(workflowId: string): Promise { - try { - const normalizedData = await loadWorkflowFromNormalizedTables(workflowId) - if (!normalizedData?.blocks) { - return false - } - - const startBlock = Object.values(normalizedData.blocks).find((block: any) => { - const blockType = block?.type - return ( - blockType === 'starter' || - blockType === 'start' || - blockType === 'start_trigger' || - blockType === 'api' || - blockType === 'api_trigger' || - blockType === 'input_trigger' - ) - }) - - return !!startBlock - } catch (error) { - logger.warn('Error checking for start block:', error) - return false - } -} - /** * Update all MCP tools that reference this workflow with the latest parameter schema. * If the workflow no longer has a start block, remove all MCP tools. diff --git a/apps/sim/app/api/workflows/[id]/deployments/[version]/activate/route.ts b/apps/sim/app/api/workflows/[id]/deployments/[version]/activate/route.ts index ea8324e5a0..18b48c8c26 100644 --- a/apps/sim/app/api/workflows/[id]/deployments/[version]/activate/route.ts +++ b/apps/sim/app/api/workflows/[id]/deployments/[version]/activate/route.ts @@ -4,6 +4,10 @@ import type { NextRequest } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' import { createLogger } from '@/lib/logs/console/logger' import { validateWorkflowPermissions } from '@/lib/workflows/utils' +import { + hasValidStartBlockInState, + isValidStartBlockType, +} from '@/lib/workflows/triggers/trigger-utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' const logger = createLogger('WorkflowActivateDeploymentAPI') @@ -22,15 +26,7 @@ function generateMcpToolSchemaFromState(state: any): Record { // Find the start block in the deployed state const startBlock = Object.values(state.blocks).find((block: any) => { - const blockType = block?.type - return ( - blockType === 'starter' || - blockType === 'start' || - blockType === 'start_trigger' || - blockType === 'api' || - blockType === 'api_trigger' || - blockType === 'input_trigger' - ) + return isValidStartBlockType(block?.type) }) as any if (!startBlock?.subBlocks?.inputFormat?.value) { @@ -86,29 +82,6 @@ function generateMcpToolSchemaFromState(state: any): Record { } } -/** - * Check if a version state has a valid start block - */ -function hasValidStartBlockInState(state: any): boolean { - if (!state?.blocks) { - return false - } - - const startBlock = Object.values(state.blocks).find((block: any) => { - const blockType = block?.type - return ( - blockType === 'starter' || - blockType === 'start' || - blockType === 'start_trigger' || - blockType === 'api' || - blockType === 'api_trigger' || - blockType === 'input_trigger' - ) - }) - - return !!startBlock -} - /** * Sync MCP tools when activating a deployment version. * If the version has no start block, remove all MCP tools. diff --git a/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts b/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts index f645a23450..74e543a419 100644 --- a/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts +++ b/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts @@ -6,6 +6,10 @@ import { generateRequestId } from '@/lib/core/utils/request' import { createLogger } from '@/lib/logs/console/logger' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' import { validateWorkflowPermissions } from '@/lib/workflows/utils' +import { + hasValidStartBlockInState, + isValidStartBlockType, +} from '@/lib/workflows/triggers/trigger-utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' const logger = createLogger('RevertToDeploymentVersionAPI') @@ -24,15 +28,7 @@ function generateMcpToolSchemaFromState(state: any): Record { // Find the start block in the deployed state const startBlock = Object.values(state.blocks).find((block: any) => { - const blockType = block?.type - return ( - blockType === 'starter' || - blockType === 'start' || - blockType === 'start_trigger' || - blockType === 'api' || - blockType === 'api_trigger' || - blockType === 'input_trigger' - ) + return isValidStartBlockType(block?.type) }) as any if (!startBlock?.subBlocks?.inputFormat?.value) { @@ -88,29 +84,6 @@ function generateMcpToolSchemaFromState(state: any): Record { } } -/** - * Check if a version state has a valid start block - */ -function hasValidStartBlockInState(state: any): boolean { - if (!state?.blocks) { - return false - } - - const startBlock = Object.values(state.blocks).find((block: any) => { - const blockType = block?.type - return ( - blockType === 'starter' || - blockType === 'start' || - blockType === 'start_trigger' || - blockType === 'api' || - blockType === 'api_trigger' || - blockType === 'input_trigger' - ) - }) - - return !!startBlock -} - /** * Sync MCP tools when reverting to a deployment version. * If the version has no start block, remove all MCP tools. diff --git a/apps/sim/lib/workflows/triggers/trigger-utils.ts b/apps/sim/lib/workflows/triggers/trigger-utils.ts index aaf6dbb697..56af72990b 100644 --- a/apps/sim/lib/workflows/triggers/trigger-utils.ts +++ b/apps/sim/lib/workflows/triggers/trigger-utils.ts @@ -7,9 +7,60 @@ import { import { getAllBlocks, getBlock } from '@/blocks' import type { BlockConfig } from '@/blocks/types' import { getTrigger } from '@/triggers' +import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' const logger = createLogger('TriggerUtils') +/** + * Valid start block types that can trigger a workflow + */ +export const VALID_START_BLOCK_TYPES = [ + 'starter', + 'start', + 'start_trigger', + 'api', + 'api_trigger', + 'input_trigger', +] as const + +export type ValidStartBlockType = (typeof VALID_START_BLOCK_TYPES)[number] + +/** + * Check if a block type is a valid start block type + */ +export function isValidStartBlockType(blockType: string): blockType is ValidStartBlockType { + return VALID_START_BLOCK_TYPES.includes(blockType as ValidStartBlockType) +} + +/** + * Check if a workflow state has a valid start block + */ +export function hasValidStartBlockInState(state: any): boolean { + if (!state?.blocks) { + return false + } + + const startBlock = Object.values(state.blocks).find((block: any) => { + const blockType = block?.type + return isValidStartBlockType(blockType) + }) + + return !!startBlock +} + +/** + * Check if a workflow has a valid start block by loading from database + */ +export async function hasValidStartBlock(workflowId: string): Promise { + try { + const normalizedData = await loadWorkflowFromNormalizedTables(workflowId) + return hasValidStartBlockInState(normalizedData) + } catch (error) { + logger.warn('Error checking for start block:', error) + return false + } +} + /** * Generates mock data based on the output type definition */