testplan_create_test_case
Creates a test case work item in Azure DevOps by defining project, title, steps, priority, area path, and iteration path. Streamlines test case management via PAT authentication.
Instructions
Creates a new test case work item.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| areaPath | No | The area path for the test case. | |
| iterationPath | No | The iteration path for the test case. | |
| priority | No | The priority of the test case. | |
| project | Yes | The unique identifier (ID or name) of the Azure DevOps project. | |
| steps | No | The steps to reproduce the test case. Make sure to format each step as '1. Step one|Expected result one 2. Step two|Expected result two. USE '|' as the delimiter between step and expected result. DO NOT use '|' in the description of the step or expected result. | |
| title | Yes | The title of the test case. |
Implementation Reference
- src/tools/testplans.ts:128-183 (handler)Handler function that creates a Test Case work item using the Azure DevOps Work Item Tracking API. Constructs a JSON patch document with title, steps (converted to XML), priority, areaPath, and iterationPath. Returns the created work item as JSON.async ({ project, title, steps, priority, areaPath, iterationPath }) => { const connection = await connectionProvider(); const witClient = await connection.getWorkItemTrackingApi(); let stepsXml; if (steps) { stepsXml = convertStepsToXml(steps); } // Create JSON patch document for work item const patchDocument = []; patchDocument.push({ op: "add", path: "/fields/System.Title", value: title, }); if (stepsXml) { patchDocument.push({ op: "add", path: "/fields/Microsoft.VSTS.TCM.Steps", value: stepsXml, }); } if (priority) { patchDocument.push({ op: "add", path: "/fields/Microsoft.VSTS.Common.Priority", value: priority, }); } if (areaPath) { patchDocument.push({ op: "add", path: "/fields/System.AreaPath", value: areaPath, }); } if (iterationPath) { patchDocument.push({ op: "add", path: "/fields/System.IterationPath", value: iterationPath, }); } const workItem = await witClient.createWorkItem({}, patchDocument, project, "Test Case"); return { content: [{ type: "text", text: JSON.stringify(workItem, null, 2) }], }; }
- src/tools/testplans.ts:116-127 (schema)Zod input schema defining parameters for creating a test case: project, title, optional steps (formatted string), priority, areaPath, iterationPath.project: z.string().describe("The unique identifier (ID or name) of the Azure DevOps project."), title: z.string().describe("The title of the test case."), steps: z .string() .optional() .describe( "The steps to reproduce the test case. Make sure to format each step as '1. Step one|Expected result one\n2. Step two|Expected result two. USE '|' as the delimiter between step and expected result. DO NOT use '|' in the description of the step or expected result." ), priority: z.number().optional().describe("The priority of the test case."), areaPath: z.string().optional().describe("The area path for the test case."), iterationPath: z.string().optional().describe("The iteration path for the test case."), },
- src/tools/testplans.ts:112-184 (registration)Registration of the 'testplan_create_test_case' tool on the MCP server using server.tool(), referencing Test_Plan_Tools.create_test_case as the tool name, with description, input schema, and handler function.server.tool( Test_Plan_Tools.create_test_case, "Creates a new test case work item.", { project: z.string().describe("The unique identifier (ID or name) of the Azure DevOps project."), title: z.string().describe("The title of the test case."), steps: z .string() .optional() .describe( "The steps to reproduce the test case. Make sure to format each step as '1. Step one|Expected result one\n2. Step two|Expected result two. USE '|' as the delimiter between step and expected result. DO NOT use '|' in the description of the step or expected result." ), priority: z.number().optional().describe("The priority of the test case."), areaPath: z.string().optional().describe("The area path for the test case."), iterationPath: z.string().optional().describe("The iteration path for the test case."), }, async ({ project, title, steps, priority, areaPath, iterationPath }) => { const connection = await connectionProvider(); const witClient = await connection.getWorkItemTrackingApi(); let stepsXml; if (steps) { stepsXml = convertStepsToXml(steps); } // Create JSON patch document for work item const patchDocument = []; patchDocument.push({ op: "add", path: "/fields/System.Title", value: title, }); if (stepsXml) { patchDocument.push({ op: "add", path: "/fields/Microsoft.VSTS.TCM.Steps", value: stepsXml, }); } if (priority) { patchDocument.push({ op: "add", path: "/fields/Microsoft.VSTS.Common.Priority", value: priority, }); } if (areaPath) { patchDocument.push({ op: "add", path: "/fields/System.AreaPath", value: areaPath, }); } if (iterationPath) { patchDocument.push({ op: "add", path: "/fields/System.IterationPath", value: iterationPath, }); } const workItem = await witClient.createWorkItem({}, patchDocument, project, "Test Case"); return { content: [{ type: "text", text: JSON.stringify(workItem, null, 2) }], }; } );
- src/tools/testplans.ts:234-259 (helper)Helper function to parse steps string and generate XML for the Microsoft.VSTS.TCM.Steps field in test case work items.function convertStepsToXml(steps: string): string { // Accepts steps in the format: '1. Step one|Expected result one\n2. Step two|Expected result two' const stepsLines = steps.split("\n").filter((line) => line.trim() !== ""); let xmlSteps = `<steps id="0" last="${stepsLines.length}">`; for (let i = 0; i < stepsLines.length; i++) { const stepLine = stepsLines[i].trim(); if (stepLine) { // Split step and expected result by '|', fallback to default if not provided const [stepPart, expectedPart] = stepLine.split("|").map((s) => s.trim()); const stepMatch = stepPart.match(/^(\d+)\.\s*(.+)$/); const stepText = stepMatch ? stepMatch[2] : stepPart; const expectedText = expectedPart || "Verify step completes successfully"; xmlSteps += ` <step id="${i + 1}" type="ActionStep"> <parameterizedString isformatted="true">${escapeXml(stepText)}</parameterizedString> <parameterizedString isformatted="true">${escapeXml(expectedText)}</parameterizedString> </step>`; } } xmlSteps += "</steps>"; return xmlSteps; }
- src/tools/testplans.ts:264-281 (helper)Utility function to escape XML special characters, used in convertStepsToXml.function escapeXml(unsafe: string): string { return unsafe.replace(/[<>&'"]/g, (c) => { switch (c) { case "<": return "<"; case ">": return ">"; case "&": return "&"; case "'": return "'"; case '"': return """; default: return c; } }); }