import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SimplifierClient } from "../../src/client/simplifier-client.js";
import { registerConnectorTools } from "../../src/tools/connector-tools.js";
import { wrapToolResult } from "../../src/tools/toolresult.js";
import { SimplifierConnectorDetails, SimplifierConnectorUpdate, SimplifierConnectorCallUpdate, SimplifierConnectorCallDetails, ConnectorTestResponse } from "../../src/client/types.js";
import { readFile } from "../../src/resourceprovider.js";
// Mock the wrapToolResult function
jest.mock("../../src/tools/toolresult.js", () => ({
wrapToolResult: jest.fn()
}));
// Mock the resourceprovider
jest.mock("../../src/resourceprovider.js", () => ({
readFile: jest.fn()
}));
describe('registerConnectorTools', () => {
let mockServer: jest.Mocked<McpServer>;
let mockSimplifierClient: jest.Mocked<SimplifierClient>;
let mockWrapToolResult: jest.MockedFunction<typeof wrapToolResult>;
let mockReadFile: jest.MockedFunction<typeof readFile>;
// Helper function to find a tool registration by name
const findToolByName = (toolName: string) => {
const call = mockServer.registerTool.mock.calls.find(call => call[0] === toolName);
if (!call) {
throw new Error(`Tool '${toolName}' not found in registered tools`);
}
return {
name: call[0],
description: call[1].description,
schema: call[1].inputSchema,
metadata: call[1].annotations,
handler: call[2]
};
};
beforeEach(() => {
// Create a mock McpServer
mockServer = {
tool: jest.fn(),
registerTool: jest.fn(),
} as any;
// Create a mock SimplifierClient
mockSimplifierClient = {
getConnector: jest.fn(),
createConnector: jest.fn(),
updateConnector: jest.fn(),
getConnectorCall: jest.fn(),
createConnectorCall: jest.fn(),
updateConnectorCall: jest.fn(),
testConnectorCall: jest.fn(),
deleteConnectorCall: jest.fn(),
deleteConnector: jest.fn(),
viewRFCFunctions: jest.fn(),
rfcWizardGetCallDetails: jest.fn(),
rfcWizardCreateCalls: jest.fn()
} as any;
// Get the mocked functions
mockWrapToolResult = wrapToolResult as jest.MockedFunction<typeof wrapToolResult>;
mockReadFile = readFile as jest.MockedFunction<typeof readFile>;
// Setup default mock for readFile
mockReadFile.mockReturnValue("This is the connector documentation content");
// Clear all mocks
jest.clearAllMocks();
});
describe('function registration', () => {
it('should register all six connector tools', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
expect(mockServer.registerTool).toHaveBeenCalledTimes(6);
// Check the connector-update tool registration
const connectorUpdate = findToolByName("connector-update");
expect(connectorUpdate.description).toContain("Create or update a Connector");
expect(connectorUpdate.schema).toMatchObject({
name: expect.any(Object),
description: expect.any(Object),
tags: expect.any(Object),
projectsBefore: expect.any(Object),
projectsAfterChange: expect.any(Object),
connectorType: expect.any(Object),
active: expect.any(Object),
endpointConfiguration: expect.any(Object)
});
expect(connectorUpdate.metadata).toMatchObject({
title: "Create or update a Connector",
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: true
});
// Check the connector-call-update tool registration
const connectorCallUpdate = findToolByName("connector-call-update");
expect(connectorCallUpdate.description).toContain("Create or update a Connector call");
expect(connectorCallUpdate.schema).toMatchObject({
connectorName: expect.any(Object),
connectorCallName: expect.any(Object),
description: expect.any(Object),
validateIn: expect.any(Object),
validateOut: expect.any(Object),
async: expect.any(Object),
autoGenerated: expect.any(Object),
connectorCallParameters: expect.any(Object)
});
expect(connectorCallUpdate.metadata).toMatchObject({
title: "Create or update a Connector Call",
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: true
});
// Check connector-call-test tool registration
const connectorCallTest = findToolByName("connector-call-test");
expect(connectorCallTest.schema).toMatchObject({
connectorName: expect.any(Object),
callName: expect.any(Object),
parameters: expect.any(Object)
});
expect(connectorCallTest.metadata).toMatchObject({
title: "Test a Connector Call",
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true
});
// Check connector-call-delete tool registration
const connectorCallDelete = findToolByName("connector-call-delete");
expect(connectorCallDelete.schema).toMatchObject({
connectorName: expect.any(Object),
callName: expect.any(Object)
});
expect(connectorCallDelete.metadata).toMatchObject({
title: "Delete a Connector Call",
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: true
});
// Check connector-delete tool registration
const connectorDelete = findToolByName("connector-delete");
expect(connectorDelete.schema).toMatchObject({
connectorName: expect.any(Object)
});
expect(connectorDelete.metadata).toMatchObject({
title: "Delete a Connector",
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: true
});
});
});
describe('connector-update tool', () => {
describe('schema validation', () => {
it('should validate required schema fields', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-update");
const schema = tool.schema!;
// Test that schema validates required fields
expect(schema.name).toBeDefined();
expect(schema.description).toBeDefined();
expect(schema.tags).toBeDefined();
expect(schema.projectsBefore).toBeDefined();
expect(schema.projectsAfterChange).toBeDefined();
expect(schema.connectorType).toBeDefined();
expect(schema.active).toBeDefined();
expect(schema.endpointConfiguration).toBeDefined();
// Test valid data passes validation - each field individually
const validName = "TestConnector";
const validDescription = "Test connector description";
const validTags = ["test", "connector"];
const validProjectsBefore = ["Project1"];
const validProjectsAfterChange = ["Project1", "Project2"];
const validConnectorType = "REST";
const validActive = true;
const validEndpointConfiguration = {
endpoint: "https://api.example.com",
certificates: [],
configuration: {},
loginMethodName: "basicAuth"
};
expect(() => schema.name.parse(validName)).not.toThrow();
expect(() => schema.description.parse(validDescription)).not.toThrow();
expect(() => schema.tags.parse(validTags)).not.toThrow();
expect(() => schema.projectsBefore.parse(validProjectsBefore)).not.toThrow();
expect(() => schema.projectsAfterChange.parse(validProjectsAfterChange)).not.toThrow();
expect(() => schema.connectorType.parse(validConnectorType)).not.toThrow();
expect(() => schema.active.parse(validActive)).not.toThrow();
expect(() => schema.endpointConfiguration.parse(validEndpointConfiguration)).not.toThrow();
});
it('should validate that name and connectorType are required', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-update");
const schema = tool.schema!;
// Test that valid string passes
expect(() => schema.name.parse("ValidName")).not.toThrow();
expect(() => schema.connectorType.parse("REST")).not.toThrow();
// Test that undefined fails validation for required fields
expect(() => schema.name.parse(undefined)).toThrow();
expect(() => schema.connectorType.parse(undefined)).toThrow();
// Test that null fails validation
expect(() => schema.name.parse(null)).toThrow();
expect(() => schema.connectorType.parse(null)).toThrow();
});
it('should allow optional fields to be omitted with defaults', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-update");
const schema = tool.schema!;
// Test that optional fields can be undefined and get defaults
expect(() => schema.description.parse(undefined)).not.toThrow();
expect(() => schema.tags.parse(undefined)).not.toThrow();
expect(() => schema.projectsBefore.parse(undefined)).not.toThrow();
expect(() => schema.projectsAfterChange.parse(undefined)).not.toThrow();
expect(() => schema.active.parse(undefined)).not.toThrow();
// Test default values are applied
expect(schema.description.parse(undefined)).toBe("");
expect(schema.tags.parse(undefined)).toEqual([]);
expect(schema.projectsBefore.parse(undefined)).toEqual([]);
expect(schema.projectsAfterChange.parse(undefined)).toEqual([]);
expect(schema.active.parse(undefined)).toBe(true);
});
it('should validate endpointConfiguration structure', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-update");
const schema = tool.schema!;
// Valid endpoint configuration
const validConfig = {
endpoint: "https://api.example.com",
certificates: ["cert1", "cert2"],
configuration: { key: "value" },
loginMethodName: "oauth2"
};
expect(() => schema.endpointConfiguration.parse(validConfig)).not.toThrow();
// Minimal valid configuration (only required fields)
const minimalConfig = {
endpoint: "https://api.example.com",
certificates: []
};
expect(() => schema.endpointConfiguration.parse(minimalConfig)).not.toThrow();
// Invalid configuration (missing required endpoint)
const invalidConfig = {
certificates: []
};
expect(() => schema.endpointConfiguration.parse(invalidConfig)).toThrow();
});
});
describe('tool handler - create new connector', () => {
let toolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
toolHandler = findToolByName("connector-update").handler;
});
it('should create a new connector when it does not exist', async () => {
const testParams = {
name: "NewConnector",
description: "New connector description",
connectorType: "REST",
active: true,
endpointConfiguration: {
endpoint: "https://api.example.com",
certificates: [],
configuration: {
specificConfig1: "TEST",
specificConfig2: "TEST"
},
loginMethodName: "basicAuth"
},
tags: ["new", "test"],
projectsBefore: [],
projectsAfterChange: ["Project1"]
};
const expectedData: SimplifierConnectorUpdate = {
name: "NewConnector",
description: "New connector description",
connectorType: "REST",
active: true,
endpointConfiguration: {
endpoint: "https://api.example.com",
certificates: [],
configuration: {
specificConfig1: "TEST",
specificConfig2: "TEST"
},
loginMethodName: "basicAuth"
},
tags: ["new", "test"],
assignedProjects: {
projectsBefore: [],
projectsAfterChange: ["Project1"]
},
timeoutTime: 0 // Will be added by the actual implementation if needed
};
const expectedResponse = "Connector created successfully";
// Mock that connector doesn't exist (throws error)
mockSimplifierClient.getConnector.mockRejectedValue(
new Error("Not found")
);
// Mock successful creation
mockSimplifierClient.createConnector.mockResolvedValue(expectedResponse);
// Mock wrapToolResult to call the function and return result
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("NewConnector", "MCP Tool: connector-update");
expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith(expect.objectContaining({
name: expectedData.name,
description: expectedData.description,
connectorType: expectedData.connectorType,
active: expectedData.active,
endpointConfiguration: expectedData.endpointConfiguration,
tags: expectedData.tags,
assignedProjects: expectedData.assignedProjects
}));
expect(mockSimplifierClient.updateConnector).not.toHaveBeenCalled();
expect(mockWrapToolResult).toHaveBeenCalledWith(
"create or update Connector NewConnector",
expect.any(Function)
);
});
it('should update existing connector when it exists', async () => {
const testParams = {
name: "ExistingConnector",
description: "Updated connector description",
connectorType: "SOAP",
active: false,
endpointConfiguration: {
endpoint: "https://api.updated.com",
certificates: ["cert1"],
configuration: { timeout: 5000 }
},
tags: ["updated"],
projectsBefore: ["Project1"],
projectsAfterChange: ["Project1", "Project2"]
};
const existingConnector: SimplifierConnectorDetails = {
name: "ExistingConnector",
description: "Old description",
connectorType: {
technicalName: "REST",
i18n: "REST Connector",
descriptionI18n: "REST API Connector"
},
active: true,
timeoutTime: 30000,
amountOfCalls: 10,
editable: true,
deletable: true,
tags: ["old"],
assignedProjects: {
projectsBefore: ["Project1"],
projectsAfterChange: ["Project1"]
},
configuration: {
endpoints: [{
endpoint: "https://api.old.com",
certificates: [],
configuration: {}
}]
}
};
const expectedResponse = "Connector updated successfully";
// Mock that connector exists
mockSimplifierClient.getConnector.mockResolvedValue(existingConnector);
// Mock successful update
mockSimplifierClient.updateConnector.mockResolvedValue(expectedResponse);
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("ExistingConnector", "MCP Tool: connector-update");
expect(mockSimplifierClient.updateConnector).toHaveBeenCalledWith(expect.objectContaining({
name: "ExistingConnector",
description: "Updated connector description",
connectorType: "SOAP",
active: false,
endpointConfiguration: testParams.endpointConfiguration,
tags: ["updated"],
assignedProjects: {
projectsBefore: ["Project1"],
projectsAfterChange: ["Project1", "Project2"]
}
}));
expect(mockSimplifierClient.createConnector).not.toHaveBeenCalled();
expect(mockWrapToolResult).toHaveBeenCalledWith(
"create or update Connector ExistingConnector",
expect.any(Function)
);
});
it('should handle errors gracefully', async () => {
const testParams = {
name: "ErrorConnector",
description: "Test error handling",
connectorType: "REST",
active: true,
endpointConfiguration: {
endpoint: "https://api.error.com",
certificates: []
},
tags: [],
projectsBefore: [],
projectsAfterChange: []
};
const errorMessage = "API Error: Unable to create connector";
// Mock that connector doesn't exist
mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found"));
// Mock creation failure
mockSimplifierClient.createConnector.mockRejectedValue(new Error(errorMessage));
// Mock wrapToolResult to catch and handle errors
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}],
isError: true
};
}
});
const result = await toolHandler(testParams);
expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("ErrorConnector", "MCP Tool: connector-update");
expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith(expect.any(Object));
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("failed");
});
it('should handle complex endpoint configurations', async () => {
const testParams = {
name: "ComplexConnector",
description: "Complex configuration test",
connectorType: "OAUTH2",
active: true,
endpointConfiguration: {
endpoint: "https://oauth.example.com",
certificates: ["cert1", "cert2", "cert3"],
configuration: {
clientId: "test-client-id",
clientSecret: "test-secret",
scope: "read write",
grantType: "authorization_code",
redirectUri: "https://callback.example.com"
},
loginMethodName: "oauth2Login"
},
tags: ["oauth", "complex", "secure"],
projectsBefore: ["ProjectA", "ProjectB"],
projectsAfterChange: ["ProjectA", "ProjectC", "ProjectD"]
};
// Mock that connector doesn't exist
mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found"));
// Mock successful creation
mockSimplifierClient.createConnector.mockResolvedValue("Complex connector created");
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith(
expect.objectContaining({
endpointConfiguration: testParams.endpointConfiguration
})
);
});
it('should create an Oracle SQL connector', async () => {
const testParams = {
name: "OraExample",
description: "connector to oracle database",
connectorType: "SQL",
active: true,
timeoutTime: 60,
endpointConfiguration: {
endpoint: "Default",
loginMethodName: "OracleDBCredentials",
certificates: [],
configuration: {
dataSource: "oracle",
host: "172.17.0.3",
port: "1521",
database: "ORCLCDB",
connectionString: "jdbc:oracle:thin:@//172.17.0.3:1521/ORCLCDB",
resultType: "resultSet"
}
},
tags: [],
projectsBefore: [],
projectsAfterChange: []
};
// Mock that connector doesn't exist
mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found"));
// Mock successful creation
mockSimplifierClient.createConnector.mockResolvedValue("Oracle SQL connector created");
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("OraExample", "MCP Tool: connector-update");
expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith(
expect.objectContaining({
name: "OraExample",
connectorType: "SQL",
endpointConfiguration: expect.objectContaining({
endpoint: "Default",
loginMethodName: "OracleDBCredentials",
configuration: expect.objectContaining({
dataSource: "oracle",
host: "172.17.0.3",
port: "1521",
database: "ORCLCDB",
connectionString: "jdbc:oracle:thin:@//172.17.0.3:1521/ORCLCDB",
resultType: "resultSet"
})
})
})
);
expect(mockSimplifierClient.updateConnector).not.toHaveBeenCalled();
});
it('should create a MySQL SQL connector', async () => {
const testParams = {
name: "MySQLExample",
description: "connector to mysql database",
connectorType: "SQL",
active: true,
timeoutTime: 60,
endpointConfiguration: {
endpoint: "Default",
loginMethodName: "MySQLCredentials",
certificates: [],
configuration: {
dataSource: "mysql",
host: "localhost",
port: "3306",
database: "mydb",
connectionString: "jdbc:mysql://localhost:3306/mydb",
resultType: "resultSet"
}
},
tags: ["mysql", "database"],
projectsBefore: [],
projectsAfterChange: ["DatabaseProject"]
};
// Mock that connector doesn't exist
mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found"));
// Mock successful creation
mockSimplifierClient.createConnector.mockResolvedValue("MySQL SQL connector created");
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith(
expect.objectContaining({
name: "MySQLExample",
connectorType: "SQL",
endpointConfiguration: expect.objectContaining({
configuration: expect.objectContaining({
dataSource: "mysql",
connectionString: "jdbc:mysql://localhost:3306/mydb"
})
})
})
);
});
it('should create a PostgreSQL SQL connector', async () => {
const testParams = {
name: "PostgreSQLExample",
description: "connector to postgresql database",
connectorType: "SQL",
active: true,
timeoutTime: 60,
endpointConfiguration: {
endpoint: "Default",
loginMethodName: "PostgresCredentials",
certificates: [],
configuration: {
dataSource: "postgresql",
host: "db.example.com",
port: "5432",
database: "proddb",
connectionString: "jdbc:postgresql://db.example.com:5432/proddb",
resultType: "resultSet"
}
},
tags: ["postgresql", "database"],
projectsBefore: [],
projectsAfterChange: []
};
// Mock that connector doesn't exist
mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found"));
// Mock successful creation
mockSimplifierClient.createConnector.mockResolvedValue("PostgreSQL connector created");
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith(
expect.objectContaining({
connectorType: "SQL",
endpointConfiguration: expect.objectContaining({
configuration: expect.objectContaining({
dataSource: "postgresql",
port: "5432"
})
})
})
);
});
it('should create a DB2 SQL connector with schema field', async () => {
const testParams = {
name: "McpDb2Test",
description: "DB2 connector for testdb database",
connectorType: "SQL",
active: true,
timeoutTime: 60,
endpointConfiguration: {
endpoint: "Default",
loginMethodName: "DB2Credentials",
certificates: [],
configuration: {
dataSource: "db2",
host: "localhost",
port: "50000",
database: "testdb",
schema: "MYSCHEMA",
connectionString: "jdbc:db2://localhost:50000/testdb:currentSchema=MYSCHEMA;",
resultType: "resultSet"
}
},
tags: [],
projectsBefore: [],
projectsAfterChange: []
};
// Mock that connector doesn't exist
mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found"));
// Mock successful creation
mockSimplifierClient.createConnector.mockResolvedValue("DB2 SQL connector created");
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("McpDb2Test", "MCP Tool: connector-update");
expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith(
expect.objectContaining({
name: "McpDb2Test",
connectorType: "SQL",
endpointConfiguration: expect.objectContaining({
endpoint: "Default",
loginMethodName: "DB2Credentials",
configuration: expect.objectContaining({
dataSource: "db2",
host: "localhost",
port: "50000",
database: "testdb",
schema: "MYSCHEMA",
connectionString: "jdbc:db2://localhost:50000/testdb:currentSchema=MYSCHEMA;",
resultType: "resultSet"
})
})
})
);
expect(mockSimplifierClient.updateConnector).not.toHaveBeenCalled();
});
it('should update an existing SQL connector', async () => {
const testParams = {
name: "ExistingSQLConnector",
description: "Updated SQL connector description",
connectorType: "SQL",
active: true,
timeoutTime: 120,
endpointConfiguration: {
endpoint: "Default",
loginMethodName: "UpdatedCredentials",
certificates: [],
configuration: {
dataSource: "oracle",
host: "new-host.example.com",
port: "1521",
database: "NEWDB",
connectionString: "jdbc:oracle:thin:@//new-host.example.com:1521/NEWDB",
resultType: "resultSet"
}
},
tags: ["sql", "updated"],
projectsBefore: ["Project1"],
projectsAfterChange: ["Project1", "Project2"]
};
const existingConnector: SimplifierConnectorDetails = {
name: "ExistingSQLConnector",
description: "Old SQL connector",
connectorType: {
technicalName: "SQL",
i18n: "SQL Connector",
descriptionI18n: "SQL Database Connector"
},
active: true,
timeoutTime: 60,
amountOfCalls: 5,
editable: true,
deletable: true,
tags: ["sql"],
assignedProjects: {
projectsBefore: ["Project1"],
projectsAfterChange: ["Project1"]
},
configuration: {
endpoints: [{
endpoint: "Default",
certificates: [],
configuration: {
dataSource: "oracle",
host: "old-host.example.com",
port: "1521",
database: "OLDDB",
connectionString: "jdbc:oracle:thin:@//old-host.example.com:1521/OLDDB",
resultType: "resultSet"
}
}]
}
};
// Mock that connector exists
mockSimplifierClient.getConnector.mockResolvedValue(existingConnector);
// Mock successful update
mockSimplifierClient.updateConnector.mockResolvedValue("SQL connector updated");
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("ExistingSQLConnector", "MCP Tool: connector-update");
expect(mockSimplifierClient.updateConnector).toHaveBeenCalledWith(
expect.objectContaining({
name: "ExistingSQLConnector",
connectorType: "SQL",
timeoutTime: 120,
endpointConfiguration: expect.objectContaining({
configuration: expect.objectContaining({
host: "new-host.example.com",
database: "NEWDB"
})
})
})
);
expect(mockSimplifierClient.createConnector).not.toHaveBeenCalled();
});
});
describe('tool handler - correct create/update logic', () => {
let toolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
toolHandler = findToolByName("connector-update").handler;
});
it('should correctly handle create and update operations', async () => {
// This test verifies that the create/update logic works correctly:
// When connector exists, it should call updateConnector
// When connector doesn't exist, it should call createConnector
const testParams = {
name: "TestConnector",
description: "Testing correct logic",
connectorType: "REST",
active: true,
endpointConfiguration: {
endpoint: "https://test.com",
certificates: []
},
tags: [],
projectsBefore: [],
projectsAfterChange: []
};
// Case 1: Connector exists - should call updateConnector
const existingConnector = {
name: "TestConnector",
description: "Existing",
connectorType: { technicalName: "REST", i18n: "REST", descriptionI18n: "REST" },
active: true,
timeoutTime: 30000,
amountOfCalls: 0,
editable: true,
deletable: true,
tags: [],
assignedProjects: { projectsBefore: [], projectsAfterChange: [] }
};
mockSimplifierClient.getConnector.mockResolvedValue(existingConnector);
mockSimplifierClient.updateConnector.mockResolvedValue("Updated");
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
await toolHandler(testParams);
// Should correctly call updateConnector when connector exists
expect(mockSimplifierClient.updateConnector).toHaveBeenCalled();
expect(mockSimplifierClient.createConnector).not.toHaveBeenCalled();
// Reset mocks
jest.clearAllMocks();
// Case 2: Connector doesn't exist - should call createConnector
mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found"));
mockSimplifierClient.createConnector.mockResolvedValue("Created");
await toolHandler(testParams);
// Should correctly call createConnector when connector doesn't exist
expect(mockSimplifierClient.createConnector).toHaveBeenCalled();
expect(mockSimplifierClient.updateConnector).not.toHaveBeenCalled();
});
});
});
describe('connector-call-update tool', () => {
describe('schema validation', () => {
it('should validate required schema fields', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-update");
const schema = tool.schema!;
// Test that schema validates required fields
expect(schema.connectorName).toBeDefined();
expect(schema.connectorCallName).toBeDefined();
expect(schema.description).toBeDefined();
expect(schema.validateIn).toBeDefined();
expect(schema.validateOut).toBeDefined();
expect(schema.async).toBeDefined();
expect(schema.autoGenerated).toBeDefined();
expect(schema.connectorCallParameters).toBeDefined();
// Test valid data passes validation
const validConnectorName = "TestConnector";
const validCallName = "testCall";
const validDescription = "Test call description";
expect(() => schema.connectorName.parse(validConnectorName)).not.toThrow();
expect(() => schema.connectorCallName.parse(validCallName)).not.toThrow();
expect(() => schema.description.parse(validDescription)).not.toThrow();
});
it('should validate that connectorName and connectorCallName are required', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-update");
const schema = tool.schema!;
// Test that valid strings pass
expect(() => schema.connectorName.parse("ValidConnector")).not.toThrow();
expect(() => schema.connectorCallName.parse("validCall")).not.toThrow();
// Test that undefined fails validation for required fields
expect(() => schema.connectorName.parse(undefined)).toThrow();
expect(() => schema.connectorCallName.parse(undefined)).toThrow();
// Test that null fails validation
expect(() => schema.connectorName.parse(null)).toThrow();
expect(() => schema.connectorCallName.parse(null)).toThrow();
});
it('should allow optional fields to be omitted with defaults', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-update");
const schema = tool.schema!;
// Test that optional fields can be undefined and get defaults
expect(() => schema.description.parse(undefined)).not.toThrow();
expect(() => schema.validateIn.parse(undefined)).not.toThrow();
expect(() => schema.validateOut.parse(undefined)).not.toThrow();
expect(() => schema.async.parse(undefined)).not.toThrow();
expect(() => schema.autoGenerated.parse(undefined)).not.toThrow();
expect(() => schema.connectorCallParameters.parse(undefined)).not.toThrow();
// Test default values are applied
expect(schema.description.parse(undefined)).toBe("");
expect(schema.validateIn.parse(undefined)).toBe(true);
expect(schema.validateOut.parse(undefined)).toBe(true);
expect(schema.async.parse(undefined)).toBe(false);
expect(schema.autoGenerated.parse(undefined)).toBe(false);
expect(schema.connectorCallParameters.parse(undefined)).toEqual([]);
});
it('should validate connectorCallParameters structure', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-update");
const schema = tool.schema!;
// Valid parameter
const validParameter = {
name: "inputParam",
alias: "input",
isInput: true,
constValue: "constant",
dataType: {
name: "String",
nameSpace: "base",
category: "base"
},
optional: false,
position: 0
};
expect(() => schema.connectorCallParameters.parse([validParameter])).not.toThrow();
// Minimal valid parameter (only required fields)
const minimalParameter = {
name: "param1",
isInput: true,
dataType: {
name: "Integer",
category: "base"
}
};
expect(() => schema.connectorCallParameters.parse([minimalParameter])).not.toThrow();
// Invalid parameter (missing required name)
const invalidParameter = {
isInput: true,
dataType: {
name: "String",
category: "base"
}
};
expect(() => schema.connectorCallParameters.parse([invalidParameter])).toThrow();
});
it('should validate dataType category enum', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-update");
const schema = tool.schema!;
// Test valid categories
const validCategories = ['base', 'domain', 'collection', 'struct'];
validCategories.forEach(category => {
const parameter = {
name: "test",
isInput: true,
dataType: {
name: "TestType",
category: category
}
};
expect(() => schema.connectorCallParameters.parse([parameter])).not.toThrow();
});
// Test invalid category
const invalidCategoryParameter = {
name: "test",
isInput: true,
dataType: {
name: "TestType",
category: "invalid"
}
};
expect(() => schema.connectorCallParameters.parse([invalidCategoryParameter])).toThrow();
});
it('should validate parameter with special name characters', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-update");
const schema = tool.schema!;
// Test parameter names with special characters (/, [])
const specialNameParameters = [
{
name: "root/nested/value",
isInput: true,
dataType: { name: "String", category: "base" }
},
{
name: "array[]",
isInput: false,
dataType: { name: "Integer", category: "base" }
},
{
name: "complex[]/nested/path",
isInput: true,
dataType: { name: "Object", category: "struct" }
}
];
expect(() => schema.connectorCallParameters.parse(specialNameParameters)).not.toThrow();
});
});
describe('tool handler - create new connector call', () => {
let toolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
toolHandler = findToolByName("connector-call-update").handler;
});
it('should create a new connector call when it does not exist', async () => {
const testParams = {
connectorName: "TestConnector",
connectorCallName: "newCall",
description: "New call description",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
connectorCallParameters: [
{
name: "inputParam",
alias: "input",
isInput: true,
dataType: {
name: "String",
nameSpace: "base",
category: "base"
},
optional: false,
position: 0
},
{
name: "outputParam",
isInput: false,
dataType: {
name: "Integer",
category: "base"
},
optional: false,
position: 1
}
]
};
const expectedData: SimplifierConnectorCallUpdate = {
name: "newCall",
description: "New call description",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
connectorCallParameters: testParams.connectorCallParameters
};
const expectedResponse = "Connector call created successfully";
// Mock that connector call doesn't exist (throws error)
mockSimplifierClient.getConnectorCall.mockRejectedValue(
new Error("Not found")
);
// Mock successful creation
mockSimplifierClient.createConnectorCall.mockResolvedValue(expectedResponse);
// Mock wrapToolResult to call the function and return result
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "newCall", "MCP Tool: connector-call-update");
expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalledWith(
"TestConnector",
expect.objectContaining({
name: expectedData.name,
description: expectedData.description,
validateIn: expectedData.validateIn,
validateOut: expectedData.validateOut,
async: expectedData.async,
autoGenerated: expectedData.autoGenerated,
connectorCallParameters: expectedData.connectorCallParameters
})
);
expect(mockSimplifierClient.updateConnectorCall).not.toHaveBeenCalled();
expect(mockWrapToolResult).toHaveBeenCalledWith(
"create or update Connector call TestConnector.newCall",
expect.any(Function)
);
});
it('should create a connector call with minimal parameters', async () => {
const testParams = {
connectorName: "MinimalConnector",
connectorCallName: "minimalCall",
description: "",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
connectorCallParameters: []
};
// Mock that connector call doesn't exist
mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found"));
// Mock successful creation
mockSimplifierClient.createConnectorCall.mockResolvedValue("Created");
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalledWith(
"MinimalConnector",
expect.objectContaining({
name: "minimalCall",
description: "",
connectorCallParameters: []
})
);
});
it('should create a connector call with complex parameters', async () => {
const testParams = {
connectorName: "ComplexConnector",
connectorCallName: "complexCall",
description: "Complex call with multiple parameter types",
validateIn: true,
validateOut: false,
async: true,
autoGenerated: false,
connectorCallParameters: [
{
name: "root/nested/input",
alias: "nestedInput",
isInput: true,
constValue: "constant-value",
dataType: {
name: "CustomType",
nameSpace: "custom",
category: "domain"
},
optional: true,
position: 0
},
{
name: "array[]",
isInput: true,
dataType: {
name: "StringArray",
category: "collection"
},
optional: false,
position: 1
},
{
name: "structOutput",
isInput: false,
dataType: {
name: "ComplexStruct",
nameSpace: "structures",
category: "structure"
},
optional: false,
position: 2
}
]
};
// Mock that connector call doesn't exist
mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found"));
// Mock successful creation
mockSimplifierClient.createConnectorCall.mockResolvedValue("Complex call created");
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalledWith(
"ComplexConnector",
expect.objectContaining({
name: "complexCall",
async: true,
connectorCallParameters: testParams.connectorCallParameters
})
);
});
});
describe('tool handler - update existing connector call', () => {
let toolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
toolHandler = findToolByName("connector-call-update").handler;
});
it('should update existing connector call when it exists', async () => {
const testParams = {
connectorName: "ExistingConnector",
connectorCallName: "existingCall",
description: "Updated call description",
validateIn: false,
validateOut: true,
async: true,
autoGenerated: false,
connectorCallParameters: [
{
name: "updatedParam",
isInput: true,
dataType: {
name: "Boolean",
category: "base"
},
optional: true,
position: 0
}
]
};
const existingCall: SimplifierConnectorCallDetails = {
name: "existingCall",
description: "Old description",
validateIn: true,
validateOut: false,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "ExistingConnector",
connectorCallParameters: [
{
name: "oldParam",
isInput: true,
dataType: {
name: "String",
category: "base"
},
optional: false,
position: 0
}
]
};
const expectedResponse = "Connector call updated successfully";
// Mock that connector call exists
mockSimplifierClient.getConnectorCall.mockResolvedValue(existingCall);
// Mock successful update
mockSimplifierClient.updateConnectorCall.mockResolvedValue(expectedResponse);
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("ExistingConnector", "existingCall", "MCP Tool: connector-call-update");
expect(mockSimplifierClient.updateConnectorCall).toHaveBeenCalledWith(
"ExistingConnector",
expect.objectContaining({
name: "existingCall",
description: "Updated call description",
validateIn: false,
validateOut: true,
async: true,
autoGenerated: false,
connectorCallParameters: testParams.connectorCallParameters
})
);
expect(mockSimplifierClient.createConnectorCall).not.toHaveBeenCalled();
expect(mockWrapToolResult).toHaveBeenCalledWith(
"create or update Connector call ExistingConnector.existingCall",
expect.any(Function)
);
});
it('should update connector call with changed parameters', async () => {
const testParams = {
connectorName: "TestConnector",
connectorCallName: "updateCall",
description: "Call with updated parameters",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
connectorCallParameters: [
{
name: "newInput1",
isInput: true,
dataType: { name: "String", category: "base" },
optional: false,
position: 0
},
{
name: "newInput2",
isInput: true,
dataType: { name: "Integer", category: "base" },
optional: true,
position: 1
},
{
name: "newOutput",
isInput: false,
dataType: { name: "Boolean", category: "base" },
optional: false,
position: 2
}
]
};
const existingCall: SimplifierConnectorCallDetails = {
name: "updateCall",
description: "Old description",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: [
{
name: "oldParam",
isInput: true,
dataType: { name: "String", category: "base" },
optional: false,
position: 0
}
]
};
// Mock that connector call exists
mockSimplifierClient.getConnectorCall.mockResolvedValue(existingCall);
// Mock successful update
mockSimplifierClient.updateConnectorCall.mockResolvedValue("Updated");
// Mock wrapToolResult
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.updateConnectorCall).toHaveBeenCalledWith(
"TestConnector",
expect.objectContaining({
connectorCallParameters: testParams.connectorCallParameters
})
);
});
it('should correctly handle create and update operations', async () => {
const testParams = {
connectorName: "TestConnector",
connectorCallName: "testCall",
description: "Testing correct logic",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
connectorCallParameters: []
};
// Case 1: Call exists - should call updateConnectorCall
const existingCall = {
name: "testCall",
description: "Existing",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: []
};
mockSimplifierClient.getConnectorCall.mockResolvedValue(existingCall);
mockSimplifierClient.updateConnectorCall.mockResolvedValue("Updated");
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
await toolHandler(testParams);
// Should correctly call updateConnectorCall when call exists
expect(mockSimplifierClient.updateConnectorCall).toHaveBeenCalled();
expect(mockSimplifierClient.createConnectorCall).not.toHaveBeenCalled();
// Reset mocks
jest.clearAllMocks();
// Case 2: Call doesn't exist - should call createConnectorCall
mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found"));
mockSimplifierClient.createConnectorCall.mockResolvedValue("Created");
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
await toolHandler(testParams);
// Should correctly call createConnectorCall when call doesn't exist
expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalled();
expect(mockSimplifierClient.updateConnectorCall).not.toHaveBeenCalled();
});
});
describe('tool handler - error handling', () => {
let toolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
toolHandler = findToolByName("connector-call-update").handler;
});
it('should handle errors when creating connector call fails', async () => {
const testParams = {
connectorName: "ErrorConnector",
connectorCallName: "errorCall",
description: "Test error handling",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
connectorCallParameters: []
};
const errorMessage = "API Error: Unable to create connector call";
// Mock that connector call doesn't exist
mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found"));
// Mock creation failure
mockSimplifierClient.createConnectorCall.mockRejectedValue(new Error(errorMessage));
// Mock wrapToolResult to catch and handle errors
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}],
isError: true
};
}
});
const result = await toolHandler(testParams);
expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("ErrorConnector", "errorCall", "MCP Tool: connector-call-update");
expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalledWith("ErrorConnector", expect.any(Object));
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("failed");
});
it('should handle errors when updating connector call fails', async () => {
const testParams = {
connectorName: "ErrorConnector",
connectorCallName: "errorCall",
description: "Test update error",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
connectorCallParameters: []
};
const existingCall = {
name: "errorCall",
description: "Existing",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "ErrorConnector",
connectorCallParameters: []
};
const errorMessage = "API Error: Unable to update connector call";
// Mock that connector call exists
mockSimplifierClient.getConnectorCall.mockResolvedValue(existingCall);
// Mock update failure
mockSimplifierClient.updateConnectorCall.mockRejectedValue(new Error(errorMessage));
// Mock wrapToolResult to catch and handle errors
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}],
isError: true
};
}
});
const result = await toolHandler(testParams);
expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("ErrorConnector", "errorCall", "MCP Tool: connector-call-update");
expect(mockSimplifierClient.updateConnectorCall).toHaveBeenCalledWith("ErrorConnector", expect.any(Object));
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("failed");
});
it('should handle errors when connector does not exist', async () => {
const testParams = {
connectorName: "NonExistentConnector",
connectorCallName: "testCall",
description: "Test on non-existent connector",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
connectorCallParameters: []
};
// Mock that connector call doesn't exist
mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found"));
// Mock that connector doesn't exist
mockSimplifierClient.createConnectorCall.mockRejectedValue(
new Error("Connector 'NonExistentConnector' does not exist")
);
// Mock wrapToolResult to catch and handle errors
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}],
isError: true
};
}
});
const result = await toolHandler(testParams);
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("NonExistentConnector");
});
});
});
describe('connector-wizard-rfc-create tool', () => {
describe('schema validation', () => {
it('should validate required schema fields', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-wizard-rfc-create");
const schema = tool.schema!;
// Test that schema validates required fields
expect(schema.connectorName).toBeDefined();
expect(schema.rfcFunctionNames).toBeDefined();
// Test valid data passes validation
expect(() => schema.connectorName.parse("TestConnector")).not.toThrow();
expect(() => schema.rfcFunctionNames.parse(["FUNCTION1", "FUNCTION2"])).not.toThrow();
});
it('should validate that connectorName and rfcFunctionNames are required', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-wizard-rfc-create");
const schema = tool.schema!;
// Test that valid values pass
expect(() => schema.connectorName.parse("ValidConnector")).not.toThrow();
expect(() => schema.rfcFunctionNames.parse(["FUNC1"])).not.toThrow();
// Test that undefined fails validation
expect(() => schema.connectorName.parse(undefined)).toThrow();
expect(() => schema.rfcFunctionNames.parse(undefined)).toThrow();
// Test that null fails validation
expect(() => schema.connectorName.parse(null)).toThrow();
expect(() => schema.rfcFunctionNames.parse(null)).toThrow();
});
it('should validate that rfcFunctionNames is an array', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-wizard-rfc-create");
const schema = tool.schema!;
// Test valid array passes
expect(() => schema.rfcFunctionNames.parse([])).not.toThrow();
expect(() => schema.rfcFunctionNames.parse(["FUNC1", "FUNC2", "FUNC3"])).not.toThrow();
// Test non-array fails
expect(() => schema.rfcFunctionNames.parse("not an array")).toThrow();
expect(() => schema.rfcFunctionNames.parse({ func: "FUNC1" })).toThrow();
});
it('should validate tool metadata', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-wizard-rfc-create");
expect(tool.metadata).toMatchObject({
title: "Create RFC connector calls using the call wizard",
readOnlyHint: false,
destructiveHint: true,
idempotentHint: false,
openWorldHint: true
});
});
});
describe('tool handler - create RFC calls', () => {
let toolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
toolHandler = findToolByName("connector-wizard-rfc-create").handler;
});
it('should successfully create RFC connector calls', async () => {
const testParams = {
connectorName: "TestRFCConnector",
rfcFunctionNames: ["BAPI_USER_GET_DETAIL", "BAPI_COMPANY_GET_DETAIL"]
};
const mockCallDetails = {
calls: [
{
callId: "call1",
call: {
name: "BAPI_USER_GET_DETAIL",
nameNonTechnicalized: "BAPI_USER_GET_DETAIL",
description: "Get user details from SAP"
},
meta: {}
},
{
callId: "call2",
call: {
name: "BAPI_COMPANY_GET_DETAIL",
nameNonTechnicalized: "BAPI_COMPANY_GET_DETAIL",
description: "Get company details from SAP"
},
meta: {}
}
],
dataTypes: {}
};
const expectedPayload = {
callsRfc: ["BAPI_USER_GET_DETAIL", "BAPI_COMPANY_GET_DETAIL"],
descriptions: {
"BAPI_USER_GET_DETAIL": "Get user details from SAP",
"BAPI_COMPANY_GET_DETAIL": "Get company details from SAP"
},
newNames: {
"BAPI_USER_GET_DETAIL": "BAPI_USER_GET_DETAIL",
"BAPI_COMPANY_GET_DETAIL": "BAPI_COMPANY_GET_DETAIL"
}
};
mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined);
mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails);
mockSimplifierClient.rfcWizardCreateCalls.mockResolvedValue("Calls created successfully");
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.viewRFCFunctions).toHaveBeenCalledWith(
"TestRFCConnector",
["BAPI_USER_GET_DETAIL", "BAPI_COMPANY_GET_DETAIL"],
"MCP Tool: connector-wizard-rfc-create"
);
expect(mockSimplifierClient.rfcWizardGetCallDetails).toHaveBeenCalledWith(
"TestRFCConnector",
["BAPI_USER_GET_DETAIL", "BAPI_COMPANY_GET_DETAIL"]
);
expect(mockSimplifierClient.rfcWizardCreateCalls).toHaveBeenCalledWith(
"TestRFCConnector",
expectedPayload
);
expect(mockWrapToolResult).toHaveBeenCalledWith(
"create 2 connector calls using the RFC connector wizard",
expect.any(Function)
);
});
it('should handle single RFC function', async () => {
const testParams = {
connectorName: "TestRFCConnector",
rfcFunctionNames: ["SINGLE_FUNCTION"]
};
const mockCallDetails = {
calls: [
{
callId: "call1",
call: {
name: "SINGLE_FUNCTION",
nameNonTechnicalized: "SINGLE_FUNCTION",
description: "A single function"
},
meta: {}
}
],
dataTypes: {}
};
mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined);
mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails);
mockSimplifierClient.rfcWizardCreateCalls.mockResolvedValue("Calls created successfully");
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.viewRFCFunctions).toHaveBeenCalled();
expect(mockSimplifierClient.rfcWizardGetCallDetails).toHaveBeenCalled();
expect(mockSimplifierClient.rfcWizardCreateCalls).toHaveBeenCalled();
});
it('should handle RFC functions with different names and technical names', async () => {
const testParams = {
connectorName: "TestRFCConnector",
rfcFunctionNames: ["_-ITIZ_-DRAW_READ_ORIGINAL_FILE"]
};
const mockCallDetails = {
calls: [
{
callId: "call1",
call: {
name: "DRAW_READ_ORIGINAL_FILE",
nameNonTechnicalized: "_-ITIZ_-DRAW_READ_ORIGINAL_FILE",
description: "Read original file from DRAW"
},
meta: {}
}
],
dataTypes: {}
};
const expectedPayload = {
callsRfc: ["_-ITIZ_-DRAW_READ_ORIGINAL_FILE"],
descriptions: {
"_-ITIZ_-DRAW_READ_ORIGINAL_FILE": "Read original file from DRAW"
},
newNames: {
"_-ITIZ_-DRAW_READ_ORIGINAL_FILE": "DRAW_READ_ORIGINAL_FILE"
}
};
mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined);
mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails);
mockSimplifierClient.rfcWizardCreateCalls.mockResolvedValue("Calls created successfully");
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result).toEqual(expectedPayload);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.rfcWizardCreateCalls).toHaveBeenCalledWith(
"TestRFCConnector",
expectedPayload
);
});
it('should return the created payload', async () => {
const testParams = {
connectorName: "TestRFCConnector",
rfcFunctionNames: ["TEST_FUNC"]
};
const mockCallDetails = {
calls: [
{
callId: "call1",
call: {
name: "TEST_FUNC",
nameNonTechnicalized: "TEST_FUNC",
description: "Test function"
},
meta: {}
}
],
dataTypes: {}
};
mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined);
mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails);
mockSimplifierClient.rfcWizardCreateCalls.mockResolvedValue("Calls created successfully");
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result).toHaveProperty('callsRfc');
expect(result).toHaveProperty('descriptions');
expect(result).toHaveProperty('newNames');
expect(result.callsRfc).toEqual(["TEST_FUNC"]);
expect(result.descriptions["TEST_FUNC"]).toBe("Test function");
expect(result.newNames["TEST_FUNC"]).toBe("TEST_FUNC");
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
});
});
describe('tool handler - error handling', () => {
let toolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
toolHandler = findToolByName("connector-wizard-rfc-create").handler;
});
it('should handle errors when viewRFCFunctions fails', async () => {
const testParams = {
connectorName: "TestRFCConnector",
rfcFunctionNames: ["TEST_FUNC"]
};
mockSimplifierClient.viewRFCFunctions.mockRejectedValue(
new Error("Failed to view RFC functions")
);
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
await fn();
return { content: [{ type: "text", text: "Success" }] };
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}],
isError: true
};
}
});
const result = await toolHandler(testParams);
expect(mockSimplifierClient.viewRFCFunctions).toHaveBeenCalled();
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("failed");
});
it('should handle errors when rfcWizardGetCallDetails fails', async () => {
const testParams = {
connectorName: "TestRFCConnector",
rfcFunctionNames: ["TEST_FUNC"]
};
mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined);
mockSimplifierClient.rfcWizardGetCallDetails.mockRejectedValue(
new Error("Failed to get call details")
);
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
await fn();
return { content: [{ type: "text", text: "Success" }] };
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}],
isError: true
};
}
});
const result = await toolHandler(testParams);
expect(mockSimplifierClient.viewRFCFunctions).toHaveBeenCalled();
expect(mockSimplifierClient.rfcWizardGetCallDetails).toHaveBeenCalled();
expect(result.isError).toBe(true);
});
it('should handle errors when function details cannot be found', async () => {
const testParams = {
connectorName: "TestRFCConnector",
rfcFunctionNames: ["MISSING_FUNC"]
};
const mockCallDetails = {
calls: [], // Empty - function not found
dataTypes: {}
};
mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined);
mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails);
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
await fn();
return { content: [{ type: "text", text: "Success" }] };
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}],
isError: true
};
}
});
const result = await toolHandler(testParams);
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("MISSING_FUNC");
});
it('should handle errors when rfcWizardCreateCalls fails', async () => {
const testParams = {
connectorName: "TestRFCConnector",
rfcFunctionNames: ["TEST_FUNC"]
};
const mockCallDetails = {
calls: [
{
callId: "call1",
call: {
name: "TEST_FUNC",
nameNonTechnicalized: "TEST_FUNC",
description: "Test function"
},
meta: {}
}
],
dataTypes: {}
};
mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined);
mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails);
mockSimplifierClient.rfcWizardCreateCalls.mockRejectedValue(
new Error("Failed to create calls")
);
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
await fn();
return { content: [{ type: "text", text: "Success" }] };
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}],
isError: true
};
}
});
const result = await toolHandler(testParams);
expect(mockSimplifierClient.rfcWizardCreateCalls).toHaveBeenCalled();
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("failed");
});
it('should handle connector not found error', async () => {
const testParams = {
connectorName: "NonExistentConnector",
rfcFunctionNames: ["TEST_FUNC"]
};
mockSimplifierClient.viewRFCFunctions.mockRejectedValue(
new Error("Connector 'NonExistentConnector' not found")
);
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
await fn();
return { content: [{ type: "text", text: "Success" }] };
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}],
isError: true
};
}
});
const result = await toolHandler(testParams);
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("NonExistentConnector");
});
});
});
describe('connector-call-test tool', () => {
describe('schema validation', () => {
it('should validate required schema fields', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-test");
const schema = tool.schema!;
// Test that schema validates required fields
expect(schema.connectorName).toBeDefined();
expect(schema.callName).toBeDefined();
expect(schema.parameters).toBeDefined();
// Test valid data passes validation
expect(() => schema.connectorName.parse("TestConnector")).not.toThrow();
expect(() => schema.callName.parse("testCall")).not.toThrow();
expect(() => schema.parameters.parse([{ name: "param1", value: "test" }])).not.toThrow();
});
it('should validate that connectorName and callName are required', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-test");
const schema = tool.schema!;
// Test that valid strings pass
expect(() => schema.connectorName.parse("ValidConnector")).not.toThrow();
expect(() => schema.callName.parse("validCall")).not.toThrow();
// Test that undefined fails validation
expect(() => schema.connectorName.parse(undefined)).toThrow();
expect(() => schema.callName.parse(undefined)).toThrow();
// Test that null fails validation
expect(() => schema.connectorName.parse(null)).toThrow();
expect(() => schema.callName.parse(null)).toThrow();
});
it('should allow parameters to be optional with empty array default', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-test");
const schema = tool.schema!;
// Test that parameters can be undefined and defaults to empty array
expect(() => schema.parameters.parse(undefined)).not.toThrow();
expect(schema.parameters.parse(undefined)).toEqual([]);
});
it('should validate parameter structure', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-test");
const schema = tool.schema!;
// Test valid parameter structure
const validParameters = [
{ name: "param1", value: "value1" },
{ name: "param2", value: 123 }
];
expect(() => schema.parameters.parse(validParameters)).not.toThrow();
// Test that parameters require a name
const invalidParameters = [
{ value: "value1" } // Missing name
];
expect(() => schema.parameters.parse(invalidParameters)).toThrow();
});
it('should allow any value type in parameters', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-test");
const schema = tool.schema!;
const parametersWithVariousValues = [
{ name: "string", value: "text" },
{ name: "number", value: 42 },
{ name: "boolean", value: false },
{ name: "null", value: null },
{ name: "undefined", value: undefined },
{ name: "object", value: { nested: "object" } },
{ name: "array", value: [1, 2, 3] }
];
expect(() => schema.parameters.parse(parametersWithVariousValues)).not.toThrow();
});
});
describe('tool handler - test connector call', () => {
let toolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
toolHandler = findToolByName("connector-call-test").handler;
});
it('should test connector call successfully with no parameters', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "simpleCall",
parameters: []
};
// Mock the connector call details with no parameters
const mockConnectorCall: SimplifierConnectorCallDetails = {
name: "simpleCall",
description: "Simple call",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: []
};
mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall);
const mockResponse: ConnectorTestResponse = {
success: true,
result: { result: { message: "Success" } }
};
mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "simpleCall");
expect(mockSimplifierClient.testConnectorCall).toHaveBeenCalledWith(
"TestConnector",
"simpleCall",
{ parameters: [] },
"MCP Tool: connector-call-test"
);
expect(mockWrapToolResult).toHaveBeenCalledWith(
"test connector call TestConnector.simpleCall",
expect.any(Function)
);
});
it('should test connector call successfully with parameters', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "processData",
parameters: [
{ name: "inputText", value: "Hello World" },
{ name: "count", value: 5 },
{ name: "enabled", value: true }
]
};
// Mock the connector call details that will be fetched
const mockConnectorCall: SimplifierConnectorCallDetails = {
name: "processData",
description: "Process data",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: [
{
name: "inputText",
isInput: true,
dataType: { name: "String", category: "base" },
optional: false,
position: 0
},
{
name: "count",
isInput: true,
dataType: { name: "Integer", category: "base" },
optional: false,
position: 1
},
{
name: "enabled",
isInput: true,
dataType: { name: "Boolean", category: "base" },
optional: false,
position: 2
}
]
};
// Mock data types that will be fetched
const mockStringType = {
id: "string-id",
name: "String",
category: "base",
description: "String type",
baseType: "string",
isStruct: false,
fields: [],
properties: []
};
const mockIntegerType = {
id: "integer-id",
name: "Integer",
category: "base",
description: "Integer type",
baseType: "number",
isStruct: false,
fields: [],
properties: []
};
const mockBooleanType = {
id: "boolean-id",
name: "Boolean",
category: "base",
description: "Boolean type",
baseType: "boolean",
isStruct: false,
fields: [],
properties: []
};
mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall);
mockSimplifierClient.getDataTypeByName = jest.fn()
.mockResolvedValueOnce(mockStringType)
.mockResolvedValueOnce(mockIntegerType)
.mockResolvedValueOnce(mockBooleanType);
const mockResponse: ConnectorTestResponse = {
success: true,
result: { result: { processedText: "HELLO WORLD", repeatCount: 5 } }
};
mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result.success).toBe(true);
expect(result.result).toEqual({ result: { processedText: "HELLO WORLD", repeatCount: 5 } });
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "processData");
expect(mockSimplifierClient.testConnectorCall).toHaveBeenCalledWith(
"TestConnector",
"processData",
expect.objectContaining({
parameters: expect.arrayContaining([
expect.objectContaining({
name: "inputText",
value: "Hello World",
dataType: mockStringType,
transfer: true
}),
expect.objectContaining({
name: "count",
value: 5,
dataType: mockIntegerType,
transfer: true
}),
expect.objectContaining({
name: "enabled",
value: true,
dataType: mockBooleanType,
transfer: true
})
])
}),
"MCP Tool: connector-call-test"
);
});
it('should handle various parameter value types', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "multiTypeCall",
parameters: [
{ name: "stringParam", value: "text" },
{ name: "numberParam", value: 42 },
{ name: "floatParam", value: 3.14 },
{ name: "booleanParam", value: true },
{ name: "objectParam", value: { key: "value", nested: { data: "test" } } },
{ name: "arrayParam", value: [1, 2, 3] },
{ name: "nullParam", value: null }
]
};
// Mock the connector call details
const mockConnectorCall: SimplifierConnectorCallDetails = {
name: "multiTypeCall",
description: "Multi type call",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: testParams.parameters.map((p, i) => ({
name: p.name,
isInput: true,
dataType: { name: "Any", category: "base" },
optional: false,
position: i
}))
};
const mockDataType = {
id: "any-id",
name: "Any",
category: "base",
description: "Any type",
baseType: "any",
isStruct: false,
fields: [],
properties: []
};
mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall);
mockSimplifierClient.getDataTypeByName = jest.fn().mockResolvedValue(mockDataType);
const mockResponse: ConnectorTestResponse = {
success: true,
result: { result: { status: "processed" } }
};
mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
await fn();
return { content: [{ type: "text", text: "Success" }] };
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "multiTypeCall");
// Check that the call was made with transformed parameters
const call = mockSimplifierClient.testConnectorCall.mock.calls[0];
expect(call[0]).toBe("TestConnector");
expect(call[1]).toBe("multiTypeCall");
expect(call[2].parameters).toHaveLength(testParams.parameters.length);
// Verify each parameter has the correct structure (excluding null value edge case)
testParams.parameters.slice(0, -1).forEach((p, i) => {
expect(call[2].parameters[i]).toMatchObject({
name: p.name,
value: p.value,
dataType: mockDataType,
transfer: true
});
});
});
it('should handle connector call execution failure', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "failingCall",
parameters: []
};
// Mock the connector call details
const mockConnectorCall: SimplifierConnectorCallDetails = {
name: "failingCall",
description: "Failing call",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: []
};
mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall);
const mockResponse: ConnectorTestResponse = {
success: false,
error: "Connector call failed: Connection timeout"
};
mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result.success).toBe(false);
expect(result.error).toBe("Connector call failed: Connection timeout");
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "failingCall");
expect(mockSimplifierClient.testConnectorCall).toHaveBeenCalledWith(
"TestConnector",
"failingCall",
{ parameters: [] },
"MCP Tool: connector-call-test"
);
});
it('should handle connector call with message field on failure', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "failingCall",
parameters: []
};
// Mock the connector call details
const mockConnectorCall: SimplifierConnectorCallDetails = {
name: "failingCall",
description: "Failing call",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: []
};
mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall);
const mockResponse: ConnectorTestResponse = {
success: false,
message: "Invalid parameters provided"
};
mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result.success).toBe(false);
expect(result.error).toBe("Invalid parameters provided");
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
});
it('should handle connector call with neither error nor message on failure', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "failingCall",
parameters: []
};
// Mock the connector call details
const mockConnectorCall: SimplifierConnectorCallDetails = {
name: "failingCall",
description: "Failing call",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: []
};
mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall);
const mockResponse: ConnectorTestResponse = {
success: false
};
mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result.success).toBe(false);
expect(result.error).toBe("Unknown error");
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
});
it('should handle client errors (404, 400, 500)', async () => {
const testParams = {
connectorName: "NonExistentConnector",
callName: "nonExistentCall",
parameters: []
};
mockSimplifierClient.testConnectorCall.mockRejectedValue(
new Error("Connector 'NonExistentConnector' or call 'nonExistentCall' not found")
);
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
await fn();
return { content: [{ type: "text", text: "Success" }] };
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` })
}]
};
}
});
await toolHandler(testParams);
expect(mockWrapToolResult).toHaveBeenCalledWith(
"test connector call NonExistentConnector.nonExistentCall",
expect.any(Function)
);
});
it('should format successful response correctly', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "successCall",
parameters: [{ name: "input", value: "test" }]
};
// Mock the connector call details
const mockConnectorCall: SimplifierConnectorCallDetails = {
name: "successCall",
description: "Success call",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: [
{
name: "input",
isInput: true,
dataType: { name: "String", category: "base" },
optional: false,
position: 0
}
]
};
const mockDataType = {
id: "string-id",
name: "String",
category: "base",
description: "String type",
baseType: "string",
isStruct: false,
fields: [],
properties: []
};
mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall);
mockSimplifierClient.getDataTypeByName = jest.fn().mockResolvedValue(mockDataType);
const mockResponse: ConnectorTestResponse = {
success: true,
result: { result: { output: "processed test" } }
};
mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result).toEqual({
success: true,
message: "Connector call 'successCall' executed successfully",
result: {result: {output: "processed test"}}
});
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
});
it('should format failure response correctly', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "failCall",
parameters: []
};
// Mock the connector call details
const mockConnectorCall: SimplifierConnectorCallDetails = {
name: "failCall",
description: "Fail call",
validateIn: true,
validateOut: true,
async: false,
autoGenerated: false,
editable: true,
executable: true,
connectorName: "TestConnector",
connectorCallParameters: []
};
mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall);
const mockResponse: ConnectorTestResponse = {
success: false,
error: "Execution error"
};
mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result).toEqual({
success: false,
message: "Connector call 'failCall' execution failed",
error: "Execution error"
});
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
});
await toolHandler(testParams);
});
});
});
describe('connector-call-delete tool', () => {
let deleteCallToolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
deleteCallToolHandler = findToolByName("connector-call-delete").handler;
});
describe('delete call tool registration', () => {
it('should register delete call tool with correct schema', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-call-delete");
const schema = tool.schema!;
// Test required fields exist
expect(schema.connectorName).toBeDefined();
expect(schema.callName).toBeDefined();
// Test valid data passes validation
expect(() => schema.connectorName.parse("TestConnector")).not.toThrow();
expect(() => schema.callName.parse("testCall")).not.toThrow();
// Test that fields are required
expect(() => schema.connectorName.parse(undefined)).toThrow();
expect(() => schema.callName.parse(undefined)).toThrow();
expect(() => schema.connectorName.parse(null)).toThrow();
expect(() => schema.callName.parse(null)).toThrow();
});
});
describe('delete call tool handler', () => {
it('should successfully delete a connector call', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "testCall"
};
const expectedResponse = "Connector call 'testCall' deleted successfully from Connector 'TestConnector'";
mockSimplifierClient.deleteConnectorCall.mockResolvedValue(expectedResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{type: "text", text: JSON.stringify(result, null, 2)}]
};
});
await deleteCallToolHandler(testParams);
expect(mockSimplifierClient.deleteConnectorCall).toHaveBeenCalledWith("TestConnector", "testCall", "MCP Tool: connector-call-delete");
expect(mockWrapToolResult).toHaveBeenCalledWith(
"delete connector call TestConnector.testCall",
expect.any(Function)
);
});
it('should handle errors when connector call is not deletable', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "protectedCall"
};
mockSimplifierClient.deleteConnectorCall.mockRejectedValue(
new Error("Connector call 'protectedCall' cannot be deleted: still in use")
);
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
await fn();
return {content: [{type: "text", text: "Success"}]};
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({error: `Tool ${caption} failed: ${error}`})
}]
};
}
});
await deleteCallToolHandler(testParams);
expect(mockSimplifierClient.deleteConnectorCall).toHaveBeenCalledWith("TestConnector", "protectedCall", "MCP Tool: connector-call-delete");
});
it('should return the string response from API on successful deletion', async () => {
const testParams = {
connectorName: "TestConnector",
callName: "testCall"
};
const expectedResponse = "Deleted successfully";
mockSimplifierClient.deleteConnectorCall.mockResolvedValue(expectedResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result).toBe("Deleted successfully");
return {
content: [{type: "text", text: JSON.stringify(result, null, 2)}]
};
});
await deleteCallToolHandler(testParams);
expect(mockWrapToolResult).toHaveBeenCalled();
});
});
describe('connector-delete tool', () => {
let deleteConnectorToolHandler: Function;
beforeEach(() => {
registerConnectorTools(mockServer, mockSimplifierClient);
deleteConnectorToolHandler = findToolByName("connector-delete").handler;
});
describe('delete connector tool registration', () => {
it('should register delete connector tool with correct schema', () => {
registerConnectorTools(mockServer, mockSimplifierClient);
const tool = findToolByName("connector-delete");
const schema = tool.schema!;
// Test required fields exist
expect(schema.connectorName).toBeDefined();
// Test valid data passes validation
expect(() => schema.connectorName.parse("TestConnector")).not.toThrow();
// Test that connectorName is required
expect(() => schema.connectorName.parse(undefined)).toThrow();
expect(() => schema.connectorName.parse(null)).toThrow();
});
});
describe('delete connector tool handler', () => {
it('should successfully delete a connector', async () => {
const testParams = {
connectorName: "TestConnector"
};
const expectedResponse = "Connector 'TestConnector' deleted successfully";
mockSimplifierClient.deleteConnector.mockResolvedValue(expectedResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
return {
content: [{type: "text", text: JSON.stringify(result, null, 2)}]
};
});
await deleteConnectorToolHandler(testParams);
expect(mockSimplifierClient.deleteConnector).toHaveBeenCalledWith("TestConnector", "MCP Tool: connector-delete");
expect(mockWrapToolResult).toHaveBeenCalledWith(
"delete connector TestConnector",
expect.any(Function)
);
});
it('should handle errors when connector is not deletable', async () => {
const testParams = {
connectorName: "ProtectedConnector"
};
mockSimplifierClient.deleteConnector.mockRejectedValue(
new Error("Connector 'ProtectedConnector' cannot be deleted: still in use by Business Objects")
);
mockWrapToolResult.mockImplementation(async (caption, fn) => {
try {
await fn();
return {content: [{type: "text", text: "Success"}]};
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({error: `Tool ${caption} failed: ${error}`})
}]
};
}
});
await deleteConnectorToolHandler(testParams);
expect(mockSimplifierClient.deleteConnector).toHaveBeenCalledWith("ProtectedConnector", "MCP Tool: connector-delete");
});
it('should return the string response from API on successful deletion', async () => {
const testParams = {
connectorName: "TestConnector"
};
const expectedResponse = "Deleted successfully";
mockSimplifierClient.deleteConnector.mockResolvedValue(expectedResponse);
mockWrapToolResult.mockImplementation(async (_caption, fn) => {
const result = await fn();
expect(result).toBe("Deleted successfully");
return {
content: [{type: "text", text: JSON.stringify(result, null, 2)}]
};
});
await deleteConnectorToolHandler(testParams);
expect(mockWrapToolResult).toHaveBeenCalled();
});
});
});
});
});