Skip to content

Latest commit

 

History

History
118 lines (85 loc) · 9.24 KB

File metadata and controls

118 lines (85 loc) · 9.24 KB

Contributing to memscope-mcp

Small focused PRs welcome. For anything large or speculative, open an issue first.

Development setup

Windows x64 + Python 3.10+ are required. The pymem dependency is Windows-only and is skipped via an environment marker on other platforms; the package installs on macOS and Linux but import memscope_mcp raises RuntimeError there.

git clone https://github.com/Boti-Ormandi/memscope-mcp.git
cd memscope-mcp
pip install -e ".[dev]"
pre-commit install
pytest tests/ -v
ruff check memscope_mcp/ tests/
ruff format --check memscope_mcp/ tests/

Pre-commit runs ruff check --fix and ruff format on every commit. CI runs the same checks plus the full pytest suite on Python 3.10 through 3.13.

Dev workflow note: MEMSCOPE_HOME

By default, logs and saved Lua scripts land in ~/.memscope-mcp/. If you want artefacts to land next to the cloned repository instead, set MEMSCOPE_HOME=$PWD in your shell before starting the server.

Project layout

The full repository layout lives in docs/architecture.md. The pieces you'll actually touch:

Adding an MCP tool

  1. Implement in memscope_mcp/tools/<your_tool>.py. Follow patterns in types.py or scanning.py.
  2. Wrap with @mcp.tool() in memscope_mcp/server.py. Call _log() so the tool call lands in session logs.
  3. Keep the docstring terse — it becomes AI-facing context and costs tokens. List parameters, types, and return shape.
  4. Update tests/test_smoke.py: add the tool name to test_tool_names and bump test_tool_count. This test pins the 10-tool surface; forgetting it makes the smoke test fail immediately.
  5. Add the tool to the README tool table.

Adding a Lua function

Lua functions live inside extensions. Pick the right extension first.

  1. Pick the extension by category:
    • Reads -> core/memory.py (which dispatches to tools/lua/memory_read.py)
    • Writes -> core/memory.py (tools/lua/memory_write.py)
    • AOB / xref scans, module/address resolution -> core/module_scan.py (tools/lua/scanning_helpers.py, tools/lua/modules.py)
    • Vector / matrix / declarative struct reads, comparisons, bitwise, formatting -> core/general.py (tools/lua/struct_helpers.py, tools/lua/comparisons.py, tools/lua/utilities.py)
    • Remote calls and allocation -> core/execution.py (tools/lua/code_execution.py)
    • Pre-attach / PEB introspection -> core/process.py (tools/lua/process_info.py)
    • Hooking primitives -> core/hooking.py (tools/lua/hooking.py)
    • Network helpers -> core/network.py (tools/lua/network.py)
  2. Add the function to the relevant tools/lua/*.py module (or directly to the extension if it is tightly scoped).
  3. Add the Lua-name -> Python-callable mapping to the dict returned by the extension's register(ctx).
  4. Update the extension's instructions string with a one-line AI-facing description (token-priced, terse).
  5. Document the function in docs/lua-reference.md under the matching category and in memscope_mcp/instructions/base.py if a shared-guidance bullet is appropriate.
  6. Conventions: return nil on failure (don't raise), accept addresses as int or hex string (use parse_address), and use ctx.table_factory(...) inside extensions or engine.lua.table() outside extensions to build Lua-side return tables.

Adding an extension

A core extension is appropriate when the functionality is generic enough to be useful on any target -- memory, scanning, hooking, process introspection. A user plugin (under plugins/) is the right shape when the functionality is target-specific.

  1. Create memscope_mcp/extensions/core/<your_ext>.py. Subclass LuaExtension from memscope_mcp/extensions/base.py. Implement name, description, instructions, and register(ctx). Override on_process_attached / on_process_detaching if the extension holds process-bound state (allocations, hooks).
  2. Register the class in memscope_mcp/extensions/core/__init__.py -- import it and add it to CORE_EXTENSIONS in the right position (General first, the rest in the order the AI is likely to encounter them).
  3. Hold cross-call state on the extension instance, not on SESSION.
  4. If the extension introduces a new conceptual surface, write a short docs/<topic>.md design doc (see docs/hooking.md and docs/peb.md for shape and tone) and link it from the relevant subsystem section in docs/architecture.md.
  5. Add a test file under tests/test_<your_ext>.py covering the registration path and any non-trivial logic. tests/test_extension_bootstrap.py already pins ordering and the basic contract.

Adding a plugin

The bundled reference plugins live under memscope_mcp/_contrib/plugins/ and ship in the wheel. Users install them to $MEMSCOPE_HOME/plugins/ (default ~/.memscope-mcp/plugins/) via memscope-mcp install-plugin <name>; the loader picks up any .py file placed there. Reference plugins:

Code style

Enforced by ruff. Configuration in pyproject.toml:

  • Line length 120
  • Rules: E, F, W, I (pycodestyle, pyflakes, isort)
  • E722 (bare except) is allowed: it's deliberate in memory-read paths where any failure means "return nil"
  • Type hints on public function signatures
  • Docstrings with Args/Returns on public functions
  • Delete unused code rather than leaving dead functions or helpers

Testing

The smoke suite (tests/test_smoke.py) is the gating invariant: it asserts the 10-tool surface, that the Lua engine initializes, that the plugin loader runs, and that the instructions builder produces output. Most regressions show up here first.

Unit tests live next to features (test_types.py, test_scanning.py, test_lua_engine.py, etc.). Hooking and netcap have dedicated coverage in test_disasm.py, test_relocation.py, test_hook_shellcode.py, test_ring_buffer.py, test_pe_exports.py, test_thread_suspension.py, test_netcap_plugin.py, test_netcap_lifecycle.py, test_netcap_udp.py, test_netcap_wsa.py, test_stream_assembly.py, test_protocol_framing.py, test_filter_packets.py, test_header_only.py, test_deref_args.py, test_hook_installation.py, test_cross_reference.py, test_session_recording.py. The extension bootstrap is pinned by test_extension_bootstrap.py. PEB reading is covered by test_peb.py (self-process tests run in CI; explorer.exe-dependent tests skip gracefully when explorer is not running). Run a focused subset with pytest -k <pattern>.

tests/conftest.py imports memscope_mcp.server once at collection time so the extension bootstrap runs before any test resolves a Lua function. If a new test needs the Lua surface initialized, it relies on this import side-effect -- nothing else is required.

There's no live-process integration test -- pymem can't attach to anything useful in a clean GitHub Actions runner, so verifying tool behavior against a real target stays manual.

PR checklist

  • ruff check and ruff format --check pass
  • pytest passes locally on Windows
  • README tool table updated if you added or removed an MCP tool
  • New Lua functions documented in the relevant extension's instructions string and in docs/lua-reference.md
  • New conceptual surface (a new extension category, a new design pattern) gets a short docs/<topic>.md
  • One logical change per commit; PR description explains the what and the why

License

By contributing, you agree your contributions will be licensed under the MIT License.