Skip to main content
Glama
aferreiraguido

python-mcp-server

README.md
# python-mcp-server

MCP (Model Context Protocol) server for deterministic static analysis of Python code, built with **LibCST** + **Jedi**.

## Motivation

This project is inspired by the deterministic analysis approach of [**cobol-mcp-server**](https://github.com/aferreiraguido/cobol-mcp-server). Just as that project demonstrates that COBOL can be analyzed deterministically via AST (without AI), **python-mcp-server** applies the same principle to the Python ecosystem:

- **LibCST** — parses source code into a concrete AST, guaranteeing 100% deterministic analysis (no guesswork).
- **Jedi** — optional type resolution for finding definitions and references (on-demand, no classpath required).
- No code execution — only static syntax analysis.

The result is an MCP server that exposes Python code analysis tools to any MCP client (OpenCode, Claude Desktop, etc.).

## Tools

| Tool | Description |
|---|---|
| `load_python_project` | Loads a project from local path, Git URL, or archive (.zip/.tar.gz) |
| `unload_python_project` | Removes a loaded project from memory |
| `list_loaded_projects` | Lists all projects currently in memory |
| `project_metadata` | Shows project metadata (build, modules, classes, functions) |
| `detect_build_system` | Detects the build system (pip, poetry, pdm, hatch, uv, conda, etc.) |
| `detect_framework` | Detects frameworks/libs in use (Flask, Django, FastAPI, SQLAlchemy, etc.) |
| `list_modules` | Lists all Python modules in the project |
| `list_classes` | Lists all classes in the project |
| `list_functions` | Lists all functions and methods in the project |
| `inspect_class` | Deep-dives into a class: bases, methods, decorators, docstring |
| `inspect_function` | Deep-dives into a function: signature, parameters, body, decorators |
| `list_imports` | Lists all imports in the project |
| `list_decorators` | Lists all decorators used with their frequencies |
| `find_decorated_elements` | Finds classes/functions decorated with a specific decorator |
| `class_hierarchy` | Shows inheritance hierarchy (bases and direct subclasses) |
| `search_source` | Searches text in the project source code |
| `multi_file_search` | Searches text across all loaded projects simultaneously |
| `find_definition` | Finds the definition of a symbol (requires `type_resolution=True`) |
| `find_references` | Finds references to a symbol (requires `type_resolution=True`) |
| `get_file_content` | Reads the raw source content of any file in the project |
| `get_source_range` | Returns specific source lines with line numbers from a file |
| `validate_code_reference` | Checks if a class, function, or variable exists (EXISTS/NOT_FOUND) |
| `resolve_type` | Resolves a type name to its definition, tracing imports across the project |
| `inspect_module` | Deep-dives into a module: docstring, variables, functions, classes, imports |
| `call_chain` | Static call graph: shows which functions are called and which call a given function |
| `variable_xref` | Cross-references a variable: shows declarations, writes, and reads across the project |
| `list_enums` | Lists all enum classes with their members and values |
| `module_dependency_graph` | Builds a dependency map showing which modules import which |
| `list_methods_by_return_type` | Finds all functions/methods that return a specific type annotation |
| `find_entry_points` | Finds where the program starts: if __name__, console_scripts, web apps, CLI apps, async runners |
| `list_api_endpoints` | Lists all API route/endpoint definitions (FastAPI, Flask, Starlette, Django) with paths and handlers |
| `extract_environment_dependencies` | Extracts environment variable reads, config file loads, and Pydantic Field(env=...) |
| `list_side_effects` | Classifies functions by side effects: FILE_IO, NETWORK, DB, LOG, MUTATION, or PURE |
| `analyze_error_handling` | Analyzes error handling: custom exceptions, raises, try/except patterns, bare excepts |
| `list_public_api` | Shows the public API surface: __all__, __init__.py re-exports, top-level public definitions |
| `find_test_mapping` | Maps production code to tests by analyzing test file imports |
| `extract_domain_vocabulary` | Extracts domain-specific vocabulary — key entities, module docstrings, domain terms |

## Building the .pyz (fat-pyz)

> **Important:** `shiv` must be installed in the Python interpreter that you use to **build** the wheel. The resulting `.pyz` will then be runnable on any Python 3.8+ system interpreter, but the **build itself** must be done with a Python version you want to ship wheels for.

The package layout is `src/pymcp/` (declared in `pyproject.toml` via `setuptools.packages.find`), so `shiv` discovers it automatically when run from the project root.

### Build inside a venv (recommended)

```bash
# 1. Create / activate a venv (use any Python 3.8+ interpreter)
python -m venv .venv
source .venv/bin/activate

# 2. Install build tooling
pip install shiv

# 3. Build the fat .pyz from the project root
shiv -o python-mcp-server.pyz -e pymcp.main:main -p "/usr/bin/env python3" .
```

> For reproducible builds, create the venv with a pinned interpreter, e.g. `python3.11 -m venv .venv`. This guarantees the bundled wheels match the target Python's ABI.

### Why this matters

- `shiv` resolves dependencies via `pip`, which downloads wheels built for **the active interpreter's ABI** (e.g. `cp311` for Python 3.11).
- If you build with Python 3.14 and try to run the `.pyz` with the system's Python 3.11, you'll get `zipimport.ZipImportError` because the bundled `cp314` wheels are not compatible.
- The `pyproject.toml` pins `requires-python = ">=3.8"`. Make sure the Python used to build the `.pyz` is the same major.minor you intend to run it with.

### Smoke test

```bash
./python-mcp-server.pyz   # starts the MCP server on stdio and logs to /tmp/python_mcp_server.log
```

The resulting `.pyz` is standalone — it includes all dependencies (`mcp`, `libcst`, `jedi`, `GitPython`) and works with any Python 3.8+ interpreter.

You can also run the installed entrypoint directly:

```bash
python-mcp-server
```

## OpenCode Configuration

Add the following to your `opencode.json` or `opencode.yml`:

```json
{
  "mcpServers": {
    "python-mcp-server": {
      "command": "python",
      "args": ["python-mcp-server.pyz"],
      "transport": "stdio"
    }
  }
}
```

Or if installed as a package:

```json
{
  "mcpServers": {
    "python-mcp-server": {
      "command": "python-mcp-server",
      "transport": "stdio"
    }
  }
}
```

## Environment Variables

| Variable | Default | Description |
|---|---|---|
| `PYMCP_LOG_FILE` | `/tmp/python_mcp_server.log` | Log file path |
| `PYMCP_LOG_LEVEL` | `INFO` | Log level (DEBUG, INFO, WARNING, ERROR) |

## Dependencies

- `mcp >= 1.0.0`
- `libcst >= 1.0.0`
- `jedi >= 0.19.0`
- `GitPython >= 3.1.0`

## License

MIT — see [LICENSE](LICENSE).