Skip to content

Commit 6dac845

Browse files
committed
fix lint
1 parent ab44c11 commit 6dac845

3 files changed

Lines changed: 106 additions & 26 deletions

File tree

  • apps/sim/app

apps/sim/app/api/mcp/serve/[serverId]/route.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from '@modelcontextprotocol/sdk/types.js'
1717
import { db } from '@sim/db'
1818
import { workflow, workflowMcpServer, workflowMcpTool } from '@sim/db/schema'
19-
import { eq } from 'drizzle-orm'
19+
import { and, eq } from 'drizzle-orm'
2020
import { type NextRequest, NextResponse } from 'next/server'
2121
import { checkHybridAuth } from '@/lib/auth/hybrid'
2222
import { getBaseUrl } from '@/lib/core/utils/urls'
@@ -209,15 +209,14 @@ async function handleToolsCall(
209209
})
210210
}
211211

212-
const tools = await db
212+
const [tool] = await db
213213
.select({
214214
toolName: workflowMcpTool.toolName,
215215
workflowId: workflowMcpTool.workflowId,
216216
})
217217
.from(workflowMcpTool)
218-
.where(eq(workflowMcpTool.serverId, serverId))
219-
220-
const tool = tools.find((t) => t.toolName === params.name)
218+
.where(and(eq(workflowMcpTool.serverId, serverId), eq(workflowMcpTool.toolName, params.name)))
219+
.limit(1)
221220
if (!tool) {
222221
return NextResponse.json(
223222
createError(id, ErrorCode.InvalidParams, `Tool not found: ${params.name}`),
@@ -252,6 +251,7 @@ async function handleToolsCall(
252251
method: 'POST',
253252
headers,
254253
body: JSON.stringify({ input: params.arguments || {}, triggerType: 'mcp' }),
254+
signal: AbortSignal.timeout(300000), // 5 minute timeout
255255
})
256256

257257
const executeResult = await response.json()

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/mcp-tool/mcp-tool.tsx

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ interface McpToolDeployProps {
3333
workflowDescription?: string | null
3434
isDeployed: boolean
3535
onAddedToServer?: () => void
36+
onSubmittingChange?: (submitting: boolean) => void
37+
onCanSaveChange?: (canSave: boolean) => void
3638
}
3739

3840
/**
@@ -79,6 +81,8 @@ export function McpToolDeploy({
7981
workflowDescription,
8082
isDeployed,
8183
onAddedToServer,
84+
onSubmittingChange,
85+
onCanSaveChange,
8286
}: McpToolDeployProps) {
8387
const params = useParams()
8488
const workspaceId = params.workspaceId as string
@@ -200,11 +204,47 @@ export function McpToolDeploy({
200204
}
201205
}, [servers, serverToolsMap])
202206

207+
// Track saved values to detect changes (use state so updates trigger re-render)
208+
const [savedValues, setSavedValues] = useState<{
209+
toolName: string
210+
toolDescription: string
211+
parameterDescriptions: Record<string, string>
212+
} | null>(null)
213+
214+
// Store saved values once loaded
215+
useEffect(() => {
216+
if (hasLoadedInitialData.current && !savedValues) {
217+
setSavedValues({
218+
toolName,
219+
toolDescription,
220+
parameterDescriptions: { ...parameterDescriptions },
221+
})
222+
}
223+
}, [toolName, toolDescription, parameterDescriptions, savedValues])
224+
225+
// Determine if there are unsaved changes
226+
const hasDeployedTools = selectedServerIds.length > 0
227+
const hasChanges = useMemo(() => {
228+
if (!savedValues || !hasDeployedTools) return false
229+
if (toolName !== savedValues.toolName) return true
230+
if (toolDescription !== savedValues.toolDescription) return true
231+
if (
232+
JSON.stringify(parameterDescriptions) !== JSON.stringify(savedValues.parameterDescriptions)
233+
) {
234+
return true
235+
}
236+
return false
237+
}, [toolName, toolDescription, parameterDescriptions, hasDeployedTools, savedValues])
238+
239+
// Notify parent about save availability
240+
useEffect(() => {
241+
onCanSaveChange?.(hasChanges && hasDeployedTools && !!toolName.trim())
242+
}, [hasChanges, hasDeployedTools, toolName, onCanSaveChange])
243+
203244
/**
204-
* Sync tool configuration changes to all deployed servers (debounced)
245+
* Save tool configuration to all deployed servers
205246
*/
206-
useEffect(() => {
207-
if (!hasLoadedInitialData.current) return
247+
const handleSave = useCallback(async () => {
208248
if (!toolName.trim()) return
209249

210250
const toolsToUpdate: Array<{ serverId: string; toolId: string }> = []
@@ -217,32 +257,41 @@ export function McpToolDeploy({
217257

218258
if (toolsToUpdate.length === 0) return
219259

220-
const timeoutId = setTimeout(async () => {
260+
onSubmittingChange?.(true)
261+
try {
221262
for (const { serverId, toolId } of toolsToUpdate) {
222-
try {
223-
await updateToolMutation.mutateAsync({
224-
workspaceId,
225-
serverId,
226-
toolId,
227-
toolName: toolName.trim(),
228-
toolDescription: toolDescription.trim() || undefined,
229-
parameterSchema,
230-
})
231-
} catch (error) {
232-
logger.error(`Failed to sync tool ${toolId}:`, error)
233-
}
263+
await updateToolMutation.mutateAsync({
264+
workspaceId,
265+
serverId,
266+
toolId,
267+
toolName: toolName.trim(),
268+
toolDescription: toolDescription.trim() || undefined,
269+
parameterSchema,
270+
})
234271
}
235-
}, 500)
236-
237-
return () => clearTimeout(timeoutId)
272+
// Update saved values after successful save (triggers re-render → hasChanges becomes false)
273+
setSavedValues({
274+
toolName,
275+
toolDescription,
276+
parameterDescriptions: { ...parameterDescriptions },
277+
})
278+
onCanSaveChange?.(false)
279+
onSubmittingChange?.(false)
280+
} catch (error) {
281+
logger.error('Failed to save tool configuration:', error)
282+
onSubmittingChange?.(false)
283+
}
238284
}, [
239285
toolName,
240286
toolDescription,
287+
parameterDescriptions,
241288
parameterSchema,
242289
servers,
243290
serverToolsMap,
244291
workspaceId,
245292
updateToolMutation,
293+
onSubmittingChange,
294+
onCanSaveChange,
246295
])
247296

248297
const serverOptions: ComboboxOption[] = useMemo(() => {
@@ -393,7 +442,17 @@ export function McpToolDeploy({
393442
}
394443

395444
return (
396-
<div className='-mx-1 space-y-4 overflow-y-auto px-1'>
445+
<form
446+
id='mcp-tool-deploy-form'
447+
className='-mx-1 space-y-4 overflow-y-auto px-1'
448+
onSubmit={(e) => {
449+
e.preventDefault()
450+
handleSave()
451+
}}
452+
>
453+
{/* Hidden submit button for parent modal to trigger */}
454+
<button type='submit' className='hidden' />
455+
397456
{servers.map((server) => (
398457
<ServerToolsQuery
399458
key={server.id}
@@ -501,6 +560,6 @@ export function McpToolDeploy({
501560
{addToolMutation.error?.message || 'Failed to add tool'}
502561
</p>
503562
)}
504-
</div>
563+
</form>
505564
)
506565
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/deploy-modal.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ export function DeployModal({
8888
const [showUndeployConfirm, setShowUndeployConfirm] = useState(false)
8989
const [templateFormValid, setTemplateFormValid] = useState(false)
9090
const [templateSubmitting, setTemplateSubmitting] = useState(false)
91+
const [mcpToolSubmitting, setMcpToolSubmitting] = useState(false)
92+
const [mcpToolCanSave, setMcpToolCanSave] = useState(false)
9193
const [hasExistingTemplate, setHasExistingTemplate] = useState(false)
9294
const [templateStatus, setTemplateStatus] = useState<{
9395
status: 'pending' | 'approved' | 'rejected' | null
@@ -531,6 +533,11 @@ export function DeployModal({
531533
form?.requestSubmit()
532534
}, [])
533535

536+
const handleMcpToolFormSubmit = useCallback(() => {
537+
const form = document.getElementById('mcp-tool-deploy-form') as HTMLFormElement
538+
form?.requestSubmit()
539+
}, [])
540+
534541
const handleTemplateDelete = useCallback(() => {
535542
const form = document.getElementById('template-deploy-form')
536543
const deleteTrigger = form?.querySelector('[data-template-delete-trigger]') as HTMLButtonElement
@@ -620,6 +627,8 @@ export function DeployModal({
620627
workflowName={workflowMetadata?.name || 'Workflow'}
621628
workflowDescription={workflowMetadata?.description}
622629
isDeployed={isDeployed}
630+
onSubmittingChange={setMcpToolSubmitting}
631+
onCanSaveChange={setMcpToolCanSave}
623632
/>
624633
)}
625634
</ModalTabsContent>
@@ -667,6 +676,18 @@ export function DeployModal({
667676
</div>
668677
</ModalFooter>
669678
)}
679+
{activeTab === 'mcp-tool' && isDeployed && (
680+
<ModalFooter className='items-center'>
681+
<Button
682+
type='button'
683+
variant='primary'
684+
onClick={handleMcpToolFormSubmit}
685+
disabled={mcpToolSubmitting || !mcpToolCanSave}
686+
>
687+
{mcpToolSubmitting ? 'Saving...' : 'Save Tool Schema'}
688+
</Button>
689+
</ModalFooter>
690+
)}
670691
{activeTab === 'template' && (
671692
<ModalFooter
672693
className={`items-center ${hasExistingTemplate && templateStatus ? 'justify-between' : ''}`}

0 commit comments

Comments
 (0)