11import { db } from '@sim/db'
2- import { mcpServers } from '@sim/db/schema'
2+ import { mcpServers , workflow , workflowBlocks } from '@sim/db/schema'
33import { createLogger } from '@sim/logger'
44import { and , eq , isNull } from 'drizzle-orm'
55import type { NextRequest } from 'next/server'
66import { withMcpAuth } from '@/lib/mcp/middleware'
77import { mcpService } from '@/lib/mcp/service'
8- import type { McpServerStatusConfig } from '@/lib/mcp/types'
8+ import type { McpServerStatusConfig , McpTool , McpToolSchema } from '@/lib/mcp/types'
99import { createMcpErrorResponse , createMcpSuccessResponse } from '@/lib/mcp/utils'
1010
1111const logger = createLogger ( 'McpServerRefreshAPI' )
1212
1313export const dynamic = 'force-dynamic'
1414
15+ /** Schema stored in workflow blocks includes description from the tool. */
16+ type StoredToolSchema = McpToolSchema & { description ?: string }
17+
18+ interface StoredTool {
19+ type : string
20+ title : string
21+ toolId : string
22+ params : {
23+ serverId : string
24+ serverUrl ?: string
25+ toolName : string
26+ serverName ?: string
27+ }
28+ schema ?: StoredToolSchema
29+ [ key : string ] : unknown
30+ }
31+
32+ /** Core param keys that are metadata, not user-entered test values */
33+ const MCP_TOOL_CORE_PARAMS = new Set ( [ 'serverId' , 'serverUrl' , 'toolName' , 'serverName' ] )
34+
35+ interface SyncResult {
36+ updatedCount : number
37+ updatedWorkflowIds : string [ ]
38+ }
39+
40+ /**
41+ * Syncs tool schemas from discovered MCP tools to all workflow blocks using those tools.
42+ * Returns the count and IDs of updated workflows.
43+ */
44+ async function syncToolSchemasToWorkflows (
45+ workspaceId : string ,
46+ serverId : string ,
47+ tools : McpTool [ ] ,
48+ requestId : string
49+ ) : Promise < SyncResult > {
50+ const toolsByName = new Map ( tools . map ( ( t ) => [ t . name , t ] ) )
51+
52+ const workspaceWorkflows = await db
53+ . select ( { id : workflow . id } )
54+ . from ( workflow )
55+ . where ( eq ( workflow . workspaceId , workspaceId ) )
56+
57+ const workflowIds = workspaceWorkflows . map ( ( w ) => w . id )
58+ if ( workflowIds . length === 0 ) return { updatedCount : 0 , updatedWorkflowIds : [ ] }
59+
60+ const agentBlocks = await db
61+ . select ( {
62+ id : workflowBlocks . id ,
63+ workflowId : workflowBlocks . workflowId ,
64+ subBlocks : workflowBlocks . subBlocks ,
65+ } )
66+ . from ( workflowBlocks )
67+ . where ( eq ( workflowBlocks . type , 'agent' ) )
68+
69+ const updatedWorkflowIds = new Set < string > ( )
70+
71+ for ( const block of agentBlocks ) {
72+ if ( ! workflowIds . includes ( block . workflowId ) ) continue
73+
74+ const subBlocks = block . subBlocks as Record < string , unknown > | null
75+ if ( ! subBlocks ) continue
76+
77+ const toolsSubBlock = subBlocks . tools as { value ?: StoredTool [ ] } | undefined
78+ if ( ! toolsSubBlock ?. value || ! Array . isArray ( toolsSubBlock . value ) ) continue
79+
80+ let hasUpdates = false
81+ const updatedTools = toolsSubBlock . value . map ( ( tool ) => {
82+ if ( tool . type !== 'mcp' || tool . params ?. serverId !== serverId ) {
83+ return tool
84+ }
85+
86+ const freshTool = toolsByName . get ( tool . params . toolName )
87+ if ( ! freshTool ) return tool
88+
89+ const newSchema : StoredToolSchema = {
90+ ...freshTool . inputSchema ,
91+ description : freshTool . description ,
92+ }
93+
94+ const schemasMatch = JSON . stringify ( tool . schema ) === JSON . stringify ( newSchema )
95+
96+ if ( ! schemasMatch ) {
97+ hasUpdates = true
98+
99+ const validParamKeys = new Set ( Object . keys ( newSchema . properties || { } ) )
100+
101+ const cleanedParams : Record < string , unknown > = { }
102+ for ( const [ key , value ] of Object . entries ( tool . params || { } ) ) {
103+ if ( MCP_TOOL_CORE_PARAMS . has ( key ) || validParamKeys . has ( key ) ) {
104+ cleanedParams [ key ] = value
105+ }
106+ }
107+
108+ return { ...tool , schema : newSchema , params : cleanedParams }
109+ }
110+
111+ return tool
112+ } )
113+
114+ if ( hasUpdates ) {
115+ const updatedSubBlocks = {
116+ ...subBlocks ,
117+ tools : { ...toolsSubBlock , value : updatedTools } ,
118+ }
119+
120+ await db
121+ . update ( workflowBlocks )
122+ . set ( { subBlocks : updatedSubBlocks , updatedAt : new Date ( ) } )
123+ . where ( eq ( workflowBlocks . id , block . id ) )
124+
125+ updatedWorkflowIds . add ( block . workflowId )
126+ }
127+ }
128+
129+ if ( updatedWorkflowIds . size > 0 ) {
130+ logger . info (
131+ `[${ requestId } ] Synced tool schemas to ${ updatedWorkflowIds . size } workflow(s) for server ${ serverId } `
132+ )
133+ }
134+
135+ return {
136+ updatedCount : updatedWorkflowIds . size ,
137+ updatedWorkflowIds : Array . from ( updatedWorkflowIds ) ,
138+ }
139+ }
140+
15141export const POST = withMcpAuth < { id : string } > ( 'read' ) (
16142 async ( request : NextRequest , { userId, workspaceId, requestId } , { params } ) => {
17143 const { id : serverId } = await params
@@ -42,6 +168,8 @@ export const POST = withMcpAuth<{ id: string }>('read')(
42168 let connectionStatus : 'connected' | 'disconnected' | 'error' = 'error'
43169 let toolCount = 0
44170 let lastError : string | null = null
171+ let syncResult : SyncResult = { updatedCount : 0 , updatedWorkflowIds : [ ] }
172+ let discoveredTools : McpTool [ ] = [ ]
45173
46174 const currentStatusConfig : McpServerStatusConfig =
47175 ( server . statusConfig as McpServerStatusConfig | null ) ?? {
@@ -50,10 +178,17 @@ export const POST = withMcpAuth<{ id: string }>('read')(
50178 }
51179
52180 try {
53- const tools = await mcpService . discoverServerTools ( userId , serverId , workspaceId )
181+ discoveredTools = await mcpService . discoverServerTools ( userId , serverId , workspaceId )
54182 connectionStatus = 'connected'
55- toolCount = tools . length
183+ toolCount = discoveredTools . length
56184 logger . info ( `[${ requestId } ] Discovered ${ toolCount } tools from server ${ serverId } ` )
185+
186+ syncResult = await syncToolSchemasToWorkflows (
187+ workspaceId ,
188+ serverId ,
189+ discoveredTools ,
190+ requestId
191+ )
57192 } catch ( error ) {
58193 connectionStatus = 'error'
59194 lastError = error instanceof Error ? error . message : 'Connection test failed'
@@ -92,6 +227,8 @@ export const POST = withMcpAuth<{ id: string }>('read')(
92227 toolCount,
93228 lastConnected : refreshedServer ?. lastConnected ?. toISOString ( ) || null ,
94229 error : lastError ,
230+ workflowsUpdated : syncResult . updatedCount ,
231+ updatedWorkflowIds : syncResult . updatedWorkflowIds ,
95232 } )
96233 } catch ( error ) {
97234 logger . error ( `[${ requestId } ] Error refreshing MCP server:` , error )
0 commit comments