Skip to content

Connect Buildkite to AI Agents: Automate Tests, Runs, and Artifacts

Roopendra Talekar Roopendra Talekar 9 min read AI & Agents
TrutoFor teams building AI agents

Give your AI agent Buildkite tools.

Connect Buildkite to AI agents natively using Truto's /tools endpoint. Fetch JSON schemas for Buildkite endpoints, bind them to your LLM, and automate CI/CD jobs, artifact retrieval, and test retries without hardcoding API calls.

In this guide

  1. 01Initialize your LLM Framework
  2. 02Fetch Buildkite Tools via Truto
  3. 03Bind Tools to the Model
  4. 04Execute the Agent Loop
  5. 05Handle Tool Execution and Rate Limits

The guide

Learn how to connect Buildkite to AI agents using Truto's /tools endpoint. Build autonomous CI/CD workflows, retry jobs, and fetch artifacts with LangChain.

You want to connect Buildkite to an AI agent so your system can autonomously trigger builds, retry failed jobs, analyze test artifacts, and manage CI/CD agent queues. Here is exactly how to do it using Truto's /tools endpoint and SDK, bypassing the need to build and maintain a custom Buildkite API integration from scratch.

Giving a Large Language Model (LLM) read and write access to your CI/CD infrastructure requires strict schema enforcement and predictable state management. If your team uses ChatGPT, check out our guide on connecting Buildkite to ChatGPT, or if you are building on Anthropic's models, read our guide on connecting Buildkite to Claude. For developers building custom autonomous workflows, you need a programmatic way to fetch these tools and bind them natively to your agent framework.

This guide breaks down exactly how to fetch AI-ready tools for Buildkite, bind them natively to an LLM using frameworks like LangChain, LangGraph, CrewAI, or the Vercel AI SDK, and execute complex infrastructure automation. For a deeper look at the architecture behind this approach, refer to our research on architecting AI agents and the SaaS integration bottleneck.

The Engineering Reality of the Buildkite API

Giving an LLM access to external data sounds simple in a prototype. You write a Node.js function that makes a fetch request and wrap it in an @tool decorator. In production against complex infrastructure systems like Buildkite, this naive approach collapses.

Buildkite's API introduces several specific integration challenges that break standard REST assumptions. If you hardcode these interactions into your agent, you will spend your sprints writing defensive integration code instead of improving your model's reasoning capabilities.

The Build ID vs. Build Number Trap

One of the most common ways LLMs hallucinate against the Buildkite API is by confusing identifiers. Buildkite uses global UUIDs for many objects, but for several critical pipeline operations - like fetching artifacts or creating annotations - the API strictly requires the sequential build_number (e.g., 42), not the global build_id (e.g., b1234567-89ab-cdef-0123-456789abcdef).

Standard LLMs trained on general REST API patterns assume that if they have an id field from a build object, they should pass that id to the /artifacts endpoint. When they do this in Buildkite, the request fails with a 404. You must enforce strict JSON schemas that force the LLM to map the correct integer value to the build_number parameter. Truto handles this translation layer automatically, ensuring the LLM only sees the parameters that matter for the specific operation it is trying to perform.

Artifact State and S3 Redirects

Artifact extraction is a core use case for CI/CD AI agents, particularly for analyzing failed test logs. However, downloading a Buildkite artifact is not a single API call. Artifacts exist in states (e.g., new, uploaded, finished), and the actual file content is stored in AWS S3.

When you request an artifact download via the Buildkite API, the response is often a 302 Found redirect containing a temporary, pre-signed S3 URL. If you expose this raw endpoint to an LLM, the model cannot follow the redirect or parse the binary stream. Your tool layer must handle the HTTP redirect, fetch the underlying file, and return the parsed text or structured JSON back to the agent's context window.

Pagination and Rate Limit Realities

Buildkite imposes rate limits on its API to prevent abuse. When building an autonomous agent that might decide to paginate through 1,000 failed jobs to find a pattern, you will inevitably hit these limits.

It is critical to understand that Truto does not automatically retry, throttle, or apply backoff on rate limit errors. When Buildkite returns an HTTP 429 Too Many Requests, Truto passes that error directly to your caller. However, Truto normalizes the upstream rate limit information into standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset) per the IETF specification. This allows your agent framework to catch the 429, read the normalized ratelimit-reset header, and pause execution natively without needing Buildkite-specific logic.

Hero Tools for Buildkite Automation

Instead of exposing the entire Buildkite API surface area to your LLM, you should restrict the model to a high-leverage subset of tools. The Truto /tools endpoint dynamically serves these schemas based on the integration configuration.

Here are six high-impact tools you can bind to your agent for CI/CD automation.

create_a_buildkite_build

This tool allows the agent to trigger a new build for a specific pipeline. It requires the organization slug, pipeline slug, commit hash (or HEAD), and the branch name. This is essential for agents that need to verify code fixes by running the test suite.

"The deploy to staging failed due to a missing environment variable. I have updated the terraform state. Trigger a new build on the infrastructure-deploy pipeline for the main branch to verify the fix."

list_all_buildkite_jobs

This tool retrieves all jobs associated with a specific build. Because builds can contain dozens of parallel jobs, this tool allows the agent to filter by state (e.g., failed) to identify exactly which step in the pipeline caused the failure.

"Build number 844 on the api-service pipeline just failed. List all jobs for this build and filter for the ones that ended in a failed state so I can see which test suite crashed."

buildkite_jobs_retry_by_job_id

When an agent identifies a flaky test or a transient infrastructure failure, it can use this tool to retry a specific failed or timed-out job without restarting the entire build. This creates a new scheduled job attempt for the same pipeline step.

"Job ID j-12345 failed due to a network timeout reaching the NPM registry. This looks like a transient issue. Retry this specific job."

buildkite_artifacts_download_by_job_id

This tool fetches the temporary download URL for an artifact generated by a specific job. Agents use this to read test coverage reports, error logs, or binary outputs to diagnose why a pipeline failed.

"Download the artifact named cypress-screenshots.zip from job ID j-98765 so I can analyze the UI test failure."

buildkite_agents_pause

This administrative tool allows an agent to temporarily pause a Buildkite agent. This prevents new jobs from being dispatched to it while allowing the current job to finish. This is highly useful for autonomous infrastructure scaling or maintenance workflows.

"Agent i-0abcd1234 is reporting 99% disk usage. Pause this agent so no new jobs are scheduled on it while I run a cleanup script."

create_a_buildkite_annotation

Annotations allow agents to communicate their findings directly in the Buildkite UI. If an agent analyzes a failed test and discovers the root cause, it can create a markdown annotation on the build to alert human developers.

"I found the root cause of the failure in build 402. Create a 'warning' style annotation on the build explaining that the database migration script is missing a default value for the status column."

To view the complete inventory of available Buildkite tools, including parameter requirements and schema definitions, visit the Buildkite integration page.

Workflows in Action

When you equip an LLM with these standardized tools, you enable workflows that previously required custom integration scripts and dedicated maintenance. Here is how an agent executes real-world CI/CD operations.

Scenario 1: Automated Flaky Test Remediation

DevOps teams lose countless hours manually restarting builds that fail due to flaky UI tests or transient network timeouts. An AI agent can monitor webhooks, analyze the failure, and act autonomously.

"Build 1042 on frontend-web failed. Check which jobs failed, read their logs, and if it is the known Cypress flaky test, retry the job and annotate the build."

  1. list_all_buildkite_jobs: The agent fetches the jobs for build 1042, filtering for state=failed.
  2. buildkite_jobs_get_log_by_job_id: The agent reads the log output for the failed job.
  3. LLM Reasoning: The agent parses the log and identifies the failure as a known TimeoutError from the Cypress runner.
  4. buildkite_jobs_retry_by_job_id: The agent triggers a retry for the specific failed job.
  5. create_a_buildkite_annotation: The agent posts a message to the Buildkite UI: "Detected transient Cypress timeout. Automatically retrying job..."

Scenario 2: Infrastructure Queue Management

Platform engineering teams often struggle with queue backlogs when dozens of developers push code simultaneously. An agent can monitor queue depths and manage agent pools to prevent bottlenecks.

"The macos-builders queue is backed up. Find any agents in that cluster that are currently paused and resume them to help clear the backlog."

  1. list_all_buildkite_agents: The agent fetches the list of all agents, filtering for the macos-builders queue and checking their connection_state.
  2. buildkite_agents_resume: For every agent that returns a paused status, the AI executes the resume tool.
  3. LLM Reasoning: The agent verifies the API returned 204 No Content for each resume operation.
  4. Final Output: The agent replies to the user confirming exactly which agents were unpaused.

Building Multi-Step Workflows

To build these autonomous workflows, your agent needs a way to fetch the tool schemas and bind them to the LLM. Using Truto's TrutoToolManager from the truto-langchainjs-toolset, you can dynamically inject the Buildkite Proxy APIs directly into your framework.

This approach is entirely framework-agnostic. Whether you use LangChain, LangGraph, or the Vercel AI SDK, the core concept remains the same: you request the tools for your Integrated Account ID, bind them to the model, and handle the execution loop.

sequenceDiagram
    participant App as Your Agent App
    participant Truto as Truto Tool Manager
    participant LLM as LLM (OpenAI/Claude)
    participant Upstream as "Buildkite API"

    App->>Truto: GET /integrated-account/{id}/tools
    Truto-->>App: Return Array of JSON Schemas
    App->>LLM: .bindTools(schemas)
    App->>LLM: invoke("Check failed jobs on build 55")
    LLM-->>App: ToolCall: list_all_buildkite_jobs({build_number: 55})
    App->>Truto: Execute Proxy API request
    Truto->>Upstream: GET /v2/organizations/.../jobs
    Upstream-->>Truto: 200 OK (Job List)
    Truto-->>App: Normalized JSON Data
    App->>LLM: invoke(ToolMessage(data))
    LLM-->>App: "I found the failed job. It was the test suite."

Implementation Code (LangChain.js)

Here is a complete example of how to fetch Buildkite tools, handle tool execution, and implement a robust backoff strategy for API rate limits. Remember, Truto normalizes the rate limit headers, so you can inspect them directly if a 429 occurs.

import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, ToolMessage } from "@langchain/core/messages";
import { TrutoToolManager } from "truto-langchainjs-toolset";
 
async function runBuildkiteAgent(prompt: string) {
  // 1. Initialize the LLM
  const model = new ChatOpenAI({ 
    modelName: "gpt-4o", 
    temperature: 0 
  });
 
  // 2. Fetch tools using your Truto API key and the specific Buildkite account ID
  const toolManager = new TrutoToolManager({
    apiKey: process.env.TRUTO_API_KEY,
    integratedAccountId: "your-buildkite-account-id",
  });
 
  // Optional: Filter tools to only include what the agent needs
  const tools = await toolManager.getTools();
  const modelWithTools = model.bindTools(tools);
 
  let messages = [new HumanMessage(prompt)];
  
  // 3. Agent Execution Loop
  while (true) {
    const response = await modelWithTools.invoke(messages);
    messages.push(response);
 
    if (!response.tool_calls || response.tool_calls.length === 0) {
      // The model has finished reasoning and returned a final answer
      console.log("Agent Answer:", response.content);
      break;
    }
 
    // 4. Handle Tool Calls
    for (const toolCall of response.tool_calls) {
      console.log(`Executing tool: ${toolCall.name}`);
      
      let toolResult;
      try {
        // The toolManager handles the HTTP request to Truto's Proxy API
        const result = await toolManager.executeTool(toolCall.name, toolCall.args);
        toolResult = JSON.stringify(result);
      } catch (error: any) {
        // 5. Handle Rate Limits (HTTP 429) natively
        if (error.status === 429) {
          const resetTime = error.headers['ratelimit-reset'];
          const waitMs = (resetTime * 1000) - Date.now();
          console.warn(`Rate limit hit. Waiting ${waitMs}ms before continuing...`);
          
          // Implementation of sleep/backoff would go here
          // For this example, we return the error to the LLM context
          toolResult = `Error: Rate limit exceeded. Try again later.`;
        } else {
          toolResult = `Error executing tool: ${error.message}`;
        }
      }
 
      // Pass the result back to the LLM
      messages.push(new ToolMessage({
        tool_call_id: toolCall.id,
        content: toolResult,
      }));
    }
  }
}
 
// Execute the workflow
runBuildkiteAgent("Find the failed job in build 100 on the 'api' pipeline and retry it.");

This architecture ensures that your core agent logic is entirely separated from the underlying Buildkite API semantics. The model never has to write custom HTTP requests, parse GraphQL responses, or guess at API URLs.

Architecting for Scale

Hardcoding API requests into your AI agents creates a massive maintenance burden. Every time an API changes, every time a pagination cursor behaves differently, and every time a vendor updates their OAuth flow, your agent breaks.

By utilizing Truto's /tools endpoint to generate dynamic, JSON-schema-backed tools, you abstract away the API complexity. Your agent interacts with a stable, normalized interface, drastically reducing hallucination rates and allowing your engineering team to focus on prompt orchestration rather than integration maintenance.

Two ways to put Buildkite to work

Truto

For product teams

Give your agent Buildkite tools

Your customers connect their own Buildkite accounts. Your product gets one API and MCP tools for Buildkite, through Truto.

FAQ

How do I connect Buildkite to an AI agent framework like LangChain?
You can connect Buildkite to LangChain by using Truto's /tools endpoint. Truto converts Buildkite API endpoints into strict JSON schemas. You can use the TrutoToolManager from the truto-langchainjs-toolset to fetch these tools and use `.bindTools()` to expose them to your LLM.
Does Truto automatically handle Buildkite API rate limits?
No, Truto does not automatically retry, throttle, or absorb rate limit errors. If Buildkite returns an HTTP 429, Truto passes that error to your caller and normalizes the rate limit information into standard IETF headers (`ratelimit-limit`, `ratelimit-remaining`, `ratelimit-reset`). Your application code is responsible for implementing retry and backoff logic.
Can AI agents download and analyze Buildkite artifacts?
Yes. By exposing the `buildkite_artifacts_download_by_job_id` tool to your agent, the LLM can request the pre-signed S3 download URL for a specific artifact. Your application layer can then download the file and pass the contents back into the LLM's context window for analysis.
What is the difference between a Buildkite build ID and a build number?
A build ID is a globally unique UUID used for internal identification, while a build number is a sequential integer specific to a pipeline (e.g., build #42). Many Buildkite endpoints require the build number rather than the UUID. Truto's strict tool schemas ensure the LLM knows exactly which identifier to provide, preventing hallucination errors.
Buildkite BuildkiteAI agent tools Get a sandbox

More from our Blog