Skip to content

Implement getBuildArtifacts tool and optimize getBuild response - #42

Open
dt-atmosic wants to merge 10 commits into
jenkinsci:mainfrom
dt-atmosic:artifacts
Open

Implement getBuildArtifacts tool and optimize getBuild response#42
dt-atmosic wants to merge 10 commits into
jenkinsci:mainfrom
dt-atmosic:artifacts

Conversation

@dt-atmosic

@dt-atmosic dt-atmosic commented Sep 1, 2025

Copy link
Copy Markdown
Collaborator

This PR implements issue #32 by adding new tools for artifact management and optimizing the getBuild response size.

New Tools Added

  1. getBuildArtifacts - Returns only the artifacts array from a build
    o Takes jobFullName and optional buildNumber parameters
    o Significantly reduces payload size compared to full getBuild response
    o Returns empty array for jobs without artifacts or non-existent jobs
    o Fixed JSON concatenation issue for top-level lists

  2. getBuildArtifact - Provides paginated access to individual artifact file contents
    o Supports offset/limit parameters for large files
    o Returns artifact content as text with pagination metadata
    o Includes safety limits to prevent excessive memory usage

Optimizations

  1. Modified getBuild response - Now excludes artifacts field
    o Created RunWithoutArtifactsSerializer to filter out artifacts
    o Updated JenkinsExportedBeanSerializerModifier to use custom serializer
    o Maintains all other build information while reducing response size

Testing done

  1. Comprehensive test coverage
    o BuildArtifactsExtensionTest - Integration tests for new tools
    o GetBuildWithoutArtifactsTest - Verifies getBuild optimization
    o BuildArtifactsExtensionCompileTest - Basic compilation verification

  2. Manual test
    o Augment Code MCP client w/ mvn hpi:run - Found JSON issue with top-level list
    o Manual test with Augment Code and production Jenkins server

Submitter checklist

  • Make sure you are opening from a topic/feature/bugfix branch (right side) and not your main branch!
  • Ensure that the pull request title represents the desired changelog entry
  • Please describe what you did
  • Link to relevant issues in GitHub or Jira
  • Link to relevant pull requests, esp. upstream and downstream changes
  • Ensure you have provided tests that demonstrate the feature works or the issue is fixed

dt-atmosic and others added 6 commits August 31, 2025 01:12
- Add BuildArtifactsExtension with getBuildArtifacts and getBuildArtifact tools
- Create RunWithoutArtifactsSerializer to exclude artifacts from getBuild responses
- Add comprehensive tests for new functionality
- Implement pagination support for large artifact files
- Maintain backward compatibility while reducing payload sizes

Addresses first part of issue jenkinsci#32: separate artifacts retrieval from build metadata
- Replace Java record with regular class for Java 11 compatibility
- Fix regex pattern in RunWithoutArtifactsSerializer with DOTALL flag
- Update test methods to use proper McpSchema.TextContent pattern
- Remove non-existent TestUtils.getTextContent() method calls
- Follow existing test patterns from the codebase
- Declare ObjectMapper inside lambda scope where it's used
- Resolves compilation error in GetBuildWithoutArtifactsTest.java
- Use record for BuildArtifactResponse
- Use TreePruner
- spotless:apply
@dt-atmosic
dt-atmosic marked this pull request as ready for review October 20, 2025 23:20
@dt-atmosic
dt-atmosic requested a review from a team as a code owner October 20, 2025 23:20
@dt-atmosic

Copy link
Copy Markdown
Collaborator Author

Working on the code coverage warnings.

@dt-atmosic dt-atmosic added the enhancement For changelog: Minor enhancement. use `major-rfe` for changes to be highlighted label Oct 22, 2025
@dt-atmosic
dt-atmosic requested review from olamy and topikachu October 22, 2025 23:14

@olamy olamy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice PR.
the title says optimize getBuild response. is it remove artifacts from the tree?

* - "unchecked": Run.getArtifacts() returns raw List requiring unchecked conversion
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@Tool(description = "Get the artifacts for a specific build or the last build of a Jenkins job")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will need annotations = @Tool.Annotations(destructiveHint = false)
as it's true per default

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — added annotations = @Tool.Annotations(destructiveHint = false) to getBuildArtifacts.

.orElse(List.of());
}

@Tool(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will need annotations = @Tool.Annotations(destructiveHint = false)
as it's true per default

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — added annotations = @Tool.Annotations(destructiveHint = false) to getBuildArtifact as well.

offset = 0L;
}
if (limit == null || limit <= 0) {
limit = 65536; // 64KB default

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be better having a configuration (e.g SystemProperties) as users may want to configure more or less.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The default is now read from a system property: SystemProperties.getInteger("io.jenkins.plugins.mcp.server.extensions.BuildArtifactsExtension.limit.default", 65536), so it still defaults to 64KB but can be tuned.

}

// Cap the limit to prevent excessive memory usage
final int maxLimit = 1048576; // 1MB max

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. should be configurable

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The cap is now SystemProperties.getInteger("io.jenkins.plugins.mcp.server.extensions.BuildArtifactsExtension.limit.max", 1048576) — defaults to 1MB, configurable.

}
// Serialize all results the same way - this fixes the JSON concatenation issue
// for top-level lists while maintaining proper JSON structure
resultBuilder.addTextContent(toJson(result));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great. it's going to the direction of #82

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I've merged the latest main, which includes the ToolResponse refactor from #82. I dropped this local toMcpResult change in favor of upstream's implementation, so list/single results are now handled consistently there.

} else {
resultBuilder.addTextContent(toJson(result));
}
// Serialize all results the same way - this fixes the JSON concatenation issue

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need of this comment here.
it;s more a PR comment :)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed — this whole toMcpResult change was reverted in favor of upstream's ToolResponse (#82), so the comment no longer exists.

}

// Convert to string (assuming text content)
String content = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about binaries artifacts? (we can attach jar, war etc....)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. getBuildArtifact now detects binary content (NUL-byte sniff over the read buffer) and returns a clear message — "Artifact appears to be binary and cannot be returned as text: " — instead of emitting garbled UTF-8. getBuildArtifacts still lists every artifact (binary or not) for discovery. Added test coverage in testGetBuildArtifactBinary. Let me know if you'd prefer base64 streaming instead.

…EZXC06JZW1B

# Conflicts:
#	README.md
#	src/main/java/io/jenkins/plugins/mcp/server/tool/McpToolWrapper.java
#	src/test/java/io/jenkins/plugins/mcp/server/EndPointTest.java
#	src/test/java/io/jenkins/plugins/mcp/server/extensions/DefaultMcpServerTest.java
… tree-prune compat

- Add @Tool.Annotations(destructiveHint=false) to getBuildArtifacts/getBuildArtifact

- Make default/max artifact size limits configurable via SystemProperties

- Detect binary artifacts and refuse with a clear message instead of garbled text

- Rework RunWithoutArtifactsSerializer to honor the upstream 'tree' parameter while still always excluding artifacts

- Update tests for the ToolResponse {status,message,result} wrapper and add binary-artifact coverage
@dt-atmosic

dt-atmosic commented May 30, 2026

Copy link
Copy Markdown
Collaborator Author

Nice PR. the title says optimize getBuild response. is it remove artifacts from the tree?

Thanks! Yes, that's correct.

@novinxy

novinxy commented Jul 29, 2026

Copy link
Copy Markdown

Hi, nice PR, just something we need from the MCP 👍
@dt-atmosic isn't it already finished? In my opinion and from the conversation it looks as everything/almost everything is resolved
Is there something I could do to help make it happen ?

@dustymabe

Copy link
Copy Markdown

Hi. Thanks for working on this!

I notice we now have some filtering for binary artifacts. One use case to consider is people running a $test_framework inside of Jenkins to perform tests and the tool generates logs itself (additional logs to the ones output to stdout/stderr). As part of the job run the logs from the tests are uploaded as artifacts in a tar.xz to the build. In this case the tar.xz would be detected as binary and filtered out IIUC.

Ultimately the use case I'm presenting is something like having an Agent/LLM via jenkins MCP say:

oh, build #100 failed and from the Jenkins logs I see test A234 failed so crack open the tarball and look at the logs for A234 to investigate further. And oh btw build #99 didn't fail on the same test, so if you need to compare a successful run to a failed one look at the logs for A234 from build #99.

dustymabe added a commit to dustymabe/fedora-coreos-pipeline that referenced this pull request Jul 30, 2026
When kola tests fail, the pipeline uploads log bundle tarballs as Jenkins
build artifacts. Add documentation on the artifact structure (journal.txt,
console.txt, ignition.json, rerun/ directory) and what to look for when
analyzing test failures.

Note the current limitation that the Jenkins MCP Server Plugin does not
yet support downloading build artifacts, and link to
jenkinsci/mcp-server-plugin#42 as a potential
future solution.

Assisted-By: <anthropic/claude-opus-4.6>
dustymabe added a commit to dustymabe/fedora-coreos-pipeline that referenced this pull request Jul 30, 2026
When kola tests fail, the pipeline uploads log bundle tarballs as Jenkins
build artifacts. Add documentation on the artifact structure (journal.txt,
console.txt, ignition.json, rerun/ directory) and what to look for when
analyzing test failures.

Note the current limitation that the Jenkins MCP Server Plugin does not
yet support downloading build artifacts, and link to
jenkinsci/mcp-server-plugin#42 as a potential
future solution.

Assisted-By: <anthropic/claude-opus-4.6>
dustymabe added a commit to dustymabe/fedora-coreos-pipeline that referenced this pull request Jul 30, 2026
When kola tests fail, the pipeline uploads log bundle tarballs as Jenkins
build artifacts. Add documentation on the artifact structure (journal.txt,
console.txt, ignition.json, rerun/ directory) and what to look for when
analyzing test failures.

Note the current limitation that the Jenkins MCP Server Plugin does not
yet support downloading build artifacts, and link to
jenkinsci/mcp-server-plugin#42 as a potential
future solution.

Assisted-By: <anthropic/claude-opus-4.6>
dustymabe added a commit to dustymabe/fedora-coreos-pipeline that referenced this pull request Jul 30, 2026
When kola tests fail, the pipeline uploads log bundle tarballs as Jenkins
build artifacts. Add documentation on the artifact structure (journal.txt,
console.txt, ignition.json, rerun/ directory) and what to look for when
analyzing test failures.

Note the current limitation that the Jenkins MCP Server Plugin does not
yet support downloading build artifacts, and link to
jenkinsci/mcp-server-plugin#42 as a potential
future solution.

Assisted-By: <anthropic/claude-opus-4.6>
dustymabe added a commit to coreos/fedora-coreos-pipeline that referenced this pull request Jul 31, 2026
When kola tests fail, the pipeline uploads log bundle tarballs as Jenkins
build artifacts. Add documentation on the artifact structure (journal.txt,
console.txt, ignition.json, rerun/ directory) and what to look for when
analyzing test failures.

Note the current limitation that the Jenkins MCP Server Plugin does not
yet support downloading build artifacts, and link to
jenkinsci/mcp-server-plugin#42 as a potential
future solution.

Assisted-By: <anthropic/claude-opus-4.6>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement For changelog: Minor enhancement. use `major-rfe` for changes to be highlighted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants