Skip to content

Commit b842827

Browse files
author
Theodore Li
committed
Directly query db for custom tool id
1 parent 4544fd4 commit b842827

File tree

3 files changed

+41
-120
lines changed

3 files changed

+41
-120
lines changed

apps/sim/executor/handlers/agent/agent-handler.test.ts

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ vi.mock('@sim/db/schema', () => ({
110110
},
111111
}))
112112

113+
const mockGetCustomToolById = vi.fn()
114+
115+
vi.mock('@/lib/workflows/custom-tools/operations', () => ({
116+
getCustomToolById: (...args: unknown[]) => mockGetCustomToolById(...args),
117+
}))
118+
113119
setupGlobalFetchMock()
114120

115121
const mockGetAllBlocks = getAllBlocks as Mock
@@ -1957,49 +1963,22 @@ describe('AgentBlockHandler', () => {
19571963
const staleInlineCode = 'return { title, content };'
19581964
const dbCode = 'return { title, content, format };'
19591965

1960-
function mockFetchForCustomTool(toolId: string) {
1961-
mockFetch.mockImplementation((url: string) => {
1962-
if (typeof url === 'string' && url.includes('/api/tools/custom')) {
1966+
function mockDBForCustomTool(toolId: string) {
1967+
mockGetCustomToolById.mockImplementation(({ toolId: id }: { toolId: string }) => {
1968+
if (id === toolId) {
19631969
return Promise.resolve({
1964-
ok: true,
1965-
headers: { get: () => null },
1966-
json: () =>
1967-
Promise.resolve({
1968-
data: [
1969-
{
1970-
id: toolId,
1971-
title: 'formatReport',
1972-
schema: dbSchema,
1973-
code: dbCode,
1974-
},
1975-
],
1976-
}),
1970+
id: toolId,
1971+
title: 'formatReport',
1972+
schema: dbSchema,
1973+
code: dbCode,
19771974
})
19781975
}
1979-
return Promise.resolve({
1980-
ok: true,
1981-
headers: { get: () => null },
1982-
json: () => Promise.resolve({}),
1983-
})
1976+
return Promise.resolve(null)
19841977
})
19851978
}
19861979

1987-
function mockFetchFailure() {
1988-
mockFetch.mockImplementation((url: string) => {
1989-
if (typeof url === 'string' && url.includes('/api/tools/custom')) {
1990-
return Promise.resolve({
1991-
ok: false,
1992-
status: 500,
1993-
headers: { get: () => null },
1994-
json: () => Promise.resolve({}),
1995-
})
1996-
}
1997-
return Promise.resolve({
1998-
ok: true,
1999-
headers: { get: () => null },
2000-
json: () => Promise.resolve({}),
2001-
})
2002-
})
1980+
function mockDBFailure() {
1981+
mockGetCustomToolById.mockRejectedValue(new Error('DB connection failed'))
20031982
}
20041983

20051984
beforeEach(() => {
@@ -2008,11 +1987,12 @@ describe('AgentBlockHandler', () => {
20081987
writable: true,
20091988
configurable: true,
20101989
})
1990+
mockGetCustomToolById.mockReset()
20111991
})
20121992

20131993
it('should always fetch latest schema from DB when customToolId is present', async () => {
20141994
const toolId = 'custom-tool-123'
2015-
mockFetchForCustomTool(toolId)
1995+
mockDBForCustomTool(toolId)
20161996

20171997
const inputs = {
20181998
model: 'gpt-4o',
@@ -2046,7 +2026,7 @@ describe('AgentBlockHandler', () => {
20462026

20472027
it('should fetch from DB when customToolId has no inline schema', async () => {
20482028
const toolId = 'custom-tool-123'
2049-
mockFetchForCustomTool(toolId)
2029+
mockDBForCustomTool(toolId)
20502030

20512031
const inputs = {
20522032
model: 'gpt-4o',
@@ -2075,7 +2055,7 @@ describe('AgentBlockHandler', () => {
20752055
})
20762056

20772057
it('should fall back to inline schema when DB fetch fails and inline exists', async () => {
2078-
mockFetchFailure()
2058+
mockDBFailure()
20792059

20802060
const inputs = {
20812061
model: 'gpt-4o',
@@ -2107,7 +2087,7 @@ describe('AgentBlockHandler', () => {
21072087
})
21082088

21092089
it('should return null when DB fetch fails and no inline schema exists', async () => {
2110-
mockFetchFailure()
2090+
mockDBFailure()
21112091

21122092
const inputs = {
21132093
model: 'gpt-4o',
@@ -2135,7 +2115,7 @@ describe('AgentBlockHandler', () => {
21352115

21362116
it('should use DB schema when customToolId resolves', async () => {
21372117
const toolId = 'custom-tool-123'
2138-
mockFetchForCustomTool(toolId)
2118+
mockDBForCustomTool(toolId)
21392119

21402120
const inputs = {
21412121
model: 'gpt-4o',
@@ -2185,10 +2165,7 @@ describe('AgentBlockHandler', () => {
21852165

21862166
await handler.execute(mockContext, mockBlock, inputs)
21872167

2188-
const customToolFetches = mockFetch.mock.calls.filter(
2189-
(call: any[]) => typeof call[0] === 'string' && call[0].includes('/api/tools/custom')
2190-
)
2191-
expect(customToolFetches.length).toBe(0)
2168+
expect(mockGetCustomToolById).not.toHaveBeenCalled()
21922169

21932170
expect(mockExecuteProviderRequest).toHaveBeenCalled()
21942171
const providerCall = mockExecuteProviderRequest.mock.calls[0]

apps/sim/executor/handlers/agent/agent-handler.ts

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { mcpServers } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
44
import { and, eq, inArray, isNull } from 'drizzle-orm'
55
import { createMcpToolId } from '@/lib/mcp/utils'
6+
import { getCustomToolById } from '@/lib/workflows/custom-tools/operations'
67
import { getAllBlocks } from '@/blocks'
78
import type { BlockOutput } from '@/blocks/types'
89
import {
@@ -289,38 +290,12 @@ export class AgentBlockHandler implements BlockHandler {
289290
}
290291

291292
try {
292-
const headers = await buildAuthHeaders(ctx.userId)
293-
const params: Record<string, string> = {}
294-
295-
if (ctx.workspaceId) {
296-
params.workspaceId = ctx.workspaceId
297-
}
298-
if (ctx.workflowId) {
299-
params.workflowId = ctx.workflowId
300-
}
301-
if (ctx.userId) {
302-
params.userId = ctx.userId
303-
}
304-
305-
const url = buildAPIUrl('/api/tools/custom', params)
306-
const response = await fetch(url.toString(), {
307-
method: 'GET',
308-
headers,
293+
const tool = await getCustomToolById({
294+
toolId: customToolId,
295+
userId: ctx.userId,
296+
workspaceId: ctx.workspaceId,
309297
})
310298

311-
if (!response.ok) {
312-
await response.text().catch(() => {})
313-
logger.error(`Failed to fetch custom tools: ${response.status}`)
314-
return null
315-
}
316-
317-
const data = await response.json()
318-
if (!data.data || !Array.isArray(data.data)) {
319-
logger.error('Invalid custom tools API response')
320-
return null
321-
}
322-
323-
const tool = data.data.find((t: any) => t.id === customToolId)
324299
if (!tool) {
325300
logger.warn(`Custom tool not found by ID: ${customToolId}`)
326301
return null

apps/sim/tools/utils.ts

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createLogger } from '@sim/logger'
22
import { getMaxExecutionTimeout } from '@/lib/core/execution-limits'
3-
import { getInternalApiBaseUrl } from '@/lib/core/utils/urls'
3+
import { getCustomToolById, listCustomTools } from '@/lib/workflows/custom-tools/operations'
44
import { AGENT, isCustomTool } from '@/executor/constants'
55
import { getCustomTool } from '@/hooks/queries/custom-tools'
66
import { useEnvironmentStore } from '@/stores/settings/environment'
@@ -320,7 +320,7 @@ export async function getToolAsync(
320320

321321
// Check if it's a custom tool
322322
if (isCustomTool(toolId)) {
323-
return fetchCustomToolFromAPI(toolId, workflowId, userId)
323+
return fetchCustomToolFromDB(toolId, workflowId, userId)
324324
}
325325

326326
return undefined
@@ -364,60 +364,29 @@ function createToolConfig(customTool: any, customToolId: string): ToolConfig {
364364
}
365365
}
366366

367-
// Create a tool config from a custom tool definition by fetching from API
368-
async function fetchCustomToolFromAPI(
367+
// Create a tool config from a custom tool definition by querying the database directly
368+
async function fetchCustomToolFromDB(
369369
customToolId: string,
370370
workflowId?: string,
371371
userId?: string
372372
): Promise<ToolConfig | undefined> {
373373
const identifier = customToolId.replace('custom_', '')
374374

375375
try {
376-
const baseUrl = getInternalApiBaseUrl()
377-
const url = new URL('/api/tools/custom', baseUrl)
378-
379-
if (workflowId) {
380-
url.searchParams.append('workflowId', workflowId)
381-
}
382-
if (userId) {
383-
url.searchParams.append('userId', userId)
384-
}
385-
386-
// For server-side calls (during workflow execution), use internal JWT token
387-
const headers: Record<string, string> = {}
388-
if (typeof window === 'undefined') {
389-
try {
390-
const { generateInternalToken } = await import('@/lib/auth/internal')
391-
const internalToken = await generateInternalToken(userId)
392-
headers.Authorization = `Bearer ${internalToken}`
393-
} catch (error) {
394-
logger.warn('Failed to generate internal token for custom tools fetch', { error })
395-
// Continue without token - will fail auth and be reported upstream
396-
}
397-
}
398-
399-
const response = await fetch(url.toString(), {
400-
headers,
401-
})
402-
403-
if (!response.ok) {
404-
await response.text().catch(() => {})
405-
logger.error(`Failed to fetch custom tools: ${response.statusText}`)
376+
if (!userId) {
377+
logger.error(`Cannot fetch custom tool without userId: ${identifier}`)
406378
return undefined
407379
}
408380

409-
const result = await response.json()
381+
// Try to find by ID first
382+
let customTool = await getCustomToolById({ toolId: identifier, userId })
410383

411-
if (!result.data || !Array.isArray(result.data)) {
412-
logger.error(`Invalid response when fetching custom tools: ${JSON.stringify(result)}`)
413-
return undefined
384+
// Fall back to searching by title
385+
if (!customTool) {
386+
const allTools = await listCustomTools({ userId })
387+
customTool = allTools.find((t) => t.title === identifier) ?? null
414388
}
415389

416-
// Try to find the tool by ID or title
417-
const customTool = result.data.find(
418-
(tool: any) => tool.id === identifier || tool.title === identifier
419-
)
420-
421390
if (!customTool) {
422391
logger.error(`Custom tool not found: ${identifier}`)
423392
return undefined
@@ -458,7 +427,7 @@ async function fetchCustomToolFromAPI(
458427
},
459428
}
460429
} catch (error) {
461-
logger.error(`Error fetching custom tool ${identifier} from API:`, error)
430+
logger.error(`Error fetching custom tool ${identifier} from DB:`, error)
462431
return undefined
463432
}
464433
}

0 commit comments

Comments
 (0)