# UVC PTZ Camera (pypi · uvc-ptz-camera-mcp)

Control a USB (UVC) pan/tilt/zoom camera, with every move confirmed from the picture.

- Trust score: 52/100 (low)
- Change this week: +13
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-22

## Components

- pypi · `uvc-ptz-camera-mcp`: 52/100 (this document), [markdown](https://verifymcp.io/servers/abhijatsaxena-uvc-ptz-camera-mcp/uvc-ptz-camera-mcp.md), [page](https://verifymcp.io/servers/abhijatsaxena-uvc-ptz-camera-mcp/uvc-ptz-camera-mcp)

## Channel facts

- Registry: `pypi`
- Package: `uvc-ptz-camera-mcp`
- Version: `0.2.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-22.

- **Supply Chain Security**: 50/100
  - Malware scan not yet available for this package.
  - 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.
  - 1 of 29 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 45/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 14 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 68/100
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 2016 tokens (~118/item across 17 items; 17 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 17/100
  - Stability observed for 5 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 67/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.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 1 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "run_shot" implies "execute" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 18 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 UVC PTZ Camera MCP server?

UVC PTZ Camera runs locally as a PyPI package, launched with uvx uvc-ptz-camera-mcp. 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 abhijatsaxena-uvc-ptz-camera-mcp -- uvx uvc-ptz-camera-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "abhijatsaxena-uvc-ptz-camera-mcp": {
      "command": "uvx",
      "args": [
        "uvc-ptz-camera-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "abhijatsaxena-uvc-ptz-camera-mcp": {
      "command": "uvx",
      "args": [
        "uvc-ptz-camera-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add abhijatsaxena-uvc-ptz-camera-mcp -- uvx uvc-ptz-camera-mcp
```

### opencode

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

### OpenClaw

```bash
openclaw mcp add abhijatsaxena-uvc-ptz-camera-mcp --command uvx --arg uvc-ptz-camera-mcp
```

### Hermes

```yaml
mcp_servers:
  abhijatsaxena-uvc-ptz-camera-mcp:
    command: "uvx"
    args: ["uvc-ptz-camera-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "abhijatsaxena-uvc-ptz-camera-mcp": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "uvc-ptz-camera-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add abhijatsaxena-uvc-ptz-camera-mcp -t stdio -c uvx -a uvc-ptz-camera-mcp
```

### Other

```json
{
  "mcpServers": {
    "abhijatsaxena-uvc-ptz-camera-mcp": {
      "command": "uvx",
      "args": [
        "uvc-ptz-camera-mcp"
      ]
    }
  }
}
```

## 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-21 (score 52, −15)

- [security regression] Malware scan: pass → unverified

### 2026-10-20 (score 67, +2)

- [functional improvement] Stability: unverified → 0.10

### 2026-10-17 (score 65, +41)

- [security improvement] Injection markers: unverified → pass
- [security improvement] Malware scan: unverified → pass
- [security] First check of Judged manipulation: pass
- [security] Stability: Stability not yet verified: not enough scan history yet (needs a 30-day window).
- [functional improvement] Tool coverage: unverified → 100
- [functional improvement] MCP protocol: unverified → pass
- [functional] First check of Tool coverage: 0
- [functional] First check of Schema quality: fail
- [functional] First check of Schema quality: fail
- [functional] First check of Destructive annotations: 0
- [functional] First check of Schema quality: good

### 2026-10-16 (score 24, −15)

- [security regression] Malware scan: pass → unverified

### 2026-10-15 (score 39, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-14 (score 24, −15)

- [security regression] Malware scan: pass → unverified
- [security] Stability: Stability not yet verified: this server's registry entry declares environment variables (an API key or similar), and its server did not start in our sandbox, so we have no schema to compare.
- [security] Tool safety: Tool safety not yet verified: this server's registry entry declares environment variables (an API key or similar), and its server did not start in our sandbox, so we have no tool text to scan.
- [functional] Schema quality: Schema quality not yet verified: this server's registry entry declares environment variables (an API key or similar), and its server did not start in our sandbox, so we have no schema to assess.
- [functional] Capabilities: Protocol version not yet verified: this server's registry entry declares environment variables (an API key or similar), and its server did not start in our sandbox, so we never saw its MCP handshake.
- [functional] Tool coverage: Tool coverage not yet verified: this server's registry entry declares environment variables (an API key or similar), and its server did not start in our sandbox, so we have no tool definitions to assess.

### 2026-10-12 (score 39)

First indexed and scored.

## MCP tools (17)

### `camera_status` (~96 tokens)

Report the camera, its axes and ranges, and whether it is a real device.

Call this first. It says which camera was found, the range each axis accepts, where the
camera believes it is pointing (a hint, not proof), what has been calibrated, and -- if
no real device was found -- the reason. `simulated: true` in any result means nothing
physical moved and no picture was ever taken.

### `aim` (~194 tokens)

Point one axis at an absolute value, and return what the picture confirms.

\`axis` is one of pan, tilt, roll, zoom. Gimbal axes are in degrees; zoom is the device's
own ratio x100, so 300 means 3x. The value is clamped to the range the camera advertises
and the clamped value is what gets sent.

This moves the camera and waits for it: allow roughly a second for a gimbal axis and
three for a zoom. `moved` is decided by comparing frames, never by the device's report --
the report is returned as `device_reported` and labelled a hint, because this hardware
was measured claiming positions it had not reached. If the picture shows no change and
the camera was not already at the target, the call fails rather than reporting success.

Input parameters:

- `axis` (string, required)
- `to` (integer, required)

### `nudge` (~91 tokens)

Move one axis relative to where the device says it is now.

Relative moves inherit the device's reported position, which is a hint: after a series
of nudges the accumulated position may drift from belief. The picture check still tells
you whether *this* call moved the camera; it cannot tell you it is at an absolute angle.

Input parameters:

- `axis` (string, required)
- `by` (integer, required)

### `sweep` (~149 tokens)

Move an axis smoothly to a value over a chosen duration.

Use this for any move that should read as deliberate: a slow pan across a scene, a
gradual push in. The axis is driven by streaming absolute targets at 15 Hz rather than
one jump, which is how this hardware was measured moving smoothly. `ease` is one of
linear, in_out, out, in; in_out starts and stops gently, which is what makes a move look
intentional. Long sweeps take as long as `seconds`, so expect to wait.

Input parameters:

- `axis` (string, required)
- `ease` (string)
- `seconds` (number)
- `to` (integer, required)

### `zoom` (~69 tokens)

Set optical zoom as a multiplier: 1.0 is wide, up to the camera's maximum.

Convenience over the raw zoom axis, which counts in hundredths. The requested ratio is
clamped to what the camera supports and the clamped value is returned.

Input parameters:

- `ratio` (number, required)

### `recentre` (~26 tokens)

Return every axis to the camera's own default: centre, level, unzoomed.

### `look` (~78 tokens)

Capture one frame of what the camera currently sees, as a PNG image.

This observes the camera's view. Call it when the user asks to see the picture, not on
your own initiative, and be aware that a still only tells you what is in front of the
lens -- not where the camera is, and not whether a move happened.

### `aim_learn` (~82 tokens)

Remember the current direction under a name, for later use.

Pan is an absolute axis whose meaning depends on where the camera is standing, so
"point at the door" is unanswerable without recording it once. Call this with the camera
already pointed where you want, and the label becomes usable with `go_to`.

Input parameters:

- `label` (string, required)

### `aim_list` (~22 tokens)

List the directions recorded for this camera, and its calibrated default pose.

### `go_to` (~87 tokens)

Point the camera at a direction recorded earlier with `aim_learn`.

Each axis is moved and confirmed from the picture, the same as `aim`. A label is resolved
to the angles recorded for it, which may drift if the camera has been physically moved
since; the picture check still proves that each axis moved, not that the label is right.

Input parameters:

- `label` (string, required)

### `plan_shot` (~286 tokens)

Compile a shot and report what it would do, without moving the camera.

Takes exactly the step list `run_shot` takes: each step is an object like
{"axis": "pan", "to": 60, "seconds": 2.5, "ease": "in_out"}, optionally with
{"hold": 1.0} to pause after it. Every step is validated against the camera's advertised
ranges and clamped exactly as it would be at execution time, so an impossible or
out-of-range shot is refused here, before anything moves.

Use this to compose a shot. It returns the per-step schedule (where each axis starts, the
value it was clamped to, how far it travels, how many ticks it costs), the per-axis
envelope, the total duration, and the axes with their ranges so the next attempt can be
written against the real limits. Nothing is written to the camera and no frame is captured,
so it is safe to call as often as you like while deciding.

The `from` values come from the device's own position report, which this server treats as a
hint rather than a fact. `run_shot` recompiles from the same hint when it runs, so a plan
and its execution agree unless the camera moved in between.

Input parameters:

- `steps` (array, required)

### `run_shot` (~181 tokens)

Execute a multi-step camera move, verifying after every waypoint.

Each step is an object: {"axis": "pan", "to": 60, "seconds": 2.5, "ease": "in_out"}.
Steps run in sequence and every axis that moves in a step is written on each tick, so a
step naming two axes produces a genuine diagonal rather than a staircase. Add
{"hold": 1.0} to pause after a step.

The whole plan is validated against the camera's advertised ranges before anything moves,
so an impossible shot fails without touching the hardware. Afterwards the report gives the
picture difference at each waypoint, which is how you tell a shot that happened from one
that did not. A long shot takes its full duration; do not set a host timeout shorter.

Input parameters:

- `steps` (array, required)

### `track_start` (~318 tokens)

Start following a subject, closing the loop on the picture.

While this runs the server keeps capturing frames and moving the camera to hold the subject
near the centre of the frame: detect, measure the offset, take a bounded step, look again.
It decides only from the picture, because this class of camera's position report cannot be
trusted -- and it reports the residual offset every iteration, so a loop that is losing the
subject looks like one instead of seeming to work.

It looks at whatever the camera sees for as long as it runs, which may be a room with people
in it. Start it when the user wants the camera following something, and stop it when they do
not. It also stops by itself: when the subject is lost for a while, when it settles inside
the dead zone, or after `max_seconds`.

\`gain` is degrees of camera movement per pixel of offset -- a starting point, not a
measurement. `invert` flips the direction, which is what a differently mounted camera needs:
if the camera drives *away* from the subject, the sign is wrong for this mount, so read
\`track_status` and start again with invert true. Movement is capped at 25 degrees per
iteration, so a badly wrong gain converges slowly rather than throwing the camera across
its range.

Input parameters:

- `dead_zone_px` (integer)
- `gain` (number)
- `invert` (boolean)
- `max_seconds` (number)
- `target` (string)

### `track_status` (~78 tokens)

Report what the follow loop is doing, with the evidence for each iteration.

Every iteration appears with the measured offset and the step applied, so a loop that is
driving the wrong way shows up as a residual distance that grows, and one whose moves the
picture never confirms shows up in `move_failures` rather than as apparent success.

### `track_stop` (~27 tokens)

Stop following, and return the summary: what it saw, what it moved, what failed.

### `mark_view` (~63 tokens)

Store the current picture under a label, so it can be compared later.

Useful before a move whose result you will judge later, or to establish what "the wide
shot" looks like. Marks live in memory for this session only.

Input parameters:

- `label` (string, required)

### `check_view` (~91 tokens)

Compare the current picture with one stored by `mark_view`.

Answers "is the camera still looking at what it was looking at?", which is the question
a scene change can otherwise hide: a person walking through the shot changes the picture
as much as a pan does, so treat a small difference as "still there" and a large one as
"worth looking at".

Input parameters:

- `label` (string, required)

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/abhijatsaxena-uvc-ptz-camera-mcp/uvc-ptz-camera-mcp#diagnostics

## Score history

- 2026-10-22: 52
- 2026-10-21: 52
- 2026-10-20: 67
- 2026-10-19: 65
- 2026-10-18: 65
- 2026-10-17: 65
- 2026-10-16: 24
- 2026-10-15: 39
- 2026-10-14: 24
- 2026-10-13: 39
- 2026-10-12: 39

## Common questions

### What is the UVC PTZ Camera MCP server?

UVC PTZ Camera is an MCP server listed in the public MCP registry as io.github.AbhijatSaxena/uvc-ptz-camera-mcp. Control a USB (UVC) pan/tilt/zoom camera, with every move confirmed from the picture. This page covers its PyPI package (uvc-ptz-camera-mcp).

### Is the UVC PTZ Camera MCP server safe to use?

UVC PTZ Camera scores 52 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 22 October 2026. 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 UVC PTZ Camera MCP server expose?

UVC PTZ Camera exposes 17 tools: camera_status, aim, nudge, sweep, zoom, and 12 more. Their descriptions and schemas cost roughly 1,938 tokens of context every time the server is loaded.

### Is the UVC PTZ Camera MCP server still maintained?

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

UVC PTZ Camera 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/uvc-ptz-camera-mcp/
- Socket report: https://socket.dev/pypi/package/uvc-ptz-camera-mcp
- Repository: https://github.com/AbhijatSaxena/uvc-ptz-camera-mcp
- Changelog RSS feed: https://verifymcp.io/servers/abhijatsaxena-uvc-ptz-camera-mcp/uvc-ptz-camera-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/abhijatsaxena-uvc-ptz-camera-mcp/uvc-ptz-camera-mcp.json
- HTML version of this page: https://verifymcp.io/servers/abhijatsaxena-uvc-ptz-camera-mcp/uvc-ptz-camera-mcp
