Skip to main content
Glama
Tiberriver256

Azure DevOps MCP Server

get_project_details

Retrieve project details from Azure DevOps, including process information, work item types with their structure and fields, and associated teams with identity expansion.

Instructions

Get comprehensive details of a project including process, work item types, and teams

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectIdNoThe ID or name of the project (Default: MyProject)
organizationIdNoThe ID or name of the organization (Default: mycompany)
includeProcessNoInclude process information in the project result
includeWorkItemTypesNoInclude work item types and their structure
includeFieldsNoInclude field information for work item types
includeTeamsNoInclude associated teams in the project result
expandTeamIdentityNoExpand identity information in the team objects

Implementation Reference

  • The core handler function that implements the get_project_details tool logic. Fetches comprehensive project details including process info, work item types, fields, and teams using Azure DevOps Node API.
    export async function getProjectDetails(
      connection: WebApi,
      options: GetProjectDetailsOptions,
    ): Promise<ProjectDetails> {
      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 AzureDevOpsResourceNotFoundError(
            `Project '${projectId}' not found`,
          );
        }
    
        // Initialize the result with the project information and ensure required properties
        const result: ProjectDetails = {
          ...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: 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: WorkItemTypeInfo[] = workItemTypes.map(
              (wit) => {
                // Create the work item type info object
                const workItemTypeInfo: 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: WorkItemTypeField) => ({
                        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: WorkItemTypeInfo) => wit.name.toLowerCase() === 'epic',
                    )
                    .map((wit: WorkItemTypeInfo) => wit.name),
                },
                {
                  name: 'Features',
                  workItemTypes: processWorkItemTypes
                    .filter(
                      (wit: WorkItemTypeInfo) =>
                        wit.name.toLowerCase() === 'feature',
                    )
                    .map((wit: WorkItemTypeInfo) => wit.name),
                },
              ],
              requirementBacklog: {
                name: 'Stories',
                workItemTypes: processWorkItemTypes
                  .filter(
                    (wit: WorkItemTypeInfo) =>
                      wit.name.toLowerCase() === 'user story' ||
                      wit.name.toLowerCase() === 'bug',
                  )
                  .map((wit: WorkItemTypeInfo) => wit.name),
              },
              taskBacklog: {
                name: 'Tasks',
                workItemTypes: processWorkItemTypes
                  .filter(
                    (wit: WorkItemTypeInfo) => wit.name.toLowerCase() === 'task',
                  )
                  .map((wit: WorkItemTypeInfo) => wit.name),
              },
            };
          }
    
          // Always set the process on the result
          result.process = processInfo;
        }
    
        return result;
      } catch (error) {
        if (error instanceof AzureDevOpsError) {
          throw error;
        }
        throw new Error(
          `Failed to get project details: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }
  • Zod schema defining the input parameters for the get_project_details tool, including projectId and optional flags for additional details.
    export const GetProjectDetailsSchema = z.object({
      projectId: z
        .string()
        .optional()
        .describe(`The ID or name of the project (Default: ${defaultProject})`),
      organizationId: z
        .string()
        .optional()
        .describe(`The ID or name of the organization (Default: ${defaultOrg})`),
      includeProcess: z
        .boolean()
        .optional()
        .default(false)
        .describe('Include process information in the project result'),
      includeWorkItemTypes: z
        .boolean()
        .optional()
        .default(false)
        .describe('Include work item types and their structure'),
      includeFields: z
        .boolean()
        .optional()
        .default(false)
        .describe('Include field information for work item types'),
      includeTeams: z
        .boolean()
        .optional()
        .default(false)
        .describe('Include associated teams in the project result'),
      expandTeamIdentity: z
        .boolean()
        .optional()
        .default(false)
        .describe('Expand identity information in the team objects'),
    });
  • Tool registration definition specifying the name, description, and input schema for the get_project_details tool.
    {
      name: 'get_project_details',
      description:
        'Get comprehensive details of a project including process, work item types, and teams',
      inputSchema: zodToJsonSchema(GetProjectDetailsSchema),
    },
  • MCP request handler switch case that parses arguments and invokes the getProjectDetails handler function.
    case 'get_project_details': {
      const args = GetProjectDetailsSchema.parse(request.params.arguments);
      const result = await getProjectDetails(connection, {
        projectId: args.projectId ?? defaultProject,
        includeProcess: args.includeProcess,
        includeWorkItemTypes: args.includeWorkItemTypes,
        includeFields: args.includeFields,
        includeTeams: args.includeTeams,
        expandTeamIdentity: args.expandTeamIdentity,
      });
      return {
        content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
      };
    }
  • TypeScript interface defining the options parameter for the getProjectDetails handler function.
    export interface GetProjectDetailsOptions {
      projectId: string;
      includeProcess?: boolean;
      includeWorkItemTypes?: boolean;
      includeFields?: boolean;
      includeTeams?: boolean;
      expandTeamIdentity?: boolean;
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It states this is a read operation ('Get'), which is helpful, but doesn't mention authentication requirements, rate limits, error conditions, or what the return format looks like. For a tool with 7 parameters and no output schema, this leaves significant behavioral gaps.

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 immediately states the tool's purpose and scope. There's no wasted language or unnecessary elaboration, making it easy for an agent to parse quickly.

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 the 7 parameters with full schema coverage but no annotations and no output schema, the description provides adequate basic purpose but lacks behavioral context needed for a read operation of this complexity. It doesn't explain what 'comprehensive details' means in practice or how the boolean parameters interact, leaving gaps for the agent.

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?

The schema description coverage is 100%, so the schema already documents all 7 parameters thoroughly. The description mentions the included details (process, work item types, teams) which aligns with some boolean parameters, but adds no additional semantic context beyond what the schema provides. This meets the baseline for high schema coverage.

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 tool's purpose with a specific verb ('Get') and resource ('project'), specifying what details are included (process, work item types, teams). It doesn't explicitly differentiate from sibling tools like 'get_project' or 'list_projects', but the focus on comprehensive details provides some implicit distinction.

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 like 'get_project' or 'list_projects' from the sibling list. It mentions what details are included but doesn't specify use cases, prerequisites, or exclusions, leaving the agent to infer usage context.

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

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/Tiberriver256/mcp-server-azure-devops'

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