diff --git a/src/tools/test-plans.ts b/src/tools/test-plans.ts index 79b8054e..c71de2ee 100644 --- a/src/tools/test-plans.ts +++ b/src/tools/test-plans.ts @@ -100,31 +100,55 @@ function configureTestPlanTools(server: McpServer, _: () => Promise, con name: z.string().describe("Name of the child test suite"), }, async ({ project, planId, parentSuiteId, name }) => { - try { - const connection = await connectionProvider(); - const testPlanApi = await connection.getTestPlanApi(); + const maxRetries = 5; + const baseDelay = 500; // milliseconds + const jitterMax = 200; // milliseconds + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + try { + const connection = await connectionProvider(); + const testPlanApi = await connection.getTestPlanApi(); + + const testSuiteToCreate = { + name, + parentSuite: { + id: parentSuiteId, + name: "", + }, + suiteType: 2, + }; - const testSuiteToCreate = { - name, - parentSuite: { - id: parentSuiteId, - name: "", - }, - suiteType: 2, - }; + const createdTestSuite = await testPlanApi.createTestSuite(testSuiteToCreate, project, planId); - const createdTestSuite = await testPlanApi.createTestSuite(testSuiteToCreate, project, planId); + return { + content: [{ type: "text", text: JSON.stringify(createdTestSuite, null, 2) }], + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; - return { - content: [{ type: "text", text: JSON.stringify(createdTestSuite, null, 2) }], - }; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; - return { - content: [{ type: "text", text: `Error creating test suite: ${errorMessage}` }], - isError: true, - }; + // Check if it's a concurrency conflict error + const isConcurrencyError = errorMessage.includes("TF26071") || errorMessage.includes("got update") || errorMessage.includes("changed by someone else"); + + // If it's a concurrency error and we have retries left, wait and retry + if (isConcurrencyError && attempt < maxRetries) { + const delay = baseDelay * Math.pow(2, attempt) + Math.random() * jitterMax; // Exponential backoff with jitter + await new Promise((resolve) => setTimeout(resolve, delay)); + continue; // Retry + } + + // If not a concurrency error or out of retries, return error + return { + content: [{ type: "text", text: `Error creating test suite: ${errorMessage}` }], + isError: true, + }; + } } + + // This should never be reached, but TypeScript requires a return value + return { + content: [{ type: "text", text: "Error creating test suite: Maximum retries exceeded" }], + isError: true, + }; } ); diff --git a/src/version.ts b/src/version.ts index 018bbc2b..33b25624 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1,2 @@ export const packageVersion = "2.4.0"; +