Skip to content

Commit 3d02bbb

Browse files
authored
fix(mcp): coerce corrupted consecutiveFailures instead of crashing the whole server list (#5593)
Root cause: updateServerStatus() only fell back to the default status config when the whole statusConfig column was null/undefined, not when it was a real object missing consecutiveFailures (e.g. the column's '{}' default on server creation). currentConfig.consecutiveFailures was then undefined, undefined + 1 evaluated to NaN, and JSON.stringify(NaN) persisted as a literal `null` into the DB the first time a freshly-created server had a connection failure. That corrupted value then failed listMcpServersContract's Zod parse client-side (consecutiveFailures: z.number() rejects null), and since the response is a single array, one bad server blanked the entire MCP servers list with "Response failed contract validation" for the whole workspace — currently affecting 81 servers across 69 production workspaces. Two fixes: - service.ts: normalize the read-back statusConfig so consecutiveFailures is always a real number, never NaN, going forward. - contracts/mcp.ts: coerce any non-number consecutiveFailures (including the already-corrupted `null` rows) to the schema's default of 0 instead of failing validation, so every already-affected workspace self-heals on next load with no DB migration needed.
1 parent 2ba0b58 commit 3d02bbb

2 files changed

Lines changed: 14 additions & 6 deletions

File tree

apps/sim/lib/api/contracts/mcp.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,14 @@ export const mcpTransportSchema = z.enum(['streamable-http'])
3838

3939
export const mcpAuthTypeSchema = z.enum(['none', 'headers', 'oauth'])
4040

41+
const consecutiveFailuresSchema = z.preprocess(
42+
(value) => (typeof value === 'number' ? value : undefined),
43+
z.number().default(0)
44+
)
45+
4146
export const mcpServerStatusConfigSchema = z
4247
.object({
43-
consecutiveFailures: z.number().default(0),
48+
consecutiveFailures: consecutiveFailuresSchema,
4449
lastSuccessfulDiscovery: z.string().nullable().default(null),
4550
})
4651
.passthrough()

apps/sim/lib/mcp/service.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -328,11 +328,14 @@ class McpService {
328328
)
329329
.limit(1)
330330

331-
const currentConfig: McpServerStatusConfig =
332-
(currentServer?.statusConfig as McpServerStatusConfig | null) ?? {
333-
consecutiveFailures: 0,
334-
lastSuccessfulDiscovery: null,
335-
}
331+
const storedConfig = currentServer?.statusConfig as Partial<McpServerStatusConfig> | null
332+
const currentConfig: McpServerStatusConfig = {
333+
consecutiveFailures:
334+
typeof storedConfig?.consecutiveFailures === 'number'
335+
? storedConfig.consecutiveFailures
336+
: 0,
337+
lastSuccessfulDiscovery: storedConfig?.lastSuccessfulDiscovery ?? null,
338+
}
336339

337340
const now = new Date()
338341

0 commit comments

Comments
 (0)