Repository: microsoft/azure-devops-mcp

Issue:
Creating a test plan and test suite using Azure Devops MCP capability is not present currently for e2e tracebility from PBI with test cases
Creating a test plan and test suite using Azure Devops MCP capability is not present currently for e2e tracebility from PBI with test cases 

Currently I need to manually create test suite in a test plan and then use testplan_create_test_case feature to add new test cases . 

If MCP can automatically create test suite for a PBI then e2e tracebility from PBI -> test case will be created. 

Hints from issue comments:
@danhellem: Creating a test plan is already part of the toolset. We currently do not support generating test suites.@danhellem: Test Suite API: https://learn.microsoft.com/en-us/rest/api/azure/devops/testplan/test-suites/create?view=azure-devops-rest-7.2&tabs=HTTP
@danhellem: work completed

Solution patch:
diff --git a/README.md b/README.md
index 7fceff0..baafc4e 100644
--- a/README.md
+++ b/README.md
@@ -129,6 +129,7 @@ Interact with these Azure DevOps services:
 - **testplan_list_test_plans**: Retrieve a paginated list of test plans from an Azure DevOps project. Allows filtering for active plans and toggling detailed information.
 - **testplan_list_test_cases**: Get a list of test cases in the test plan.
 - **testplan_show_test_results_from_build_id**: Get a list of test results for a given project and build ID.
+- **testplan_create_test_suite**: Creates a new test suite in a test plan.
 
 ### 📖 Wiki
 
diff --git a/src/tools/test-plans.ts b/src/tools/test-plans.ts
index 60fe577..210e175 100644
--- a/src/tools/test-plans.ts
+++ b/src/tools/test-plans.ts
@@ -13,6 +13,7 @@ const Test_Plan_Tools = {
   test_results_from_build_id: "testplan_show_test_results_from_build_id",
   list_test_cases: "testplan_list_test_cases",
   list_test_plans: "testplan_list_test_plans",
+  create_test_suite: "testplan_create_test_suite",
 };
 
 function configureTestPlanTools(server: McpServer, _: () => Promise<string>, connectionProvider: () => Promise<WebApi>) {
@@ -78,6 +79,39 @@ function configureTestPlanTools(server: McpServer, _: () => Promise<string>, con
     }
   );
 
+  /*
+    Create Test Suite - CREATE
+  */
+  server.tool(
+    Test_Plan_Tools.create_test_suite,
+    "Creates a new test suite in a test plan.",
+    {
+      project: z.string().describe("Project ID or project name"),
+      planId: z.number().describe("ID of the test plan that contains the suites"),
+      parentSuiteId: z.number().describe("ID of the parent suite under which the new suite will be created, if not given by user this can be id of a root suite of the test plan"),
+      name: z.string().describe("Name of the child test suite"),
+    },
+    async ({ project, planId, parentSuiteId, name }) => {
+      const connection = await connectionProvider();
+      const testPlanApi = await connection.getTestPlanApi();
+
+      const testSuiteToCreate = {
+        name,
+        parentSuite: {
+          id: parentSuiteId,
+          name: "",
+        },
+        suiteType: 2,
+      };
+
+      const createdTestSuite = await testPlanApi.createTestSuite(testSuiteToCreate, project, planId);
+
+      return {
+        content: [{ type: "text", text: JSON.stringify(createdTestSuite, null, 2) }],
+      };
+    }
+  );
+
   /*
     Add Test Cases to Suite - ADD
   */



Test patch:
diff --git a/test/src/tools/test-plan.test.ts b/test/src/tools/test-plan.test.ts
index d9ca769..48eac1c 100644
--- a/test/src/tools/test-plan.test.ts
+++ b/test/src/tools/test-plan.test.ts
@@ -32,6 +32,7 @@ describe("configureTestPlanTools", () => {
     mockTestPlanApi = {
       getTestPlans: jest.fn(),
       createTestPlan: jest.fn(),
+      createTestSuite: jest.fn(),
       addTestCasesToSuite: jest.fn(),
       getTestCaseList: jest.fn(),
     } as unknown as ITestPlanApi;
@@ -57,7 +58,14 @@ describe("configureTestPlanTools", () => {
     it("registers test plan tools on the server", () => {
       configureTestPlanTools(server, tokenProvider, connectionProvider);
       expect((server.tool as jest.Mock).mock.calls.map((call) => call[0])).toEqual(
-        expect.arrayContaining(["testplan_list_test_plans", "testplan_create_test_plan", "testplan_add_test_cases_to_suite", "testplan_list_test_cases", "testplan_show_test_results_from_build_id"])
+        expect.arrayContaining([
+          "testplan_list_test_plans",
+          "testplan_create_test_plan",
+          "testplan_create_test_suite",
+          "testplan_add_test_cases_to_suite",
+          "testplan_list_test_cases",
+          "testplan_show_test_results_from_build_id",
+        ])
       );
     });
   });
@@ -117,6 +125,118 @@ describe("configureTestPlanTools", () => {
     });
   });
 
+  describe("create_test_suite tool", () => {
+    it("should call createTestSuite with the correct parameters and return the expected result", async () => {
+      configureTestPlanTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "testplan_create_test_suite");
+      if (!call) throw new Error("testplan_create_test_suite tool not registered");
+      const [, , , handler] = call;
+
+      (mockTestPlanApi.createTestSuite as jest.Mock).mockResolvedValue({ id: 10, name: "New Test Suite" });
+      const params = {
+        project: "proj1",
+        planId: 1,
+        parentSuiteId: 5,
+        name: "New Test Suite",
+      };
+      const result = await handler(params);
+
+      expect(mockTestPlanApi.createTestSuite).toHaveBeenCalledWith(
+        {
+          name: "New Test Suite",
+          parentSuite: {
+            id: 5,
+            name: "",
+          },
+          suiteType: 2,
+        },
+        "proj1",
+        1
+      );
+      expect(result.content[0].text).toBe(JSON.stringify({ id: 10, name: "New Test Suite" }, null, 2));
+    });
+
+    it("should handle API errors when creating test suite", async () => {
+      configureTestPlanTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "testplan_create_test_suite");
+      if (!call) throw new Error("testplan_create_test_suite tool not registered");
+      const [, , , handler] = call;
+
+      (mockTestPlanApi.createTestSuite as jest.Mock).mockRejectedValue(new Error("API Error"));
+
+      const params = {
+        project: "proj1",
+        planId: 1,
+        parentSuiteId: 5,
+        name: "Failed Test Suite",
+      };
+
+      await expect(handler(params)).rejects.toThrow("API Error");
+    });
+
+    it("should create test suite with different parent suite IDs", async () => {
+      configureTestPlanTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "testplan_create_test_suite");
+      if (!call) throw new Error("testplan_create_test_suite tool not registered");
+      const [, , , handler] = call;
+
+      (mockTestPlanApi.createTestSuite as jest.Mock).mockResolvedValue({
+        id: 15,
+        name: "Child Test Suite",
+        parentSuite: { id: 10 },
+      });
+      const params = {
+        project: "proj1",
+        planId: 2,
+        parentSuiteId: 10,
+        name: "Child Test Suite",
+      };
+      const result = await handler(params);
+
+      expect(mockTestPlanApi.createTestSuite).toHaveBeenCalledWith(
+        {
+          name: "Child Test Suite",
+          parentSuite: {
+            id: 10,
+            name: "",
+          },
+          suiteType: 2,
+        },
+        "proj1",
+        2
+      );
+      expect(result.content[0].text).toBe(
+        JSON.stringify(
+          {
+            id: 15,
+            name: "Child Test Suite",
+            parentSuite: { id: 10 },
+          },
+          null,
+          2
+        )
+      );
+    });
+
+    it("should handle empty or null response from createTestSuite", async () => {
+      configureTestPlanTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "testplan_create_test_suite");
+      if (!call) throw new Error("testplan_create_test_suite tool not registered");
+      const [, , , handler] = call;
+
+      (mockTestPlanApi.createTestSuite as jest.Mock).mockResolvedValue(null);
+      const params = {
+        project: "proj1",
+        planId: 1,
+        parentSuiteId: 5,
+        name: "Empty Response Suite",
+      };
+      const result = await handler(params);
+
+      expect(result.content[0].text).toBe(JSON.stringify(null, null, 2));
+    });
+  });
+
   describe("list_test_cases tool", () => {
     it("should call getTestCaseList with the correct parameters and return the expected result", async () => {
       configureTestPlanTools(server, tokenProvider, connectionProvider);