Skip to content

feat: circular import detection via MCP tool + CLI subcommand #888

@goutham80808

Description

@goutham80808

Problem

I've been using CodeGraph to navigate a large TypeScript monorepo, and circular imports have been a recurring pain point — they cause build failures with tsc, slow down bundlers, and create maintenance traps where refactoring one module breaks another in non-obvious ways.

I went looking for a tool to catch these cycles early and discovered something interesting: CodeGraph already has a complete circular import detector buried in the codebase — it's just never called.

Evidence

The detector lives at src/graph/queries.ts:225:

findCircularDependencies(): string[][] {
  // ... full DFS implementation with white/gray/black coloring
}

It's re-exported on the CodeGraph class at src/index.ts:1017. The algorithm is complete, well-tested, and handles multi-cycle graphs correctly.

But it has zero callers. It's dead code — implemented but never wired to any output channel.

I verified this by grepping the entire codebase:

$ grep -r "findCircularDependencies" src/
src/graph/queries.ts:225:    findCircularDependencies(): string[][] {
src/index.ts:1017:    findCircularDependencies(): string[][] {

Only the definition and re-export. No function ever calls it.

Why This Matters

Circular imports are one of those problems that:

  • Don't show up in code review (hard to spot manually in large graphs)
  • Only surface when the build breaks (or worse, cause subtle runtime bugs)
  • Get worse over time (one cycle begets more as developers work around it)

Having a tool that catches them before CI would save significant debugging time. CodeGraph already has the graph data needed to detect them — the algorithm is sitting right there.

Proposed Solution

Surface findCircularDependencies() on two channels:

1. MCP tool: codegraph_check

A new tool that AI agents can call to detect circular imports in the current project.

// Tool definition
{
  name: 'codegraph_check',
  description: 'Detect circular import cycles in the project graph',
  inputSchema: { type: 'object', properties: {} }
}

Output (markdown, sorted deterministically):

**Circular imports detected (2 cycle(s)):**

**Cycle 1** (2 file(s)): src/a.ts → src/b.ts → src/a.ts
**Cycle 2** (3 file(s)): src/c.ts → src/d.ts → src/e.ts → src/c.ts

When no cycles exist:

No circular imports found. The dependency graph is acyclic.

Key design decision: Ship functional but NOT in DEFAULT_MCP_TOOLS. Rationale: CLAUDE.md documents extensive eval evidence that agents under-pick new tools, and adding to the default surface without proven demand could dilute tool selection. The tool is callable — agents just need to know it exists.

2. CLI subcommand: codegraph check [path]

A deterministic, user-invoked command for developers.

# Check current project
codegraph check

# Check specific path
codegraph check src/modules

# JSON output for CI
codegraph check --json

Exit behavior:

  • Exit 0 — graph is acyclic
  • Exit 1 — cycles detected (file paths listed in output)

This makes it git-hook ready:

# .git/hooks/pre-commit
codegraph check || exit 1

Implementation Scope

5 files, ~340 lines, 0 deletions:

File Change
src/mcp/tools.ts Tool definition + dispatch case + handleCheck handler
src/bin/codegraph.ts check [path] subcommand
__tests__/mcp-check-circular-imports.test.ts 4 MCP tests (new)
__tests__/cli-check.test.ts 2 CLI tests (new)
CHANGELOG.md [Unreleased] entry

No schema changes. No breaking changes. No new dependencies.

What I Need

I have the implementation ready on a branch (feat/codegraph-check-circular-imports). Looking for feedback on:

  1. Is the callable-but-not-listed approach for the MCP tool the right call? I followed the existing DEFAULT_MCP_TOOLS philosophy, but open to other opinions.
  2. Should the CLI exit non-zero on cycles? This makes it git-hook ready but could be surprising if someone just wants to see the cycles. The --json flag provides machine-readable output.
  3. Anything else you'd want to see before merging?

Happy to open a PR if the direction looks right.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions