Files
mcbebop/pyproject.toml
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

129 lines
3.8 KiB
TOML

[project]
name = "mcbebop"
version = "2026.10.02"
description = "MCP server for the Parrot Bebop 2 drone: telemetry, camera, files, and every ARSDK command"
readme = "README.md"
requires-python = ">=3.12"
# Our own code is MIT. The vendored arsdk-xml/ is Parrot SA's, BSD-3-Clause,
# and it ships in both artifacts because the package cannot decode a single
# command without it, so the distribution as a whole is both.
license = "MIT AND BSD-3-Clause"
license-files = ["LICENSE", "LICENSE-arsdk-xml"]
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
keywords = ["mcp", "drone", "parrot", "bebop", "arsdk", "fastmcp"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Scientific/Engineering",
]
dependencies = [
# 4.x is the current family; <5 because FastMCP has moved import paths
# across majors before (mcvnc was broken by the 3 -> 4 move).
"fastmcp>=4.0.10,<5",
"pydantic-settings>=2.7",
"zeroconf>=0.147", # mDNS discovery of _arsdk-090c._udp
"pillow>=11.0", # downscale camera frames before they reach the model
]
[project.urls]
Homepage = "https://warehack.ing"
Repository = "https://git.supported.systems/rsp2k/mcbebop"
[project.scripts]
mcbebop = "mcbebop.server:main"
[build-system]
requires = ["uv_build>=0.11.3,<0.12.0"]
build-backend = "uv_build"
[tool.uv.build-backend]
# Err toward excluding. This repo sits next to a real aircraft, and `captures/`
# holds video of the owner's home plus `ckcm.bin` system logs that carry the
# serial, the CPU id and a real GPS fix together. None of it may ever reach an
# sdist, which is immutable and mirrored within minutes of upload.
#
# `.gitignore` does NOT protect this: it governs git, and source-exclude
# governs the sdist. A file can easily be in one and not the other, so the
# patterns below are deliberately broader than the directories that exist
# today -- a capture dropped into a new directory next year is still excluded.
# test_packaging.py asserts both halves stay in agreement.
source-exclude = [
# Operator-private context and anything credential-shaped.
"CLAUDE.md",
".env",
".env.*",
".mcp.json",
".claude",
".claude/**",
# Dev-only trees. Tests carry fixtures copy-pasted from a live aircraft.
"tests",
"tests/**",
"docs",
"docs/**",
"scripts",
"scripts/**",
"*.ipynb",
# Captures off the aircraft, at any depth, plus log dumps.
"captures",
"captures/**",
"**/captures/**",
"**/logs/**",
"ckcm*",
"**/ckcm*",
# Recorded media and binary blobs by extension, so a stray file in a
# directory nobody thought to list is still caught.
"*.rtpcap",
"*.raw",
"*.h264",
"*.bin",
"*.pcap",
"*.pcapng",
"*.mp4",
"*.jpg",
"*.jpeg",
"*.png",
"*.wav",
"*.ulg",
"**/*.rtpcap",
"**/*.raw",
"**/*.h264",
"**/*.bin",
"**/*.jpg",
"**/*.jpeg",
"**/*.png",
# Caches and build output. .pyc files embed the absolute build path, which
# leaks a username and the local directory layout.
"**/__pycache__",
"**/__pycache__/**",
"*.pyc",
"**/*.pyc",
".pytest_cache",
".pytest_cache/**",
".ruff_cache",
".ruff_cache/**",
".venv",
".venv/**",
"dist",
"dist/**",
"build",
"build/**",
"*.egg-info",
]
[dependency-groups]
dev = ["ruff>=0.16", "pytest>=8.0", "pytest-asyncio>=0.25"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
markers = ["drone: needs a real Bebop 2 on the network (run with '-m drone')"]
addopts = "-m 'not drone'"
[tool.ruff]
line-length = 110
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I", "W", "UP", "B", "SIM", "RUF"]