UNPKG

@tiberriver256/mcp-server-azure-devops

Version:

Azure DevOps reference server for the Model Context Protocol (MCP)

185 lines 9.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getProjectDetails = getProjectDetails; const errors_1 = require("../../../shared/errors"); /** * Get detailed information about a project * * @param connection The Azure DevOps WebApi connection * @param options Options for getting project details * @returns The project details * @throws {AzureDevOpsResourceNotFoundError} If the project is not found */ async function getProjectDetails(connection, options) { try { const { projectId, includeProcess = false, includeWorkItemTypes = false, includeFields = false, includeTeams = false, expandTeamIdentity = false, } = options; // Get the core API const coreApi = await connection.getCoreApi(); // Get the basic project information const project = await coreApi.getProject(projectId); if (!project) { throw new errors_1.AzureDevOpsResourceNotFoundError(`Project '${projectId}' not found`); } // Initialize the result with the project information and ensure required properties const result = { ...project, // Ensure capabilities is always defined capabilities: project.capabilities || { versioncontrol: { sourceControlType: 'Git' }, processTemplate: { templateName: 'Unknown', templateTypeId: 'unknown' }, }, }; // If teams are requested, get them if (includeTeams) { const teams = await coreApi.getTeams(projectId, expandTeamIdentity); result.teams = teams; } // If process information is requested, get it if (includeProcess) { // Get the process template ID from the project capabilities const processTemplateId = project.capabilities?.processTemplate?.templateTypeId || 'unknown'; // Always create a process object, even if we don't have a template ID // In a real implementation, we would use the Process API // Since it's not directly available in the WebApi type, we'll simulate it // This is a simplified version for the implementation // In a real implementation, you would need to use the appropriate API // Create the process info object directly const processInfo = { id: processTemplateId, name: project.capabilities?.processTemplate?.templateName || 'Unknown', description: 'Process template for the project', isDefault: true, type: 'system', }; // If work item types are requested, get them if (includeWorkItemTypes) { // In a real implementation, we would get work item types from the API // For now, we'll use the work item tracking API to get basic types const workItemTrackingApi = await connection.getWorkItemTrackingApi(); const workItemTypes = await workItemTrackingApi.getWorkItemTypes(projectId); // Map the work item types to our format const processWorkItemTypes = workItemTypes.map((wit) => { // Create the work item type info object const workItemTypeInfo = { name: wit.name || 'Unknown', referenceName: wit.referenceName || `System.Unknown.${Date.now()}`, description: wit.description, isDisabled: false, states: [ { name: 'New', stateCategory: 'Proposed' }, { name: 'Active', stateCategory: 'InProgress' }, { name: 'Resolved', stateCategory: 'InProgress' }, { name: 'Closed', stateCategory: 'Completed' }, ], }; // If fields are requested, don't add fields here - we'll add them after fetching from API return workItemTypeInfo; }); // If fields are requested, get the field definitions from the API if (includeFields) { try { // Instead of getting all fields and applying them to all work item types, // let's get the fields specific to each work item type for (const wit of processWorkItemTypes) { try { // Get fields specific to this work item type using the specialized method const typeSpecificFields = await workItemTrackingApi.getWorkItemTypeFieldsWithReferences(projectId, wit.name); // Map the fields to our format wit.fields = typeSpecificFields.map((field) => ({ name: field.name || 'Unknown', referenceName: field.referenceName || 'Unknown', type: field.type?.toString().toLowerCase() || 'string', required: field.isRequired || false, isIdentity: field.isIdentity || false, isPicklist: field.isPicklist || false, description: field.description, })); } catch (typeFieldError) { console.error(`Error fetching fields for work item type ${wit.name}:`, typeFieldError); // Fallback to basic fields wit.fields = [ { name: 'Title', referenceName: 'System.Title', type: 'string', required: true, }, { name: 'Description', referenceName: 'System.Description', type: 'html', required: false, }, ]; } } } catch (fieldError) { console.error('Error in field processing:', fieldError); // Fallback to default fields if API call fails processWorkItemTypes.forEach((wit) => { wit.fields = [ { name: 'Title', referenceName: 'System.Title', type: 'string', required: true, }, { name: 'Description', referenceName: 'System.Description', type: 'html', required: false, }, ]; }); } } processInfo.workItemTypes = processWorkItemTypes; // Add hierarchy information if available // This is a simplified version - in a real implementation, you would // need to get the backlog configuration and map it to the work item types processInfo.hierarchyInfo = { portfolioBacklogs: [ { name: 'Epics', workItemTypes: processWorkItemTypes .filter((wit) => wit.name.toLowerCase() === 'epic') .map((wit) => wit.name), }, { name: 'Features', workItemTypes: processWorkItemTypes .filter((wit) => wit.name.toLowerCase() === 'feature') .map((wit) => wit.name), }, ], requirementBacklog: { name: 'Stories', workItemTypes: processWorkItemTypes .filter((wit) => wit.name.toLowerCase() === 'user story' || wit.name.toLowerCase() === 'bug') .map((wit) => wit.name), }, taskBacklog: { name: 'Tasks', workItemTypes: processWorkItemTypes .filter((wit) => wit.name.toLowerCase() === 'task') .map((wit) => wit.name), }, }; } // Always set the process on the result result.process = processInfo; } return result; } catch (error) { if (error instanceof errors_1.AzureDevOpsError) { throw error; } throw new Error(`Failed to get project details: ${error instanceof Error ? error.message : String(error)}`); } } //# sourceMappingURL=feature.js.map