-
Notifications
You must be signed in to change notification settings - Fork 5
AIML-224: Consolidate RouteCoverageService tools to fix Claude API 64-char limit #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
a5b7767 to
c7cee2c
Compare
- Replace 6 methods with single get_route_coverage method - Reduce tool name from 72 chars to 34 chars (mcp__contrast__get_route_coverage) - Add 14 unit tests and 5 integration tests (all passing) - Maintain backward compatibility for all 6 original use cases - Fix integration test to handle UnauthorizedException properly Fixes AIML-224 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Document the requirement to set beads to in_progress when starting work. This ensures proper status tracking throughout the development lifecycle. - Add new 'Bead Status Management' section - Document status lifecycle: open → in_progress → closed - Provide examples using both bd CLI and MCP tool - Emphasize setting in_progress when starting work 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
PROBLEM: When get_route_coverage was called with empty strings for sessionMetadataName and sessionMetadataValue, it incorrectly created a POST request with empty metadata filters, resulting in "No sessions found with the provided filters" instead of returning all routes via the unfiltered GET endpoint. ROOT CAUSE: The code checked `if (sessionMetadataName != null)` which evaluates to true for empty strings. This caused empty strings to be treated as valid filters instead of being treated as null (no filter). FIX: - Updated parameter validation to check `!= null && !isEmpty()` for both sessionMetadataName and sessionMetadataValue - Empty strings now trigger the GET endpoint (unfiltered query) instead of POST endpoint with empty filters - Updated JavaDoc and tool description to clarify that empty strings are treated as null TESTS: - Added 2 unit tests: testGetRouteCoverage_EmptyStringParameters_TreatedAsNull and testGetRouteCoverage_EmptySessionMetadataNameOnly_TreatedAsNull - Added integration test: testGetRouteCoverage_EmptyStrings_TreatedAsNull - All 16 unit tests pass Fixes MCP-OU8 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Added testGetRouteCoverage_SessionMetadataFilter_EmptyValue() to verify that when sessionMetadataName is provided with a non-empty value but sessionMetadataValue is an empty string, an IllegalArgumentException is thrown with the message "sessionMetadataValue is required". This completes the test coverage for MCP-3EG, which was already fixed by the empty string handling changes in MCP-OU8 (commit d7eecba). All 17 unit tests pass. Fixes MCP-3EG 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
22264b6 to
d9149ea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR consolidates 6 route coverage methods into a single unified method to comply with Claude API's 64-character tool name limit. The new get_route_coverage method (34 chars) replaces methods with names up to 81 characters long. The consolidation also reduces code complexity by 31% while maintaining backward compatibility through flexible optional parameters.
Key Changes:
- Unified 6 route coverage methods into one with optional filtering parameters
- New method supports unfiltered queries, session metadata filters, and latest session filters
- Added comprehensive unit tests (14) and integration tests (7) with smart test data discovery
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/main/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageService.java | Consolidated 6 methods into single getRouteCoverage() with optional parameters; removed app name lookup logic |
| src/test/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageServiceTest.java | Added 14 unit tests covering all parameter combinations, error handling, and edge cases |
| src/test/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageServiceIntegrationTest.java | Added 7 integration tests with automatic test data discovery from real TeamServer |
| CLAUDE.md | Added documentation for bead status management workflow |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/test/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageServiceTest.java
Show resolved
Hide resolved
src/test/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageServiceIntegrationTest.java
Show resolved
Hide resolved
src/test/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageServiceIntegrationTest.java
Show resolved
Hide resolved
Document the complete workflow for promoting a draft stacked PR to ready-for-review after its base PR has been merged to main. Includes: - Prerequisites and validation steps - 10-step detailed workflow (verify, rebase, push, update, test) - Full example commands from AIML-224 experience - Common issues and troubleshooting guidance - User experience phrases: 'move stacked PR to ready', 'promote stacked PR', etc. This codifies the process used successfully for PR #25 (AIML-224).
| logger.info("Retrieving route coverage for application ID: {}", appId); | ||
|
|
||
| // Validate parameters - treat empty strings as null | ||
| if (sessionMetadataName != null && !sessionMetadataName.isEmpty() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use the Spring StringUtils library or add this dependency that gives many String helper API's:
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html
So this could be:
if (StringUtils.hasText(sessionMetadataName) && !StringUtils.hasText(sessionMetadataValue)) {
var errorMsg = "sessionMetadataValue is required when sessionMetadataName is provided";
logger.error(errorMsg);
throw new IllegalArgumentException(errorMsg);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will incorporate this into the project downstream and use it going forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll fix this downstream as it requires adding the library and updating all the places it could be used.
| requestExtended.setSessionId(latest.getAgentSession().getAgentSessionId()); | ||
| logger.debug("Using latest session ID: {}", latest.getAgentSession().getAgentSessionId()); | ||
|
|
||
| } else if (sessionMetadataName != null && !sessionMetadataName.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| } else if (sessionMetadataName != null && !sessionMetadataName.isEmpty()) { | |
| } else if (StringUtils.hasText(sessionMetadataName)) { |
|
|
||
| @BeforeAll | ||
| void discoverTestData() { | ||
| System.out.println("\n╔════════════════════════════════════════════════════════════════════════════════╗"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like other comments about System.out.... This is not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed downstream. Unless you mean the ascii border.
| List<Application> applications = appsResponse.getApplications(); | ||
| System.out.println(" Found " + applications.size() + " application(s) in organization"); | ||
|
|
||
| if (applications.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if clause not needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll address the integration tests separately.
|
|
||
| // Check for session metadata | ||
| try { | ||
| SessionMetadataResponse sessionResponse = sdkExtension.getLatestSessionMetadata(orgID, app.getAppId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't know why there is so much logic in this unit test?
IMO We should just be creating the test data, setting expectations(stubbing), call method under test and then verifying the results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want real integration tests we have a standard to use wiremock to mock the TS API calls and then validate the business logic meets expectations and processed the TS API call propery.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll address the integration tests separately. I understand the concern.
Document the complete workflow for promoting a draft stacked PR to ready-for-review after its base PR has been merged to main. Includes: - Prerequisites and validation steps - 10-step detailed workflow (verify, rebase, push, update, test) - Full example commands from AIML-224 experience - Common issues and troubleshooting guidance - User experience phrases: 'move stacked PR to ready', 'promote stacked PR', etc. This codifies the process used successfully for PR #25 (AIML-224).
* Add appID and appName fields to VulnLight record (AIML-228) Enables correlation of vulnerabilities to their owning applications by including application identifiers in VulnLight objects returned by all vulnerability listing tools. Changes: - Add appID and appName fields to VulnLight record with JavaDoc - Update VulnerabilityMapper to extract application data from Trace - Add APPLICATION expand to all vulnerability query operations - Add unit tests for application field mapping (null and populated cases) - Add integration test assertions verifying appID/appName presence - All tests pass (250/250 unit + integration tests) Benefits: - Users can immediately identify which app owns each vulnerability - Eliminates need to query all apps to find vulnerability ownership - Simplifies testing and debugging of app-specific vulnerability tools - Backwards compatible (new fields only, none removed) Tools affected: - list_all_vulnerabilities - list_vulnerabilities - list_vulns_by_app_and_metadata - list_vulns_by_app_latest_session 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Add stacked PR workflow documentation to CLAUDE.md Documents the process for creating draft PRs that depend on unmerged PRs (stacked branches), including: - Identifying the base PR - Creating draft PR with proper configuration - Required warning message format - Verification steps - Example command Triggered by phrases like "ready for stacked PR" or "ready for draft review". 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Add coding standards section to CLAUDE.md Documents Java coding conventions for the project: - Prefer var for local variables when type is obvious - Use isEmpty() instead of size() comparisons for collections 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Add 'Promoting Stacked PR to Ready for Review' workflow to CLAUDE.md Document the complete workflow for promoting a draft stacked PR to ready-for-review after its base PR has been merged to main. Includes: - Prerequisites and validation steps - 10-step detailed workflow (verify, rebase, push, update, test) - Full example commands from AIML-224 experience - Common issues and troubleshooting guidance - User experience phrases: 'move stacked PR to ready', 'promote stacked PR', etc. This codifies the process used successfully for PR #25 (AIML-224). * Restructure AI Development Workflow with stacked branch clarifications Enhanced the workflow documentation to clarify stacked branch handling and ensure consistent high-quality PR descriptions: - Added Workflow Overview with decision tree and label definitions - Clarified stacked-branch label usage for branches based on PR branches - Created shared "Creating High-Quality PR Descriptions" section - Updated "Moving to Review" for standard PRs (pr-created + in-review labels) - Updated "Stacked PRs" workflow (pr-created label only, draft status) - Enhanced "Promoting Stacked PR" to add in-review label on promotion - Emphasized human review as bottleneck requiring effortless reviews Key improvements: - Consistent PR description quality across both workflows - Clear label lifecycle (pr-created vs in-review timing) - Stacked beads must depend on parent bead - Both workflows reference shared description format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
Problem
RouteCoverageService tool names violate Claude API's 64-character limit, preventing the MCP server from working with Claude:
mcp__contrast__get_application_route_coverage_by_app_id_and_session_metadata(77 chars)mcp__contrast__get_application_route_coverage_by_app_name_latest_session(75 chars)mcp__contrast__get_application_route_coverage_by_app_id_latest_session(73 chars)mcp__contrast__get_application_route_coverage_by_app_name_and_session_metadata(81 chars)Solution
Consolidated 6 methods into 1 unified method with optional parameters:
mcp__contrast__get_route_coverage(34 chars) ✅appId(required),sessionMetadataName,sessionMetadataValue,useLatestSession(all optional)Code Changes
RouteCoverageService.java
getRouteCoverage()methodnullparameters = unfiltered querysessionMetadataName/Value= session metadata filteruseLatestSession=true= latest session filterBackward Compatibility
All 6 original use cases work through parameter combinations:
getRouteCoverage(appId, null, null, null)getRouteCoverage(appId, name, value, null)getRouteCoverage(appId, null, null, true)Note: App name lookups removed - users should use a separate tool to resolve app name → app ID.
Test Results
Unit Tests
RouteCoverageServiceTest.javawith 14 unit testsIntegration Tests
RouteCoverageServiceIntegrationTest.javawith 7 integration testsFull Test Suite
Checklist
mvn verify)Files Changed
src/main/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageService.java(consolidated)src/test/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageServiceTest.java(created)src/test/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageServiceIntegrationTest.java(created)Fixes AIML-224