Skip to main content
Glama
azure-devops-client.tsβ€’5.81 kB
import * as azdev from 'azure-devops-node-api'; import type { WorkItem } from 'azure-devops-node-api/interfaces/WorkItemTrackingInterfaces.js'; import type { TeamSettingsIteration } from 'azure-devops-node-api/interfaces/WorkInterfaces.js'; export class AzureDevOpsClient { private connection: azdev.WebApi; private orgUrl: string; private project: string; constructor(orgUrl: string, token: string, project: string) { this.orgUrl = orgUrl; this.project = project; const authHandler = azdev.getPersonalAccessTokenHandler(token); this.connection = new azdev.WebApi(orgUrl, authHandler); } // Get all work items by query async getWorkItemsByQuery(wiql: string): Promise<WorkItem[]> { const witApi = await this.connection.getWorkItemTrackingApi(); const queryResult = await witApi.queryByWiql({ query: wiql }, { project: this.project }); if (!queryResult.workItems || queryResult.workItems.length === 0) { return []; } const ids = queryResult.workItems.map(wi => wi.id!); // Azure DevOps API has a limit of 200 work items per request, so batch the requests const batchSize = 200; const workItems: WorkItem[] = []; for (let i = 0; i < ids.length; i += batchSize) { const batchIds = ids.slice(i, i + batchSize); const batchWorkItems = await witApi.getWorkItems(batchIds, undefined, undefined, undefined, 1); workItems.push(...batchWorkItems); } return workItems; } // Get work items for current user in current sprint async getMyCurrentSprintItems(userEmail: string): Promise<WorkItem[]> { const wiql = ` SELECT [System.Id], [System.Title], [System.State], [System.AssignedTo] FROM WorkItems WHERE [System.AssignedTo] = '${userEmail}' AND [System.IterationPath] = @currentIteration ORDER BY [System.ChangedDate] DESC `; return this.getWorkItemsByQuery(wiql); } // Get all tickets by state async getWorkItemsByState(state: string): Promise<WorkItem[]> { const wiql = ` SELECT [System.Id], [System.Title], [System.State], [System.AssignedTo] FROM WorkItems WHERE [System.State] = '${state}' ORDER BY [System.ChangedDate] DESC `; return this.getWorkItemsByQuery(wiql); } // Get current sprint information async getCurrentSprint(): Promise<TeamSettingsIteration> { const workApi = await this.connection.getWorkApi(); const teamContext = { project: this.project, team: this.project }; const iterations = await workApi.getTeamIterations(teamContext, 'current'); return iterations[0]; } // Get work item details async getWorkItem(id: number): Promise<WorkItem> { const witApi = await this.connection.getWorkItemTrackingApi(); return await witApi.getWorkItem(id); } // Get work item comments async getWorkItemComments(id: number): Promise<any> { const witApi = await this.connection.getWorkItemTrackingApi(); try { return await witApi.getComments(this.project, id); } catch (error) { return { comments: [] }; } } // Get all work items in current sprint async getAllCurrentSprintItems(): Promise<WorkItem[]> { const wiql = ` SELECT [System.Id], [System.Title], [System.State], [System.AssignedTo] FROM WorkItems WHERE [System.IterationPath] = @currentIteration ORDER BY [System.State] ASC, [System.ChangedDate] DESC `; return this.getWorkItemsByQuery(wiql); } // Get all user stories in current sprint async getAllCurrentSprintUserStories(): Promise<WorkItem[]> { const wiql = ` SELECT [System.Id], [System.Title], [System.State], [System.AssignedTo] FROM WorkItems WHERE [System.IterationPath] = 'ericssondotcom-vnext\\Sprint 154' AND [System.WorkItemType] = 'User Story' ORDER BY [System.State] ASC, [System.ChangedDate] DESC `; return this.getWorkItemsByQuery(wiql); } // Get child tasks for a work item async getChildTasks(parentId: number): Promise<WorkItem[]> { const witApi = await this.connection.getWorkItemTrackingApi(); const workItem = await witApi.getWorkItem(parentId, undefined, undefined, 1); if (!workItem.relations) { return []; } // Filter child relations const childIds = workItem.relations .filter(rel => rel.rel === 'System.LinkTypes.Hierarchy-Forward') .map(rel => { const url = rel.url!; const id = parseInt(url.substring(url.lastIndexOf('/') + 1)); return id; }); if (childIds.length === 0) { return []; } const children = await witApi.getWorkItems(childIds, undefined, undefined, undefined, 1); return children; } // Get user stories with my verify tasks async getUserStoriesWithMyVerifyTasks(userEmail: string): Promise<WorkItem[]> { // First get all user stories const userStories = await this.getAllCurrentSprintUserStories(); // For each user story, get child tasks const storiesWithVerifyTasks: WorkItem[] = []; for (const story of userStories) { const children = await this.getChildTasks(story.id!); // Check if any child task is assigned to user and title starts with "Verify" const hasMyVerifyTask = children.some(child => { const title = child.fields?.['System.Title'] || ''; const assignedTo = child.fields?.['System.AssignedTo']?.uniqueName || ''; const workItemType = child.fields?.['System.WorkItemType'] || ''; return workItemType === 'Task' && title.startsWith('Verify') && assignedTo === userEmail; }); if (hasMyVerifyTask) { storiesWithVerifyTasks.push(story); } } return storiesWithVerifyTasks; } }

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/LinhDangOpti/mcpserver'

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