pipeline_timeline
Retrieve and filter Azure DevOps pipeline run stages and jobs by state and result to analyze execution progress and outcomes.
Instructions
Retrieve the timeline of stages and jobs for a pipeline run, to reduce the amount of data returned, you can filter by state and result
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | No | The ID or name of the project (Default: MyProject) | |
| runId | Yes | Run identifier | |
| timelineId | No | Optional timeline identifier to select a specific timeline record | |
| pipelineId | No | Optional pipeline numeric ID for reference only | |
| state | No | Optional state filter (single value or array) applied to returned timeline records | |
| result | No | Optional result filter (single value or array) applied to returned timeline records |
Implementation Reference
- The core handler function `getPipelineTimeline` that fetches the pipeline timeline from Azure DevOps API, applies optional filters by state and result, and handles errors.export async function getPipelineTimeline( connection: WebApi, options: GetPipelineTimelineOptions, ): Promise<PipelineTimeline> { try { const buildApi = await connection.getBuildApi(); const projectId = options.projectId ?? defaultProject; const { runId, timelineId, state, result } = options; const route = `${encodeURIComponent(projectId)}/_apis/build/builds/${runId}/timeline`; const baseUrl = connection.serverUrl.replace(/\/+$/, ''); const url = new URL(`${route}`, `${baseUrl}/`); url.searchParams.set('api-version', API_VERSION); if (timelineId) { url.searchParams.set('timelineId', timelineId); } const requestOptions = buildApi.createRequestOptions( 'application/json', API_VERSION, ); const response = await buildApi.rest.get<PipelineTimeline | null>( url.toString(), requestOptions, ); if (response.statusCode === 404 || !response.result) { throw new AzureDevOpsResourceNotFoundError( `Timeline not found for run ${runId} in project ${projectId}`, ); } const timeline = response.result as PipelineTimeline & { records?: TimelineRecord[]; }; const stateFilters = normalizeFilter(state); const resultFilters = normalizeFilter(result); if (Array.isArray(timeline.records) && (stateFilters || resultFilters)) { const filteredRecords = timeline.records.filter((record) => { const recordState = stateToString(record.state); const recordResult = resultToString(record.result); const stateMatch = !stateFilters || (recordState && stateFilters.has(recordState)); const resultMatch = !resultFilters || (recordResult && resultFilters.has(recordResult)); return stateMatch && resultMatch; }); return { ...timeline, records: filteredRecords, } as PipelineTimeline; } return timeline; } catch (error) { if (error instanceof AzureDevOpsError) { throw error; } if (error instanceof Error) { const message = error.message.toLowerCase(); if ( message.includes('authentication') || message.includes('unauthorized') || message.includes('401') ) { throw new AzureDevOpsAuthenticationError( `Failed to authenticate: ${error.message}`, ); } if ( message.includes('not found') || message.includes('does not exist') || message.includes('404') ) { throw new AzureDevOpsResourceNotFoundError( `Pipeline timeline or project not found: ${error.message}`, ); } } throw new AzureDevOpsError( `Failed to retrieve pipeline timeline: ${ error instanceof Error ? error.message : String(error) }`, ); } }
- src/features/pipelines/tool-definitions.ts:47-53 (registration)Tool registration definition in the pipelinesTools array, specifying name, description, input schema, and MCP enabled flag.{ name: 'pipeline_timeline', description: 'Retrieve the timeline of stages and jobs for a pipeline run, to reduce the amount of data returned, you can filter by state and result', inputSchema: zodToJsonSchema(GetPipelineTimelineSchema), mcp_enabled: true, },
- src/features/pipelines/index.ts:120-129 (registration)Dispatcher case in handlePipelinesRequest that parses arguments, calls the handler, and formats the response for the pipeline_timeline tool.case 'pipeline_timeline': { const args = GetPipelineTimelineSchema.parse(request.params.arguments); const result = await getPipelineTimeline(connection, { ...args, projectId: args.projectId ?? defaultProject, }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; }
- Helper function to normalize filter values (state or result) into a Set for efficient matching.function normalizeFilter(value?: string | string[]): Set<string> | undefined { if (!value) { return undefined; } const values = Array.isArray(value) ? value : [value]; const normalized = values .map((item) => (typeof item === 'string' ? item.trim().toLowerCase() : '')) .filter((item) => item.length > 0); return normalized.length > 0 ? new Set(normalized) : undefined; }