Skip to main content
Glama
ennuiii

Azure DevOps MCP Server with PAT Authentication

by ennuiii

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
NameRequiredDescriptionDefault
areaPathNoThe area path for the test case.
iterationPathNoThe iteration path for the test case.
priorityNoThe priority of the test case.
projectYesThe unique identifier (ID or name) of the Azure DevOps project.
stepsNoThe 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.
titleYesThe title of the test case.

Implementation Reference

  • 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) }],
      };
    }
  • 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."),
    },
  • 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) }],
        };
      }
    );
  • 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;
    }
  • 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;
        }
      });
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden of behavioral disclosure. While 'Creates' implies a write/mutation operation, the description doesn't mention authentication requirements, permission levels needed, whether the creation is reversible, what happens on failure, or what the response looks like. For a creation tool with zero annotation coverage, this leaves significant behavioral questions unanswered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that states the core purpose without any wasted words. It's appropriately sized and front-loaded with the essential information. Every word earns its place in this minimal but complete statement of function.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given this is a creation tool with 6 parameters, no annotations, and no output schema, the description is minimally adequate but incomplete. While it states what the tool does, it doesn't provide behavioral context, usage guidance, or output expectations. The 100% schema coverage helps, but for a mutation tool with no safety annotations, more behavioral disclosure would be beneficial.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 6 parameters thoroughly. The description adds no additional parameter information beyond what's in the schema. According to scoring rules, when schema_description_coverage is high (>80%), the baseline is 3 even with no param info in description, which applies here.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Creates') and resource ('a new test case work item'), providing specific verb+resource pairing. However, it doesn't differentiate from sibling tools like 'wit_create_work_item' or 'testplan_add_test_cases_to_suite', which could create confusion about when to use this specific test case creation tool versus other creation tools in the system.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With multiple sibling creation tools (wit_create_work_item, testplan_add_test_cases_to_suite, testplan_create_test_plan), there's no indication of when this specific test case creation tool is appropriate versus other work item creation tools. No prerequisites, exclusions, or comparison to alternatives are mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ennuiii/DevOpsMcpPAT'

If you have feedback or need assistance with the MCP directory API, please join our Discord server