mcp

package
v1.31.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 11 Imported by: 0

README

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

Available Tools
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

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.

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)
Option 2: Using uv tool install

If you prefer to install pyscn once and use the binary path:

1. Install pyscn via uv tool
# 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
3. Configure Cursor

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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?"

Tool Usage

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
Adding New Tools
  1. 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)
  1. 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
}
  1. Rebuild:
make build-mcp

Troubleshooting

Server Not Found

Issue: command not found: pyscn-mcp

Solutions:

  1. Build the binary: make build-mcp
  2. Add to PATH or use absolute path in config
  3. 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
Tool Not Appearing

Issue: Tool doesn't show up in AI assistant

Solutions:

  1. Restart the AI assistant completely
  2. Check server logs: ./pyscn-mcp 2> mcp.log
  3. Verify config file syntax
  4. Test with MCP Inspector
Analysis Fails

Issue: Tool returns error or times out

Solutions:

  1. Check path exists and is accessible
  2. Verify Python code is valid
  3. Increase timeout in config
  4. Check logs for specific errors
Slow Performance

Issue: Analysis takes too long

Solutions:

  1. Analyze specific files instead of entire project
  2. Disable heavy analyses (clone detection)
  3. Increase timeout
  4. 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

Performance

  • 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

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegisterTools

func RegisterTools(s *server.MCPServer, handlers *HandlerSet)

RegisterTools registers all pyscn MCP tools with the server

Types

type Dependencies

type Dependencies struct {
	// contains filtered or unexported fields
}

Dependencies aggregates the shared services required by MCP handlers.

func NewDependencies

func NewDependencies(cfg *config.Config, configPath string) *Dependencies

NewDependencies constructs the dependency set with sane defaults.

func (*Dependencies) BuildAnalyzeUseCase

func (d *Dependencies) BuildAnalyzeUseCase() (*app.AnalyzeUseCase, error)

BuildAnalyzeUseCase assembles a fresh AnalyzeUseCase with injected dependencies.

func (*Dependencies) Config

func (d *Dependencies) Config() *config.Config

Config exposes the loaded configuration snapshot.

func (*Dependencies) ConfigPath

func (d *Dependencies) ConfigPath() string

ConfigPath returns the configured config file path (may be empty to trigger discovery).

type HandlerSet

type HandlerSet struct {
	// contains filtered or unexported fields
}

HandlerSet exposes MCP tool handlers with shared dependencies.

func NewHandlerSet

func NewHandlerSet(deps *Dependencies) *HandlerSet

NewHandlerSet constructs a handler set.

func (*HandlerSet) HandleAnalyzeCode

func (h *HandlerSet) HandleAnalyzeCode(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)

HandleAnalyzeCode handles the analyze_code tool

func (*HandlerSet) HandleCheckCohesion added in v1.11.0

func (h *HandlerSet) HandleCheckCohesion(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)

HandleCheckCohesion handles the check_cohesion tool

func (*HandlerSet) HandleCheckComplexity

func (h *HandlerSet) HandleCheckComplexity(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)

HandleCheckComplexity handles the check_complexity tool

func (*HandlerSet) HandleCheckCoupling

func (h *HandlerSet) HandleCheckCoupling(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)

HandleCheckCoupling handles the check_coupling tool

func (*HandlerSet) HandleDetectClones

func (h *HandlerSet) HandleDetectClones(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)

HandleDetectClones handles the detect_clones tool

func (*HandlerSet) HandleDetectDIAntipatterns added in v1.16.0

func (h *HandlerSet) HandleDetectDIAntipatterns(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)

HandleDetectDIAntipatterns handles the detect_di_antipatterns tool

func (*HandlerSet) HandleFindDeadCode

func (h *HandlerSet) HandleFindDeadCode(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)

HandleFindDeadCode handles the find_dead_code tool

func (*HandlerSet) HandleGetHealthScore

func (h *HandlerSet) HandleGetHealthScore(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)

HandleGetHealthScore handles the get_health_score tool

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL