Get Pipeline Logs
get_pipeline_logsFetch logs from Azure DevOps builds to diagnose pipeline failures. Specify pipeline group and name, optionally target a specific build or filter to failed steps only.
Instructions
Fetch logs from a specific Azure DevOps build to investigate pipeline failures.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| group_name | Yes | Pipeline group name | |
| pipeline_name | Yes | Specific pipeline name (e.g. "mcp-ssh-tool CI") | |
| build_id | No | Specific build ID. If omitted, fetches the latest build. | |
| failed_only | No | Only return logs from failed steps |
Implementation Reference
- src/azure-devops.ts:180-222 (handler)Core handler that fetches pipeline build logs from Azure DevOps. Retrieves the build timeline, filters records (optionally for failed steps only), and fetches the last 50 lines of log text for up to 5 steps.
export async function getPipelineLogs( org: string, project: string, buildId: number, pat: string, failedOnly: boolean ): Promise<string> { const timelineUrl = `${BASE}/${org}/${project}/_apis/build/builds/${buildId}/timeline?api-version=7.1`; const timeline = asObject(await azureGet(timelineUrl, pat)); const records = Array.isArray(timeline.records) ? timeline.records.map(asObject) : []; const selected = records.filter((record) => failedOnly ? asString(record.result) === 'failed' && getNestedString(record, 'log', 'url') : getNestedString(record, 'log', 'url') ); if (!selected.length) { return 'No failed steps found or logs not available yet.'; } const authHeader = `Basic ${Buffer.from(`:${pat}`, 'utf8').toString('base64')}`; const parts: string[] = []; for (const record of selected.slice(0, 5)) { const logUrl = getNestedString(record, 'log', 'url'); const stepName = asString(record.name) ?? 'unknown-step'; const result = asString(record.result) ?? 'unknown'; if (!logUrl) { continue; } try { const text = await fetchAzureLogText(logUrl, authHeader); const relevant = text.split('\n').slice(-50).join('\n'); parts.push(`\n=== ${stepName} (${result}) ===\n${relevant}`); } catch { parts.push(`\n=== ${stepName} - log fetch failed ===`); } } return parts.join('\n'); } - src/types.ts:105-114 (schema)Zod schema defining the input parameters for the get_pipeline_logs tool: group_name, pipeline_name, optional build_id, and failed_only (default true).
export const RegisteredPipelineLogsSchema = z.object({ group_name: z.string().describe('Pipeline group name'), pipeline_name: z.string().describe('Specific pipeline name (e.g. "mcp-ssh-tool CI")'), build_id: z .number() .int() .optional() .describe('Specific build ID. If omitted, fetches the latest build.'), failed_only: z.boolean().default(true).describe('Only return logs from failed steps') }); - src/types.ts:134-134 (schema)TypeScript type inferred from the RegisteredPipelineLogsSchema.
export type RegisteredPipelineLogsInput = z.infer<typeof RegisteredPipelineLogsSchema>; - src/app.ts:424-479 (registration)Registration of the 'get_pipeline_logs' tool on the MCP server, including the handler that looks up the pipeline from the registry, optionally fetches the latest build ID, then delegates to the core getPipelineLogs function.
server.registerTool( 'get_pipeline_logs', { title: 'Get Pipeline Logs', description: 'Fetch logs from a specific Azure DevOps build to investigate pipeline failures.', inputSchema: RegisteredPipelineLogsSchema, annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } }, async (input: RegisteredPipelineLogsInput) => { const row = getAzurePipeline(input.group_name, input.pipeline_name); if (!row) { throw new Error(`Pipeline not registered: ${input.group_name}/${input.pipeline_name}`); } if (!row.pipeline_id) { throw new Error(`Pipeline ID not resolved for ${input.group_name}/${input.pipeline_name}`); } const pat = decodePatToken(row.pat_token_encrypted); let buildId = input.build_id; if (!buildId) { const latest = await getLatestRun(row.organization, row.project, row.pipeline_id, pat); buildId = latest?.id; if (latest) { recordPipelineRun(row.group_name, row.pipeline_name, latest); } } if (!buildId) { throw new Error('No recent builds found'); } const logs = await getPipelineLogs( row.organization, row.project, buildId, pat, input.failed_only ); return formatResponse({ group: input.group_name, pipeline: input.pipeline_name, build_id: buildId, logs }); } );