# PyMemoryEditor (pypi · pymemoryeditor)

Read, write and scan process memory, Cheat Engine style, on Windows, Linux and macOS.

- Trust score: 77/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-05

## Components

- pypi · `pymemoryeditor`: 77/100 (this document), [markdown](https://verifymcp.io/servers/jeanextreme002-pymemoryeditor/pymemoryeditor.md), [page](https://verifymcp.io/servers/jeanextreme002-pymemoryeditor/pymemoryeditor)

## Channel facts

- Registry: `pypi`
- Package: `pymemoryeditor`
- Version: `3.0.2`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-10-05.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs hatchling.build at install time, a recognised build step with no custom scripting around it.
  - No production dependencies, so there is no dependency health to assess.
- **Provenance & Transparency**: 100/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to JeanExtreme002/PyMemoryEditor).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 3 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 65/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 3758 tokens (~268/item across 14 items; 14 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 71/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 0% of tool parameters carry a description.
  - Structured output schemas are declared (93% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 14 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 15 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the PyMemoryEditor MCP server?

PyMemoryEditor runs locally as a PyPI package, launched with uvx pymemoryeditor. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add jeanextreme002-pymemoryeditor -- uvx pymemoryeditor
```

### Cursor

```json
{
  "mcpServers": {
    "jeanextreme002-pymemoryeditor": {
      "command": "uvx",
      "args": [
        "pymemoryeditor"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "jeanextreme002-pymemoryeditor": {
      "command": "uvx",
      "args": [
        "pymemoryeditor"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add jeanextreme002-pymemoryeditor -- uvx pymemoryeditor
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "jeanextreme002-pymemoryeditor": {
      "type": "local",
      "command": [
        "uvx",
        "pymemoryeditor"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add jeanextreme002-pymemoryeditor --command uvx --arg pymemoryeditor
```

### Hermes

```yaml
mcp_servers:
  jeanextreme002-pymemoryeditor:
    command: "uvx"
    args: ["pymemoryeditor"]
```

### Netclaw

```json
{
  "McpServers": {
    "jeanextreme002-pymemoryeditor": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "pymemoryeditor"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add jeanextreme002-pymemoryeditor -t stdio -c uvx -a pymemoryeditor
```

### Other

```json
{
  "mcpServers": {
    "jeanextreme002-pymemoryeditor": {
      "command": "uvx",
      "args": [
        "pymemoryeditor"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-10-04 (score 77, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-03 (score 62, −15)

- [security regression] Malware scan: pass → unverified

### 2026-10-02 (score 77)

First indexed and scored.

## MCP tools (14)

### `server_info` (~69 tokens)

Report the server's capabilities, limits and open sessions.

Call this first. It answers the questions whose wrong answer wastes the
most turns: whether writing is enabled at all, which processes are
reachable, how long a scan may run, and which sessions are already
open from earlier in the conversation.

Output parameters:

- `result` (object)

### `list_processes` (~193 tokens)

List running processes this server is allowed to open.

:param name_filter: keep only processes whose name contains this
    substring, case-insensitively. Use it — an unfiltered list on a
    desktop is several hundred entries of pure noise.
:param limit: maximum entries to return, capped at
    ``server_info().limits.max_page_size``.
:param offset: entries to skip, for paging. It exists because the cap
    used to be a different number from the one ``server_info``
    advertised, with no way past it — so everything after the first
    page was simply unreachable.

Processes blocked by the server's policy are omitted, and the result
says how many were hidden so the absence of an expected target is
distinguishable from it not running.

Input parameters:

- `limit` (integer)
- `name_filter` (string)
- `offset` (integer)

Output parameters:

- `result` (object)

### `process_info` (~114 tokens)

Summarize an open target: bitness, address space, modules, threads.

The module list is the useful half for reverse engineering: a module's
\``base_address`` moves every launch (ASLR) but offsets *inside* it do
not, so ``base + offset`` is how a found address becomes a recipe that
survives a restart. See ``find_pointer_paths`` for discovering those
offsets and ``resolve_pointer_chain`` for replaying them.

Input parameters:

- `session_id` (string, required)

Output parameters:

- `result` (object)

### `list_memory_regions` (~154 tokens)

Page through the target's memory map.

:param writable_only: keep only writable regions — where a program's
    mutable state (the values worth scanning for) lives.
:param executable_only: keep only executable regions — code.
:param path_filter: keep only regions backed by a file whose path
    contains this substring.
:param limit: rows per page. The server caps it — see
    ``server_info().limits.max_page_size``.
:param offset: rows to skip, for paging.

Input parameters:

- `executable_only` (boolean)
- `limit` (integer)
- `offset` (integer)
- `path_filter` (string)
- `session_id` (string, required)
- `writable_only` (boolean)

Output parameters:

- `result` (object)

### `scan_value` (~547 tokens)

Scan the target's memory for a value. The first step of the loop.

This is Cheat Engine's "First Scan". It returns a ``scan_id``, a match
count and a few sample addresses — not the addresses themselves, which
routinely number in the tens of thousands. Narrow the set with
\``refine_scan`` after the value changes in the target, and repeat until
a handful remain; then read them with ``list_scan_results``.

:param value_type: ``int``, ``float``, ``bool``, ``str`` or ``bytes``.
:param value: the value to match. Hex is accepted for ``int``
    (``"0x64"``) and required for ``bytes`` (``"DEADBEEF"``).
:param scan_type: ``exact`` (default), ``not_exact``, ``bigger``,
    ``bigger_or_exact``, ``smaller``, ``smaller_or_exact``, ``between``
    or ``not_between``.
:param end_value: the upper bound, required by ``between`` /
    ``not_between`` and rejected otherwise.
:param bufflength: width in bytes — ``4`` for a typical ``int``, ``8``
    for a ``double``. Leave at ``0`` for the default (int→4, float→8,
    bool→1) or, for ``str`` / ``bytes``, to infer it from ``value``.

    Numeric widths are restricted to 1, 2, 4 or 8 for ``int``, 4 or 8
    for ``float``, and 1 for ``bool``; anything else is refused rather
    than half-supported. Text is capped, inferred or not — see
    ``server_info().limits``.
:param writable_only: restrict the scan to writable memory (the
    default). A value the program changes lives in writable memory, so
    this usually cuts the work and the false positives by an order of
    magnitude. Turn it off only when hunting constants.

A scan stops early at the server's result cap or time budget; when it
does, the result sets ``partial`` and explains what to narrow. Refining
a partial set can converge on the wrong address, because the right one
may never have been in it.

Input parameters:

- `bufflength` (integer)
- `end_value` (string)
- `scan_type` (string)
- `session_id` (string, required)
- `value` (string, required)
- `value_type` (string, required)
- `writable_only` (boolean)

Output parameters:

- `result` (object)

### `scan_pattern` (~214 tokens)

Scan for a byte pattern (an AOB scan) with ``?`` wildcards.

The IDA / Cheat Engine technique for finding a structure or a piece of
code whose address moves between builds but whose surrounding bytes do
not: ``"48 8B ? ? 00 00 89"``. Returns a ``scan_id`` like
\``scan_value``.

:param pattern: space-separated hex bytes, where ``?`` or ``??`` is a
    single-byte wildcard.

Note that this covers the target's readable, **non-shared** regions —
heap, stack and anonymous mappings. File-backed code (a ``.dll`` /
\``.so`` / ``.dylib`` image) is a shared mapping and is deliberately
excluded, so patterns matching instructions inside a loaded module will
not be found here. Reach module code through ``process_info``'s module
bases plus a static offset instead.

Input parameters:

- `pattern` (string, required)
- `session_id` (string, required)

Output parameters:

- `result` (object)

### `refine_scan` (~267 tokens)

Narrow an existing result set. The second step, repeated.

This is Cheat Engine's "Next Scan", and it is what makes the whole
workflow work: change the value in the target (take damage, spend a
coin), then keep only the addresses that changed to match. Two or three
rounds usually collapse tens of thousands of candidates to one.

It re-reads the addresses already in the set rather than walking memory
again, so it is orders of magnitude faster than a fresh scan and cannot
find addresses the original scan missed. Each refine returns a *new*
\``scan_id``; the previous set stays live, so a refine that narrows too
far (down to zero) can be retried from the one before it.

:param scan_id: the set to narrow.
:param scan_type: comparison to apply — the same names ``scan_value``
    takes. For ``str`` / ``bytes`` sets only ``exact`` / ``not_exact``
    are available.
:param value: the value to compare against.
:param end_value: upper bound for ``between`` / ``not_between``.

Input parameters:

- `end_value` (string)
- `scan_id` (string, required)
- `scan_type` (string)
- `value` (string)

Output parameters:

- `result` (object)

### `list_scan_results` (~119 tokens)

Page through a result set, reading each address's *current* value.

Values are read live, so calling this twice on a set that is still
changing is itself a useful signal: the address whose value tracks what
you see in the target is the one you want.

:param limit: rows per page. The server caps it — see
    ``server_info().limits.max_page_size``.
:param offset: rows to skip.

Input parameters:

- `limit` (integer)
- `offset` (integer)
- `scan_id` (string, required)

Output parameters:

- `result` (object)

### `read_value` (~208 tokens)

Read one value from an address.

:param address: hex string, e.g. ``"0x7FFD1234"``.
:param value_type: ``int``, ``float``, ``bool``, ``str`` or ``bytes``.
:param bufflength: width in bytes. Required for ``str`` / ``bytes``
    (nothing else says how far to read); defaults to int→4, float→8,
    bool→1 for the numeric types.

    Numeric widths are restricted to the ones every path agrees on:
    1, 2, 4 or 8 for ``int``, 4 or 8 for ``float``, 1 for ``bool``.
    Anything else is refused rather than half-supported. Text widths
    are capped — see ``server_info().limits``.

Input parameters:

- `address` (string, required)
- `bufflength` (integer)
- `session_id` (string, required)
- `value_type` (string)

Output parameters:

- `result` (object)

### `resolve_pointer_chain` (~248 tokens)

Walk a known multi-level pointer chain to its final address.

Replays the recipe a Cheat Engine table records —
\``"game.exe"+0x10F4F4 -> [+0x0] -> [+0x158]`` — which is the form that
survives a restart. Combine with ``process_info``'s module
\``base_address`` to build ``base_address`` for the current run.

:param base_address: where the chain starts, hex. Usually
    ``module_base + static_offset``.
:param offsets: the chain's offsets in order, as hex strings
    (``["0x0", "0x158"]``). **Negative offsets are allowed**
    (``"-0x8"``) — walking backwards through a struct is ordinary in a
    published recipe. The last one is added *without* a final
    dereference, so the result is the address the value lives at — read
    it with ``read_value``. Pass an empty list to dereference
    ``base_address`` once.

Input parameters:

- `base_address` (string, required)
- `offsets`
- `session_id` (string, required)

Output parameters:

- `result` (object)

### `find_pointer_paths` (~431 tokens)

Reverse-scan for static pointer paths that reach an address.

The inverse of ``resolve_pointer_chain``, and the step that turns a
throwaway find into something reusable: an address from ``scan_value``
is different every run, but a path rooted in a module's image is the
same recipe every time. Run this on the address you confirmed, then
replay the best path in the next run with ``resolve_pointer_chain``.

:param target_address: the dynamic address to find paths to.
:param max_depth: pointer levels to follow. Cost grows sharply with
    depth; 1–4 is the useful range and 3 is a good default. A chain
    needs at least one level, so ``0`` clamps to 1 rather than to the
    default — and ``null`` means "use the default", as it does for
    every argument here.
:param max_offset: largest offset a single hop may add — effectively
    the assumed struct size. Larger finds more and noisier paths.
    An explicit ``0`` is meaningful rather than "unset": it keeps only
    hops that point exactly at the address. ``null`` means "use the
    default" (1024) — it used to mean 0, i.e. the narrowest search
    possible, which is the opposite of what a client sending null for
    an unset field intends.
:param max_results: stop after this many paths.

This is the most expensive tool here, and it runs in two phases: it
maps every pointer in the target's writable memory, then walks that map
backwards. Only the first phase reports progress, so the server's time
budget bounds it precisely and the second phase only between paths —
a deep walk that finds nothing can still run long. Expect seconds to
minutes on a large process, and start shallow.

Input parameters:

- `max_depth` (integer)
- `max_offset` (integer)
- `max_results` (integer)
- `session_id` (string, required)
- `target_address` (string, required)

Output parameters:

- `result` (object)

### `close_process` (~28 tokens)

Detach from a process and drop its scan result sets.

Input parameters:

- `session_id` (string, required)

Output parameters:

- `result` (object)

### `open_process` (~252 tokens)

Attach to a process and return a ``session_id`` for the other tools.

:param pid: the process id to open. Preferred — it is unambiguous.
:param name: a process name (or fragment) to resolve instead, matched
    case-insensitively as a substring. When several processes match,
    no process is opened and the candidates are returned for you to
    pick a ``pid`` from.

Pass exactly one of the two. The returned session stays open until
\``close_process`` or server shutdown; reuse its id rather than
reopening the same target, since each open costs a handle and resets
the cached region map.

At most ``max_open_sessions`` (see ``server_info``) can be open at
once; reaching it is refused, not rotated, so ``close_process`` a
target you are done with.

Attaching to a process the operator has not pre-approved requires the
\**user's** approval, asked for at the moment you call this. Say which
process you want and why; if the request is refused, report that rather
than trying a different target.

Input parameters:

- `name` (string)
- `pid` (integer)

### `write_value` (~312 tokens)

Write a value to an address. Absent when the server is ``--read-only``.

The previous value is read first and returned as ``previous_value``, so
the change is reversible: to undo, call this again with that value.
Nothing else in this server can put it back.

:param address: hex string, e.g. ``"0x7FFD1234"``.
:param value_type: ``int``, ``float``, ``bool``, ``str`` or ``bytes``.
:param value: the value to write. Hex for ``bytes``.
:param bufflength: for numbers, the exact write width — 1, 2, 4 or 8
    for ``int``, 4 or 8 for ``float``, 1 for ``bool``.

    For ``bytes`` it is a maximum number of bytes, which truncates and
    never pads. For ``str`` it caps **characters**, not bytes, so
    non-ASCII text can overwrite more bytes than the number you pass:
    ``bufflength=2`` with ``"日本語"`` writes ``"日本"`` — six bytes.
    The result's ``previous_value_bufflength`` always reports the span
    that was actually replaced, so check it rather than assuming.

Input parameters:

- `address` (string, required)
- `bufflength` (integer)
- `session_id` (string, required)
- `value` (string, required)
- `value_type` (string, required)

Output parameters:

- `result` (object)

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/jeanextreme002-pymemoryeditor/pymemoryeditor#diagnostics

## Score history

- 2026-10-05: 77
- 2026-10-04: 77
- 2026-10-03: 62
- 2026-10-02: 77

## Common questions

### What is the PyMemoryEditor MCP server?

PyMemoryEditor is an MCP server listed in the public MCP registry as io.github.JeanExtreme002/PyMemoryEditor. Read, write and scan process memory, Cheat Engine style, on Windows, Linux and macOS. This page covers its PyPI package (pymemoryeditor).

### Is the PyMemoryEditor MCP server safe to use?

PyMemoryEditor scores 77 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 5 October 2026. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the PyMemoryEditor MCP server expose?

PyMemoryEditor exposes 14 tools: server_info, list_processes, process_info, list_memory_regions, scan_value, and 9 more. Their descriptions and schemas cost roughly 3,156 tokens of context every time the server is loaded.

### Is the PyMemoryEditor MCP server still maintained?

PyMemoryEditor is still listed as active in the MCP registry. We last reached this channel on 5 October 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the PyMemoryEditor MCP server under?

PyMemoryEditor declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- PyPI project: https://pypi.org/project/pymemoryeditor/
- Socket report: https://socket.dev/pypi/package/pymemoryeditor
- Repository: https://github.com/JeanExtreme002/PyMemoryEditor
- Website: https://pymemoryeditor.readthedocs.io/en/latest/mcp.html
- Changelog RSS feed: https://verifymcp.io/servers/jeanextreme002-pymemoryeditor/pymemoryeditor.xml
- Changelog JSON feed: https://verifymcp.io/servers/jeanextreme002-pymemoryeditor/pymemoryeditor.json
- HTML version of this page: https://verifymcp.io/servers/jeanextreme002-pymemoryeditor/pymemoryeditor
