mem
by pi-oilrig
README.md
# mem — MCP server for process memory introspection
Live process memory introspection, debugger, and valgrind runner. Linux-only
(x86_64). Runs as an MCP stdio server for the pi agent.
## Tools
| Tool | What it does |
|------|-------------|
| `maps` | Read /proc/\<pid\>/maps — memory layout of a running process |
| `mem_read` | Read bytes from a process's address space (process_vm_readv) |
| `mem_write` | Write bytes into a process's address space (process_vm_writev) |
| `mem_search` | Search process memory for a hex byte pattern |
| `run` | Spawn a command as a child of this server (makes it inspectable) |
| `children` | List supervised child processes |
| `attach` | ptrace-attach to a process (stops it) |
| `registers` | Read CPU registers (PTRACE_GETREGS, x86_64) |
| `detach` | ptrace-detach from a process (resumes it) |
| `valgrind_run` | Run a command under valgrind |
| `valgrind_status` | Poll a running valgrind job |
## Requirements
- Linux (process_vm_readv/writev, ptrace, /proc)
- `ptrace_scope` = 0 (or use `run` tool to spawn child processes)
- valgrind (optional, for valgrind_run)
## ptrace_scope limitation
On Ubuntu (default `ptrace_scope=1`), `process_vm_readv` and `ptrace` only
work on **direct child processes** of the MCP server. To inspect a program:
1. Use `run` to spawn it — the server is its parent, all tools work
2. Or set `ptrace_scope=0`: `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope`
3. Or use `CAP_SYS_PTRACE`
The `run` tool makes the child fully inspectable without any sysadmin
changes.
## Debugging workflow
```
1. run → spawns the target program, returns PID
2. maps → read its memory layout
3. mem_read → read bytes from any address
4. attach → ptrace-stop the process
5. registers → read CPU registers (rip, rsp, rax, etc.)
6. mem_write → patch a variable or instruction
7. detach → resume execution
8. valgrind_run → check for memory errors
```
## Wiring
`pi.mcpServers` in `package.json` declares the server; the hub merges it
into pi's `mcp.json` when the package is registered (this workspace is a
watched hub workspace, so a new package dir wires itself). The `command` is
relative to this dir on purpose — hub resolves it. Build first, then restart
pi: pi reads `mcp.json` at startup.
## Build
```sh
cd /home/feb/dev/_pi_extensions/mem
cargo build --release
```
## Safety
- `mem_write` can crash or corrupt the target process.
- `attach` stops the target process. Always `detach` when done.
- valgrind jobs run with a timeout (default 300s).
## Not wired by default
`pi.mcpServers` is empty on purpose — 11 tools on every session's surface for a
debugger that fires a few times a year is a bad trade. Wire it where you need
it, with a `.mcp.json` in the project root:
```json
{ "mcpServers": { "mem": {
"command": "/home/feb/dev/_pi_extensions/mem/target/release/mem",
"args": [], "directTools": true, "lifecycle": "lazy-keep-alive"
} } }
```
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues