pyscn MCP Server
Model Context Protocol (MCP) integration for pyscn - Python Code Quality Analyzer.
Overview
pyscn MCP Server exposes pyscn's code analysis capabilities as MCP tools, allowing AI assistants like Claude, Cursor, and ChatGPT to directly analyze Python code quality.
Features
| Tool |
Description |
Key Metrics |
analyze_code |
Comprehensive code analysis |
All metrics combined |
check_complexity |
Cyclomatic complexity analysis |
McCabe complexity, nesting depth |
detect_clones |
Code clone detection |
APTED + LSH, Type 1-4 clones |
check_coupling |
Class coupling analysis |
CBO (Coupling Between Objects) |
find_dead_code |
Dead code detection |
CFG-based unreachable code |
get_health_score |
Overall code health |
Score (0-100), Grade (A-F) |
Quick Start
Claude Code: Plugin Install (Recommended)
For Claude Code, install the plugin. It registers the MCP server and also adds Agent Skills that teach Claude when to use each analysis:
claude plugin marketplace add ludo-technologies/pyscn
claude plugin install pyscn-mcp@pyscn-marketplace
For other MCP clients, or to set up only the MCP server, use one of the options below.
Option 1: Using uvx (Recommended)
The simplest way to use pyscn-mcp is with uvx, which automatically handles installation and execution:
{
"mcpServers": {
"pyscn-mcp": {
"command": "uvx",
"args": ["pyscn-mcp"],
"env": {
"PYSCN_CONFIG": "/path/to/.pyscn.toml"
}
}
}
}
Benefits:
- ✅ No manual installation needed
- ✅ Always uses the latest version
- ✅ Works across all platforms
- ✅ Supports environment variables
Environment Variables:
PYSCN_CONFIG: Path to custom configuration file (optional)
If you prefer to install pyscn once and use the binary path:
# Install pyscn as a uv tool
uv tool install pyscn
This installs pyscn and includes the compiled pyscn-mcp binary.
2. Locate the Binary Path
The MCP server binary is included in the uv tool installation. To find its path:
# Get the tool directory
uv tool dir
# Example output: C:\Users\YourName\AppData\Local\uv\tools\pyscn
The binary location depends on your platform:
- Windows:
<uv_tool_dir>\pyscn\bin\pyscn-mcp.exe
- Linux:
<uv_tool_dir>/pyscn/bin/pyscn-mcp
- macOS:
<uv_tool_dir>/pyscn/bin/pyscn-mcp
Add to Cursor settings (Settings → Features → Model Context Protocol):
Windows Example:
{
"mcpServers": {
"pyscn-mcp": {
"command": "C:\\Users\\YourName\\AppData\\Local\\uv\\tools\\pyscn\\bin\\pyscn-mcp.exe",
"args": [],
"env": {
"PYSCN_CONFIG": "/path/to/.pyscn.toml"
}
}
}
}
Linux Example:
{
"mcpServers": {
"pyscn-mcp": {
"command": "/home/username/.local/share/uv/tools/pyscn/bin/pyscn-mcp",
"args": [],
"env": {
"PYSCN_CONFIG": "/path/to/.pyscn.toml"
}
}
}
}
macOS Example:
{
"mcpServers": {
"pyscn-mcp": {
"command": "/Users/username/Library/Application Support/uv/tools/pyscn/bin/pyscn-mcp",
"args": [],
"env": {
"PYSCN_CONFIG": "/path/to/.pyscn.toml"
}
}
}
}
Note: Replace the path with your actual uv tool dir output path.
Configuration
Configuration File Priority
pyscn-mcp uses the TOML-only configuration loader. YAML, JSON, XDG, and
home-directory-specific configuration locations are not searched.
PYSCN_CONFIG - When set, this explicit file path is loaded at server
startup. A load failure is logged and the server registers its tools with a
built-in-default configuration snapshot; it does not continue searching
other locations. The explicit path is still retained, however, so tools
that load configuration while handling a request (including
analyze_code) report that configuration error instead of silently
analyzing with defaults. Fix or unset an invalid PYSCN_CONFIG value.
- Server working directory - When
PYSCN_CONFIG is unset, the server
searches upward from its current working directory. A .pyscn.toml file
takes priority over pyproject.toml with a [tool.pyscn] section.
- Per-tool loading - Configuration is not consumed uniformly by every
tool.
analyze_code resolves project analysis settings from the target path
upward when no explicit path is set. check_complexity, detect_clones,
check_cohesion, and find_dead_code pass the configured path to their
use-case loaders and may reload it for each request. check_coupling uses
selected values from the startup snapshot together with domain defaults; it
does not construct a CBO configuration loader.
- Built-in defaults - Used when no supported configuration is found.
Best Practice: Set PYSCN_CONFIG when the MCP server may start outside the
project directory. Otherwise, place .pyscn.toml in the project root.
Testing
Restart Cursor
Restart Cursor to load the MCP server.
Test It Out
Try asking your AI assistant:
- "Analyze the code quality of /path/to/my/project"
- "Check the complexity of functions in main.py"
- "Find duplicate code in my project"
- "What's the health score of my codebase?"
analyze_code
Description: Comprehensive Python code quality analysis
Parameters:
path (required): Path to Python code (file or directory)
analyses (optional): Array of analyses to run
- Options:
["complexity", "dead_code", "clone", "cbo", "lcom", "deps", "communities"]
- Default: all analyses, including
communities
recursive (optional): Recursively analyze directories (default: true)
output_mode (optional): "summary" (default) returns the health score and high-level metrics; "full" returns the complete report, including community_analysis and its community_context_map when community detection runs
Example:
Analyze the code at /home/user/project with all metrics
Output: JSON with comprehensive analysis results
{
"complexity": { ... },
"dead_code": { ... },
"clone": { ... },
"cbo": { ... },
"summary": {
"health_score": 85,
"grade": "A",
"total_files": 42,
"average_complexity": 5.2,
"dead_code_count": 2,
"clone_pairs": 5
}
}
Module communities (context map for AI agents)
Community detection groups modules into clusters by their import structure so an agent knows which files to inspect together. It runs by default; when analyses is provided, include "communities" in that list and set output_mode: "full" to retrieve the full context map.
Example:
Map the module architecture of /home/user/project and tell me which files to review together
The full response embeds a compact, deterministic community_context_map under community_analysis:
{
"community_analysis": {
"total_communities": 2,
"community_risk_score": 65,
"community_context_map": {
"version": 1,
"bundles": [
{
"community_id": "community_1",
"modules": ["app.orders.service", "app.orders.repository"],
"module_count": 2,
"packages": ["app.orders"],
"risk_level": "low",
"bridge_modules": [],
"suggested_review_scope": "app/orders/",
"summary": "2 modules; 1 package; risk low; 0 cross-community edges."
}
],
"bridge_modules": [
{
"module": "app.core.hub",
"connects": ["community_1", "community_3"],
"reason": "3 cross-community import edges"
}
]
}
}
}
Field notes:
bundles[] are clusters to review together. modules is capped for large clusters with a ... +N more marker; module_count always holds the true total. suggested_review_scope (a path prefix) is omitted when members share no common package.
bridge_modules[] couple two or more communities. Pull them into the review scope before changing cluster boundaries.
check_complexity
Description: Analyze cyclomatic complexity of Python execution scopes
Parameters:
path (required): Path to Python code
min_complexity (optional): Minimum complexity to report (default: 1)
max_complexity (optional): Maximum allowed complexity; 0 uses the default of 10 (default: 0)
show_details (optional): Include detailed metrics (default: true)
output_mode (optional): "summary", "detailed", or "full" (default: "summary")
max_results (optional): Maximum findings in summary or detailed output; 0 means unlimited (default: 0)
Example:
Check complexity of functions with complexity > 10 in src/
Output: Complexity analysis with risk levels
Summary and detailed findings use the same min_complexity and report_unchanged filtered scope population returned by full mode. Their aggregate max_scope_complexity and average_scope_complexity fields still describe the complete analyzed population; max_complexity remains the function-population maximum.
{
"functions": [
{
"name": "complex_function",
"file_path": "src/main.py",
"start_line": 42,
"metrics": {
"complexity": 15,
"nesting_depth": 4
},
"risk_level": "high"
}
],
"summary": {
"average_complexity": 8.5,
"high_risk_functions": 3
}
}
detect_clones
Description: Detect code clones using APTED tree edit distance and LSH
Parameters:
path (required): Path to Python code
similarity_threshold (optional): Minimum similarity 0.0-1.0 (default: 0.8)
min_lines (optional): Minimum lines to consider (default: 5)
group_clones (optional): Group related clones (default: true)
output_mode (optional): "summary", "detailed", or "full" (default: "summary")
max_results (optional): Maximum findings in summary or detailed output; 0 means unlimited (default: 0)
Example:
Find duplicate code with similarity > 0.85 in my project
Output: Clone pairs and groups
{
"clone_pairs": [
{
"clone1": {
"file_path": "src/a.py",
"start_line": 10,
"end_line": 25
},
"clone2": {
"file_path": "src/b.py",
"start_line": 42,
"end_line": 57
},
"similarity": 0.92,
"type": "Type-2"
}
],
"statistics": {
"total_clone_pairs": 5,
"average_similarity": 0.87
}
}
check_coupling
Description: Analyze class coupling (CBO - Coupling Between Objects)
Parameters:
path (required): Path to Python code
min_cbo (optional): Minimum CBO for high-coupling findings (default: 10)
output_mode (optional): "summary", "detailed", or "full" (default: "summary")
max_results (optional): Maximum findings in summary or detailed output; 0 means unlimited (default: 0)
Example:
Check the coupling of classes in src/
Output: CBO metrics per class
{
"classes": [
{
"name": "MyClass",
"file_path": "src/service.py",
"cbo": 8,
"risk_level": "high",
"coupled_classes": ["ClassA", "ClassB", ...]
}
],
"summary": {
"average_coupling": 4.2,
"high_coupling_classes": 3
}
}
check_cohesion
Description: Analyze class cohesion using LCOM4
Parameters:
path (required): Path to Python code
output_mode (optional): "summary", "detailed", or "full" (default: "summary")
max_results (optional): Maximum findings in summary or detailed output; 0 means unlimited (default: 0)
find_dead_code
Description: Find unreachable code using CFG analysis
Parameters:
path (required): Path to Python code
min_severity (optional): Minimum severity: info, warning, error (default: warning)
output_mode (optional): "summary", "detailed", or "full" (default: "summary")
max_results (optional): Maximum findings in summary or detailed output; 0 means unlimited (default: 0)
Example:
Find dead code with severity >= warning in my project
Output: Detailed mode returns structured issues from functions and executable class suites. function keeps the raw scope name for compatibility; scope_kind identifies its owner and scope_label is the canonical display label.
{
"issues": [
{
"file": "src/util.py",
"function": "process_data",
"line": 42,
"column": 5,
"scope_kind": "function",
"scope_label": "process_data",
"severity": "warning",
"reason": "unreachable_branch"
}
],
"summary": {
"total_issues": 5,
"critical_issues": 2,
"warning_issues": 3,
"info_issues": 0,
"files_analyzed": 4
}
}
Full mode returns DeadCodeResponse; its existing functions, total_functions, and affected_functions fields remain function-only. Class suites are reported additively in class_scopes, total_class_scopes, and affected_class_scopes. Aggregate finding, block, health, and module metrics include both populations.
get_health_score
Description: Get overall code health score (0-100) with grade
Parameters:
path (required): Path to Python code
Example:
What's the health score of my codebase?
Output: Health score and breakdown
{
"health_score": 85,
"grade": "A",
"is_healthy": true,
"partial": false,
"diagnostics": [],
"failures": [],
"category_scores": {
"complexity_score": 90,
"dead_code_score": 95,
"duplication_score": 80,
"coupling_score": 85,
"cohesion_score": 92,
"dependency_score": 88,
"architecture_score": 82
},
"summary": {
"total_files": 42,
"analyzed_files": 42,
"skipped_files": 0,
"average_complexity": 5.2,
"high_complexity_count": 1,
"dead_code_count": 2,
"clone_pairs": 5,
"high_coupling_classes": 3,
"high_lcom_classes": 1
}
}
Use Cases
1. AI Code Review
Scenario: Get AI-powered code review with actual metrics
Example Prompts:
- "Review this function for complexity and suggest improvements"
- "What are the quality issues in this module?"
- "Is this code maintainable?"
Benefits:
- Objective metrics instead of subjective opinions
- Specific refactoring suggestions
- Quantifiable improvement targets
2. Refactoring Assistant
Scenario: Find refactoring opportunities
Example Prompts:
- "Find duplicate code that can be refactored"
- "Which functions are too complex?"
- "What classes have high coupling?"
Benefits:
- Data-driven refactoring decisions
- Prioritized by impact
- Clear before/after metrics
3. Quality Gate
Scenario: Check if code is ready for review/deployment
Example Prompts:
- "Is this code ready for production?"
- "Check if the code meets our quality standards"
- "What's the overall quality of this PR?"
Benefits:
- Automated quality checks
- Consistent standards
- Fast feedback loop
4. Code Understanding
Scenario: Understand unfamiliar code
Example Prompts:
- "Explain this complex function"
- "What are the dependencies in this module?"
- "Which parts of this code are tightly coupled?"
Benefits:
- Context-aware explanations
- Visual dependency maps
- Complexity hotspots
Development
Building
# Build for current platform
make build-mcp
# Build for all platforms
make build-mcp-all
# Install globally
make install-mcp
Testing
Manual Testing
# Run the server directly
./pyscn-mcp
Then interact with it via stdin/stdout (JSON-RPC format).
MCP Inspector
Use the official MCP Inspector:
npx @modelcontextprotocol/inspector pyscn-mcp
This provides a web UI for:
- Viewing available tools
- Testing tool calls
- Inspecting request/response payloads
- Debugging issues
- Define the tool in
mcp/tools.go:
s.AddTool(mcp.NewTool("my_new_tool",
mcp.WithDescription("Tool description"),
mcp.WithString("path", mcp.Required(), mcp.Description("Path parameter")),
), HandleMyNewTool)
- Implement the handler in
mcp/handlers.go:
func HandleMyNewTool(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Parse arguments
// Call existing use cases
// Return JSON result
}
- Rebuild:
make build-mcp
Troubleshooting
Server Not Found
Issue: command not found: pyscn-mcp
Solutions:
- Build the binary:
make build-mcp
- Add to PATH or use absolute path in config
- Verify with:
which pyscn-mcp (Unix) or where pyscn-mcp (Windows)
Permission Denied
Issue: Permission denied when running pyscn-mcp
Solution (Unix):
chmod +x pyscn-mcp
Issue: Tool doesn't show up in AI assistant
Solutions:
- Restart the AI assistant completely
- Check server logs:
./pyscn-mcp 2> mcp.log
- Verify config file syntax
- Test with MCP Inspector
Analysis Fails
Issue: Tool returns error or times out
Solutions:
- Check path exists and is accessible
- Verify Python code is valid
- Increase timeout in config
- Check logs for specific errors
Issue: Analysis takes too long
Solutions:
- Analyze specific files instead of entire project
- Disable heavy analyses (clone detection)
- Increase timeout
- Use filters (min_complexity, similarity_threshold)
Architecture
AI Assistant (Claude/Cursor)
↓ JSON-RPC via stdio
pyscn MCP Server
↓ Function calls
Tool Handlers (mcp/handlers.go)
↓ Domain requests
Use Cases (app/)
↓ Business logic
Analyzers (internal/analyzer/)
↓ Tree-sitter parsing
Python Code
- Complexity analysis: ~100,000+ lines/sec
- Clone detection: Depends on code size
- LSH acceleration for large codebases
- ~1000 fragments/sec without LSH
- ~10,000+ fragments/sec with LSH
- CBO analysis: ~50,000+ lines/sec
- Dead code: ~80,000+ lines/sec
Security
- Path validation: Prevents directory traversal
- Resource limits: Timeout and memory constraints
- Sandboxing: Analysis runs in isolated context
- No code execution: Static analysis only
Contributing
See CONTRIBUTING.md for development guidelines.
License
MIT License - see LICENSE
Support