Skip to main content
Glama

CallGraph MCP Server

MCP server for analyzing call graphs in Axelor projects using ASM bytecode analysis.

What is this?

A Model Context Protocol (MCP) server that lets Claude Code intelligently query your project's call graph:

  • Find where methods/classes are used

  • Analyze change impact

  • Navigate dependencies

  • Trace call chains across Java bytecode

Key Features:

  • ASM bytecode analysis instead of source parsing (100% accurate)

  • SQLite relational database instead of vector database

  • Gradle dependency discovery for automatic Axelor package detection

  • Smart caching with SHA256-based invalidation

Related MCP server: CodeGB

Quick Start

1. Install dependencies

pip install -r requirements.txt

2. Configure Claude Code

Add to your .claude/mcp.json or claude_desktop_config.json:

Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "sqlite-callgraph": {
      "command": "uvx",
      "args": ["mcp-server-sqlite", "--db-path", "C:/path/to/project/.callgraph.db"]
    }
  }
}

3. Extract your project's call graph

cd /path/to/your/axelor/project

# Incremental extraction (default, uses cache)
python /path/to/CallGraph/run_asm_extraction.py .

# Full reset (first time or after schema changes)
python /path/to/CallGraph/run_asm_extraction.py . --init

# Limit extraction for testing (requires --init)
python /path/to/CallGraph/run_asm_extraction.py . --init --limit 100

This creates .callgraph.db in your project directory.

Modes:

  • Default (Incremental): Smart caching - only re-extracts modified packages

  • --init: Full reset - drops all tables and rebuilds from scratch

4. Restart Claude Desktop

The MCP server provides direct SQLite access to the call graph database.

Architecture Overview

Project (.callgraph.db SQLite database)
    ↓ Extraction via
run_asm_extraction.py
    ├─> GradleDependencyManager   # Auto-discover Axelor deps
    └─> ASMExtractor               # Python client
        ↓ REST API
    ASMAnalysisService (Java)      # Port 8766
        ↓ ASM ClassVisitor
    Bytecode Analysis (.class files)
        ↓ Storage
    SQLite Tables:
    ├─> symbol_index   # FQN → URI → package
    ├─> nodes          # classes, methods
    └─> edges          # calls, inheritance, member_of

Components

1. run_asm_extraction.py

CLI entry point for the extraction pipeline.

Usage:

# Incremental extraction (default, uses cache)
python run_asm_extraction.py /path/to/project

# Full reset (first time or after schema changes)
python run_asm_extraction.py /path/to/project --init

# Limit extraction for testing (requires --init to avoid partial data)
python run_asm_extraction.py /path/to/project --init --limit 100

Modes:

  • Incremental (default): Uses SHA256 caching - only re-extracts modified packages

  • --init: Full reset - drops all tables and rebuilds from scratch

Process (always runs both steps):

  1. Package Discovery via GradleDependencyManager (Gradle)

  2. Symbol Indexing via ASMExtractor.build_symbol_index() (FQN → URI)

  3. Call Graph Extraction via ASMExtractor.extract() (Nodes + Edges)


2. GradleDependencyManager.py

Discovers and manages Axelor dependencies via Gradle.

Features:

  • Query Gradle for runtime JAR dependencies (Axelor only)

  • Extract JARs to axelor-repos/ cache directory

  • Provide package metadata (group, artifact, version, jar, sources, classes)

Cache Structure:

axelor-repos/
├── axelor-core-7.2.6/
│   ├── classes/    # .class files from JAR
│   └── sources/    # .java files from sources JAR
└── axelor-base-8.2.9/
    ├── classes/
    └── sources/

3. ASMExtractor.py

Python client for ASMAnalysisService with SQLite storage.

Database Schema:

-- Symbol index (FQN → URI → package)
CREATE TABLE symbol_index (
    fqn TEXT PRIMARY KEY,
    uri TEXT NOT NULL,
    package TEXT NOT NULL,
    line INTEGER                    -- Line number (methods only)
);

-- Nodes (classes and methods)
CREATE TABLE nodes (
    fqn TEXT PRIMARY KEY,
    type TEXT NOT NULL,             -- 'class', 'interface', 'enum', 'method'
    package TEXT NOT NULL,
    line INTEGER,
    visibility TEXT,                -- 'public', 'private', 'protected', 'package'
    has_override BOOLEAN,           -- TRUE if @Override annotation present
    is_transactional BOOLEAN        -- TRUE if @Transactional annotation present
);

-- Edges (relationships)
CREATE TABLE edges (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    from_fqn TEXT NOT NULL,
    edge_type TEXT NOT NULL,        -- 'call', 'inheritance', 'member_of'
    to_fqn TEXT NOT NULL,
    kind TEXT,                      -- 'invoke', 'extends', 'implements', 'argument', 'return'
    from_package TEXT NOT NULL,
    to_package TEXT NOT NULL,
    from_line INTEGER
);

Constructor:

ASMExtractor(db_path=".callgraph.db", service_url="http://localhost:8766", init=False)
  • init=True: Full reset - drops and recreates all tables (INIT mode)

  • init=False (default): Incremental mode - creates tables if they don't exist

Key Methods:

  • init_database(): Full reset - drops and recreates all tables (called automatically when init=True)

  • clean_package_data(package_name): Removes all data for a specific package (used automatically in incremental mode)

  • build_symbol_index(): Builds FQN → URI mapping with automatic cache invalidation

  • extract(): Extracts call graph from bytecode

Performance:

  • Symbol indexing: ~6 minutes for 39 packages (107k symbols, 9.4k classes)

  • Call graph extraction: ~40 seconds for 9,466 .class files (252 files/sec)

  • Total extraction (--init): ~6-7 minutes complete project

  • Incremental mode: Only re-extracts modified packages (70%+ speedup on subsequent runs)

Optimizations:

  • Batch SQL queries (IN clauses): 99.95% reduction in database queries

  • Before: ~650k queries → After: ~220 queries

  • Insertions by batches of 5000 rows


4. ASMAnalysisService (Java)

REST service for analyzing Java bytecode using ASM.

Technology:

  • ASM: Bytecode manipulation framework

  • Spark Java: Lightweight HTTP framework

  • Jackson: JSON serialization

Endpoints:

  • GET /health - Health check

  • POST /index - Extract symbols (classes AND methods) with nodeType and line

  • POST /analyze - Extract complete call graph with metadata

  • POST /shutdown - Gracefully shut down the service

Extracted Metadata:

  • Class modifiers (public, abstract, final, etc.)

  • Method modifiers and visibility

  • Annotations: @Override, @Transactional (Spring, Jakarta, javax)

  • Line numbers for methods

  • Inheritance relationships

  • Method calls with line numbers

Running the Service:

cd Extracteurs/ASMAnalysisService
./gradlew.bat run
# Service starts on port 8766
# Logs written to asm-service.log

MCP Tools (SQLite)

Use your configured SQLite MCP server with direct SQL queries.

Query Examples

Find methods using class X as argument:

SELECT DISTINCT e.to_fqn AS method_fqn, e.from_fqn AS argument_type
FROM edges e
WHERE e.edge_type = 'member_of' AND e.kind = 'argument'
  AND e.from_fqn = 'com.axelor.apps.openauction.db.Lot'
ORDER BY e.to_fqn;

Find all calls to a method:

SELECT e.from_fqn AS caller, e.to_fqn AS callee, e.from_line AS line, e.from_package
FROM edges e
WHERE e.edge_type = 'call' AND e.to_fqn LIKE '%setStatus%'
ORDER BY e.from_package;

Count symbols by package:

SELECT package, COUNT(*) as symbol_count,
       SUM(CASE WHEN type = 'class' THEN 1 ELSE 0 END) as class_count,
       SUM(CASE WHEN type = 'method' THEN 1 ELSE 0 END) as method_count
FROM nodes
GROUP BY package ORDER BY symbol_count DESC;

Find transactional methods:

SELECT fqn, package, line
FROM nodes
WHERE type = 'method' AND is_transactional = 1
ORDER BY package, line;

Find public methods with @Override:

SELECT fqn, package, line
FROM nodes
WHERE type = 'method' AND visibility = 'public' AND has_override = 1
ORDER BY package, line;

Comparison: JavaParser vs ASM

Feature

JavaParser (Old)

ASM (New)

Input

Source code (.java)

Bytecode (.class)

Requires sources

Yes

No (works with JARs)

Analysis speed

Slower (parsing)

Faster (bytecode)

Accuracy

Source-level

Bytecode-level (100%)

Dependencies

Manual download

Gradle auto-discovery

Storage

ChromaDB (~150 MB)

SQLite (~50 MB)

Query speed

~50ms

~10ms


Benefits of ASM Approach

  1. No source code required: Works directly with JARs from Maven/Gradle cache

  2. Automatic dependency discovery: Gradle integration

  3. 100% accurate: Bytecode analysis reflects actual compilation

  4. Faster: No parsing overhead

  5. Smaller database: Relational storage vs vector embeddings

  6. Better caching: SHA256-based invalidation

  7. Simpler queries: SQL vs vector similarity search


Performance

Extraction Times (Real Project - 39 Packages)

Full Extraction (--init): ~6-7 minutes total

  • STEP 1 (Gradle discovery): ~10 seconds

  • STEP 2 (Symbol indexing): ~6 minutes for 107k symbols

  • STEP 3 (Call graph extraction): ~40 seconds for 9,466 .class files (252 files/sec)

Incremental Mode: Only re-extracts modified packages (70%+ speedup)

Database Stats

Real Project Example:

  • Size: ~292 MB

  • Symbols: 107,312 (9,466 classes + 97,846 methods)

  • Edges: 319,272 (calls, inheritance, member_of)

  • Packages: 39 Axelor packages

  • Entities: 2,089 (22% of classes)

Database Contents:

  • symbol_index: 107,312 rows (~60 MB)

  • nodes: 94,267 rows (~52 MB)

  • edges: 319,272 rows (~180 MB)

Query Performance

  • Simple queries (COUNT, SELECT WHERE): ~10ms

  • Complex joins (call chains): ~50-100ms

  • Full-text search (LIKE): ~20-30ms


Troubleshooting

ASMAnalysisService not starting

Solutions:

  1. Check Java: java -version (need Java 11+)

  2. Build service: cd Extracteurs/ASMAnalysisService && gradlew.bat build

  3. Start manually: gradlew.bat service:run

Cache not detected

Verification:

ls axelor-repos/axelor-core-7.2.6/classes/
# Should contain .class files

FQN resolution fails or stale data

Solution:

  • Full reset: python run_asm_extraction.py /path/to/project --init

  • This drops all tables and rebuilds from scratch

Kill stuck Java processes

# Windows
wmic process where "name='java.exe' and CommandLine like '%ASMAnalysisService%'" delete

Development

Project Structure

CallGraph/
├── run_asm_extraction.py            # CLI entry point
├── mcp_callgraph_server.py          # Legacy MCP server
├── mcp.json                         # MCP configuration
├── ASM_ARCHITECTURE.md              # Detailed architecture
├── Extracteurs/
│   ├── GradleDependencyManager.py   # Gradle integration
│   ├── ASMExtractor.py              # Python client
│   └── ASMAnalysisService/          # Java service
│       └── service/                 # Kotlin/Gradle implementation
└── axelor-repos/                    # Cached Axelor packages

File Naming Conventions

PascalCase for Python files (matches main class):

  • ASMExtractor.pyclass ASMExtractor

  • GradleDependencyManager.pyclass GradleDependencyManager


License

MIT

Resources

Related MCP Connectors

Related MCP Servers