Files
mcbebop/README.md
T
rsp2k c5e84c47ae Pre-publish privacy audit: scrub the simulator serial, harden the sdist, add licences
Audited the package against the two-stage procedure before a first PyPI
publish. The sdist and wheel were already tight, but three things needed
fixing and the controls needed to become real rather than documented.

The simulator volunteered high="PI04" as its product serial, which is the
real Bebop 2 serial prefix. Nothing unique to one aircraft, but a realistic
prefix invites being quoted into a bug report as a specimen, so it now reads
"N0TAREAL" / "0000000SIM" with a comment saying why it is nonsense on
purpose. The low half and the 500.0 no-fix GPS sentinel were already fake.

Hardened [tool.uv.build-backend] source-exclude well past the directories
that exist today: captures at any depth, log dumps, recorded media by
extension, caches, and anything credential-shaped. .gitignore governs git
and source-exclude governs the sdist; a capture can sit in one and not the
other, which is how this kind of data reaches an immutable index. Verified
the broad patterns do not over-reach: arsdk-xml/ with PROVENANCE.md and
tools/logs.py both still ship.

Added LICENSE (MIT) and LICENSE-arsdk-xml (Parrot SA's BSD-3-Clause), and
corrected the declared licence to "MIT AND BSD-3-Clause". The vendored XML
ships in both artifacts because nothing here decodes a command without it,
so MIT alone understated what is in the box. Both texts now appear in the
artifacts and in the metadata.

test_packaging.py grows privacy guards that fail on a serial prefix, a P7
CPU id, any MAC, a high-precision coordinate, an absolute home path, or any
private address other than the drone's own documented 192.168.42.0/24. Each
pattern was checked against the real identifiers to confirm it bites, since
a guard that passes on an empty tree proves nothing.

Example address in test_arsdk_session.py moved to RFC 5737 space.

504 tests pass, ruff clean.
2026-10-03 11:21:13 -06:00

96 lines
3.8 KiB
Markdown

# mcbebop
An MCP server for the Parrot Bebop 2, so an agent can talk to the drone without
anyone writing code for each question.
Parrot abandoned the Bebop line, but the aircraft is still a capable, cheap
platform and its protocol is fully described by Parrot's own XML, which this
package vendors. Every one of the **264 commands** is reachable; nothing is
hand-wrapped.
> **Status: in development.** Not flight-tested. See the safety section.
## What it talks to
| Interface | Notes |
|---|---|
| ARSDK3 over UDP | commands and telemetry; this package implements the protocol directly |
| ARStream2 | live H.264 video. The Bebop 2 serves no RTSP, despite what older libraries assume |
| FTP | media, flight plans, blackbox logs |
| Telnet | read-only shell, only after the drone's debug mode is enabled |
## Safety
Commands are classified by consequence. Observing and configuring are open;
anything that can spin a motor or change the flight envelope refuses until
`arm()` is called with a reason, and re-locks on a timer and on disconnect.
`Landing` and `Emergency` are deliberately never locked, because refusing to
land an airborne aircraft is the more dangerous answer.
`connect(target="sim")` runs everything against a protocol-accurate simulator,
which is where anything involving motion should be rehearsed.
## The simulator streams video
The simulator can push RTP/H.264 exactly as the aircraft does, so a viewer's
whole video path can be developed and measured without a drone. It answers the
handshake with `arstream2_server_stream_port: 5004`, sends nothing until
`ardrone3.MediaStreaming.VideoEnable` arrives with 1, then streams from its own
5004 to whatever `arstream2_client_stream_port` the client named, and stops on a
0, on a link loss, or at shutdown.
```bash
python -m mcbebop.sim --video clip.h264 # steady 30 fps
python -m mcbebop.sim --video clip.h264 --start-offset random --seed 7
python -m mcbebop.sim --video flight.rtpcap # a real capture, replayed
```
`MCBEBOP_SIM_VIDEO_SOURCE=clip.h264` does the same for `connect(target="sim")`.
Two kinds of source, and they are **different instruments**:
| Source | Pacing | Use it for |
|---|---|---|
| `.h264` Annex-B elementary stream | packetised here, steady frame rate | does the decoder work, does the renderer work |
| `.rtpcap` capture off the aircraft | the recorded inter-packet gaps, packet for packet | latency and jitter, bursts, loss behaviour |
Make the first from any video, at the resolution the aircraft streams:
```bash
ffmpeg -i anything.mp4 -t 10 -vf scale=856:480 -r 30 \
-c:v libx264 -preset ultrafast -pix_fmt yuv420p -g 30 -f h264 clip.h264
```
`-f h264` already writes Annex-B, so no bitstream filter is wanted;
`h264_mp4toannexb` converts the other direction and ffmpeg rejects it here.
Make the second from a real drone. Start the recorder first, because RTP is
connectionless and anything sent before the bind is gone, then enable video
from a session that holds the ARSDK link:
```bash
python -m mcbebop.media.capture flight.rtpcap --seconds 30 # binds 55004
```
`--start-offset random` is worth knowing about. It begins mid-GOP, which is
what a viewer switched on while the drone is already flying is handed, and
`--seed` makes a failure repeatable. Parameter sets repeat about once a second
on the packetised path, which is what lets a late joiner recover at all.
## Install
```bash
uvx mcbebop
claude mcp add mcbebop -- uvx mcbebop
```
## Licence
This package is MIT (`LICENSE`), and it vendors one third-party component:
`src/mcbebop/arsdk-xml/` is Parrot SA's own protocol definition, BSD-3-Clause
(`LICENSE-arsdk-xml`). It ships in both the sdist and the wheel because nothing
here can decode a single command without it. What that snapshot is and how it
differs from upstream is recorded in `arsdk-xml/PROVENANCE.md`.
So the distribution as a whole is `MIT AND BSD-3-Clause`.