Skip to content

Conversation

@ChrisEdwards
Copy link
Collaborator

@ChrisEdwards ChrisEdwards commented Nov 11, 2025

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:

  • New tool name: mcp__contrast__get_route_coverage (34 chars) ✅
  • Parameters: appId (required), sessionMetadataName, sessionMetadataValue, useLatestSession (all optional)

Code Changes

RouteCoverageService.java

  • Replaced 6 methods with single getRouteCoverage() method
  • Reduced code from 197 lines → 135 lines (31% reduction)
  • All optional parameters support filtering:
    • null parameters = unfiltered query
    • sessionMetadataName/Value = session metadata filter
    • useLatestSession=true = latest session filter

Backward Compatibility

All 6 original use cases work through parameter combinations:

  1. Unfiltered: getRouteCoverage(appId, null, null, null)
  2. Session metadata: getRouteCoverage(appId, name, value, null)
  3. Latest session: 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

  • Created RouteCoverageServiceTest.java with 14 unit tests
  • All scenarios covered: unfiltered, metadata filter, latest session, error handling
  • Result: ✅ All 14 tests passing

Integration Tests

  • Created RouteCoverageServiceIntegrationTest.java with 7 integration tests
  • Tests real TeamServer API integration
  • Smart test discovery automatically finds suitable test data
  • Result: ✅ All 7 tests passing

Full Test Suite

Tests run: 250, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

Checklist

  • Code consolidation complete
  • Unit tests written and passing (14 tests)
  • Integration tests written and passing (7 tests)
  • Full test suite passes (mvn verify)
  • Tool name under 64-char limit (34 chars)
  • Backward compatibility maintained
  • Rebased onto main after AIML-223 merge

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

ChrisEdwards and others added 4 commits November 12, 2025 16:54
- 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]>
@ChrisEdwards ChrisEdwards force-pushed the AIML-224-consolidate-route-coverage branch from 22264b6 to d9149ea Compare November 12, 2025 21:54
@ChrisEdwards ChrisEdwards changed the base branch from AIML-223 to main November 12, 2025 21:54
@ChrisEdwards ChrisEdwards marked this pull request as ready for review November 12, 2025 21:55
Copy link
Contributor

Copilot AI left a 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.

ChrisEdwards added a commit that referenced this pull request Nov 13, 2025
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() &&
Copy link
Collaborator

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);
}

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
} else if (sessionMetadataName != null && !sessionMetadataName.isEmpty()) {
} else if (StringUtils.hasText(sessionMetadataName)) {


@BeforeAll
void discoverTestData() {
System.out.println("\n╔════════════════════════════════════════════════════════════════════════════════╗");
Copy link
Collaborator

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.

Copy link
Collaborator Author

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()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

if clause not needed

Copy link
Collaborator Author

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());
Copy link
Collaborator

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.

Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

@ChrisEdwards ChrisEdwards merged commit 9b2488d into main Nov 13, 2025
4 checks passed
ChrisEdwards added a commit that referenced this pull request Nov 14, 2025
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).
ChrisEdwards added a commit that referenced this pull request Nov 14, 2025
* 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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants