Contributing
Prerequisites
Section titled “Prerequisites”- Python 3.10+ — mcilspy uses
str | Noneunion syntax and other 3.10+ features - uv — for dependency management and running the project (install uv)
- ilspycmd (optional) — only needed if you are working on decompilation tools. Metadata tools and tests that mock the subprocess work without it.
Development Setup
Section titled “Development Setup”-
Clone the repository
Terminal window git clone https://git.supported.systems/MCP/mcilspy.gitcd mcilspy -
Install in development mode
Terminal window uv pip install -e ".[dev]"This installs mcilspy in editable mode along with dev dependencies:
pytest,pytest-asyncio, andruff. -
Verify the install
Terminal window mcilspy --helpOr run directly:
Terminal window uv run mcilspy
Running Tests
Section titled “Running Tests”The test suite uses pytest with async support via pytest-asyncio.
# Run all testspytest
# Run with verbose outputpytest -v
# Run a specific test filepytest tests/test_models.py
# Run tests matching a patternpytest -k "test_decompile"Tests are configured in pyproject.toml:
[tool.pytest.ini_options]testpaths = ["tests"]asyncio_mode = "auto"asyncio_default_fixture_loop_scope = "function"The asyncio_mode = "auto" setting means you do not need to decorate async test functions with @pytest.mark.asyncio — pytest-asyncio detects them automatically.
Code Quality
Section titled “Code Quality”mcilspy uses ruff for both linting and formatting.
# Check for lint issuesruff check src/
# Auto-fix what can be fixedruff check --fix src/
# Format coderuff format src/The ruff configuration in pyproject.toml:
[tool.ruff]target-version = "py310"line-length = 100src = ["src"]
[tool.ruff.lint]select = ["E", "F", "W", "I", "UP", "B", "SIM"]ignore = ["E501"]
[tool.ruff.format]quote-style = "double"Project Structure
Section titled “Project Structure”mcilspy/ src/mcilspy/ __init__.py # Package version __main__.py # python -m mcilspy entry point server.py # FastMCP tool definitions (16 tools, 2 prompts) ilspy_wrapper.py # Async subprocess wrapper for ilspycmd metadata_reader.py # dnfile-based PE metadata parsing il_parser.py # Method extraction from IL/C# output models.py # Pydantic request/response models constants.py # Timeouts, limits, configuration utils.py # PATH discovery helpers tests/ test_models.py test_il_parser.py test_ilspy_wrapper.py test_server.py ... pyproject.toml README.md LICENSECoding Conventions
Section titled “Coding Conventions”Async by default. All tool functions in server.py are async. The ilspy_wrapper uses asyncio.create_subprocess_exec for non-blocking subprocess management.
Pydantic for validation. Request parameters are validated through Pydantic models in models.py. Add new fields with sensible defaults to maintain backward compatibility.
No stdout prints. MCP uses stdin/stdout for the JSON-RPC transport. Any print() to stdout corrupts the protocol stream. Use logger (which writes to stderr) for diagnostics. The main() function should only call mcp.run().
Error consistency. Use the _format_error() helper in server.py for all error responses. The format is **Error** (context): description.
Constants centralized. Timeouts, output limits, and search limits live in constants.py. Do not hardcode magic numbers in tool functions.
Testing with Claude Code
Section titled “Testing with Claude Code”You can test your local changes with Claude Code without publishing:
claude mcp add mcilspy-local -- uv run --directory /path/to/mcilspy mcilspyThis starts the MCP server from your local source tree. Changes to the source are picked up on the next server restart.