Skip to content

Commit 13976a2

Browse files
committed
added tests
1 parent f84d3ce commit 13976a2

2 files changed

Lines changed: 665 additions & 8 deletions

File tree

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

Lines changed: 337 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { beforeEach, describe, expect, it, type Mock, vi } from 'vitest'
1+
import { afterEach, beforeEach, describe, expect, it, type Mock, vi } from 'vitest'
22
import { isHosted } from '@/lib/environment'
33
import { getAllBlocks } from '@/blocks'
44
import { BlockType } from '@/executor/consts'
@@ -1380,5 +1380,341 @@ describe('AgentBlockHandler', () => {
13801380
expect(requestBody.provider).toBe('openai')
13811381
expect(requestBody.model).toBe('gpt-5')
13821382
})
1383+
1384+
it('should handle MCP tools in agent execution', async () => {
1385+
mockExecuteTool.mockImplementation((toolId, params, skipProxy, skipPostProcess, context) => {
1386+
if (toolId.startsWith('mcp-')) {
1387+
return Promise.resolve({
1388+
success: true,
1389+
output: {
1390+
content: [
1391+
{
1392+
type: 'text',
1393+
text: `MCP tool ${toolId} executed with params: ${JSON.stringify(params)}`,
1394+
},
1395+
],
1396+
},
1397+
})
1398+
}
1399+
return Promise.resolve({ success: false, error: 'Unknown tool' })
1400+
})
1401+
1402+
mockFetch.mockImplementationOnce(() => {
1403+
return Promise.resolve({
1404+
ok: true,
1405+
headers: {
1406+
get: (name: string) => {
1407+
if (name === 'Content-Type') return 'application/json'
1408+
if (name === 'X-Execution-Data') return null
1409+
return null
1410+
},
1411+
},
1412+
json: () =>
1413+
Promise.resolve({
1414+
content: 'I will use MCP tools to help you.',
1415+
model: 'gpt-4o',
1416+
tokens: { prompt: 15, completion: 25, total: 40 },
1417+
toolCalls: [
1418+
{
1419+
name: 'mcp-server1-list_files',
1420+
arguments: { path: '/tmp' },
1421+
result: {
1422+
success: true,
1423+
output: { content: [{ type: 'text', text: 'Files listed' }] },
1424+
},
1425+
},
1426+
{
1427+
name: 'mcp-server2-search',
1428+
arguments: { query: 'test', limit: 5 },
1429+
result: {
1430+
success: true,
1431+
output: { content: [{ type: 'text', text: 'Search results' }] },
1432+
},
1433+
},
1434+
],
1435+
timing: { total: 150 },
1436+
}),
1437+
})
1438+
})
1439+
1440+
const inputs = {
1441+
model: 'gpt-4o',
1442+
userPrompt: 'List files and search for test data',
1443+
apiKey: 'test-api-key',
1444+
tools: [
1445+
{
1446+
type: 'mcp',
1447+
title: 'List Files',
1448+
schema: {
1449+
function: {
1450+
name: 'mcp-server1-list_files',
1451+
description: 'List files in directory',
1452+
parameters: {
1453+
type: 'object',
1454+
properties: {
1455+
path: { type: 'string', description: 'Directory path' },
1456+
},
1457+
},
1458+
},
1459+
},
1460+
usageControl: 'auto' as const,
1461+
},
1462+
{
1463+
type: 'mcp',
1464+
title: 'Search',
1465+
schema: {
1466+
function: {
1467+
name: 'mcp-server2-search',
1468+
description: 'Search for data',
1469+
parameters: {
1470+
type: 'object',
1471+
properties: {
1472+
query: { type: 'string', description: 'Search query' },
1473+
limit: { type: 'number', description: 'Result limit' },
1474+
},
1475+
},
1476+
},
1477+
},
1478+
usageControl: 'auto' as const,
1479+
},
1480+
],
1481+
}
1482+
1483+
const mcpContext = {
1484+
...mockContext,
1485+
workspaceId: 'test-workspace-123',
1486+
}
1487+
1488+
mockGetProviderFromModel.mockReturnValue('openai')
1489+
1490+
const result = await handler.execute(mockBlock, inputs, mcpContext)
1491+
1492+
expect((result as any).content).toBe('I will use MCP tools to help you.')
1493+
expect((result as any).toolCalls.count).toBe(2)
1494+
expect((result as any).toolCalls.list).toHaveLength(2)
1495+
1496+
expect((result as any).toolCalls.list[0].name).toBe('mcp-server1-list_files')
1497+
expect((result as any).toolCalls.list[0].result.success).toBe(true)
1498+
expect((result as any).toolCalls.list[1].name).toBe('mcp-server2-search')
1499+
expect((result as any).toolCalls.list[1].result.success).toBe(true)
1500+
})
1501+
1502+
it('should handle MCP tool execution errors', async () => {
1503+
mockExecuteTool.mockImplementation((toolId, params) => {
1504+
if (toolId === 'mcp-server1-failing_tool') {
1505+
return Promise.resolve({
1506+
success: false,
1507+
error: 'MCP server connection failed',
1508+
})
1509+
}
1510+
return Promise.resolve({ success: false, error: 'Unknown tool' })
1511+
})
1512+
1513+
mockFetch.mockImplementationOnce(() => {
1514+
return Promise.resolve({
1515+
ok: true,
1516+
headers: {
1517+
get: (name: string) => {
1518+
if (name === 'Content-Type') return 'application/json'
1519+
if (name === 'X-Execution-Data') return null
1520+
return null
1521+
},
1522+
},
1523+
json: () =>
1524+
Promise.resolve({
1525+
content: 'Let me try to use this tool.',
1526+
model: 'gpt-4o',
1527+
tokens: { prompt: 10, completion: 15, total: 25 },
1528+
toolCalls: [
1529+
{
1530+
name: 'mcp-server1-failing_tool',
1531+
arguments: { param: 'value' },
1532+
result: {
1533+
success: false,
1534+
error: 'MCP server connection failed',
1535+
},
1536+
},
1537+
],
1538+
timing: { total: 100 },
1539+
}),
1540+
})
1541+
})
1542+
1543+
const inputs = {
1544+
model: 'gpt-4o',
1545+
userPrompt: 'Try to use the failing tool',
1546+
apiKey: 'test-api-key',
1547+
tools: [
1548+
{
1549+
type: 'mcp',
1550+
title: 'Failing Tool',
1551+
schema: {
1552+
function: {
1553+
name: 'mcp-server1-failing_tool',
1554+
description: 'A tool that will fail',
1555+
parameters: {
1556+
type: 'object',
1557+
properties: {
1558+
param: { type: 'string' },
1559+
},
1560+
},
1561+
},
1562+
},
1563+
usageControl: 'auto' as const,
1564+
},
1565+
],
1566+
}
1567+
1568+
const mcpContext = {
1569+
...mockContext,
1570+
workspaceId: 'test-workspace-123',
1571+
}
1572+
1573+
mockGetProviderFromModel.mockReturnValue('openai')
1574+
1575+
const result = await handler.execute(mockBlock, inputs, mcpContext)
1576+
1577+
expect((result as any).content).toBe('Let me try to use this tool.')
1578+
expect((result as any).toolCalls.count).toBe(1)
1579+
expect((result as any).toolCalls.list[0].result.success).toBe(false)
1580+
expect((result as any).toolCalls.list[0].result.error).toBe('MCP server connection failed')
1581+
})
1582+
1583+
it('should transform MCP tools correctly for agent execution', async () => {
1584+
const inputs = {
1585+
model: 'gpt-4o',
1586+
userPrompt: 'Use MCP tools to help me',
1587+
apiKey: 'test-api-key',
1588+
tools: [
1589+
{
1590+
type: 'mcp',
1591+
title: 'Read File',
1592+
schema: {
1593+
function: {
1594+
name: 'mcp-filesystem-read_file',
1595+
description: 'Read file from filesystem',
1596+
parameters: { type: 'object', properties: {} },
1597+
},
1598+
},
1599+
usageControl: 'auto' as const,
1600+
},
1601+
{
1602+
type: 'mcp',
1603+
title: 'Web Search',
1604+
schema: {
1605+
function: {
1606+
name: 'mcp-web-search',
1607+
description: 'Search the web',
1608+
parameters: { type: 'object', properties: {} },
1609+
},
1610+
},
1611+
usageControl: 'force' as const,
1612+
},
1613+
],
1614+
}
1615+
1616+
mockGetProviderFromModel.mockReturnValue('openai')
1617+
1618+
mockFetch.mockImplementationOnce(() => {
1619+
return Promise.resolve({
1620+
ok: true,
1621+
headers: {
1622+
get: (name: string) => {
1623+
if (name === 'Content-Type') return 'application/json'
1624+
if (name === 'X-Execution-Data') return null
1625+
return null
1626+
},
1627+
},
1628+
json: () =>
1629+
Promise.resolve({
1630+
content: 'Used MCP tools successfully',
1631+
model: 'gpt-4o',
1632+
tokens: { prompt: 20, completion: 30, total: 50 },
1633+
toolCalls: [],
1634+
timing: { total: 200 },
1635+
}),
1636+
})
1637+
})
1638+
1639+
mockTransformBlockTool.mockImplementation((tool: any) => ({
1640+
id: tool.schema?.function?.name || `mcp-${tool.title.toLowerCase().replace(' ', '-')}`,
1641+
name: tool.schema?.function?.name || tool.title,
1642+
description: tool.schema?.function?.description || `MCP tool: ${tool.title}`,
1643+
parameters: tool.schema?.function?.parameters || { type: 'object', properties: {} },
1644+
usageControl: tool.usageControl,
1645+
}))
1646+
1647+
const result = await handler.execute(mockBlock, inputs, mockContext)
1648+
1649+
// Verify that the agent executed successfully with MCP tools
1650+
expect(result).toBeDefined()
1651+
expect(mockFetch).toHaveBeenCalled()
1652+
1653+
// Verify the agent returns the expected response format
1654+
expect((result as any).content).toBe('Used MCP tools successfully')
1655+
expect((result as any).model).toBe('gpt-4o')
1656+
})
1657+
1658+
it('should provide workspaceId context for MCP tool execution', async () => {
1659+
let capturedContext: any
1660+
mockExecuteTool.mockImplementation((toolId, params, skipProxy, skipPostProcess, context) => {
1661+
capturedContext = context
1662+
if (toolId.startsWith('mcp-')) {
1663+
return Promise.resolve({
1664+
success: true,
1665+
output: { content: [{ type: 'text', text: 'Success' }] },
1666+
})
1667+
}
1668+
return Promise.resolve({ success: false, error: 'Unknown tool' })
1669+
})
1670+
1671+
mockFetch.mockImplementationOnce(() => {
1672+
return Promise.resolve({
1673+
ok: true,
1674+
headers: {
1675+
get: (name: string) => (name === 'Content-Type' ? 'application/json' : null),
1676+
},
1677+
json: () =>
1678+
Promise.resolve({
1679+
content: 'Using MCP tool',
1680+
model: 'gpt-4o',
1681+
tokens: { prompt: 10, completion: 10, total: 20 },
1682+
toolCalls: [{ name: 'mcp-test-tool', arguments: {} }],
1683+
timing: { total: 50 },
1684+
}),
1685+
})
1686+
})
1687+
1688+
const inputs = {
1689+
model: 'gpt-4o',
1690+
userPrompt: 'Test MCP context',
1691+
apiKey: 'test-api-key',
1692+
tools: [
1693+
{
1694+
type: 'mcp',
1695+
title: 'Test Tool',
1696+
schema: {
1697+
function: {
1698+
name: 'mcp-test-tool',
1699+
description: 'Test MCP tool',
1700+
parameters: { type: 'object', properties: {} },
1701+
},
1702+
},
1703+
usageControl: 'auto' as const,
1704+
},
1705+
],
1706+
}
1707+
1708+
const contextWithWorkspace = {
1709+
...mockContext,
1710+
workspaceId: 'test-workspace-456',
1711+
}
1712+
1713+
mockGetProviderFromModel.mockReturnValue('openai')
1714+
1715+
await handler.execute(mockBlock, inputs, contextWithWorkspace)
1716+
1717+
expect(contextWithWorkspace.workspaceId).toBe('test-workspace-456')
1718+
})
13831719
})
13841720
})

0 commit comments

Comments
 (0)