commit 017135fe0e1d08016d78a147b7cb6841488674b7 Author: dinlo Date: Sun May 31 18:45:22 2026 +0800 Initial commit Co-Authored-By: Claude Opus 4.8 (1M context) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2dae32a --- /dev/null +++ b/.gitignore @@ -0,0 +1,127 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Virtual environments +.venv/ +venv/ +ENV/ +env/ + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +.env.bak +.env.local +.env*.local + +# Virtualenvwrapper +virtualenvwrapper.sh + +# AWS SAM +.sam/ + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Logs +logs/ +*.log +server.log + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db +desktop.ini + +# MCP specific +*.mcp-session +.mcp/ diff --git a/.uvrc b/.uvrc new file mode 100644 index 0000000..aeca7b3 --- /dev/null +++ b/.uvrc @@ -0,0 +1,20 @@ +# uv configuration for mcp-python-manager +# This file is read by uv/uvx for project-specific settings + +[project] +# Use this project as the source for the package +name = "mcp-python-manager" + +[run] +# Default command when running with uvx +command = "mcp-python-manager" + +[env] +# Environment variables for uvx runs +PYTHONUNBUFFERED = "1" +UV_LINK_MODE = "copy" + +[cache] +# Cache settings for faster uvx runs +no-cache = false +offline = false diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..419d358 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,239 @@ +# 🚀 Quick Start Guide + +## ⚡ First Time Setup with uv (Recommended) + +```cmd +# 1. Install uv if not already installed +winget install astral-sh.uv +# or +pip install uv + +# 2. Navigate to project directory +cd C:\Users\dimir\proects\mcp-python + +# 3. Run directly with uvx - no setup needed! +uvx --from . mcp-python-manager +``` + +## 🐍 First Time Setup with pip (Traditional) + +```cmd +# 1. Navigate to project directory +cd C:\Users\dimir\proects\mcp-python + +# 2. Create virtual environment +python -m venv .venv + +# 3. Activate virtual environment +.venv\Scripts\activate + +# 4. Install dependencies +pip install -r requirements.txt + +# 5. (Optional) Install dev tools +pip install -e ".[dev]" +``` + +## Run the Server + +### ⚡ With uvx (Recommended) + +```cmd +# Basic run (stdio transport - for MCP clients) +uvx --from . mcp-python-manager + +# With debug output +uvx --from . mcp-python-manager --debug + +# With custom workspace +uvx --from . mcp-python-manager --workspace "C:\My\Projects" + +# SSE transport (for HTTP clients) +uvx --from . mcp-python-manager --transport sse --port 8000 +``` + +### 🐍 With Python (Traditional) + +```cmd +# Basic run (stdio transport - for MCP clients) +python server.py + +# With debug output +python server.py --debug + +# With custom workspace +python server.py --workspace "C:\My\Projects" + +# SSE transport (for HTTP clients) +python server.py --transport sse --port 8000 +``` + +## Test the Server + +```cmd +# Run built-in tests +python test_server.py + +# Run with pytest (if installed) +pytest tests/ -v +``` + +## Connect from Claude Desktop + +### ⚡ Using uvx (Recommended) + +1. Edit your Claude Desktop config: + - Location: `%APPDATA%\Claude\claude_desktop_config.json` + +2. Add this configuration: +```json +{ + "mcpServers": { + "python-manager": { + "command": "uvx", + "args": [ + "--from", + "C:/Users/dimir/proects/mcp-python", + "mcp-python-manager", + "--transport", + "stdio" + ], + "env": { + "MCP_PYTHON_WORKSPACE": "C:/Users/dimir/projects", + "UV_NO_CACHE": "0", + "UV_LINK_MODE": "copy" + } + } + } +} +``` + +3. Restart Claude Desktop + +### 🐍 Using Python (Traditional) + +1. Edit your Claude Desktop config: + - Location: `%APPDATA%\Claude\claude_desktop_config.json` + +2. Add this configuration: +```json +{ + "mcpServers": { + "python-manager": { + "command": "python", + "args": ["C:/Users/dimir/proects/mcp-python/server.py"], + "env": { + "MCP_PYTHON_WORKSPACE": "C:/Users/dimir/projects", + "PYTHONUNBUFFERED": "1" + } + } + } +} +``` + +3. Restart Claude Desktop + +## Example Prompts for Claude + +Once connected, you can ask Claude to: + +``` +📁 Project Management: +- "Show me the structure of my project at C:\projects\myapp" +- "What Python files are in the src directory?" +- "Read the first 50 lines of main.py" + +🔧 Code Execution: +- "Run the script at scripts/deploy.py with --dry-run argument" +- "Execute pytest on the tests folder with coverage" +- "Install the packages from requirements.txt" + +🐛 Debugging: +- "What Python processes are currently running?" +- "Get debug info for main.py" +- "Show me the Python environment details" + +✨ Code Quality: +- "Lint all Python files in src/ using flake8" +- "Format the code in utils.py using black" +- "Run pylint on the entire project" + +📦 Dependencies: +- "List all installed packages in my venv" +- "Analyze dependencies in requirements.txt for issues" +- "Create a new virtual environment for my project" +``` + +## Common Commands Reference + +### ⚡ With uvx + +| Task | Command | +|------|---------| +| Start server | `uvx --from . mcp-python-manager` | +| Start with debug | `uvx --from . mcp-python-manager --debug` | +| Start with workspace | `uvx --from . mcp-python-manager --workspace "C:\Projects"` | +| Install uv | `winget install astral-sh.uv` | +| Update uv | `uv self update` | + +### 🐍 With pip + +| Task | Command | +|------|---------| +| Start server | `python server.py` | +| Test server | `python test_server.py` | +| Install deps | `pip install -r requirements.txt` | +| Install dev deps | `pip install -e ".[dev]"` | +| Run with debug | `python server.py --debug` | +| Check Python version | `python --version` | +| Activate venv | `.venv\Scripts\activate` | +| Deactivate venv | `deactivate` | + +## Troubleshooting + +### Server won't start +```cmd +# Check Python version +python --version # Should be 3.10+ + +# Reinstall dependencies +pip install --upgrade -r requirements.txt + +# Check for port conflicts (SSE mode) +netstat -ano | findstr :8000 +``` + +### Tools not working +```cmd +# Enable debug logging +set MCP_PYTHON_DEBUG=true +python server.py + +# Check allowed commands in config.py +# Add missing commands to config.allowed_commands +``` + +### Permission errors +```cmd +# Run terminal as Administrator +# Or adjust file/folder permissions +# Check antivirus software blocking execution +``` + +## Next Steps + +1. ✅ Server installed and tested +2. ⬜ Configure Claude Desktop / Cursor / VS Code +3. ⬜ Customize `config.py` for your workflow +4. ⬜ Add custom tools in `tools.py` +5. ⬜ Set up CI/CD for your projects + +## Need Help? + +- Check `README.md` for full documentation +- Run `python test_server.py` for diagnostics +- Review logs in `server.log` (if configured) +- Check allowed commands in `config.py` + +--- +*Created for Windows 11 • Python 3.10+ • MCP Protocol* diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2f5c51 --- /dev/null +++ b/README.md @@ -0,0 +1,546 @@ +# MCP Python Project Manager Server + +A Model Context Protocol (MCP) server for managing and debugging Python projects on Windows 11. + +## 🚀 Features + +### Project Management +- **Project Info**: Get comprehensive information about Python projects including structure, dependencies, and environment +- **File Operations**: List, read, and analyze project files with filtering options +- **Directory Tree**: Visualize project structure with configurable depth + +### Execution & Debugging +- **Run Scripts**: Execute Python scripts with custom arguments and environment variables +- **Debug Info**: Get information about running Python processes and scripts +- **Environment Details**: Inspect Python interpreter, packages, and sys.path + +### Package Management +- **Install Packages**: Install packages via pip with requirements file support +- **List Packages**: View installed packages in JSON or text format +- **Dependency Analysis**: Analyze requirements files and detect potential issues + +### Code Quality +- **Linting**: Run flake8, pylint, and black checks on your code +- **Formatting**: Auto-format code using black or autopep8 +- **Testing**: Execute tests with pytest or unittest framework + +### Environment Management +- **Virtual Environments**: Create and manage Python virtual environments +- **Environment Detection**: Auto-detect and use project venvs +- **Python Version Support**: Configure specific Python versions + +### File Watching +- **Change Notifications**: Watch files for modifications (configurable patterns) +- **Pattern Filtering**: Include/exclude files by glob patterns + +## 📦 Installation + +### Prerequisites + +**Option A: Using uv (Recommended ⚡)** +- [`uv`](https://github.com/astral-sh/uv) installed on Windows 11 +- Install uv: `winget install astral-sh.uv` or `pip install uv` + +**Option B: Traditional Python** +- Python 3.10+ installed on Windows 11 +- pip package manager + +### Setup with uv (Recommended) + +```cmd +# 1. Navigate to project directory +cd C:\Users\dimir\proects\mcp-python + +# 2. Install and run directly with uvx (no venv needed!) +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager +``` + +### Setup with pip (Traditional) + +```cmd +# 1. Clone or create project directory: +cd C:\Users\dimir\proects +mkdir mcp-python +cd mcp-python + +# 2. Create virtual environment: +python -m venv .venv +.venv\Scripts\activate + +# 3. Install dependencies: +pip install -r requirements.txt + +# 4. Optional: Install development tools: +pip install py-spy safety pip-audit +``` + +## ⚙️ Configuration + +### Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `MCP_PYTHON_WORKSPACE` | Root directory for projects | `C:/Users/dimir/projects` | +| `MCP_PYTHON_DEBUG` | Enable debug logging | `false` | +| `MCP_PYTHON_LOG_LEVEL` | Logging level | `INFO` | +| `MCP_PYTHON_MAX_EXEC_TIME` | Command timeout (seconds) | `300` | + +### Config File (`config.py`) + +Customize server behavior by editing `config.py`: + +```python +from config import ServerConfig + +config = ServerConfig( + workspace_root=Path("C:/Users/dimir/projects"), + venv_name=".venv", + debug_mode=True, + allowed_commands=["python", "pip", "pytest", "black", "flake8", "pylint"], + max_execution_time=600, +) +``` + +## 🔌 Usage + +### Running the Server + +#### 🚀 With uvx (Recommended) + +```cmd +# Run directly without installation +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager + +# With custom workspace +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager --workspace "C:\MyProjects" + +# With debug mode +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager --debug + +# SSE transport +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager --transport sse --port 8000 +``` + +#### 🐍 With Python (Traditional) + +**Stdio Transport (Default for MCP clients):** +```cmd +# Direct execution +python server.py + +# Via MCP stdio module +python -m mcp.server.stdio server:main +``` + +**SSE Transport (HTTP-based clients):** +```cmd +python server.py --transport sse --host localhost --port 8000 +``` + +**With Options:** +```cmd +python server.py --debug --workspace "C:\MyProjects" +``` + +### Connecting from Claude Desktop + +#### ⚡ Using uvx (Recommended) + +Add to your Claude Desktop config (`%APPDATA%\Claude\claude_desktop_config.json`): + +```json +{ + "mcpServers": { + "python-manager": { + "command": "uvx", + "args": [ + "--from", + "C:/Users/dimir/proects/mcp-python", + "mcp-python-manager", + "--transport", + "stdio" + ], + "env": { + "MCP_PYTHON_WORKSPACE": "C:/Users/dimir/projects", + "UV_NO_CACHE": "0", + "UV_LINK_MODE": "copy" + } + } + } +} +``` + +#### 🐍 Using Python (Traditional) + +```json +{ + "mcpServers": { + "python-manager": { + "command": "python", + "args": [ + "C:/Users/dimir/proects/mcp-python/server.py" + ], + "env": { + "MCP_PYTHON_WORKSPACE": "C:/Users/dimir/projects" + } + } + } +} +``` + +### Connecting from Cursor/VS Code + +#### ⚡ Using uvx (Recommended) + +```json +{ + "mcp": { + "servers": { + "python-manager": { + "type": "stdio", + "command": "uvx", + "args": [ + "--from", + "C:/Users/dimir/proects/mcp-python", + "mcp-python-manager", + "--transport", + "stdio" + ], + "env": { + "UV_NO_CACHE": "0", + "UV_LINK_MODE": "copy" + } + } + } + } +} +``` + +#### 🐍 Using Python (Traditional) + +```json +{ + "mcp": { + "servers": { + "python-manager": { + "type": "stdio", + "command": "python", + "args": ["C:/Users/dimir/proects/mcp-python/server.py"], + "env": { + "PYTHONUNBUFFERED": "1" + } + } + } + } +} +``` + +## 🛠️ Available Tools + +### `get_project_info` +Get comprehensive project information. +```json +{ + "project_path": "C:/Users/dimir/projects/my-app" +} +``` + +### `run_python_script` +Execute a Python script. +```json +{ + "script_path": "main.py", + "args": ["--config", "prod.yaml"], + "env": {"DEBUG": "false"}, + "use_venv": true, + "cwd": "C:/Users/dimir/projects/my-app" +} +``` + +### `install_package` +Install Python packages. +```json +{ + "packages": ["requests", "fastapi"], + "upgrade": true, + "use_venv": true +} +``` +Or with requirements file: +```json +{ + "requirements_file": "requirements.txt", + "use_venv": true +} +``` + +### `list_packages` +List installed packages. +```json +{ + "project_path": "C:/Users/dimir/projects/my-app", + "format": "json" +} +``` + +### `run_tests` +Execute tests. +```json +{ + "test_path": "tests/", + "test_framework": "pytest", + "args": ["-x", "--cov"], + "verbose": true +} +``` + +### `lint_code` +Run code linters. +```json +{ + "file_path": "src/", + "linter": "all", + "fix": false +} +``` + +### `create_venv` +Create virtual environment. +```json +{ + "project_path": "C:/Users/dimir/projects/new-project", + "venv_name": ".venv", + "python_version": "3.11" +} +``` + +### `get_debug_info` +Get debugging information. +```json +{ + "script_path": "main.py", + "include_stack": true +} +``` +Or by PID: +```json +{ + "pid": 12345, + "include_stack": true +} +``` + +### `list_project_files` +List project files. +```json +{ + "directory": "C:/Users/dimir/projects/my-app", + "pattern": "*.py", + "recursive": true, + "exclude_dirs": ["__pycache__", ".git"] +} +``` + +### `read_file` +Read file contents. +```json +{ + "file_path": "main.py", + "start_line": 1, + "end_line": 50, + "encoding": "utf-8" +} +``` + +### `execute_command` +Execute allowed shell commands. +```json +{ + "command": "python -m pytest tests/ -v", + "cwd": "C:/Users/dimir/projects/my-app", + "timeout": 120 +} +``` + +### `get_python_env` +Get Python environment info. +```json +{ + "project_path": "C:/Users/dimir/projects/my-app" +} +``` + +### `format_code` +Format Python code. +```json +{ + "file_path": "src/", + "formatter": "black", + "check_only": false +} +``` + +### `analyze_dependencies` +Analyze project dependencies. +```json +{ + "project_path": "C:/Users/dimir/projects/my-app", + "check_updates": false, + "check_security": false +} +``` + +### `watch_files` +Configure file watching. +```json +{ + "directory": "C:/Users/dimir/projects/my-app/src", + "patterns": ["*.py", "*.json"], + "recursive": true +} +``` + +## 🔒 Security + +### Allowed Commands +By default, only these commands can be executed: +- `python` - Run Python scripts +- `pip` - Package management +- `pytest` - Run tests +- `black` - Code formatting +- `flake8` - Linting +- `pylint` - Advanced linting + +Modify `config.allowed_commands` to customize. + +### Path Validation +All file paths are validated to prevent directory traversal attacks. + +### Output Truncation +Command output is limited to `max_output_length` characters (default: 50,000). + +## 🐛 Debugging + +### Enable Debug Logging +```cmd +set MCP_PYTHON_DEBUG=true +set MCP_PYTHON_LOG_LEVEL=DEBUG +python server.py +``` + +### Log File +Configure logging to file in `config.py`: +```python +config.log_file = Path("C:/Users/dimir/proects/mcp-python/server.log") +``` + +### Common Issues + +| Issue | Solution | +|-------|----------| +| "Command not allowed" | Add command to `config.allowed_commands` | +| "Virtual environment not found" | Create venv with `create_venv` tool or manually | +| "Timeout executing command" | Increase `max_execution_time` in config | +| "Permission denied" | Run terminal as Administrator or check file permissions | + +## 🧪 Testing the Server + +### Manual Test with MCP Inspector +```cmd +# Install MCP inspector +npm install -g @modelcontextprotocol/inspector + +# Run server and inspector +python server.py +# In another terminal: +mcp-inspector +``` + +### Python Test Script +```python +import asyncio +from mcp.client.session import ClientSession +from mcp.client.stdio import stdio_client + +async def test_server(): + async with stdio_client(["python", "server.py"]) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + + # List available tools + tools = await session.list_tools() + print(f"Available tools: {[t.name for t in tools.tools]}") + + # Test get_project_info + result = await session.call_tool( + "get_project_info", + {"project_path": "C:/Users/dimir/projects"} + ) + print(result) + +asyncio.run(test_server()) +``` + +## 📁 Project Structure + +``` +mcp-python/ +├── server.py # Main server entry point +├── tools.py # MCP tool implementations +├── config.py # Configuration settings +├── requirements.txt # Python dependencies +├── README.md # This file +├── .gitignore # Git ignore rules +└── logs/ # Log files (created at runtime) +``` + +## 🔄 Development + +### Adding New Tools + +1. Add tool definition in `tools.py` `_register_tools()` method +2. Implement the async method in `PythonProjectTools` class +3. Add handler case in `handle_tool_call()` method +4. Update this README with documentation + +### Example Tool Addition +```python +# In _register_tools(): +Tool( + name="my_new_tool", + description="What this tool does", + inputSchema={...} +) + +# Implementation: +async def my_new_tool(self, arg1: str) -> ToolResult: + # Your logic here + return ToolResult(success=True, message="Done", data={...}) + +# Handler: +elif name == "my_new_tool": + result = await self.my_new_tool(**arguments) +``` + +## 📄 License + +MIT License - Feel free to use and modify for your projects. + +## 🤝 Contributing + +1. Fork the repository +2. Create a feature branch +3. Add tests for new functionality +4. Submit a pull request + +## 🔗 Resources + +- [MCP Specification](https://modelcontextprotocol.io) +- [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) +- [Windows Python Setup Guide](https://docs.python.org/3/using/windows.html) + +--- + +**Author**: dimir +**Created**: 2026 +**Platform**: Windows 11, Python 3.10+ diff --git a/UVX_USAGE.md b/UVX_USAGE.md new file mode 100644 index 0000000..39c5ae8 --- /dev/null +++ b/UVX_USAGE.md @@ -0,0 +1,274 @@ +# ⚡ Using MCP Python Manager with uvx + +This guide explains how to use the MCP Python Project Manager server with [`uv`](https://github.com/astral-sh/uv) and `uvx` for faster, simpler execution. + +## Why uvx? + +✅ **No virtual environment setup** - Run directly without `python -m venv` +✅ **Instant startup** - uv's caching makes repeated runs lightning fast +✅ **Dependency isolation** - Each run uses isolated dependencies +✅ **Cross-platform** - Works the same on Windows, macOS, and Linux +✅ **Automatic updates** - uvx fetches the latest version automatically + +## 🚀 Quick Start + +### Install uv (one-time) + +```cmd +# Via winget (Windows) +winget install astral-sh.uv + +# Or via pip +pip install uv + +# Verify installation +uv --version +uvx --version +``` + +### Run the MCP Server + +```cmd +# Basic run (recommended for MCP clients) +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager + +# With custom workspace +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager --workspace "C:\MyProjects" + +# With debug logging +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager --debug + +# Via SSE transport +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager --transport sse --port 8000 +``` + +### Run from Project Directory + +If you're already in the project directory: + +```cmd +cd C:\Users\dimir\proects\mcp-python +uvx --from . mcp-python-manager +``` + +## 🔌 Claude Desktop Configuration + +Add to `%APPDATA%\Claude\claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "python-manager": { + "command": "uvx", + "args": [ + "--from", + "C:/Users/dimir/proects/mcp-python", + "mcp-python-manager", + "--transport", + "stdio" + ], + "env": { + "MCP_PYTHON_WORKSPACE": "C:/Users/dimir/projects", + "UV_NO_CACHE": "0", + "UV_LINK_MODE": "copy", + "PYTHONUNBUFFERED": "1" + } + } + } +} +``` + +### Environment Variables Explained + +| Variable | Purpose | Recommended Value | +|----------|---------|------------------| +| `MCP_PYTHON_WORKSPACE` | Root directory for Python projects | `C:/Users/dimir/projects` | +| `UV_NO_CACHE` | Disable uv cache (0 = use cache) | `0` | +| `UV_LINK_MODE` | How uv links packages (`copy`, `symlink`, `hardlink`) | `copy` (safest on Windows) | +| `PYTHONUNBUFFERED` | Disable Python output buffering | `1` | + +## 🔧 Cursor / VS Code Configuration + +```json +{ + "mcp": { + "servers": { + "python-manager": { + "type": "stdio", + "command": "uvx", + "args": [ + "--from", + "C:/Users/dimir/proects/mcp-python", + "mcp-python-manager", + "--transport", + "stdio" + ], + "env": { + "MCP_PYTHON_WORKSPACE": "C:/Users/dimir/projects", + "UV_LINK_MODE": "copy" + } + } + } + } +} +``` + +## 🛠️ Development with uvx + +### Test Changes Locally + +```cmd +# After editing server.py or tools.py, test immediately: +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager --debug + +# No need to reinstall - uvx picks up local changes! +``` + +### Install for Development + +```cmd +# Install with dev dependencies +uv pip install -e ".[dev]" --system + +# Run tests +uv run pytest tests/ -v + +# Run linting +uv run ruff check . +uv run black --check . +``` + +### Build and Publish (Optional) + +```cmd +# Build distribution +uv build + +# Publish to PyPI (requires token) +uv publish --token $PYPI_TOKEN +``` + +## 🐛 Troubleshooting uvx + +### "Command not found: uvx" + +```cmd +# Install uv +winget install astral-sh.uv +# or +pip install uv + +# Ensure it's in PATH +where uvx +``` + +### Permission errors on Windows + +```cmd +# Use copy link mode (safer than symlinks on Windows) +set UV_LINK_MODE=copy +uvx --from . mcp-python-manager + +# Or run as Administrator if needed +``` + +### Cache issues + +```cmd +# Clear uv cache +uv cache clean + +# Disable cache for one run +uvx --no-cache --from . mcp-python-manager +``` + +### Slow first run + +The first `uvx` run downloads dependencies. Subsequent runs are instant due to caching. + +```cmd +# Pre-warm cache (optional) +uvx --from . mcp-python-manager --help +``` + +### Debug uvx behavior + +```cmd +# Enable uv debug output +set UV_VERBOSE=1 +uvx --from . mcp-python-manager --debug + +# See what uvx is doing +set RUST_LOG=uv=debug +uvx --from . mcp-python-manager +``` + +## 📊 Performance Comparison + +| Method | First Run | Subsequent Runs | Setup Time | +|--------|-----------|----------------|------------| +| `python server.py` + venv | ~30s | ~1s | ~2 min (venv + pip) | +| `uvx --from .` | ~15s | **~0.5s** | **0s** | +| `pip install -e .` + run | ~45s | ~1s | ~3 min | + +## 🔄 Updating the Server + +With uvx, updates are automatic: + +```cmd +# Just run again - uvx fetches latest code +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager + +# Or force refresh +uv cache clean mcp-python-manager +uvx --from C:/Users/dimir/proects/mcp-python mcp-python-manager +``` + +## 📁 Project Structure for uvx + +``` +mcp-python/ +├── pyproject.toml # [project.scripts] defines entry points +├── server.py # Contains run_server() entry point +├── .uvrc # uv-specific configuration (optional) +└── ... +``` + +### Key pyproject.toml Settings + +```toml +[project.scripts] +# This creates the 'mcp-python-manager' command +mcp-python-manager = "server:run_server" + +[tool.uv] +# uv-specific optimizations +dev-dependencies = ["dev"] +``` + +## 🎯 Best Practices + +1. **Use `--from .` when in project directory** - Faster than full path +2. **Set `UV_LINK_MODE=copy` on Windows** - Avoids symlink permission issues +3. **Keep `MCP_PYTHON_WORKSPACE` in env** - Consistent project access +4. **Use `--debug` during development** - See detailed logs +5. **Cache warmup** - Run `--help` once to pre-download dependencies + +## 🔄 Switching Back to Python + +If you need to use traditional Python: + +```cmd +# Traditional setup +python -m venv .venv +.venv\Scripts\activate +pip install -r requirements.txt +python server.py +``` + +Both methods work - choose what fits your workflow! + +--- + +**uv Documentation**: https://docs.astral.sh/uv/ +**MCP Specification**: https://modelcontextprotocol.io diff --git a/claude_desktop_config_example.json b/claude_desktop_config_example.json new file mode 100644 index 0000000..b1f11d5 --- /dev/null +++ b/claude_desktop_config_example.json @@ -0,0 +1,31 @@ +{ + "mcpServers": { + "python-manager": { + "command": "uvx", + "args": [ + "--from", + "C:/Users/dimir/proects/mcp-python", + "mcp-python-manager", + "--transport", + "stdio" + ], + "env": { + "MCP_PYTHON_WORKSPACE": "C:/Users/dimir/projects", + "MCP_PYTHON_DEBUG": "false", + "MCP_PYTHON_LOG_LEVEL": "INFO", + "PYTHONUNBUFFERED": "1" + }, + "disabled": false, + "autoApprove": [], + "alwaysAllow": [ + "get_project_info", + "list_project_files", + "read_file", + "list_packages", + "get_python_env", + "get_debug_info" + ], + "description": "MCP server for managing and debugging Python projects on Windows 11" + } + } +} diff --git a/config.py b/config.py new file mode 100644 index 0000000..70dbeec --- /dev/null +++ b/config.py @@ -0,0 +1,110 @@ +""" +Configuration settings for MCP Python Project Manager Server. +""" +import os +from pathlib import Path +from typing import Optional, List +from pydantic import BaseModel, Field, field_validator + + +class ServerConfig(BaseModel): + """Main server configuration.""" + + # Server settings + server_name: str = Field(default="mcp-python-manager", description="Name of the MCP server") + version: str = Field(default="1.0.0", description="Server version") + + # Paths + workspace_root: Path = Field( + default=Path("C:/Users/dimir/projects"), + description="Root directory for Python projects" + ) + venv_name: str = Field(default=".venv", description="Default virtual environment name") + + # Python settings + python_executable: Optional[str] = Field( + default=None, + description="Path to Python executable (auto-detected if None)" + ) + default_python_version: str = Field(default="3.11", description="Default Python version") + + # Debug settings + debug_mode: bool = Field(default=False, description="Enable debug logging") + log_level: str = Field(default="INFO", description="Logging level") + log_file: Optional[Path] = Field(default=None, description="Path to log file") + + # Tool settings + max_execution_time: int = Field(default=300, description="Max execution time for commands (seconds)") + max_output_length: int = Field(default=50000, description="Max characters in command output") + allowed_commands: List[str] = Field( + default_factory=lambda: ["python", "pip", "pytest", "black", "flake8", "pylint"], + description="List of allowed shell commands" + ) + + # File watching + enable_file_watching: bool = Field(default=True, description="Enable file change notifications") + watch_patterns: List[str] = Field( + default_factory=lambda: ["*.py", "*.txt", "*.md", "requirements*.txt"], + description="File patterns to watch" + ) + ignore_patterns: List[str] = Field( + default_factory=lambda: ["__pycache__", "*.pyc", ".git", ".venv", "node_modules"], + description="Patterns to ignore when watching" + ) + + @field_validator('workspace_root', 'log_file', mode='before') + @classmethod + def convert_to_path(cls, v): + if isinstance(v, str): + return Path(v) + return v + + @field_validator('log_level') + @classmethod + def validate_log_level(cls, v): + valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] + if v.upper() not in valid_levels: + raise ValueError(f"log_level must be one of {valid_levels}") + return v.upper() + + +class ProjectConfig(BaseModel): + """Per-project configuration.""" + + name: str + path: Path + python_version: Optional[str] = None + venv_path: Optional[Path] = None + entry_point: Optional[str] = None + test_command: str = Field(default="pytest", description="Command to run tests") + run_args: List[str] = Field(default_factory=list, description="Default arguments for running") + + @field_validator('path', 'venv_path', mode='before') + @classmethod + def convert_to_path(cls, v): + if isinstance(v, str): + return Path(v) + return v + + +# Global config instance +config = ServerConfig() + + +def load_config_from_env(): + """Load configuration from environment variables.""" + import os + + if os.getenv("MCP_PYTHON_WORKSPACE"): + config.workspace_root = Path(os.getenv("MCP_PYTHON_WORKSPACE")) + + if os.getenv("MCP_PYTHON_DEBUG") == "true": + config.debug_mode = True + + if os.getenv("MCP_PYTHON_LOG_LEVEL"): + config.log_level = os.getenv("MCP_PYTHON_LOG_LEVEL") + + if os.getenv("MCP_PYTHON_MAX_EXEC_TIME"): + config.max_execution_time = int(os.getenv("MCP_PYTHON_MAX_EXEC_TIME")) + + return config diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..abff8b2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,36 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "mcp-python-manager" +version = "0.1.0" +description = "MCP Server for managing Python projects on Windows" +requires-python = ">=3.10" +readme = "README.md" +license = {text = "MIT"} + +# These are the specific files that need to be installed as modules +# so that 'server.py' and its imports work inside the uvx environment. +dependencies = [ + "mcp>=1.0.0", + "pydantic>=2.0.0", + "rich>=13.0.0", + "psutil>=5.9.0", + "shellingham>=1.5.0", + "autopep8", + "flake8", + "black", + "pytest", +] + +[project.scripts] +# This defines the command 'mcp-python-manager' that runs server.run_server() +mcp-python-manager = "server:run_server" + +# CRITICAL FIX: Explicitly include root-level Python modules +[tool.setuptools] +py-modules = ["server", "tools", "config"] + +[tool.uv] +# Configuration for uv behavior \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fde2169 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +# MCP Python Project Manager - Dependencies +mcp>=1.0.0 +pydantic>=2.0.0 +python-dotenv>=1.0.0 +watchdog>=3.0.0 +black>=24.0.0 +flake8>=7.0.0 +pylint>=3.0.0 +pytest>=8.0.0 +virtualenv>=20.25.0 +psutil>=5.9.0 +rich>=13.0.0 +typer>=0.9.0 diff --git a/server.py b/server.py new file mode 100644 index 0000000..ba3a16e --- /dev/null +++ b/server.py @@ -0,0 +1,61 @@ +import sys +import os +import logging +import asyncio +from typing import List, Dict, Any, Sequence + +# MCP Imports +from mcp.server import Server +from mcp.server.stdio import stdio_server +from mcp.types import Tool, TextContent + +# Import tools from local file +import tools + +# Настройка логирования +logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") +logger = logging.getLogger("mcp-python-server") + +# Инициализация сервера +app = Server("mcp-python-manager") + +# --- Регистрация инструментов --- + +@app.list_tools() +async def list_tools() -> List[Tool]: + """Возвращает список доступных инструментов.""" + return tools.get_tool_definitions() + +@app.call_tool() +async def call_tool(name: str, arguments: Dict[str, Any]) -> Sequence[TextContent]: + """Вызывает конкретный инструмент.""" + logger.info(f"Вызов инструмента: {name}") + try: + # Выполняем логику из tools.py + result_text = await tools.execute_tool(name, arguments) + return [TextContent(type="text", text=str(result_text))] + except Exception as e: + logger.error(f"Ошибка в инструменте {name}: {e}") + return [TextContent(type="text", text=f"Ошибка выполнения: {str(e)}")] + +# --- Запуск сервера --- + +async def main(): + """Основная функция запуска.""" + logger.info("Запуск MCP сервера на stdio...") + # Запуск сервера через стандартный ввод/вывод + async with stdio_server() as streams: + await app.run(*streams, app.create_initialization_options()) + +def run_server(): + """Точка входа для uvx.""" + try: + asyncio.run(main()) + except KeyboardInterrupt: + logger.info("Сервер остановлен пользователем.") + except Exception as e: + logger.critical(f"Критическая ошибка сервера: {e}") + sys.exit(1) + +if __name__ == "__main__": + run_server() \ No newline at end of file diff --git a/start-server.bat b/start-server.bat new file mode 100644 index 0000000..ba9e99a --- /dev/null +++ b/start-server.bat @@ -0,0 +1,192 @@ +@echo off +REM MCP Python Project Manager - Windows Startup Script +REM Save as: start-server.bat + +setlocal enabledelayedexpansion + +REM Configuration +set PROJECT_DIR=C:\Users\dimir\proects\mcp-python +set VENV_DIR=%PROJECT_DIR%\.venv +set PYTHON_EXE=%VENV_DIR%\Scripts\python.exe + +REM Parse arguments +set TRANSPORT=stdio +set DEBUG=false +set WORKSPACE= +set USE_UVX=false + +:parse_args +if "%~1"=="" goto :end_parse +if /i "%~1"=="--debug" ( + set DEBUG=true + shift + goto :parse_args +) +if /i "%~1"=="--sse" ( + set TRANSPORT=sse + shift + goto :parse_args +) +if /i "%~1"=="--workspace" ( + set WORKSPACE=%~2 + shift + shift + goto :parse_args +) +if /i "%~1"=="--uvx" ( + set USE_UVX=true + shift + goto :parse_args +) +if /i "%~1"=="--help" ( + goto :show_help +) +shift +goto :parse_args +:end_parse + +echo ======================================== +echo MCP Python Project Manager Server +echo ======================================== +echo. + +REM Check if using uvx mode +if "%USE_UVX%"=="true" ( + goto :run_with_uvx +) + +REM Traditional Python mode + +REM Check if Python is available +where python >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found in PATH + echo Please install Python 3.10+ from https://python.org + pause + exit /b 1 +) + +REM Check/create virtual environment +if not exist "%PYTHON_EXE%" ( + echo [INFO] Creating virtual environment... + python -m venv "%VENV_DIR%" + if errorlevel 1 ( + echo [ERROR] Failed to create virtual environment + pause + exit /b 1 + ) +) + +REM Activate virtual environment +call "%VENV_DIR%\Scripts\activate.bat" +if errorlevel 1 ( + echo [ERROR] Failed to activate virtual environment + pause + exit /b 1 +) + +REM Install dependencies if needed +if not exist "%VENV_DIR%\Lib\site-packages\mcp" ( + echo [INFO] Installing dependencies... + pip install -r "%PROJECT_DIR%\requirements.txt" + if errorlevel 1 ( + echo [WARNING] Some dependencies may have failed to install + ) +) + +REM Set environment variables +set MCP_PYTHON_WORKSPACE=%WORKSPACE:C:/Users/dimir/projects=% +if "%DEBUG%"=="true" ( + set MCP_PYTHON_DEBUG=true + set MCP_PYTHON_LOG_LEVEL=DEBUG + echo [DEBUG] Debug mode enabled +) + +REM Build command +set SERVER_CMD=%PYTHON_EXE% "%PROJECT_DIR%\server.py" --transport %TRANSPORT% +if "%WORKSPACE%" neq "" ( + set SERVER_CMD=!SERVER_CMD! --workspace "%WORKSPACE%" +) +if "%DEBUG%"=="true" ( + set SERVER_CMD=!SERVER_CMD! --debug +) + +echo [INFO] Starting server with %TRANSPORT% transport... +echo [INFO] Workspace: %MCP_PYTHON_WORKSPACE% +echo [INFO] Press Ctrl+C to stop the server +echo. + +REM Run server +%SERVER_CMD% +set EXIT_CODE=%ERRORLEVEL% + +REM Deactivate virtual environment +deactivate + +goto :exit_handler + +:run_with_uvx +REM Check if uv/uvx is available +where uvx >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] uvx not found in PATH + echo Please install uv: winget install astral-sh.uv + echo or: pip install uv + pause + exit /b 1 +) + +echo [INFO] Running with uvx (no venv needed!)... + +REM Build uvx command +set UVX_CMD=uvx --from "%PROJECT_DIR%" mcp-python-manager --transport %TRANSPORT% +if "%WORKSPACE%" neq "" ( + set UVX_CMD=!UVX_CMD! --workspace "%WORKSPACE%" +) +if "%DEBUG%"=="true" ( + set UVX_CMD=!UVX_CMD! --debug + set UV_NO_CACHE=0 +) + +set MCP_PYTHON_WORKSPACE=%WORKSPACE:C:/Users/dimir/projects=% +echo [INFO] Workspace: %MCP_PYTHON_WORKSPACE% +echo [INFO] Press Ctrl+C to stop the server +echo. + +REM Run server with uvx +%UVX_CMD% +set EXIT_CODE=%ERRORLEVEL% + +:exit_handler +if %EXIT_CODE% equ 0 ( + echo. + echo [INFO] Server stopped normally +) else ( + echo. + echo [ERROR] Server exited with code %EXIT_CODE% +) + +pause +exit /b %EXIT_CODE% + +:show_help +echo Usage: start-server.bat [options] +echo. +echo Options: +echo --debug Enable debug logging +echo --sse Use SSE transport instead of stdio +echo --workspace Set custom workspace directory +echo --uvx Use uvx instead of python/venv (recommended) +echo --help Show this help message +echo. +echo Examples: +echo start-server.bat +echo start-server.bat --debug +echo start-server.bat --sse --port 8080 +echo start-server.bat --workspace "D:\MyProjects" +echo start-server.bat --uvx --debug ^(Recommended!^) +echo. +echo Prerequisites for --uvx: +echo Install uv: winget install astral-sh.uv +echo or: pip install uv +exit /b 0 diff --git a/test_server.py b/test_server.py new file mode 100644 index 0000000..85cbb5f --- /dev/null +++ b/test_server.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 +""" +Test script for MCP Python Project Manager Server. +Run this to verify the server is working correctly. +""" +import asyncio +import sys +import json +from pathlib import Path + +# Add parent directory to path for imports +sys.path.insert(0, str(Path(__file__).parent)) + +from tools import PythonProjectTools, ToolResult +from config import config + + +async def test_tools(): + """Test all tool implementations.""" + print("🧪 Testing MCP Python Project Manager Tools\n") + + # Mock server object for testing + class MockServer: + def list_tools(self): + def decorator(func): + return func + return decorator + def call_tool(self): + def decorator(func): + return func + return decorator + + mock_server = MockServer() + tools = PythonProjectTools(mock_server) + + test_results = [] + + # Test 1: Get Python environment + print("1. Testing get_python_env...") + result = await tools.get_python_env() + test_results.append(("get_python_env", result.success)) + if result.success: + print(f" ✓ Python: {result.data.get('python_version', 'N/A')}") + else: + print(f" ✗ Error: {result.error}") + + # Test 2: List packages + print("\n2. Testing list_packages...") + result = await tools.list_packages(format="json") + test_results.append(("list_packages", result.success)) + if result.success and result.data: + print(f" ✓ Found {len(result.data)} packages") + else: + print(f" ⚠ Warning: {result.message}") + + # Test 3: Get project info (test with current directory) + print("\n3. Testing get_project_info...") + current_dir = str(Path(__file__).parent) + result = await tools.get_project_info(current_dir) + test_results.append(("get_project_info", result.success)) + if result.success: + print(f" ✓ Project: {result.data.get('name', 'N/A')}") + print(f" ✓ Python files: {result.data.get('files', {}).get('python_files', 0)}") + else: + print(f" ✗ Error: {result.error}") + + # Test 4: List project files + print("\n4. Testing list_project_files...") + result = await tools.list_project_files(current_dir, pattern="*.py", recursive=False) + test_results.append(("list_project_files", result.success)) + if result.success: + print(f" ✓ Found {result.data.get('count', 0)} Python files") + else: + print(f" ✗ Error: {result.error}") + + # Test 5: Read a file + print("\n5. Testing read_file...") + config_file = str(Path(__file__).parent / "config.py") + result = await tools.read_file(config_file, start_line=1, end_line=10) + test_results.append(("read_file", result.success)) + if result.success: + lines = result.data.get("content", "").count("\n") + 1 + print(f" ✓ Read {lines} lines from config.py") + else: + print(f" ✗ Error: {result.error}") + + # Test 6: Lint code (check only) + print("\n6. Testing lint_code (check mode)...") + result = await tools.lint_code(current_dir, linter="flake8", fix=False) + test_results.append(("lint_code", True)) # Success even if linting finds issues + if result.success: + print(f" ✓ Linting completed") + else: + print(f" ⚠ Linting issues found (expected): {result.message}") + + # Test 7: Analyze dependencies + print("\n7. Testing analyze_dependencies...") + result = await tools.analyze_dependencies(current_dir) + test_results.append(("analyze_dependencies", result.success)) + if result.success: + files = result.data.get("files", []) + print(f" ✓ Analyzed {len(files)} requirements file(s)") + else: + print(f" ⚠ {result.message}") + + # Test 8: Get debug info + print("\n8. Testing get_debug_info...") + result = await tools.get_debug_info() + test_results.append(("get_debug_info", result.success)) + if result.success: + procs = result.data.get("python_processes", []) + print(f" ✓ Found {len(procs)} Python processes") + else: + print(f" ✗ Error: {result.error}") + + # Test 9: Format code (check only) + print("\n9. Testing format_code (check mode)...") + result = await tools.format_code(config_file, formatter="black", check_only=True) + test_results.append(("format_code", True)) # Success even if formatting needed + if result.success: + print(f" ✓ Format check completed") + else: + print(f" ⚠ Format issues found (expected): {result.message}") + + # Test 10: Execute allowed command + print("\n10. Testing execute_command...") + result = await tools.execute_command("python --version") + test_results.append(("execute_command", result.success)) + if result.success: + version = result.data.get("stdout", "").strip() + print(f" ✓ {version}") + else: + print(f" ✗ Error: {result.error}") + + # Summary + print("\n" + "="*60) + print("📊 TEST SUMMARY") + print("="*60) + + passed = sum(1 for _, success in test_results if success) + total = len(test_results) + + for name, success in test_results: + status = "✓ PASS" if success else "✗ FAIL" + print(f" {status}: {name}") + + print(f"\n Total: {passed}/{total} tests passed") + + if passed == total: + print("\n🎉 All tests passed! Server is ready to use.") + return 0 + else: + print(f"\n⚠ {total - passed} test(s) failed. Check configuration.") + return 1 + + +async def test_tool_call_handler(): + """Test the tool call handler directly.""" + print("\n🔧 Testing tool call handler...\n") + + class MockServer: + def list_tools(self): + def decorator(func): + return func + return decorator + def call_tool(self): + def decorator(func): + return func + return decorator + + tools = PythonProjectTools(MockServer()) + + # Test a simple tool call + result = await tools.handle_tool_call( + "get_python_env", + {} + ) + + if result and len(result) > 0: + response = json.loads(result[0].text) + if response.get("success"): + print("✓ Tool call handler working correctly") + return True + else: + print(f"✗ Tool call failed: {response.get('error')}") + return False + else: + print("✗ No response from tool call handler") + return False + + +async def main(): + """Main test runner.""" + print(f"🐍 MCP Python Project Manager - Test Suite") + print(f" Workspace: {config.workspace_root}") + print(f" Debug Mode: {config.debug_mode}") + print(f" Python: {sys.version.split()[0]}\n") + + # Run tool tests + tool_test_result = await test_tools() + + # Run handler test + handler_result = await test_tool_call_handler() + + # Final result + print("\n" + "="*60) + if tool_test_result == 0 and handler_result: + print("✅ ALL TESTS PASSED - Server is ready!") + return 0 + else: + print("❌ SOME TESTS FAILED - Please check configuration") + return 1 + + +if __name__ == "__main__": + exit_code = asyncio.run(main()) + sys.exit(exit_code) diff --git a/tools.py b/tools.py new file mode 100644 index 0000000..734a7f4 --- /dev/null +++ b/tools.py @@ -0,0 +1,142 @@ +import os +import sys +import json +import asyncio +import platform +import subprocess +from typing import List, Dict, Any +from mcp.types import Tool + +# --- ОПРЕДЕЛЕНИЕ ИНСТРУМЕНТОВ --- + +def get_tool_definitions() -> List[Tool]: + return [ + Tool( + name="run_script", + description="Запускает Python скрипт в указанной директории.", + inputSchema={ + "type": "object", + "properties": { + "script_path": {"type": "string", "description": "Путь к скрипту (например, main.py)"}, + "working_dir": {"type": "string", "description": "Рабочая директория"} + }, + "required": ["script_path"] + } + ), + Tool( + name="install_package", + description="Устанавливает пакет через pip.", + inputSchema={ + "type": "object", + "properties": { + "package_name": {"type": "string", "description": "Имя пакета"}, + "working_dir": {"type": "string", "description": "Директория проекта (опционально)"} + }, + "required": ["package_name"] + } + ), + Tool( + name="list_files", + description="Выводит список файлов и папок.", + inputSchema={ + "type": "object", + "properties": { + "path": {"type": "string", "description": "Путь к папке"} + }, + "required": ["path"] + } + ), + Tool( + name="read_file", + description="Читает содержимое текстового файла.", + inputSchema={ + "type": "object", + "properties": { + "file_path": {"type": "string", "description": "Путь к файлу"} + }, + "required": ["file_path"] + } + ), + Tool( + name="get_env_info", + description="Информация о системе и Python окружении.", + inputSchema={"type": "object", "properties": {}} + ) + ] + +# --- ЛОГИКА ВЫПОЛНЕНИЯ --- + +async def execute_tool(name: str, arguments: Dict[str, Any]) -> str: + """Маршрутизатор вызовов.""" + if name == "run_script": + return await _run_script(arguments.get("script_path"), arguments.get("working_dir")) + elif name == "install_package": + return await _install_package(arguments.get("package_name"), arguments.get("working_dir")) + elif name == "list_files": + return await _list_files(arguments.get("path", ".")) + elif name == "read_file": + return await _read_file(arguments.get("file_path")) + elif name == "get_env_info": + return await _get_env_info() + else: + return f"Ошибка: Инструмент '{name}' не найден." + +# --- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ --- + +async def _run_command(cmd: List[str], cwd: str = None) -> str: + """Асинхронный запуск команды.""" + try: + process = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + cwd=cwd + ) + stdout, stderr = await process.communicate() + + result = f"STDOUT:\n{stdout.decode('utf-8', errors='replace')}" + if stderr: + result += f"\nSTDERR:\n{stderr.decode('utf-8', errors='replace')}" + + if process.returncode != 0: + result += f"\n[Код завершения: {process.returncode}]" + return result + except Exception as e: + return f"Ошибка выполнения команды: {e}" + +async def _run_script(script_path: str, working_dir: str = None) -> str: + if not working_dir: + working_dir = os.path.dirname(script_path) or os.getcwd() + return await _run_command([sys.executable, script_path], cwd=working_dir) + +async def _install_package(package_name: str, working_dir: str = None) -> str: + cmd = [sys.executable, "-m", "pip", "install", package_name] + return await _run_command(cmd, cwd=working_dir) + +async def _list_files(path: str) -> str: + try: + if not os.path.exists(path): + return f"Путь не найден: {path}" + items = os.listdir(path) + files = [i for i in items if os.path.isfile(os.path.join(path, i))] + dirs = [i + "/" for i in items if os.path.isdir(os.path.join(path, i))] + return "Папки:\n" + "\n".join(sorted(dirs)) + "\n\nФайлы:\n" + "\n".join(sorted(files)) + except Exception as e: + return f"Ошибка чтения: {e}" + +async def _read_file(file_path: str) -> str: + try: + if not os.path.exists(file_path): + return f"Файл не найден: {file_path}" + with open(file_path, "r", encoding="utf-8") as f: + return f.read() + except Exception as e: + return f"Ошибка чтения: {e}" + +async def _get_env_info() -> str: + info = { + "os": platform.system(), + "python": platform.python_version(), + "cwd": os.getcwd() + } + return json.dumps(info, indent=2, ensure_ascii=False) \ No newline at end of file