feat(ai): add mcp integration (#13004)

This commit is contained in:
Antoine Moreaux
2025-07-03 21:23:58 +02:00
committed by GitHub
parent bc94d58af7
commit e5522c8efe
33 changed files with 1888 additions and 1332 deletions

View File

@ -62,15 +62,13 @@ describe('AgentToolService Integration', () => {
);
expect(tools).toBeDefined();
expect(Object.keys(tools)).toHaveLength(8);
expect(Object.keys(tools)).toHaveLength(6);
expect(Object.keys(tools)).toContain('create_testObject');
expect(Object.keys(tools)).toContain('update_testObject');
expect(Object.keys(tools)).toContain('find_testObject');
expect(Object.keys(tools)).toContain('find_one_testObject');
expect(Object.keys(tools)).toContain('soft_delete_testObject');
expect(Object.keys(tools)).toContain('soft_delete_many_testObject');
expect(Object.keys(tools)).toContain('destroy_testObject');
expect(Object.keys(tools)).toContain('destroy_many_testObject');
});
it('should generate read-only tools for agent with read permissions only', async () => {
@ -646,156 +644,6 @@ describe('AgentToolService Integration', () => {
});
});
describe('Destroy Operations', () => {
it('should destroy a single record', async () => {
const roleWithDestroyPermission = {
...context.testRole,
canDestroyAllObjectRecords: true,
};
const mockRepository = createMockRepository();
const existingRecord = createTestRecord('test-record-id', {
name: 'Test Record',
});
jest
.spyOn(context.agentService, 'findOneAgent')
.mockResolvedValue(context.testAgent as any);
jest
.spyOn(context.roleRepository, 'findOne')
.mockResolvedValue(roleWithDestroyPermission);
jest
.spyOn(
context.workspacePermissionsCacheService,
'getRolesPermissionsFromCache',
)
.mockResolvedValue({
data: {
[context.testRoleId]: {
[context.testObjectMetadata.id]: {
canRead: true,
canUpdate: true,
canSoftDelete: true,
canDestroy: true,
},
},
},
version: '1.0',
});
jest
.spyOn(context.objectMetadataService, 'findManyWithinWorkspace')
.mockResolvedValue([context.testObjectMetadata]);
setupRepositoryMock(context, mockRepository);
mockRepository.findOne.mockResolvedValue(existingRecord);
mockRepository.remove.mockResolvedValue(existingRecord);
const tools = await context.agentToolService.generateToolsForAgent(
context.testAgentId,
context.testWorkspaceId,
);
const destroyTool = tools['destroy_testObject'];
expect(destroyTool).toBeDefined();
if (!destroyTool.execute) {
throw new Error(
'Destroy tool is missing or does not have an execute method',
);
}
const result = await destroyTool.execute(
{ id: 'test-record-id' },
{
toolCallId: 'test-tool-call-id',
messages: [
{
role: 'user',
content: 'Destroy record',
},
],
},
);
expectSuccessResult(result, 'Successfully destroyed testObject');
expect(mockRepository.remove).toHaveBeenCalledWith(existingRecord);
});
it('should destroy multiple records', async () => {
const roleWithDestroyPermission = {
...context.testRole,
canDestroyAllObjectRecords: true,
};
const mockRepository = createMockRepository();
const existingRecords = createTestRecords(3);
jest
.spyOn(context.agentService, 'findOneAgent')
.mockResolvedValue(context.testAgent as any);
jest
.spyOn(context.roleRepository, 'findOne')
.mockResolvedValue(roleWithDestroyPermission);
jest
.spyOn(
context.workspacePermissionsCacheService,
'getRolesPermissionsFromCache',
)
.mockResolvedValue({
data: {
[context.testRoleId]: {
[context.testObjectMetadata.id]: {
canRead: true,
canUpdate: true,
canSoftDelete: true,
canDestroy: true,
},
},
},
version: '1.0',
});
jest
.spyOn(context.objectMetadataService, 'findManyWithinWorkspace')
.mockResolvedValue([context.testObjectMetadata]);
setupRepositoryMock(context, mockRepository);
mockRepository.find.mockResolvedValue(existingRecords);
mockRepository.remove.mockResolvedValue(existingRecords);
const tools = await context.agentToolService.generateToolsForAgent(
context.testAgentId,
context.testWorkspaceId,
);
const destroyManyTool = tools['destroy_many_testObject'];
expect(destroyManyTool).toBeDefined();
if (!destroyManyTool.execute) {
throw new Error(
'Destroy many tool is missing or does not have an execute method',
);
}
const result = await destroyManyTool.execute(
{
filter: { id: { in: ['record-1', 'record-2', 'record-3'] } },
},
{
toolCallId: 'test-tool-call-id',
messages: [
{
role: 'user',
content: 'Destroy many records',
},
],
},
);
expectSuccessResult(
result,
'Successfully destroyed 3 testObject records',
);
});
});
describe('Edge Cases', () => {
it('should handle empty search criteria in find records', async () => {
const mockRepository = createMockRepository();

View File

@ -12,6 +12,7 @@ import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
import { ToolService } from 'src/engine/core-modules/ai/services/tool.service';
export interface AgentToolTestContext {
module: TestingModule;
@ -77,6 +78,10 @@ export const createAgentToolTestModule =
getRolesPermissionsFromCache: jest.fn(),
},
},
{
provide: ToolService,
useClass: ToolService,
},
],
}).compile();