# shotbox (pypi · shotbox)

Sealed X11/Wayland sessions to see, drive and check real GUI programs: a11y, pictures, sound.

- Trust score: 78/100 (medium)
- Change this week: +16
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-24

## Components

- pypi · `shotbox`: 78/100 (this document), [markdown](https://verifymcp.io/servers/mishan-shotbox/shotbox.md), [page](https://verifymcp.io/servers/mishan-shotbox/shotbox)

## Channel facts

- Registry: `pypi`
- Package: `shotbox`
- Version: `0.10.0`
- 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-24.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs setuptools.build_meta 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 mishan/shotbox).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 0 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 57/100
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 5243 tokens (~275/item across 19 items; 19 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 check failed: the tool surface changed between 0.6.0 and 0.10.0: 12 tool removals, 0 breaking changes, 14 additions.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 19 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 20 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).

## Install

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

shotbox runs locally as a PyPI package, launched with uvx shotbox. 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 mishan-shotbox -- uvx shotbox
```

### Cursor

```json
{
  "mcpServers": {
    "mishan-shotbox": {
      "command": "uvx",
      "args": [
        "shotbox"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "mishan-shotbox": {
      "command": "uvx",
      "args": [
        "shotbox"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add mishan-shotbox -- uvx shotbox
```

### opencode

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

### OpenClaw

```bash
openclaw mcp add mishan-shotbox --command uvx --arg shotbox
```

### Hermes

```yaml
mcp_servers:
  mishan-shotbox:
    command: "uvx"
    args: ["shotbox"]
```

### Netclaw

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

### Vellum

```bash
assistant mcp add mishan-shotbox -t stdio -c uvx -a shotbox
```

### Other

```json
{
  "mcpServers": {
    "mishan-shotbox": {
      "command": "uvx",
      "args": [
        "shotbox"
      ]
    }
  }
}
```

## 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-24 (score 78, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-23 (score 63, −16)

- [security regression] Malware scan: pass → unverified
- [functional regression] Schema quality: 236 → 275
- [functional] Package version: 0.9.0 → 0.10.0

### 2026-10-20 (score 79, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-19 (score 64, −15)

- [security regression] Malware scan: pass → unverified
- [functional] Package version: 0.8.0 → 0.9.0

### 2026-10-18 (score 79, +17)

- [security regression] Stability: 0.03 → unverified
- [security regression] Tool safety: pass → unverified
- [security regression] Stability: 0.03 → fail
- [security improvement] Malware scan: unverified → pass
- [functional regression] Schema quality: 202 → 234
- [functional regression] Capabilities: fail → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional improvement] MCP protocol: fail → pass
- [functional improvement] Security disclosure: fail → pass
- [functional] First check of Schema quality: unverified
- [functional] Package version: 0.7.1 → 0.8.0

### 2026-10-17 (score 62, +2)

- [security regression] Tool safety: pass → unverified
- [security improvement] Malware scan: unverified → pass
- [security] Stability: Stability not yet verified: we do not have a sandbox capture of the MCP schema this version of the package serves yet.
- [functional regression] Schema quality: 183 → 202
- [functional regression] Schema quality: 183 → 208
- [functional regression] Capabilities: fail → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional improvement] Tool coverage: 56% → 100%
- [functional improvement] Stability: unverified → 0.03
- [functional] First check of Schema quality: unverified
- [functional] Package version: 0.6.0 → 0.7.1
- [functional] Package version: 0.6.0 → 0.7.0

### 2026-10-16 (score 60)

First indexed and scored.

## MCP tools (19)

### `start_session` (~419 tokens)

Start a sealed session: a display of its own (X11 under Xvfb, or Wayland under a headless sway), its own D-Bus and accessibility bus, and a scratch home. Nothing in it reaches the desktop of the person you're working for, and nothing of theirs reaches it. The answer gives its name, display and size, the programs spawn_program can start in it, and anything it couldn't seal. It lasts until stop_session.

Input parameters:

- `a11y` (boolean): An accessibility bus (default true)
- `audio` (boolean): A sound server whose sink plays to nowhere
- `clock` (string): ISO 8601, 2026-09-26T18:40:00, in tz or UTC: where its wall clock starts and runs on from, so dates in pictures don't change (needs libfaketime)
- `dark` (boolean): GTK and libadwaita's dark style
- `gamepads` (integer): Virtual Xbox 360 pads, for use_gamepad
- `high_contrast` (boolean): Their high contrast style
- `identity` (string): USER@HOST: who and where its programs are, for whoami, hostname, ls -l and $USER
- `name` (string): What to call it (s1, s2... by default)
- `net` (string): A network its programs share and nothing else is on: nothing reaches in or out
- `screen` (string): WIDTHxHEIGHT (1280x800), up to 7680x4320
- `text_scale` (number): Their text size, 1.25 for 125%
- `tz` (string): Its time zone, Europe/Berlin (UTC)
- `usage` (boolean): Say, when it's stopped, what its programs used: peak memory and CPU time
- `wayland` (boolean): Wayland, not X11
- `xwayland` (boolean): Wayland, with Xwayland for X programs

### `stop_session` (~105 tokens)

Stop a session start_session made, when you're done with it: every program in it is killed, unsaved work and all, and its scratch directory is deleted, with any clip record_video saved there (one saved at a path is kept). In a session started with usage, the answer says its programs' peak memory and CPU time. You needn't stop sessions before the server ends: they all stop with it.

Input parameters:

- `session` (string, required): Its name, from start_session

### `spawn_program` (~95 tokens)

Start one of the programs the person allowed this server to run, by its name, in a session; nothing else can run. The answer gives its pid. Then wait_for its window, by the program's name, before looking at it. If it doesn't come up, get_program_output says why.

Input parameters:

- `program` (string, required): Its name in the allow list
- `session` (string, required): Its name, from start_session

### `get_program_output` (~141 tokens)

Read the last lines (40, or lines) a spawned program wrote to stdout and stderr, after a line saying whether it's still running or exited, with its status. It works after the program has exited, until the session stops; for a program spawned more than once, it reads the latest. Use it when a window never comes up or a program seems to have crashed; to wait for a line in a log file, use wait_for line.

Input parameters:

- `lines` (integer): How many lines from the end (40)
- `program` (string, required): Its name in the allow list
- `session` (string, required): Its name, from start_session

### `get_accessible_tree` (~159 tokens)

Read what a screen reader would find: every visible node, a line each, indented, with its role, name, description and the states a screen reader says (focusable, focused, checked...). The cheapest way to see what's on screen and what can be clicked by name. A node with an empty name ("") is one a screen reader can say nothing for. Use take_screenshot for how it looks.

Input parameters:

- `bounds` (boolean): Add @x,y WIDTHxHEIGHT to each line, in click_element's x,y
- `session` (string, required): Its name, from start_session
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id

### `take_screenshot` (~373 tokens)

Take a picture of the screen, or of one window, RETURNED AS AN IMAGE for you to look at: the only way to see colors, layout and what's drawn. Pictures cost far more than get_accessible_tree; take the window rather than the screen, and crop when you need a part; zoom enlarges one to read small text. vision checks nothing is told apart by color alone; burst, as one image, is for motion, an animation or a transition. labels numbers each named node's box on it, to click by name what you see. The answer gives its size, if it's scaled or a part how an x,y on it maps to click_element's, and with labels each number's tree line.

Input parameters:

- `burst` (integer): Pictures to take
- `crop` (string): WIDTHxHEIGHT+X+Y, cut to what's taken
- `every` (number): With burst, seconds between them
- `labels` (boolean): Number each named node's box on it, and list each number's tree line
- `park` (boolean): Move the pointer away first, so nothing shows its hover
- `session` (string, required): Its name, from start_session
- `vision` (string): protanopia (no red cones), deuteranopia (no green), tritanopia (no blue), or gray (no color)
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id
- `zoom` (array): [x0, y0, x1, y1]: a region's corners in click_element's x,y (a picture's, mapped as its answer says), shown as large as fits, up to 8 times; not with crop or burst

### `record_video` (~323 tokens)

Record a video of the screen, or of one window, and save it as an .mp4: in the session's scratch directory, gone when the session stops, or at path. The answer is the saved path, how long the clip is, and up to 8 frames from it RETURNED AS ONE IMAGE, with the times they show: you can't play the clip, but a person or another tool can. A clip stops at 100 MB. sound also says what it heard, as record_audio does. until_line ends it at an event instead; the answer gives the line. For a few moments of motion, take_screenshot's burst is cheaper.

Input parameters:

- `crop` (string): WIDTHxHEIGHT+X+Y, inside what's taken
- `file` (string): until_line's, by its path from the allow file's directory
- `path` (string): Where to save it, a new .mp4, by its path from the allow file's directory
- `seconds` (number): How long; with until_line, the most (60)
- `session` (string, required): Its name, from start_session
- `sound` (boolean): What plays too, in a session started with audio
- `tail` (number): Seconds to go on after the line (0)
- `until_line` (string): A regex: stop tail seconds after a line matching it is written to file
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id

### `click_element` (~503 tokens)

Click an element by its accessible name, or a point by x and y, on the screen or inside a window. By name is the one to use: it waits up to timeout seconds for the node to show, be placed and enabled, hold still and be uncovered (else it says which it isn't), raises its window and clicks its middle, and the answer gives the node and its bounds. When two nodes match it lists them, with where they are; when none does, the nearest names. A click gives a window the keyboard, which nothing else does on X11. hold and state, by x and y, hold the button as press_keys holds keys.

Input parameters:

- `action` (string): By name, do the node's accessible action of this name in place of a click, for a node a click won't reach. Its names and what they do are its toolkit's: a click that can't place a node lists them, as…
- `button` (integer): 1 left, 2 middle, 3 right
- `changes` (boolean): Also say what this changed in the accessible tree once the screen holds still (not a field's text, nor where things are): tree lines, + added, - gone, ~ changed
- `double` (boolean): Click twice, as a double click
- `force` (boolean): By name, don't wait for the node to be enabled, still and uncovered (with action, enabled): to check a disabled one does nothing
- `hold` (number): Seconds to keep it down before letting go
- `name` (string): A node's name, as get_accessible_tree gives it: exactly, or a regex matched in full
- `role` (string): Narrows name: exactly, or a regex
- `session` (string, required): Its name, from start_session
- `state` (string): Only press, leaving it down until a call with up; or only let go
- `timeout` (number): By name, seconds to wait for the node (10)
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id
- `x` (integer): Pixels from the left: of the window, with window
- `y` (integer): Pixels from the top: of the window, with window

### `scroll_wheel` (~313 tokens)

Turn the mouse wheel over a node by its name, found as click_element finds it (waiting for it, its window raised), or at a point by x and y, leaving the pointer there. Use it for what's past a list's, a page's or a window's edge, then wait_for stable and look again; to move the keyboard focus, use press_keys.

Input parameters:

- `changes` (boolean): Also say what this changed in the accessible tree once the screen holds still (not a field's text, nor where things are): tree lines, + added, - gone, ~ changed
- `dx` (integer): Clicks right; negative, left
- `dy` (integer): Clicks down; negative, up
- `force` (boolean): By name, don't wait for the node to be enabled, still and uncovered (with action, enabled): to check a disabled one does nothing
- `name` (string): A node's name, as get_accessible_tree gives it: exactly, or a regex matched in full
- `role` (string): Narrows name: exactly, or a regex
- `session` (string, required): Its name, from start_session
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id
- `x` (integer): Pixels from the left: of the window, with window
- `y` (integer): Pixels from the top: of the window, with window

### `press_keys` (~211 tokens)

Press keys in turn, each a chord: Tab, Return, Escape, ctrl+s, alt+shift+Tab, F6. They go to whatever has the keyboard; click a window first with click_element if nothing does. hold presses them all together and keeps them down that long (walking while running); state down leaves them down, across calls, until a call with state up (move or click while a key is held). The answer says what it pressed.

Input parameters:

- `changes` (boolean): Also say what this changed in the accessible tree once the screen holds still (not a field's text, nor where things are): tree lines, + added, - gone, ~ changed
- `hold` (number): Seconds to keep it down before letting go
- `keys` (array, required): Chords, pressed one after another
- `session` (string, required): Its name, from start_session
- `state` (string): Only press, leaving it down until a call with up; or only let go

### `use_gamepad` (~287 tokens)

Work a virtual gamepad, in a session started with gamepads: press buttons together, tapped, held for hold seconds, or with state down left down until a call with state up; push a stick to x, y; pull a trigger to value; or neutral, letting go of everything. A stick or trigger stays where it's put, unless hold says for how long. Programs in the session see a real Xbox 360 controller, and none of the machine's. For keys and the mouse, press_keys and click_element.

Input parameters:

- `action` (string, required): Press buttons, push a stick, pull a trigger, or let go of everything
- `buttons` (array): a b x y lb rb back start guide ls rs, and the d-pad's up down left right
- `hold` (number): Seconds to keep it down before letting go
- `pad` (integer): Which of the session's pads (1)
- `session` (string, required): Its name, from start_session
- `state` (string): Only press, leaving it down until a call with up; or only let go
- `value` (number): The trigger, let go to pulled all the way
- `which` (string): Which stick or trigger
- `x` (number): The stick, left to right
- `y` (number): The stick, up to down

### `type_text` (~124 tokens)

Type text into whatever has the keyboard focus, a key at a time. ASCII only: a character with no key fails the call, once what's before it is typed. For keys and shortcuts (Return, ctrl+s), use press_keys.

Input parameters:

- `changes` (boolean): Also say what this changed in the accessible tree once the screen holds still (not a field's text, nor where things are): tree lines, + added, - gone, ~ changed
- `session` (string, required): Its name, from start_session
- `text` (string, required): The text to type

### `wait_for` (~582 tokens)

Wait until something is so, instead of sleeping; it fails after timeout seconds, saying so, and listing the windows there are when a window never came. what says what to wait for, and value which: window: a title, exactly or as a regex, app=RE, or the name a program was started by with spawn_program, for its window whatever its title; answers with the window's title, size and place. stable: the screen (or window) unchanged for value seconds (0.5; up to 60), as after a click, for the repaint. announcement: a regex in what was announced to a screen reader; each is waited for once. focus: the name of the node with the keyboard focus, as click_element takes one; answers with its tree line. node: a regex in the name or text of a node showing in the accessible tree, as a status line saying a page is drawn, where the screen holding still says nothing; answers with its tree line, and its text if that matched. match: a PNG's path, from the allow file's directory, that the screen (or window, cropped to crop) should look like; answers how long it took. change: any change from how it looked when the wait began; answers how long it took. line: a regex in a line of file written after the wait starts (or, with from_start, anywhere in it), for a program that logs its events; a file not there yet is waited for, and one rotated or rewritten is read from the top; answers with the line. To see how things are now, without waiting, use get_accessible_tree.

Input parameters:

- `crop` (string): WIDTHxHEIGHT+X+Y, inside what's taken
- `file` (string): line's, by its path from the allow file's directory
- `from_start` (boolean): line: anywhere in file
- `fuzz` (number): match, change: percent a pixel may differ by (0)
- `ignore` (array): match, change: boxes of the picture to leave out, as a clock or a cursor, each WIDTHxHEIGHT+X+Y
- `margin` (integer): match, with crop: pixels the picture may be from the crop's place each way, up to 32; answers where it matched (0)
- `most` (integer): match, change: pixels past fuzz allowed (0)
- `session` (string, required): Its name, from start_session
- `timeout` (number): Seconds before it fails (10)
- `value` (string): What to wait for, as what says
- `what` (string, required): The kind of wait
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id

### `check_accessible_names` (~139 tokens)

Lint the accessibility tree for nodes a screen reader has no name to say for: controls, images (not inside a named control) and anything else that takes the focus. The answer: a tree line each, with bounds, then what's wrong; or "No problems.". Use it to check labels after a change; for colors, use check_contrast.

Input parameters:

- `ignore` (array): Regexes for lines that can't be fixed yet
- `session` (string, required): Its name, from start_session
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id

### `check_contrast` (~363 tokens)

Check that text, buttons, links and images stand out from their background: WCAG's 4.5:1 for text (3 for large text) and 3:1 for images unless text_ratio and graphics say otherwise, and with edges, a button's or field's edge too. It raises the window and measures a picture of the screen in each node's bounds. The answer: each node that fails, a tree line with bounds, its ratio and colors, or "No problems."; with passes, then each that passes, the same; then what it couldn't measure, which doesn't fail unless strict (too small, off the screen, or GTK 4.14+'s, whose positions are wrong: of that, only text is measured, where it's found near them), then what's behind a modal dialog, not measured, which doesn't fail even with strict (check it with the dialog closed), and why. A window where nothing can be measured is an error, not a pass. Switches, sliders, check boxes, disabled nodes and what's scrolled out of view are skipped. For missing names, use check_accessible_names.

Input parameters:

- `edges` (number): Least for edges (unchecked)
- `graphics` (number): Least for images (3)
- `ignore` (array): Regexes for lines that can't be fixed yet
- `passes` (boolean): Also what passes, with its ratio
- `session` (string, required): Its name, from start_session
- `strict` (boolean): What it couldn't measure fails too
- `text_ratio` (number): Least for text (4.5)
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id

### `check_focus_order` (~349 tokens)

Check the keyboard focus order in one call: press Tab, or key, until the focus comes back round, and list where it went, a tree line each, after a "began in" line. Then the controls the key never reached, and how it ended: round again (fine), stuck (a trap, or a text view nothing leaves), not round after most presses, lost (no node has the focus), stopped (a text view closed as it was left), never in the page (with document, a page with controls), left the page (a web editor that keeps Tab, left by F6, and Tab never back), or nothing focused to begin with (click the window with click_element first). With visible, "focus not shown on N" counts stops that don't show it. A text view that takes Tab is left as a person would, with ctrl+Tab, shift+Tab, Escape or F6. The focus stays where it ended. To press a few keys yourself, use press_keys.

Input parameters:

- `document` (boolean): Only what's in a browser window's web page: Tab still goes through the browser's own controls, but they aren't listed
- `ignore` (array): Regexes for unreached controls to leave out
- `key` (string): The chord to press (Tab)
- `most` (integer): Presses at most (50)
- `session` (string, required): Its name, from start_session
- `visible` (boolean): Also check each place it lands shows it has the focus
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id

### `get_speech` (~189 tokens)

Hear what Orca, a real screen reader, says as the focus moves: it starts Orca in the session, presses Tab until the focus comes round (or keys, in turn), and answers a (start) line, then a line a press: the focused node's tree line, then what Orca said. Tab stops before a text view, which would take it as typing. Use it over get_accessible_tree when what's heard matters (labels, mnemonics, states, silent stops). Orca takes seconds to start.

Input parameters:

- `keys` (array): Chords to press in turn, instead of Tab
- `most` (integer): Tab presses at most (50)
- `session` (string, required): Its name, from start_session
- `window` (string): A window's title, exactly or a regex matched in full, or app=RE for its app id

### `list_announcements` (~138 tokens)

List what programs in the session have announced to a screen reader since it started, oldest first, a line each with its politeness: "polite: Saved", "assertive: Error". That's GTK's announcements, a browser's live regions, alerts and info bars as they appear (GTK 3's, Qt's, libadwaita's) and the focus renamed. Reading doesn't clear them; "Nothing announced yet." means none so far. Use it to check what a screen reader user heard after an action; to wait for one, use wait_for announcement.

Input parameters:

- `session` (string, required): Its name, from start_session

### `record_audio` (~173 tokens)

Record a session's sound and say how it was: call with action start, have the program play, then call with action stop. The session must have been started with audio. stop answers with the sound's length, its peak and RMS level in dBFS, the samples clipped, and each gap of silence between the first sound and the last (what a program that falls behind leaves), then any problem found. The sound itself isn't returned or kept; to keep it, use record_video with sound. Raise gap when the program is meant to fall silent between sounds, as between two beeps.

Input parameters:

- `action` (string, required): start, then stop
- `gap` (number): The shortest silence, in milliseconds, reported as a gap (1)
- `session` (string, required): Its name, from start_session

## Diagnostics

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

## Score history

- 2026-10-24: 78
- 2026-10-23: 63
- 2026-10-22: 79
- 2026-10-21: 79
- 2026-10-20: 79
- 2026-10-19: 64
- 2026-10-18: 79
- 2026-10-17: 62
- 2026-10-16: 60

## Common questions

### What is the shotbox MCP server?

shotbox is an MCP server listed in the public MCP registry as io.github.mishan/shotbox. Sealed X11/Wayland sessions to see, drive and check real GUI programs: a11y, pictures, sound. This page covers its PyPI package (shotbox).

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

shotbox scores 78 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 24 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 shotbox MCP server expose?

shotbox exposes 19 tools: start_session, stop_session, spawn_program, get_program_output, get_accessible_tree, and 14 more. Their descriptions and schemas cost roughly 4,986 tokens of context every time the server is loaded.

### Is the shotbox MCP server still maintained?

shotbox is still listed as active in the MCP registry. We last reached this channel on 24 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 shotbox MCP server under?

shotbox 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/shotbox/
- Socket report: https://socket.dev/pypi/package/shotbox
- Repository: https://github.com/mishan/shotbox
- Website: https://github.com/mishan/shotbox/blob/main/docs/mcp.md
- Changelog RSS feed: https://verifymcp.io/servers/mishan-shotbox/shotbox.xml
- Changelog JSON feed: https://verifymcp.io/servers/mishan-shotbox/shotbox.json
- HTML version of this page: https://verifymcp.io/servers/mishan-shotbox/shotbox
