55 "encoding/json"
66 "errors"
77 "fmt"
8+ "strings"
89 "time"
910
1011 "github.com/buildkite/buildkite-mcp-server/pkg/trace"
@@ -76,7 +77,9 @@ type GetBuildArgs struct {
7677 OrgSlug string `json:"org_slug"`
7778 PipelineSlug string `json:"pipeline_slug"`
7879 BuildNumber string `json:"build_number"`
79- DetailLevel string `json:"detail_level"` // summary, detailed, full
80+ DetailLevel string `json:"detail_level"` // summary, detailed, full
81+ JobState string `json:"job_state"` // NEW: comma-separated states
82+ IncludeAgent bool `json:"include_agent"` // NEW: include full agent details
8083}
8184
8285// GetBuildTestEngineRunsArgs struct
@@ -104,29 +107,30 @@ func summarizeBuild(build buildkite.Build) BuildSummary {
104107}
105108
106109// detailBuild converts a buildkite.Build to BuildDetail with job summary
107- func detailBuild (build buildkite.Build ) BuildDetail {
110+ // filteredJobs is used for job_summary stats, while build.Jobs is used for jobs_total
111+ func detailBuild (build buildkite.Build , filteredJobs []buildkite.Job ) BuildDetail {
108112 summary := summarizeBuild (build )
109113
110- // Create job summary
114+ // Create job summary from filtered jobs
111115 jobSummary := & JobSummary {
112- Total : len (build . Jobs ),
116+ Total : len (filteredJobs ),
113117 ByState : make (map [string ]int ),
114118 }
115119
116- for _ , job := range build . Jobs {
120+ for _ , job := range filteredJobs {
117121 if job .State == "" {
118122 continue
119123 }
120124 jobSummary .ByState [job .State ]++
121125 }
122126
123127 return BuildDetail {
124- BuildSummary : summary ,
128+ BuildSummary : summary , // jobs_total reflects ALL jobs (unfiltered)
125129 Source : build .Source ,
126130 Author : build .Author ,
127131 StartedAt : build .StartedAt ,
128132 FinishedAt : build .FinishedAt ,
129- JobSummary : jobSummary ,
133+ JobSummary : jobSummary , // job_summary reflects filtered jobs
130134 }
131135}
132136
@@ -274,7 +278,10 @@ func ListBuilds(client BuildsClient) (tool mcp.Tool, handler mcp.TypedToolHandle
274278 case "summary" :
275279 result = createPaginatedBuildResult (builds , summarizeBuild , headers )
276280 case "detailed" :
277- result = createPaginatedBuildResult (builds , detailBuild , headers )
281+ // For list_builds, use all jobs (no filtering)
282+ result = createPaginatedBuildResult (builds , func (b buildkite.Build ) BuildDetail {
283+ return detailBuild (b , b .Jobs )
284+ }, headers )
278285 case "full" :
279286 result = PaginatedResult [buildkite.Build ]{
280287 Items : builds ,
@@ -368,6 +375,12 @@ func GetBuild(client BuildsClient) (tool mcp.Tool, handler mcp.TypedToolHandlerF
368375 mcp .WithString ("detail_level" ,
369376 mcp .Description ("Response detail level: 'summary' (essential fields), 'detailed' (medium detail), or 'full' (complete build data). Default: 'detailed'" ),
370377 ),
378+ mcp .WithString ("job_state" ,
379+ mcp .Description ("Filter jobs by state. Comma-separated for multiple states (e.g., \" failed,broken,canceled\" ). Valid states: scheduled, running, passed, failed, canceled, skipped, broken, waiting, waiting_failed, blocked, etc." ),
380+ ),
381+ mcp .WithBoolean ("include_agent" ,
382+ mcp .Description ("Include full agent details in job objects. When false (default), only agent.id is included to reduce response size." ),
383+ ),
371384 mcp .WithToolAnnotation (mcp.ToolAnnotation {
372385 Title : "Get Build" ,
373386 ReadOnlyHint : mcp .ToBoolPtr (true ),
@@ -393,6 +406,8 @@ func GetBuild(client BuildsClient) (tool mcp.Tool, handler mcp.TypedToolHandlerF
393406 attribute .String ("pipeline_slug" , args .PipelineSlug ),
394407 attribute .String ("build_number" , args .BuildNumber ),
395408 attribute .String ("detail_level" , args .DetailLevel ),
409+ attribute .String ("job_state" , args .JobState ),
410+ attribute .Bool ("include_agent" , args .IncludeAgent ),
396411 )
397412
398413 // Set default detail level
@@ -418,14 +433,53 @@ func GetBuild(client BuildsClient) (tool mcp.Tool, handler mcp.TypedToolHandlerF
418433 return mcp .NewToolResultError (err .Error ()), nil
419434 }
420435
436+ // Parse job states filter
437+ var requestedStates map [string ]bool
438+ if args .JobState != "" {
439+ states := strings .Split (args .JobState , "," )
440+ requestedStates = make (map [string ]bool , len (states ))
441+ for _ , state := range states {
442+ requestedStates [strings .TrimSpace (state )] = true
443+ }
444+ }
445+
446+ // Filter jobs if states specified
447+ jobs := build .Jobs
448+ if requestedStates != nil {
449+ filteredJobs := make ([]buildkite.Job , 0 )
450+ for _ , job := range build .Jobs {
451+ if job .State != "" && requestedStates [job .State ] {
452+ filteredJobs = append (filteredJobs , job )
453+ }
454+ }
455+ jobs = filteredJobs
456+ }
457+
458+ // Strip agent details if not requested
459+ if ! args .IncludeAgent && len (jobs ) > 0 {
460+ jobsWithMinimalAgent := make ([]buildkite.Job , len (jobs ))
461+ for i , job := range jobs {
462+ jobCopy := job
463+ // Keep only agent ID, strip verbose details
464+ jobCopy .Agent = buildkite.Agent {ID : job .Agent .ID }
465+ jobsWithMinimalAgent [i ] = jobCopy
466+ }
467+ jobs = jobsWithMinimalAgent
468+ }
469+
421470 var result any
422471 switch detailLevel {
423472 case "summary" :
473+ // Summary level ignores job filtering
424474 result = summarizeBuild (build )
425475 case "detailed" :
426- result = detailBuild (build )
476+ // Detailed level uses filtered jobs for job_summary
477+ result = detailBuild (build , jobs )
427478 case "full" :
428- result = build
479+ // Full level returns build with filtered jobs
480+ buildCopy := build
481+ buildCopy .Jobs = jobs
482+ result = buildCopy
429483 default :
430484 return mcp .NewToolResultError ("detail_level must be 'summary', 'detailed', or 'full'" ), nil
431485 }
@@ -677,8 +731,8 @@ func WaitForBuild(client BuildsClient) (tool mcp.Tool, handler mcp.TypedToolHand
677731 }
678732 }
679733
680- // default to detailed
681- result := detailBuild (build )
734+ // default to detailed, use all jobs (no filtering)
735+ result := detailBuild (build , build . Jobs )
682736
683737 return mcpTextResult (span , & result )
684738 }, []string {"read_builds" }
0 commit comments