Skip to main content
ClaudeWave

A motion sensor for AI agents: MCP server that reports when a screen region changes or goes still. Scores, never pixels.

MCP ServersRegistry oficial1 estrellas0 forksTypeScriptMITActualizado today
ClaudeWave Trust Score
95/100
Verified
Passed
  • Open-source license (MIT)
  • Actively maintained (<30d)
  • Clear description
  • Topics declared
  • Documented (README)
Last scanned: 9/17/2026
Install in Claude Code / Claude Desktop
Method: NPX · pir-mcp
Claude Code CLI
claude mcp add pir-mcp -- npx -y pir-mcp
claude_desktop_config.json (Claude Desktop)
{
  "mcpServers": {
    "pir-mcp": {
      "command": "npx",
      "args": ["-y", "pir-mcp"]
    }
  }
}
1. Run the command above in your terminal (Claude Code), or paste the JSON config into claude_desktop_config.json (Claude Desktop).
2. Replace any <placeholder> values with your API keys or paths.
3. Restart Claude. The MCP server and its tools appear automatically.
Casos de uso

Resumen de MCP Servers

# pir-mcp

A motion sensor for AI agents.

__*"Gives your LLM agents enough info to be a helpful "watcher" without being a privacy nightmare like Windows Recall"*__ 
*-Solteris-Dev*

A PIR sensor tells you *that* something moved, never *what*. This is the same
idea for a screen: an MCP server that watches named rectangles and answers
"did this change?" and "has this gone still?" with a number, a size and a
timestamp. No tool in it returns pixels, image files, or anything that could
be turned back into a picture.

It exists because an agent driving a desktop keeps needing the same two
things: *wake me when the build output moves* and *tell me if this status bar
has stopped ticking*. Both are motion questions. Neither needs a screenshot.

## What it does

- **`pir_pick_region`** ask the person at the screen to draw the rectangle,
  the way a regional screenshot works (slurp, slop). The human chooses what
  is sensed; the agent gets the geometry. Use this whenever someone is there.
- **`pir_pick_mask`** the same gesture for a patch inside a region to ignore
  (a clock, a spinner, a caret).
- **`pir_define_region`** the scriptable path: name a rectangle by global
  layout coordinates, with optional masks.
- **`pir_pick_window`** / **`pir_define_window`** watch a window instead of a
  fixed rectangle. Its geometry is looked up again before every capture, so
  the region follows the window when it moves; a resize counts as maximal
  change. The pick offers your visible windows as boxes to click.
- **`pir_sample`** capture once, score against the previous sample of that
  region. Cheap; also the way to check the capture command works.
- **`pir_wait_for_change`** block until the region departs from how it looked
  when the call began, or time out. A doorbell.
- **`pir_wait_for_stillness`** block until nothing has changed for
  `still_for_ms`, or time out. A short window means "the animation settled,
  safe to act". A long window on something that should keep changing means
  it is frozen, and `still=true` is the alarm.
- **`pir_list_regions`**, **`pir_remove_region`** housekeeping.

Every comparison reports two numbers over a coarse grid of averaged colour
cells (at most 64 across, each at least 4 px), both 0 for identical and 1 for
black against white:

- **`rmse`** over the whole grid: did the region as a whole move? A cursor
  edge or an antialiased caret scores about 0.02; a dialog opening 0.1 or
  more. This is the default metric, threshold 0.05.
- **`peak`** the single most-changed cell: did *anything* in it move? A clock
  digit flipping inside a 1920-wide status bar scores rmse 0.018 (invisible
  to the default) but peak 0.145. Use `metric=peak` for a small thing that
  should tick inside a larger region.

Averaging is what makes this a sensor rather than a camera: 64 cells cannot
be read.

## What it deliberately does not do

- It never returns image data. There is no snapshot tool and none is planned.
  If an agent needs to *see*, use the screenshot tool your environment already
  has, so that choice stays explicit and yours.
- It never writes frames to disk. A capture lives in memory for the
  milliseconds it takes to reduce it to a grid, and only the grid of the last
  sample is kept per region.
- It does not capture on its own. Every sample is a tool call the agent made,
  visible in the transcript, and `pir_pick_region` carries a `purpose` line
  the agent has to write down before you draw.

What it *does* reveal, so you can decide whether that is acceptable: that a
given rectangle changed, at a given time, by a given magnitude. On a region
covering a chat window that is presence information. Choose regions
accordingly; the server has no opinion.

The capture itself is delegated to a command you configure, run with your
privileges. The default is `grim`, so nothing here has screen access that you
did not already give to grim.

## Install

It is on npm as [`pir-mcp`](https://www.npmjs.com/package/pir-mcp) and in
the [official MCP Registry](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.Solteris-Dev/pir-mcp)
as `io.github.Solteris-Dev/pir-mcp`, so most clients can run it with
`npx -y pir-mcp` and no clone:

```sh
claude mcp add -s user pir -- npx -y pir-mcp
```

From source:

```sh
git clone https://github.com/Solteris-Dev/pir-mcp
cd pir-mcp && npm install && npm run build
```

Requires Node 20+ and a screenshot tool that can write binary PPM to stdout.

### Capture command

`PIR_CAPTURE_CMD` is a template; `{x} {y} {w} {h}` are substituted and the
result runs under `sh -c`. It must print a binary PPM (`P6`) to stdout.

| environment | command |
|---|---|
| wlroots / Hyprland / Sway (default) | `grim -g "{x},{y} {w}x{h}" -t ppm -` |
| X11 with maim | `maim -g {w}x{h}+{x}+{y} -f png \| magick png:- ppm:-` |
| X11 with ImageMagick | `import -window root -crop {w}x{h}+{x}+{y} +repage ppm:-` |
| macOS | `screencapture -x -R{x},{y},{w},{h} -t png /dev/stdout \| magick png:- ppm:-` |

Coordinates are whatever the capture tool uses. On a wlroots layout that is
the global layout, so an output placed left of the primary has negative x.

### Selector command

`PIR_SELECT_CMD` runs when an agent calls `pir_pick_region` or
`pir_pick_mask` and must print `x,y wxh`. Set it to an empty string to remove
those tools entirely.

| environment | command |
|---|---|
| wlroots / Hyprland / Sway (default) | `slurp -f "%x,%y %wx%h"` |
| X11 | `slop -f "%x,%y %wx%h"` |
| macOS | see [contrib/macos](contrib/macos/README.md) (untested) |

### Window commands

`PIR_WINDOW_GEOMETRY_CMD` gets `{id}` substituted and must print `x,y wxh`
for that window; `PIR_PICK_WINDOW_CMD` must print the id of the window the
person chose. Both default to Hyprland (via `jq` and `slurp -r`). Set either
to an empty string to remove the corresponding tools. Window ids are limited
to `[A-Za-z0-9_.:-]` before they reach a shell.

| environment | geometry | pick |
|---|---|---|
| Hyprland (default) | `hyprctl -j clients \| jq -r --arg id "{id}" '.[] \| select(.address==$id) \| "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"'` | visible windows as slurp boxes, prints the address |
| Sway | `swaymsg -t get_tree \| jq -r '.. \| select(.id? == {id}) \| "\(.rect.x),\(.rect.y) \(.rect.width)x\(.rect.height)"'` | `swaymsg -t get_tree \| jq -r '.. \| select(.visible? == true) \| "\(.rect.x),\(.rect.y) \(.rect.width)x\(.rect.height) \(.id)"' \| slurp -r -f '%l'` |
| X11 | `xdotool getwindowgeometry --shell {id} \| awk -F= '/^X/{x=$2}/^Y/{y=$2}/^WIDTH/{w=$2}/^HEIGHT/{h=$2}END{print x","y" "w"x"h}'` | `xdotool selectwindow` |

Only the Hyprland pair has been run; the others are written from the tools'
documentation.

### Claude Code

```sh
claude mcp add -s user pir -- npx -y pir-mcp
# or, from a source checkout:
claude mcp add -s user pir -- node /path/to/pir-mcp/dist/stdio.js
```

Blocking calls default to 55 s and are capped by `PIR_MAX_WAIT_MS` (540 s).
Keep the cap under your host's MCP tool timeout; an agent that needs to watch
for longer just calls again.

### Other settings

| variable | default | meaning |
|---|---|---|
| `PIR_REGIONS` | unset | JSON file of regions to define at startup |
| `PIR_THRESHOLD` | `0.05` | default change threshold |
| `PIR_INTERVAL_MS` | `500` | default sampling period |
| `PIR_DEFAULT_WAIT_MS` | `55000` | default timeout for blocking calls |
| `PIR_MAX_WAIT_MS` | `540000` | cap for blocking calls |
| `PIR_CAPTURE_TIMEOUT_MS` | `10000` | how long one capture may take |
| `PIR_SELECT_TIMEOUT_MS` | `60000` | how long the person has to draw a selection |
| `PIR_MAX_CELLS` | `64` | grid resolution on the long side; smaller is coarser and more private |

A regions file looks like:

```json
[
  { "name": "bar", "x": 0, "y": 0, "w": 1920, "h": 26,
    "masks": [{ "x": 1690, "y": 0, "w": 110, "h": 26 }] }
]
```

## Example

The case that produced this: a status bar that occasionally froze for hours
while its process looked healthy. The clock in it should change every
minute, so a bar that is still for three minutes is a frozen bar.

```
pir_pick_region         name=bar purpose="watch the status bar for a freeze"
pir_wait_for_stillness  name=bar still_for_ms=180000 metric=peak timeout_ms=540000
```

`still=true` comes back only if the bar stopped; otherwise the call returns
`still=false` at the timeout and the agent calls again. Nothing on the screen
was ever seen, and the person drew the rectangle themselves.

## Contributing starting points

`contrib/macos/` holds an AppKit rectangle selector and capture recipe,
written blind and untested. If you run it on a real Mac, fix what breaks and
send it back.

## Tests

```sh
npm test
```

## License

MIT.
ai-agentsclaude-codehyprlandmcpmcp-servermodel-context-protocolmotion-detectionprivacyscreenwayland

Lo que la gente pregunta sobre pir-mcp

¿Qué es Solteris-Dev/pir-mcp?

+

Solteris-Dev/pir-mcp es mcp servers para el ecosistema de Claude AI. A motion sensor for AI agents: MCP server that reports when a screen region changes or goes still. Scores, never pixels. Tiene 1 estrellas en GitHub y su última actualización registrada es del 2026-09-16.

¿Cómo se instala pir-mcp?

+

Puedes instalar pir-mcp clonando el repositorio (https://github.com/Solteris-Dev/pir-mcp) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.

¿Es seguro usar Solteris-Dev/pir-mcp?

+

Nuestro agente de seguridad ha analizado Solteris-Dev/pir-mcp y le ha asignado un Trust Score de 95/100 (tier: Verified). Revisa el desglose completo de comprobaciones superadas y flags en esta página.

¿Quién mantiene Solteris-Dev/pir-mcp?

+

Solteris-Dev/pir-mcp es mantenido por Solteris-Dev. La última actividad registrada en GitHub es del 2026-09-16, con 0 issues abiertos.

¿Hay alternativas a pir-mcp?

+

Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.

Despliega pir-mcp en tu cloud

Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.

¿Mantienes este repo? Añade un badge a tu README

Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.

Featured on ClaudeWave: Solteris-Dev/pir-mcp
[![Featured on ClaudeWave](https://claudewave.com/api/badge/solteris-dev-pir-mcp)](https://claudewave.com/repo/solteris-dev-pir-mcp)
<a href="https://claudewave.com/repo/solteris-dev-pir-mcp"><img src="https://claudewave.com/api/badge/solteris-dev-pir-mcp" alt="Featured on ClaudeWave: Solteris-Dev/pir-mcp" width="320" height="64" /></a>

Más MCP Servers

Alternativas a pir-mcp