Skip to main content
Glama

Azure DevOps MCP Server with PAT Authentication

by ennuiii
work.ts5.89 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { AccessToken } from "@azure/identity"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { WebApi } from "azure-devops-node-api"; import { z } from "zod"; import { TreeStructureGroup } from "azure-devops-node-api/interfaces/WorkItemTrackingInterfaces.js"; const WORK_TOOLS = { list_team_iterations: "work_list_team_iterations", create_iterations: "work_create_iterations", assign_iterations: "work_assign_iterations", }; function configureWorkTools(server: McpServer, tokenProvider: () => Promise<AccessToken>, connectionProvider: () => Promise<WebApi>) { server.tool( WORK_TOOLS.list_team_iterations, "Retrieve a list of iterations for a specific team in a project.", { project: z.string().describe("The name or ID of the Azure DevOps project."), team: z.string().describe("The name or ID of the Azure DevOps team."), timeframe: z.enum(["current"]).optional().describe("The timeframe for which to retrieve iterations. Currently, only 'current' is supported."), }, async ({ project, team, timeframe }) => { try { const connection = await connectionProvider(); const workApi = await connection.getWorkApi(); const iterations = await workApi.getTeamIterations({ project, team }, timeframe); if (!iterations) { return { content: [{ type: "text", text: "No iterations found" }], isError: true }; } return { content: [{ type: "text", text: JSON.stringify(iterations, null, 2) }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; return { content: [{ type: "text", text: `Error fetching team iterations: ${errorMessage}` }], isError: true, }; } } ); server.tool( WORK_TOOLS.create_iterations, "Create new iterations in a specified Azure DevOps project.", { project: z.string().describe("The name or ID of the Azure DevOps project."), iterations: z .array( z.object({ iterationName: z.string().describe("The name of the iteration to create."), startDate: z.string().optional().describe("The start date of the iteration in ISO format (e.g., '2023-01-01T00:00:00Z'). Optional."), finishDate: z.string().optional().describe("The finish date of the iteration in ISO format (e.g., '2023-01-31T23:59:59Z'). Optional."), }) ) .describe("An array of iterations to create. Each iteration must have a name and can optionally have start and finish dates in ISO format."), }, async ({ project, iterations }) => { try { const connection = await connectionProvider(); const workItemTrackingApi = await connection.getWorkItemTrackingApi(); const results = []; for (const { iterationName, startDate, finishDate } of iterations) { // Step 1: Create the iteration const iteration = await workItemTrackingApi.createOrUpdateClassificationNode( { name: iterationName, attributes: { startDate: startDate ? new Date(startDate) : undefined, finishDate: finishDate ? new Date(finishDate) : undefined, }, }, project, TreeStructureGroup.Iterations ); if (iteration) { results.push(iteration); } } if (results.length === 0) { return { content: [{ type: "text", text: "No iterations were created" }], isError: true }; } return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; return { content: [{ type: "text", text: `Error creating iterations: ${errorMessage}` }], isError: true, }; } } ); server.tool( WORK_TOOLS.assign_iterations, "Assign existing iterations to a specific team in a project.", { project: z.string().describe("The name or ID of the Azure DevOps project."), team: z.string().describe("The name or ID of the Azure DevOps team."), iterations: z .array( z.object({ identifier: z.string().describe("The identifier of the iteration to assign."), path: z.string().describe("The path of the iteration to assign, e.g., 'Project/Iteration'."), }) ) .describe("An array of iterations to assign. Each iteration must have an identifier and a path."), }, async ({ project, team, iterations }) => { try { const connection = await connectionProvider(); const workApi = await connection.getWorkApi(); const teamContext = { project, team }; const results = []; for (const { identifier, path } of iterations) { const assignment = await workApi.postTeamIteration({ path: path, id: identifier }, teamContext); if (assignment) { results.push(assignment); } } if (results.length === 0) { return { content: [{ type: "text", text: "No iterations were assigned to the team" }], isError: true }; } return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; return { content: [{ type: "text", text: `Error assigning iterations: ${errorMessage}` }], isError: true, }; } } ); } export { WORK_TOOLS, configureWorkTools };

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