73 lines
3.9 KiB
TypeScript
73 lines
3.9 KiB
TypeScript
import { test, expect, attachMcpConnector, createMcpConnector, FakeMcp, unique, AI_REPLY } from '@e2e'
|
|
|
|
test.describe('MCP connectors', () => {
|
|
test('an agent answers with a tool from a connected MCP server @mcp', async ({ agent, widget, llm, adminApi, request }) => {
|
|
const mcp = new FakeMcp(request)
|
|
const connector = await createMcpConnector(adminApi, { name: unique('orders'), url: mcp.url })
|
|
await attachMcpConnector(adminApi, connector.id, agent.id)
|
|
const orderId = unique('A')
|
|
await llm.script(agent, (s) => s
|
|
.when(/order/i).callTool('lookup_order', { order_id: orderId }).thenReply('Your order shipped on 2 May via DHL.')
|
|
.otherwise().reply('Happy to help!'))
|
|
|
|
await widget.embed()
|
|
await widget.open()
|
|
await widget.send(`Where is my order ${orderId}?`)
|
|
|
|
await expect(widget.lastAgentMessage()).toHaveText('Your order shipped on 2 May via DHL.', AI_REPLY)
|
|
// The answer came from the MCP server, through the backend, back to the model.
|
|
const toolResults = (await llm.requests(agent)).flatMap((r) => r.toolResults).join('\n')
|
|
expect(toolResults).toContain(`Order ${orderId}: shipped on 2 May via DHL`)
|
|
})
|
|
|
|
test('a connector\'s secrets are masked when read back, but still reach the server @mcp @security', async ({ agent, widget, llm, adminApi, request }) => {
|
|
const mcp = new FakeMcp(request)
|
|
const secret = `Bearer ${unique('mcp-secret')}`
|
|
const connector = await createMcpConnector(adminApi, { name: unique('orders'), url: mcp.url, headers: { Authorization: secret } })
|
|
|
|
expect(connector.headers).toEqual({ Authorization: '********' })
|
|
expect(JSON.stringify(await adminApi.get(`/mcp-tools/${connector.id}`))).not.toContain(secret)
|
|
|
|
// Saving the masked value back keeps the real one.
|
|
await adminApi.put(`/mcp-tools/${connector.id}`, { data: { headers: { Authorization: '********' } } })
|
|
await attachMcpConnector(adminApi, connector.id, agent.id)
|
|
await llm.script(agent, (s) => s.otherwise().callTool('lookup_order', { order_id: 'A1' }).thenReply('Checked.'))
|
|
await widget.embed()
|
|
await widget.open()
|
|
await widget.send('Where is my order?')
|
|
await expect(widget.lastAgentMessage()).toHaveText('Checked.', AI_REPLY)
|
|
expect((await mcp.requests()).map((r) => r.authorization)).toContain(secret)
|
|
})
|
|
|
|
// Nothing listens on port 1: the connection is refused at once.
|
|
const DEAD_SERVER = 'http://127.0.0.1:1/mcp'
|
|
|
|
test('a server that is down costs only its own tools, not the agent\'s other connectors @mcp', async ({ agent, widget, llm, adminApi, request }) => {
|
|
const mcp = new FakeMcp(request)
|
|
const down = await createMcpConnector(adminApi, { name: unique('down'), url: DEAD_SERVER })
|
|
const up = await createMcpConnector(adminApi, { name: unique('orders'), url: mcp.url })
|
|
await attachMcpConnector(adminApi, down.id, agent.id)
|
|
await attachMcpConnector(adminApi, up.id, agent.id)
|
|
const orderId = unique('A')
|
|
await llm.script(agent, (s) => s
|
|
.when(/order/i).callTool('lookup_order', { order_id: orderId }).thenReply('Your order shipped on 2 May via DHL.')
|
|
.otherwise().reply('Happy to help!'))
|
|
|
|
await widget.embed()
|
|
await widget.open()
|
|
await widget.send(`Where is my order ${orderId}?`)
|
|
|
|
await expect(widget.lastAgentMessage()).toHaveText('Your order shipped on 2 May via DHL.', AI_REPLY)
|
|
const toolResults = (await llm.requests(agent)).flatMap((r) => r.toolResults).join('\n')
|
|
expect(toolResults).toContain(`Order ${orderId}: shipped on 2 May via DHL`)
|
|
})
|
|
|
|
test('testing a server that is down says it could not connect @mcp', async ({ adminApi }) => {
|
|
const down = await createMcpConnector(adminApi, { name: unique('down'), url: DEAD_SERVER })
|
|
|
|
const result = await adminApi.post<{ success: boolean; error: string | null }>(`/mcp-tools/${down.id}/test`)
|
|
|
|
expect(result).toMatchObject({ success: false, error: expect.stringContaining('Could not connect to the server') })
|
|
})
|
|
})
|