PyEye Server
# PyEye šļø
## PyEye Server
[](https://github.com/okeefeco/pyeye-mcp/actions/workflows/ci.yml)
[](https://codecov.io/gh/okeefeco/pyeye-mcp)
[](https://www.python.org/downloads/)
[](https://github.com/psf/black)
[](https://github.com/astral-sh/ruff)
[](https://github.com/pre-commit/pre-commit)
An extensible MCP (Model Context Protocol) server that provides intelligent Python code analysis, navigation, and understanding capabilities for AI assistants like Claude.
## Features
- š **Semantic Code Navigation**: Resolve any identifier to a stable canonical handle, then inspect or outline it
- š **Structural Traversal**: Walk `members`, `callees`, `imports`, `imported_by`, `subclasses`, `superclasses`, and `submodules` ā one hop with `expand`, many with `trace`
- š¤ **Honest by Construction**: Unsupported questions (notably "who calls this") are *refused*, not guessed; absent counts mean "not measured", never "zero"
- šļø **Multi-Project Support**: Analyze multiple projects and dependencies simultaneously
- š¦ **Namespace Packages**: Handle packages distributed across multiple repositories
- š **Standalone Scripts**: Analyze notebooks, scripts, and examples alongside formal packages
- š **Auto-Update**: Automatically detects and reflects file changes with smart cache invalidation
- āļø **Configuration System**: Flexible configuration via files, env vars, or auto-discovery
- š **Extensible Plugin System**: Framework-specific analyzers (Pydantic, Django, Flask)
- š **Fast & Cached**: Intelligent caching with LRU eviction and performance optimization
- šÆ **Type-Aware**: Full understanding of Python type hints and annotations
- š **Performance Monitoring**: Built-in metrics tracking with p50/p95/p99 latencies
- š”ļø **Input Validation**: Secure parameter validation and path checking
- š¤ **Development Automation**: Release automation, dogfooding metrics, and worktree safety
## Installation
PyEye can be installed in three ways: as a Claude Code plugin (zero-config), into your project's venv, or globally.
### Option 0: Claude Code Plugin (zero-config)
When PyEye is installed as a Claude Code plugin, dependencies install automatically into a per-user venv on first session start ā no manual `pip install` needed.
- **First session start**: a `SessionStart` hook runs `uv sync --no-dev --frozen` against the plugin checkout, with the venv built at `${CLAUDE_PLUGIN_DATA}/.venv` (typically `~/.claude/plugins/data/<plugin-id>/.venv`).
- **Subsequent sessions**: the hook compares `uv.lock` in the plugin checkout against the last-installed copy in `${CLAUDE_PLUGIN_DATA}` and is a silent no-op unless the lockfile changed.
- **Requirement**: `uv` must be on your `PATH`. See [uv installation](https://docs.astral.sh/uv/getting-started/installation/).
Both the install hook and the MCP server invocation use `uv run`, so this works on Linux, macOS, and Windows without OS-specific configuration.
### Option 1: Project-Specific Installation (Recommended)
Install directly into your Python project's virtual environment:
```bash
# Activate your project's virtual environment
source /path/to/your/project/venv/bin/activate
# Install from PyPI
pip install pyeye-mcp
# Or install from source
git clone https://github.com/okeefeco/pyeye-mcp.git
pip install -e ./pyeye-mcp
```
Then create a `.mcp.json` file in your project root:
```json
{
"mcpServers": {
"pyeye": {
"type": "stdio",
"command": "python",
"args": ["-m", "pyeye.mcp"],
"env": {}
}
}
}
```
This way, the MCP server uses your project's environment and has access to all your project's dependencies.
### Option 2: Global Installation
For analyzing multiple projects or using with global Python:
```bash
# Install globally with pipx (recommended for isolation)
pipx install pyeye-mcp
# Or with pip
pip install --user pyeye-mcp
# Or from source
git clone https://github.com/okeefeco/pyeye-mcp.git
cd pyeye-mcp
pip install --user .
```
#### Configure with Claude Code (Global)
```bash
# Add the MCP server globally (available in all projects)
claude mcp add pyeye -s user -- python -m pyeye.mcp
# Verify it's connected
claude mcp list
```
### Configure with Claude Desktop
Add to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"pyeye": {
"command": "python",
"args": ["-m", "pyeye.mcp"],
"env": {}
}
}
}
```
Note: Use the full path to Python if needed (e.g., `/usr/local/bin/python3` or `C:\\Python311\\python.exe`).
### Configure with GitHub Copilot (VS Code)
As of 2025, GitHub Copilot has full MCP support in VS Code, JetBrains, Eclipse, and Xcode. Follow these steps to use this PyEye server with GitHub Copilot:
#### Prerequisites
- **GitHub Copilot Business or Enterprise subscription** (required for MCP support)
- **VS Code version 1.102 or later** (MCP support is GA)
- **Organization MCP policy enabled** by your admin
#### Step 1: Enable MCP in Your Organization
Your GitHub Copilot administrator needs to enable the MCP servers policy:
1. Go to your organization settings on GitHub
2. Navigate to **Copilot** ā **Policies**
3. Enable **"MCP servers in Copilot"** policy
4. Save changes
#### Step 2: Install the MCP Server
Install the PyEye server in your project or globally:
```bash
# Option A: Install in your project's virtual environment (recommended)
pip install pyeye-mcp
# Option B: Install globally with pipx
pipx install pyeye-mcp
# Option C: Install from source
git clone https://github.com/okeefeco/pyeye-mcp.git
pip install -e ./pyeye-mcp
```
#### Step 3: Configure VS Code
Add the MCP server configuration to your VS Code settings:
**User Settings** (applies to all projects):
```json
// File: ~/.config/Code/User/settings.json (Linux/Mac)
// or %APPDATA%\Code\User\settings.json (Windows)
{
"github.copilot.chat.mcpServers": {
"pyeye": {
"command": "python",
"args": ["-m", "pyeye.mcp"],
"env": {}
}
}
}
```
**Workspace Settings** (project-specific):
```json
// File: .vscode/settings.json in your project root
{
"github.copilot.chat.mcpServers": {
"pyeye": {
"command": "${workspaceFolder}/.venv/bin/python",
"args": ["-m", "pyeye.mcp"],
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
}
}
}
```
#### Step 4: Verify Connection
1. Open VS Code in your Python project
2. Open the GitHub Copilot Chat panel
3. Type: `@mcp list` to see available MCP servers
4. You should see `pyeye` in the list
5. Test with: `@mcp pyeye resolve MyClass`
#### Troubleshooting
**MCP not available:**
- Ensure you have Copilot Business/Enterprise (not Free/Pro)
- Check that your organization admin enabled the MCP policy
- Update VS Code to version 1.102 or later
**Server not connecting:**
- Verify Python path in the configuration
- Check that `pyeye` is installed: `python -m pyeye.mcp --help`
- Look for errors in VS Code Output panel ā GitHub Copilot Logs
**Import errors:**
- If using a virtual environment, ensure the path points to the venv Python
- Add `PYTHONPATH` to the env configuration if needed
#### Other IDEs
**JetBrains IDEs** (IntelliJ, PyCharm, etc.):
- MCP support is GA - configure in Settings ā Tools ā GitHub Copilot ā MCP Servers
**Visual Studio**:
- MCP support is in preview - configure in Tools ā Options ā GitHub Copilot ā MCP Servers
**Eclipse & Xcode**:
- MCP support is GA - see IDE-specific documentation for configuration
## Configuration
The server can be configured to analyze packages in other locations. Create a `.pyeye.json` file in your project:
```json
{
"packages": [
"../my-shared-library",
"~/repos/company-utils",
"/absolute/path/to/package"
],
"namespaces": {
"mycompany": [
"~/repos/mycompany-auth",
"~/repos/mycompany-api"
]
}
}
```
### Configuration Methods
Configuration is loaded in the following order (later sources override earlier ones):
1. **Global Config**: `~/.config/pyeye/config.json` or `~/.pyeye.json` - User defaults
2. **Project Config**: `.pyeye.json` in project root or `[tool.pyeye]` in `pyproject.toml`
3. **Override File**: `.pyeye.override.json` - Local development overrides (git-ignored)
4. **Auto-Discovery**: Automatically detects source layouts and sibling packages if no packages configured
### Using Override Files
Override files are perfect for local development configurations that shouldn't be committed:
```json
// .pyeye.override.json (git-ignored)
{
"packages": [
"../my-local-dev-package",
"~/dev/experimental"
],
"namespaces": {
"company.feature": ["/home/user/feature-branch"]
}
}
```
### Performance Settings
All performance-critical settings can be configured via environment variables to tune for your specific workload:
| Environment Variable | Default | Description | Valid Range |
|---------------------|---------|-------------|-------------|
| `PYEYE_MAX_PROJECTS` | 10 | Maximum number of projects in memory | 1-1000 |
| `PYEYE_CACHE_TTL` | 300 | Cache time-to-live in seconds | 0-86400 (24h) |
| `PYEYE_WATCHER_DEBOUNCE` | 0.5 | File watcher debounce delay in seconds | 0.0-10.0 |
| `PYEYE_MAX_FILE_SIZE` | 1048576 | Maximum file size to analyze (bytes) | 1KB-100MB |
| `PYEYE_MAX_WORKERS` | 4 | Maximum concurrent analysis workers | 1-32 |
| `PYEYE_ANALYSIS_TIMEOUT` | 30.0 | Analysis timeout in seconds | 1.0-300.0 |
| `PYEYE_ENABLE_MEMORY_PROFILING` | false | Enable memory profiling | true/false |
| `PYEYE_ENABLE_PERFORMANCE_METRICS` | false | Enable performance metrics | true/false |
| **Connection Pooling** | | **Optimize multi-project workflows** | |
| `PYEYE_ENABLE_CONNECTION_POOLING` | true | Enable connection pooling for multiple projects | true/false |
| `PYEYE_POOL_MAX_CONNECTIONS` | 10 | Maximum pooled project connections | 1-100 |
| `PYEYE_POOL_TTL` | 3600 | Connection time-to-live in seconds | 60-86400 |
#### Performance Tuning Examples
**Large codebase with stable files:**
```bash
export PYEYE_MAX_PROJECTS=50 # Handle more projects
export PYEYE_CACHE_TTL=1800 # 30 minute cache
export PYEYE_WATCHER_DEBOUNCE=2.0 # Less frequent updates
```
**Active development with frequent changes:**
```bash
export PYEYE_MAX_PROJECTS=5 # Fewer projects, faster switching
export PYEYE_CACHE_TTL=60 # 1 minute cache
export PYEYE_WATCHER_DEBOUNCE=0.1 # Near real-time updates
```
**Memory-constrained environment:**
```bash
export PYEYE_MAX_PROJECTS=3 # Minimal project cache
export PYEYE_MAX_FILE_SIZE=524288 # 512KB file limit
export PYEYE_MAX_WORKERS=2 # Fewer workers
```
This file is automatically ignored by git and takes precedence over all other configuration sources.
### Auto-Detection of Source Layouts
PyEye automatically detects source layouts from `pyproject.toml` build backend metadata, supporting projects that use the `src/` directory pattern. This works with multiple build backends:
**Setuptools:**
```toml
[tool.setuptools.packages.find]
where = ["src"]
```
**Poetry:**
```toml
[[tool.poetry.packages]]
include = "mypackage"
from = "src"
```
**Hatch:**
```toml
[tool.hatch.build.targets.wheel]
sources = ["src"]
```
**PDM:**
```toml
[tool.pdm.build]
package-dir = "src"
```
If no configuration is found in `pyproject.toml`, PyEye will also check for the presence of a `src/` directory containing Python packages and automatically add it to the package paths.
**Note:** Explicit `[tool.pyeye]` configuration always takes precedence over auto-detected layouts.
## Core Tools
PyEye exposes a small set of **progressive-disclosure primitives** that work on
canonical handles (a re-exported path collapses to its definition site). They are
cheap by default ā they return structural facts and pointers, never source
content. For the full mechanics, the authoritative list of supported edges, and
worked examples, see the `python-explore` skill (`skills/python-explore/SKILL.md`).
### Navigation & Inspection
- **`resolve`** - Resolve a name or dotted path to canonical handle(s); the usual entry point
- **`resolve_at`** - Resolve the symbol at a `file:line:column` location
- **`inspect`** - Structured facts for a handle: signature, type, docstring, edge counts
- **`outline`** - Structural outline of a module, package, or class and its members
- **`expand`** - Follow one relationship edge from a handle (e.g. `members`, `imports`, `imported_by`, `subclasses`); the skill lists the full supported-edge set
- **`trace`** - Multi-hop traversal across edges (e.g. `follow=["imports"]` for the dependency closure, `follow=["subclasses"]` for the full hierarchy)
### Multi-Project
- **`configure_packages`** - Register additional package locations to analyze alongside the project
### Known limitation: no reverse references
The interface does not answer **"who calls / references this"**. Reverse-reference
support is deferred to a planned Pyright backend
([#333](https://github.com/okeefeco/pyeye-mcp/issues/333)). When you ask for a
`callers` or `references` edge, pyeye reports it as unsupported rather than
guessing, and `inspect`'s `edge_counts` omits those keys entirely instead of
reporting `0`. Delegate caller questions to a language server; pyeye's `resolve`
gives you the definition-site position an LSP reference query needs.
Forward and structural questions *are* answered: `callees` (what a function calls),
`imported_by` / `imports`, `subclasses` / `superclasses`, `members`, `submodules`.
> **Removed in v2.0.** `find_references`, `get_call_hierarchy`, and
> `analyze_dependencies` were removed in
> [#505](https://github.com/okeefeco/pyeye-mcp/issues/505) ā not deprecated ā
> because they returned confidently wrong answers rather than merely incomplete
> ones. See the CHANGELOG's migration map for replacements.
### Framework-Specific Tools (Auto-Activated)
#### Django (when Django is detected)
- **`find_django_models`** - Find all Django models
- **`find_django_views`** - Find all views
- **`find_django_urls`** - Find URL patterns
- **`find_django_templates`** - Find templates
- **`find_django_migrations`** - Find migrations
#### Pydantic (when Pydantic is detected)
- **`find_pydantic_models`** - Discover all BaseModel classes
- **`get_model_schema`** - Extract complete model schema
- **`find_validators`** - Locate all validation methods
- **`find_field_validators`** - Find field-specific validators
- **`find_model_config`** - Extract model configurations
- **`trace_model_inheritance`** - Map model inheritance hierarchies
- **`find_computed_fields`** - Find computed_field and @property fields
#### Flask (when Flask is detected)
- **`find_flask_routes`** - Discover all route decorators with methods and endpoints
- **`find_flask_blueprints`** - Locate Blueprint definitions and registrations
- **`find_flask_views`** - Find view functions and MethodView classes
- **`find_flask_templates`** - Locate Jinja2 templates and render_template calls
- **`find_flask_extensions`** - Identify Flask extensions (SQLAlchemy, Login, CORS, etc.)
- **`find_flask_config`** - Find configuration files and app.config usage
- **`find_error_handlers`** - Locate @app.errorhandler decorators
- **`find_cli_commands`** - Find Flask CLI commands (@app.cli.command)
## Advanced Features
### Multi-Project Support
Analyze your main project along with local dependencies:
```python
# Configure to analyze multiple packages
configure_packages(
packages=["../my-lib", "~/repos/shared-utils"],
namespaces={"company": ["~/repos/company-*"]}
)
```
### Namespace Packages
Handle packages distributed across multiple repositories:
```python
# company.auth in repo A, company.api in repo B
configure_packages(
namespaces={"company": ["~/repos/company-auth", "~/repos/company-api"]}
)
```
### Auto-Update on File Changes
The server uses file watching to automatically update when code changes:
- Detects modifications in real-time
- Invalidates cache for changed files
- Maintains separate watchers per project
## Architecture
```text
PyEye
āāā Core Server (FastMCP)
ā āāā MCP tools (primitives + auto-activated framework plugins)
āāā Project Manager
ā āāā Multi-project support (LRU cache, max 10)
ā āāā Connection pooling (optional optimization)
ā āāā Namespace resolver
ā āāā Configuration loader (multiple sources)
āāā Analysis Engine
ā āāā Jedi (semantic analysis & type inference)
āāā Caching & Performance
ā āāā File watchers (watchdog with debouncing)
ā āāā Granular cache (5min TTL with smart invalidation)
ā āāā Metrics collection (p50/p95/p99 latencies)
ā āāā Performance monitoring & reporting
āāā Validation & Security
ā āāā Input validation (MCP tool parameters)
ā āāā Path security checks
ā āāā Safe file operations
āāā Plugin System
ā āāā Base plugin class (AnalyzerPlugin)
ā āāā Pydantic plugin (7 specialized tools)
ā āāā Django plugin (5 specialized tools)
ā āāā Flask plugin (8 specialized tools)
āāā Utility Systems
ā āāā Dependency tracking & circular detection
ā āāā Import analysis & re-export resolution
ā āāā Scope management (main/all/namespace scoping)
ā āāā Async utilities (concurrent operations)
ā āāā Cross-platform path handling
āāā Development & Automation
āāā Release automation agent
āāā Dogfooding metrics tracking
āāā Worktree safety management
```
## Plugin Development
Create custom plugins for your project patterns:
```python
from pyeye.plugins.base import AnalyzerPlugin
class MyProjectPlugin(AnalyzerPlugin):
def name(self) -> str:
return "MyProject"
def detect(self) -> bool:
# Return True if this plugin should activate
return (self.project_path / "my_framework.conf").exists()
def find_patterns(self, pattern_name: str):
# Find your custom patterns
pass
```
## Performance Monitoring Details
The server includes comprehensive performance monitoring to help identify bottlenecks and optimize performance for large-scale deployments.
### Connection Pooling for Multi-Project Workflows
Connection pooling is enabled by default to optimize performance when working with multiple projects. You can customize the pooling behavior:
```bash
# Customize connection pooling (already enabled by default)
export PYEYE_POOL_MAX_CONNECTIONS=20 # Increase pool size for many projects
export PYEYE_POOL_TTL=7200 # Increase TTL to 2 hours
# Or disable pooling if needed
export PYEYE_ENABLE_CONNECTION_POOLING=false
# Start the server
uv run mcp dev src/pyeye/server.py
```
Connection pooling provides significant performance improvements:
- **Reduced initialization time** for frequently accessed projects
- **Shared connections** across multiple analysis operations
- **Automatic eviction** of idle connections based on TTL
- **Memory-efficient** pooling with configurable limits
### Metrics Tracked
- **Operation Latencies**: p50, p95, p99 percentiles for all MCP tools
- **Cache Performance**: Hit rate, miss rate, evictions
- **Connection Pool Stats**: Pool size, hits, misses, evictions, reuse rate
- **Memory Usage**: RSS, VMS, percentage used
- **Error Rates**: Track failures per operation
- **Throughput**: Operations per second
### Performance Baselines
The following performance baselines are enforced in CI:
| Operation | p50 (ms) | p95 (ms) | p99 (ms) |
|-----------|----------|----------|----------|
| symbol_search | 50 | 100 | 200 |
| goto_definition | 30 | 75 | 150 |
| cache_lookup | 0.1 | 0.5 | 1.0 |
## Development
```bash
# Install development dependencies
uv add --dev pytest black ruff mypy
# Run tests
uv run pytest
# Format code
uv run black src/
uv run ruff check src/
# Test the server
uv run mcp dev src/pyeye/server.py
```
## Documentation
- [Installation Guide](#installation)
- [Configuration Guide](#configuration)
- [Troubleshooting Guide](docs/TROUBLESHOOTING.md) - Common issues and solutions
- [API Reference](#core-tools)
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
MIT License - see LICENSE file for details
## Acknowledgments
Built on top of:
- [Jedi](https://github.com/davidhalter/jedi) - Python static analysis and type inference
- [FastMCP](https://github.com/jlowin/fastmcp) - MCP server framework
- [Watchdog](https://github.com/gorakhargosh/watchdog) - File system monitoring
TDQS
Scored across 25 tools
Each tool has a clearly distinct purpose, from dependency analysis to framework-specific finders and structural queries. Even similar-sounding tools like 'expand' and 'outline' are precisely differentiated by their descriptions (single-hop vs full skeleton). The deprecated tools are noted but do not cause confusion.
Tools predominantly use underscore_case and descriptive verb-noun patterns (e.g., find_*, get_*, trace_*). However, there is some variation: single verbs like 'expand' and 'inspect' coexist with 'find_blueprints' and 'get_model_schema'. The consistency is high but not perfect, with no mixed casing.
25 tools is on the higher side but well-scoped for a comprehensive Python code analysis server covering dependency, symbol resolution, structural queries, and framework-specific finders (Flask, Pydantic). The count feels justified given the breadth of functionality, though a slight reduction could improve coherence.
The tool set covers a wide range of analysis needs: dependency mapping, symbol resolution, structural inspection, multihop traversal, and framework-specific queries. The presence of deprecated tools (analyze_dependencies, get_call_hierarchy) indicates API transition, but the current surface is largely complete for the domain. Minor gaps exist (e.g., no tool for runtime-injected members beyond static analysis limits).