Skip to content

PR: Add detect_cycles MCP Tool for Circular Dependency Detection#61

Merged
userFRM merged 2 commits intouserFRM:mainfrom
VooDisss:circular-dependencies
Feb 26, 2026
Merged

PR: Add detect_cycles MCP Tool for Circular Dependency Detection#61
userFRM merged 2 commits intouserFRM:mainfrom
VooDisss:circular-dependencies

Conversation

@VooDisss
Copy link
Contributor

Summary

Implement a new MCP tool detect_cycles that identifies circular dependencies in codebases using a funnel design pattern with TOON-optimized output.


Changes

New Files

  • crates/rpg-nav/src/cycles.rs - Core cycle detection algorithm using DFS

Modified Files

  • crates/rpg-nav/src/lib.rs - Export cycles module
  • crates/rpg-mcp/src/params.rs - Add DetectCyclesParams
  • crates/rpg-mcp/src/tools.rs - Tool handler with TOON-optimized output
  • crates/rpg-mcp/src/prompts/server_instructions.md - Documentation

Features

Parameters (all optional)

Parameter Type Description
area string Filter to area(s), comma-separated (e.g., "Navigation")
max_cycles number Limit cycles returned (e.g., 10)
min_cycle_length number Skip trivial 2-cycles (use 3+)
max_cycle_length number Max cycle length (default: 20)
cross_file_only boolean Only cycles spanning multiple files
cross_area_only boolean Only cycles spanning multiple hierarchy areas
sort_by string Sort by: "length", "file_count", "entity_count"
include_files boolean Include file paths (default: true)
ignore_rpgignore boolean Include ignored files (default: false)

Output Examples

No Parameters (Summary)

# Circular Dependencies

cycles: {total: 512, entities: 467, files: 46, areas: 8, cross_file: 152, cross_area: 45}

length_dist: len2=200 len3=150 len4=90 len5+=72

area_breakdown[8]{area,cycles,len2,len3,len4+,files}:
  Navigation,45,20,15,10,8
  Parser,38,12,14,12,6
  Core,30,8,10,12,4

available_areas[8]: Nav,Parser,Core,Database,CLI,MCP,Encoder,Utils

---
next_step: Use area/max_cycles/cross_file_only/cross_area_only to filter

With Filters

# Circular Dependencies

cycles: {total: 10, entities: 45, files: 8, areas: 2, cross_file: 10, cross_area: 2}

length_dist: len2=3 len3=5 len4=2 len5+=0

filters: {area: Navigation, max_cycles: 10, cross_file: true, cross_area: false}

cycles[10]{chain,len,files}:
  handler.rs:process->router.rs:route->handler.rs:process,len=3,handler.rs,router.rs
  util.rs:foo->util.rs:bar->util.rs:foo,len=2,util.rs

---
next_step: Use area/max_cycles/cross_file_only/cross_area_only to filter

Agent Guidance (Funnel Design)

The tool is designed with a funnel pattern to guide agents:

  1. First call (no params): Returns summary statistics, length distribution, area breakdown, and available areas. Shows next_step prompting to call again with filters.

  2. Subsequent calls: Agent uses filters to get specific cycle details.

The next_step line in every response guides the agent:

next_step: Use area/max_cycles/cross_file_only/cross_area_only to filter

This is documented in server_instructions.md so the LLM knows how to use the tool effectively.


Testing

Unit Tests (16 tests, all pass)

Test Description
test_simple_cycle_detection Basic 2-node cycle
test_two_node_cycle A → B → A pattern
test_multiple_cycles Multiple cycles in graph
test_cross_file_only_filter Filter by cross-file
test_cross_area_only_filter Filter by cross-area
test_area_filter_isolates_one_area Filter by hierarchy area
test_min_cycle_length_skips_two_node_cycles Skip len=2 cycles
test_max_cycle_length Max length limit
test_area_breakdown_counts_cycles_not_entities Bug fix: count cycles not entities
test_files_in_cycles_correct_when_include_files_false Bug fix: compute from entities
test_files_in_cycles File counting
test_deterministic_output Output consistency
test_cycle_ignores_contains_edges Edge filtering
test_cycle_format Output format
test_empty_graph No cycles case
test_no_cycles_in_linear_chain A → B → C (no cycle)

MCP Integration Tests

Test Case Parameters Expected
Baseline {} Summary + area breakdown + next_step
Area filter {"area": "Navigation"} Only Navigation cycles
cross_file_only {"cross_file_only": true} Only multi-file cycles
cross_area_only {"cross_area_only": true} Only multi-area cycles
Combined {"cross_file": true, "cross_area": true} Both filters work
max_cycles {"max_cycles": 5} Max 5 cycles shown
max_cycles: 0 {"max_cycles": 0} "none requested" message
include_files: false {"include_files": false} files_in_cycles computed from entities

Algorithm Notes

  • Uses DFS with recursion stack tracking
  • Deduplicates cycles via lexicographic normalization
  • Excludes Contains edge kinds (only structural)
  • Respects .rpgignore patterns
  • Does not guarantee finding ALL simple cycles (Johnson's algorithm would be overkill); finds representative cycles useful for architectural analysis

Related

  • Complements existing analyze_health tool

Implements a new MCP tool that identifies circular dependencies in codebases
using a funnel design pattern with TOON-optimized output.

## New Features
- Cycle detection using DFS algorithm with deduplication
- TOON-optimized output format for LLM efficiency
- Funnel design: summary first, then filtered details on follow-up calls
- Area breakdown showing cycles per hierarchy area
- Cross-file and cross-area cycle filtering
- Respects .rpgignore patterns

## Parameters (all optional)
- area: Filter by hierarchy area(s), comma-separated
- max_cycles: Limit cycles returned
- min_cycle_length: Skip trivial 2-cycles
- cross_file_only: Only multi-file cycles
- cross_area_only: Only multi-area cycles
- sort_by: length/file_count/entity_count
- include_files: Include file paths
- ignore_rpgignore: Include ignored files

## Output
First call returns summary + area breakdown + next_step guidance.
Subsequent calls with filters show specific cycle details.

Example summary output:
  cycles: {total: 512, entities: 467, files: 46, areas: 8, cross_file: 152}
  area_breakdown[N]{area,cycles,len2,len3,len4+,files}:

## Testing
- 16 unit tests covering all filter paths
- MCP integration tests for edge cases
- Bug fixes: area breakdown counts, files_in_cycles computation

## Files
- crates/rpg-nav/src/cycles.rs (new) - Core algorithm
- crates/rpg-mcp/src/tools.rs - Tool handler
- crates/rpg-mcp/src/params.rs - Parameters
- crates/rpg-mcp/src/prompts/server_instructions.md - Docs
Copy link
Owner

@userFRM userFRM left a comment

Choose a reason for hiding this comment

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

Code Review

Verified locally: clean fast-forward merge, fmt, clippy (default + --no-default-features), all workspace tests pass.

What's good

  • Solid DFS algorithm: Modified DFS with recursion stack tracking, lexicographic normalization for dedup. Correctly excludes Contains edges.
  • Good funnel design: First call returns summary + area breakdown + next_step prompt. Filtered calls return detailed cycles. The TOON output is compact and LLM-friendly.
  • Thorough filtering: area, cross-file, cross-area, min/max length, sort options. All tested independently.
  • 16 unit tests covering: linear chains (no cycles), 2-node/3-node/multi-cycle graphs, all filter paths, deterministic output, edge case (empty graph), include_files=false correctness.
  • Stats computed from entity data, not cycle.files — correct even when include_files=false.
  • Area breakdown counts cycles, not entities — explicitly tested with a dedicated regression test.
  • Custom glob_match avoids a crate dep for the .rpgignore filtering.
  • MCP integration: params, tool handler, server_instructions.md all consistent.

Minor observations (non-blocking)

  1. The algorithm notes honestly state it doesn't find ALL simple cycles (Johnson's would be needed) — good transparency. Representative cycles are the right trade-off for architectural analysis.
  2. The max_cycle_length default of 20 is a reasonable safety valve against exponential blowup.

LGTM. Merging.

@userFRM userFRM merged commit cd4fa3f into userFRM:main Feb 26, 2026
3 checks passed
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.

2 participants