Skip to content

Commit f94df25

Browse files
fix(workflows): ignore nonfatal mcp sync failures
1 parent 2f57f08 commit f94df25

File tree

6 files changed

+113
-14
lines changed

6 files changed

+113
-14
lines changed

apps/sim/app/api/workflows/[id]/deploy/route.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,36 @@ describe('Workflow deploy route', () => {
191191
)
192192
})
193193

194+
it('returns success when MCP sync throws after deploy succeeds', async () => {
195+
mockValidateWorkflowAccess.mockResolvedValue({
196+
workflow: { id: 'wf-1', name: 'Test Workflow', workspaceId: 'ws-1' },
197+
auth: {
198+
success: true,
199+
userId: 'api-user',
200+
userName: 'API Key Actor',
201+
userEmail: 'api@example.com',
202+
authType: 'api_key',
203+
},
204+
})
205+
mockDeployWorkflow.mockResolvedValue({
206+
success: true,
207+
deployedAt: '2024-01-01T00:00:00Z',
208+
deploymentVersionId: 'dep-1',
209+
})
210+
mockSyncMcpToolsForWorkflow.mockRejectedValue(new Error('MCP sync failed'))
211+
212+
const req = new NextRequest('http://localhost:3000/api/workflows/wf-1/deploy', {
213+
method: 'POST',
214+
headers: { 'x-api-key': 'test-key' },
215+
})
216+
const response = await POST(req, { params: Promise.resolve({ id: 'wf-1' }) })
217+
218+
expect(response.status).toBe(200)
219+
const data = await response.json()
220+
expect(data.isDeployed).toBe(true)
221+
expect(mockRecordAudit).toHaveBeenCalled()
222+
})
223+
194224
it('allows API-key auth for undeploy using hybrid auth userId', async () => {
195225
mockValidateWorkflowAccess.mockResolvedValue({
196226
workflow: { id: 'wf-1', name: 'Test Workflow', workspaceId: 'ws-1' },

apps/sim/app/api/workflows/[id]/deploy/route.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,13 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
242242

243243
logger.info(`[${requestId}] Workflow deployed successfully: ${id}`)
244244

245-
// Sync MCP tools with the latest parameter schema
246-
await syncMcpToolsForWorkflow({ workflowId: id, requestId, context: 'deploy' })
245+
try {
246+
await syncMcpToolsForWorkflow({ workflowId: id, requestId, context: 'deploy' })
247+
} catch (syncError) {
248+
logger.error(`[${requestId}] Failed to sync MCP tools after deploy for workflow ${id}`, {
249+
error: syncError,
250+
})
251+
}
247252

248253
const { actorName, actorEmail } = getAuditActorMetadata(auth)
249254

apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,29 @@ describe('Workflow deployment version revert route', () => {
224224
})
225225
)
226226
})
227+
228+
it('returns success when MCP sync throws after revert succeeds', async () => {
229+
mockValidateWorkflowAccess.mockResolvedValue({
230+
workflow: { id: 'wf-1', name: 'Test Workflow', workspaceId: 'ws-1' },
231+
auth: {
232+
success: true,
233+
userId: 'api-user',
234+
userName: 'API Key Actor',
235+
userEmail: 'api@example.com',
236+
authType: 'api_key',
237+
},
238+
})
239+
mockSyncMcpToolsForWorkflow.mockRejectedValue(new Error('MCP sync failed'))
240+
241+
const req = new NextRequest('http://localhost:3000/api/workflows/wf-1/deployments/3/revert', {
242+
method: 'POST',
243+
headers: { 'x-api-key': 'test-key' },
244+
})
245+
const response = await POST(req, { params: Promise.resolve({ id: 'wf-1', version: '3' }) })
246+
247+
expect(response.status).toBe(200)
248+
const data = await response.json()
249+
expect(data.message).toBe('Reverted to deployment version')
250+
expect(mockRecordAudit).toHaveBeenCalled()
251+
})
227252
})

apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,18 @@ export async function POST(
103103
.set({ lastSynced: new Date(), updatedAt: new Date() })
104104
.where(eq(workflow.id, id))
105105

106-
await syncMcpToolsForWorkflow({
107-
workflowId: id,
108-
requestId,
109-
state: deployedState,
110-
context: 'revert',
111-
})
106+
try {
107+
await syncMcpToolsForWorkflow({
108+
workflowId: id,
109+
requestId,
110+
state: deployedState,
111+
context: 'revert',
112+
})
113+
} catch (syncError) {
114+
logger.error(`[${requestId}] Failed to sync MCP tools after revert for workflow ${id}`, {
115+
error: syncError,
116+
})
117+
}
112118

113119
try {
114120
const socketServerUrl = env.SOCKET_SERVER_URL || 'http://localhost:3002'

apps/sim/app/api/workflows/[id]/deployments/[version]/route.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,32 @@ describe('Workflow deployment version route', () => {
204204
)
205205
})
206206

207+
it('returns success when MCP sync throws after activation succeeds', async () => {
208+
mockValidateWorkflowAccess.mockResolvedValue({
209+
workflow: { id: 'wf-1', name: 'Test Workflow', workspaceId: 'ws-1' },
210+
auth: {
211+
success: true,
212+
userId: 'api-user',
213+
userName: 'API Key Actor',
214+
userEmail: 'api@example.com',
215+
authType: 'api_key',
216+
},
217+
})
218+
mockSyncMcpToolsForWorkflow.mockRejectedValue(new Error('MCP sync failed'))
219+
220+
const req = new NextRequest('http://localhost:3000/api/workflows/wf-1/deployments/3', {
221+
method: 'PATCH',
222+
headers: { 'content-type': 'application/json', 'x-api-key': 'test-key' },
223+
body: JSON.stringify({ isActive: true }),
224+
})
225+
const response = await PATCH(req, { params: Promise.resolve({ id: 'wf-1', version: '3' }) })
226+
227+
expect(response.status).toBe(200)
228+
const data = await response.json()
229+
expect(data.success).toBe(true)
230+
expect(mockRecordAudit).toHaveBeenCalled()
231+
})
232+
207233
it('returns write auth failure before parsing or updating metadata', async () => {
208234
mockValidateWorkflowAccess.mockResolvedValue({
209235
error: { message: 'Write permission required', status: 403 },

apps/sim/app/api/workflows/[id]/deployments/[version]/route.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,12 +271,19 @@ export async function PATCH(
271271
}
272272
}
273273

274-
await syncMcpToolsForWorkflow({
275-
workflowId: id,
276-
requestId,
277-
state: versionRow.state,
278-
context: 'activate',
279-
})
274+
try {
275+
await syncMcpToolsForWorkflow({
276+
workflowId: id,
277+
requestId,
278+
state: versionRow.state,
279+
context: 'activate',
280+
})
281+
} catch (syncError) {
282+
logger.error(
283+
`[${requestId}] Failed to sync MCP tools after activation for workflow ${id}`,
284+
syncError
285+
)
286+
}
280287

281288
// Apply name/description updates if provided alongside activation
282289
let updatedName: string | null | undefined

0 commit comments

Comments
 (0)