code-analyze-mcp
Standalone MCP server for code structure analysis using tree-sitter. Directory trees, symbol definitions, and call graphs without reading raw source files. Supports Rust, Python, Go, Java, TypeScript, Fortran, JavaScript, C/C++, and C#. Benchmarked up to 68% fewer tokens vs native tools.
README
<p align="center">
<h1 align="center">aptu-coder</h1>
<p align="center"> <a href="https://crates.io/crates/aptu-coder"><img alt="crates.io" src="https://img.shields.io/crates/v/aptu-coder.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20"></a> <a href="https://api.reuse.software/info/github.com/clouatre-labs/aptu-coder"><img alt="REUSE" src="https://img.shields.io/reuse/compliance/github.com/clouatre-labs/aptu-coder?style=for-the-badge" height="20"></a> <a href="https://slsa.dev"><img alt="SLSA Level 3" src="https://img.shields.io/badge/SLSA-Level%203-green?style=for-the-badge" height="20"></a> <a href="https://www.bestpractices.dev/projects/12275"><img alt="OpenSSF Best Practices" src="https://img.shields.io/cii/level/12275?style=for-the-badge" height="20"></a> </p>
<p align="center">Standalone MCP server for code structure analysis using tree-sitter. OpenSSF silver certified: fewer than 1% of open source projects reach this level.</p>
<!-- mcp-name: io.github.clouatre-labs/aptu-coder -->
[!NOTE] Native agent tools (regex search, path matching, file reading) handle targeted lookups well.
aptu-coderhandles the mechanical, non-AI work: mapping directory structure, extracting symbols, and tracing call graphs. Offloading this to a dedicated tool reduces token usage and speeds up coding with better accuracy.
Benchmarks
Auth migration task on Claude Code against Django (Python) source tree. Full methodology.
| Mode | Sonnet 4.6 | Haiku 4.5 |
|---|---|---|
| MCP | 112k tokens, $0.39 | 406k tokens, $0.42 |
| Native | 276k tokens, $0.95 | 473k tokens, $0.53 |
| Savings | 59% fewer tokens, 59% cheaper | 14% fewer tokens, 21% cheaper |
AeroDyn integration audit task on Claude Code against OpenFAST (Fortran) source tree. Full methodology.
| Mode | Sonnet 4.6 | Haiku 4.5 |
|---|---|---|
| MCP | 472k tokens, $1.65 | 687k tokens, $0.72 |
| Native | 877k tokens, $2.85 | 2162k tokens, $2.21 |
| Savings | 46% fewer tokens, 42% cheaper | 68% fewer tokens, 68% cheaper |
Overview
aptu-coder is a Model Context Protocol server that gives AI agents precise structural context about a codebase: directory trees, symbol definitions, and call graphs, without reading raw files. It supports Rust, Python, Go, Java, TypeScript, TSX, Fortran, JavaScript, C/C++, and C#, and integrates with any MCP-compatible orchestrator.
Supported Languages
All languages are enabled by default. Disable individual languages at compile time via Cargo feature flags.
| Language | Extensions | Feature flag |
|---|---|---|
| Rust | .rs |
lang-rust |
| Python | .py |
lang-python |
| TypeScript | .ts |
lang-typescript |
| TSX | .tsx |
lang-tsx |
| Go | .go |
lang-go |
| Java | .java |
lang-java |
| Fortran | .f, .f77, .f90, .f95, .f03, .f08, .for, .ftn |
lang-fortran |
| JavaScript | .js, .mjs, .cjs |
lang-javascript |
| C | .c |
lang-cpp |
| C++ | .cc, .cpp, .cxx, .h, .hpp, .hxx |
lang-cpp |
| C# | .cs |
lang-csharp |
To build with a subset of languages, disable default features and opt in:
[dependencies]
aptu-coder-core = { version = "*", default-features = false, features = ["lang-rust", "lang-python"] }
The current version is published on crates.io. Replace "*" with the latest version string if you prefer a pinned dependency.
Installation
Homebrew (macOS and Linux)
brew install clouatre-labs/tap/aptu-coder
Update: brew upgrade aptu-coder
cargo-binstall (no Rust required)
cargo binstall aptu-coder
cargo install (requires Rust toolchain)
cargo install aptu-coder
Quick Start
Build from source
cargo build --release
The binary is at target/release/aptu-coder.
Configure MCP Client
After installation via brew or cargo, register with the Claude Code CLI:
claude mcp add --transport stdio aptu-coder -- aptu-coder
If you built from source, use the binary path directly:
claude mcp add --transport stdio aptu-coder -- /path/to/repo/target/release/aptu-coder
stdio is intentional: this server runs locally and processes files directly on disk. The low-latency, zero-network-overhead transport matches the use case. Streamable HTTP adds a network hop with no benefit for a local tool.
Or add manually to .mcp.json at your project root (shared with your team via version control):
{
"mcpServers": {
"aptu-coder": {
"command": "aptu-coder",
"args": []
}
}
}
Tools
All optional parameters may be omitted. Shared optional parameters for analyze_directory, analyze_file, and analyze_symbol (analyze_module does not support these):
| Parameter | Type | Default | Description |
|---|---|---|---|
summary |
boolean | auto | Compact output; auto-triggers above 50K chars |
cursor |
string | -- | Pagination cursor from a previous response's next_cursor |
page_size |
integer | 100 | Items per page |
force |
boolean | false | Bypass output size warning |
verbose |
boolean | false | true = full output with section headers and imports (Markdown-style headers in analyze_directory; adds I: section in analyze_file); false = compact format |
summary=true and cursor are mutually exclusive. Passing both returns an error.
analyze_directory
Walks a directory tree, counts lines of code, functions, and classes per file. Respects .gitignore rules. Default output is a flat PAGINATED list. Pass verbose=true for FILES / TEST FILES section headers. Pass summary=true for a compact STRUCTURE tree with aggregate counts.
Required: path (string) -- directory to analyze
Additional optional: max_depth (integer, default unlimited) -- recursion limit; use 2-3 for large monorepos
analyze_directory path: /path/to/project
analyze_directory path: /path/to/project max_depth: 2
analyze_directory path: /path/to/project summary: true
analyze_directory path: /path/to/project verbose: true
analyze_file
Extracts functions, classes, and imports from a single file.
Required: path (string) -- file to analyze
Additional optional:
ast_recursion_limit(integer, optional) -- tree-sitter AST traversal depth cap; leave unset for unlimited depth. Minimum value is 1; 0 is treated as unset.fields(array of strings, optional) -- limit output to specific sections. Valid values:"functions","classes","imports". Omit to return all sections. The FILE header (path, line count, section counts) is always emitted regardless. Ignored whensummary=true. When"imports"is listed explicitly, theI:section is rendered regardless of theverboseflag.
analyze_file path: /path/to/file.rs
analyze_file path: /path/to/file.rs page_size: 50
analyze_file path: /path/to/file.rs cursor: eyJvZmZzZXQiOjUwfQ==
analyze_module
Extracts a minimal function/import index from a single file. ~75% smaller output than analyze_file. Use when you need function names and line numbers or the import list, without signatures, types, or call graphs. Returns an actionable error if called on a directory path, steering to analyze_directory.
Required: path (string) -- file to analyze
analyze_module path: /path/to/file.rs
analyze_symbol
Builds a call graph for a named symbol across all files in a directory. Uses sentinel values <module> (top-level calls) and <reference> (type references). Functions called >3 times show (•N) notation.
Required:
path(string) -- directory to searchsymbol(string) -- symbol name, case-sensitive exact-match
Additional optional:
follow_depth(integer, default 1) -- call graph traversal depthmax_depth(integer, default unlimited) -- directory recursion limitast_recursion_limit(integer, optional) -- tree-sitter AST traversal depth cap; leave unset for unlimited depth. Minimum value is 1; 0 is treated as unset.impl_only(boolean, optional) -- when true, restrict callers to only those originating from animpl Trait for Typeblock (Rust only). ReturnsINVALID_PARAMSif the path contains no.rsfiles. Emits aFILTER:header showing how many callers were retained out of total.match_mode(string, default exact) -- Symbol lookup strategy:exact: Case-sensitive exact match (default)insensitive: Case-insensitive exact matchprefix: Case-insensitive prefix match; returns an error listing candidates when multiple symbols matchcontains: Case-insensitive substring match; returns an error listing candidates when multiple symbols match All non-exact modes return an error with candidate names when the match is ambiguous; use the listed candidates to refine to a unique match.
The tool also returns structuredContent with typed arrays for programmatic consumption: callers (production callers), test_callers (callers from test files), and callees (direct callees), each as Option<Vec<CallChainEntry>>. A CallChainEntry has three fields: symbol (string), file (string), and line (JSON integer; usize in the Rust API). These arrays represent depth-1 relationships only; follow_depth does not affect them.
Example output:
FOCUS: format_structure_paginated (1 defs, 1 callers, 3 callees)
CALLERS (1-1 of 1):
format_structure_paginated <- analyze_directory
<- format_structure_paginated
CALLEES: 3 (use cursor for callee pagination)
analyze_symbol path: /path/to/project symbol: my_function
analyze_symbol path: /path/to/project symbol: my_function follow_depth: 3
analyze_symbol path: /path/to/project symbol: my_function max_depth: 3 follow_depth: 2
Output Management
For large codebases, two mechanisms prevent context overflow:
Pagination
analyze_file and analyze_symbol append a NEXT_CURSOR: line when output is truncated. Pass the token back as cursor to fetch the next page. summary=true and cursor are mutually exclusive; passing both returns an error.
# Response ends with:
NEXT_CURSOR: eyJvZmZzZXQiOjUwfQ==
# Fetch next page:
analyze_symbol path: /my/project symbol: my_function cursor: eyJvZmZzZXQiOjUwfQ==
Summary Mode
When output exceeds 50K chars, the server auto-compacts results using aggregate statistics. Override with summary: true (force compact) or summary: false (disable).
# Force summary for large project
analyze_directory path: /huge/codebase summary: true
# Disable summary (get full details, may be large)
analyze_directory path: /project summary: false
Non-Interactive Pipelines
In single-pass subagent sessions, prompt caches are written but never reused. Benchmarks showed MCP responses writing ~2x more to cache than native-only workflows, adding cost with no quality gain. Set DISABLE_PROMPT_CACHING=1 (or DISABLE_PROMPT_CACHING_HAIKU=1 for Haiku-specific pipelines) to avoid this overhead.
The server's own instructions expose a 4-step recommended workflow for unknown repositories: survey the repo root with analyze_directory at max_depth=2, drill into the source package, run analyze_module on key files for a function/import index (or analyze_file when signatures and types are needed), then use analyze_symbol to trace call graphs. MCP clients that surface server instructions will present this workflow automatically to the agent.
Environment Variables
| Variable | Default | Description |
|---|---|---|
CODE_ANALYZE_FILE_CACHE_CAPACITY |
100 |
Maximum number of file-analysis results held in the in-process LRU cache. Increase for large repos where many files are queried repeatedly. |
CODE_ANALYZE_DIR_CACHE_CAPACITY |
20 |
Maximum number of directory-analysis results held in the in-process LRU cache. |
DISABLE_PROMPT_CACHING |
unset | Set to 1 to disable prompt caching (recommended for single-pass subagent sessions). |
DISABLE_PROMPT_CACHING_HAIKU |
unset | Set to 1 to disable prompt caching for Haiku-specific pipelines only. |
Observability
All four tools emit metrics to daily-rotated JSONL files at $XDG_DATA_HOME/aptu-coder/ (fallback: ~/.local/share/aptu-coder/). Each record captures tool name, duration, output size, and result status. Files are retained for 30 days. See docs/OBSERVABILITY.md for the full schema.
Documentation
- ARCHITECTURE.md - Design goals, module map, data flow, language handler system, caching strategy
- MCP Best Practices - Best practices for agentic loops, orchestration patterns, MCP tool design, memory management, and safety controls
- OBSERVABILITY.md - Metrics schema, JSONL format, and retention policy
- ROADMAP.md - Development history and future direction
- DESIGN-GUIDE.md - Design decisions, rationale, and replication guide for building high-performance MCP servers
- CONTRIBUTING.md - Development workflow, commit conventions, PR checklist
- SECURITY.md - Security policy and vulnerability reporting
License
Apache-2.0. See LICENSE for details.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.