Repository navigation
Releases
onvif-mcp v0.2.1
Fixed
ptz.move_relativefailed on strict (gSOAP) cameras: onvif-go v1.2.0 wrote thePanTiltandZoom
children of the move vector outside the ONVIF schema namespace, and a Reolink TrackMix answered every
move withtag name namespace mismatch in element 'PanTilt'. Fixed in onvif-go v1.2.1, which this
release requires. The same fix coversAbsoluteMoveandContinuousMovein the library.
Verified
- The
hardwaretier passed on a physical camera for the first time: the read matrix on five cameras
(Axis, Reolink, three Bosch) and the relative nudge pair withptz.stopon the Reolink TrackMix.
Preset moves (ptz.goto_preset) have still not been run on hardware.
onvif-mcp v0.2.0
Two gated PTZ tools over stdio and a read-only streamable HTTP transport. Not yet verified on a
physical camera: the hardware tier for ptz.move_relative and ptz.stop has not been run.
Added
- HTTP hardening from review: JSON-RPC batch arrays are refused with 400 (one 64 KiB batch
oftools/listcalls could be expanded into a roughly 40 MB reply); the tool name in
the HTTP audit line is truncated to 64 bytes; the policy resource reports
control_allowedas false over HTTP; the TLS minimum-version test now genuinely
exercises the server, and shutdown has tests for 503 +Connection: closewhile draining
and for closing connections still open past the deadline. - Streamable HTTP transport, read-only:
-transport httpwith-listen(default
127.0.0.1:8808),-http-tokens,-http-allow-remote,-http-allowed-hosts,
-tls-certand-tls-key. Stateless, JSON responses, HTTP/1.1 only. Authentication is
mandatory: static bearer tokens held as SHA-256 digests in a mode-600 file, one uniform
401 for every failure, constant-time comparison.Hostis checked against an allowlist
on every bind and anyOriginheader is refused. A non-loopback bind needs
-http-allow-remote, in-process TLS (1.2 or newer) and allowed hosts. Bounded
connections (total and per remote IP), timeouts, header and body sizes, requests in
flight, tool calls in flight (total and per token) and per-call deadline; single-phase
drain on shutdown with a second signal ending the process. Details and the limits table
are in the README ("Serving over HTTP"). -new-token ID: print a fresh 256-bit token and its token-file entry, then exit.- Per-token
grantlist in the token file:camera.snapshot,camera.discoverand
stream.inspectare available over HTTP only to tokens granted them, and are hidden from
tools/listotherwise. make demo-http: the virtual camera plus the HTTP server on loopback, read-only, with a
throwaway token.- Tool-call audit line on HTTP (
tool,caller,transport,outcome) and an access log
with the token id; tokens, digests andAuthorizationvalues are never logged. ptz.move_relative(write, gated): nudge a PTZ camera by a small relative amount in the ONVIF
generic translation space, with the space URI set explicitly on each vector. Each axis is capped at
0.25 per call by the server; larger values are refused, not clamped, with aninvalid_argument
that names the field, the range, the source and a corrected call. Dry run by default, rate limited
per device (shared withptz.goto_preset), sent once and never retried, audited with the typed
numbers sent. Relative moves do not consult the camera's reported limits.ptz.stop(halt, gated): stop pan, tilt and zoom on a profile, then read the status and report
confirmed_idleorsent_unconfirmed; a stop that cannot be delivered is an error. It has no dry
run, is never rate limited, does not need the audit log, and runs detached from the request and
from shutdown. Bursts are bounded per device (two rounds in flight, profile tokens validated against
the camera's own list). Stop is per profile.- A third risk class,
RiskHalt, ininternal/safety, withPolicy.EvaluateHalt,HaltInputand
HaltAnnotations; aNonIdempotentregistration option for writes that compound; a typed
WithMotionaudit option. - The
onvif://server/policyresource names the new tools, listshalt_tools, and saysptz.stophas
no dry run and is not rate limited. - Shutdown: a second SIGINT or SIGTERM inside 5 seconds of the first is held so it cannot abort a
dispatched stop or a move in flight; a signal after that ends the process at once. - An opt-in
hardwaretest for a relative nudge pair andptz.stop(ONVIF_MCP_HW_NUDGE); it has
not been run against hardware. - Tests that pin the go-sdk v1.7.0 handler-context and shutdown behaviour this design relies on.
- A golden for the
onvif://server/policyresource, with control on and off.
Fixed
ptz.stopsends Stop even when the camera's profile list cannot be read for any reason (timeout,
dropped connection, garbage reply); the profile read is best effort and gets half the attempt.ptz.stopno longer reports a stop as not sent when a round may already have dispatched it (caller
context ended, round panicked, or a later attempt failed after an earlier one sent); the audit
record follows. Round time is capped inside the halt budget.ptz.stoprounds are keyed by camera id as well as device, so cameras sharing an endpoint with
different credentials never share a round.ptz.move_relativeadds a hint on a SOAP fault about the explicit translation space; validation
timeouts no longer mention a preset; theptz.stopdescription no longer claims it stops motion
started elsewhere.
Changed
- Behaviour change for embedders and tests, none for stdio users: the write guard now
fails closed on identity.Policy.EvaluateandPolicy.EvaluateHalttake the request
context and refuse (caller_unidentified/caller_not_grantedin the audit trail,
with one agent-facing message) unless it carries a caller with control; the check runs
before the camera lookup, so it reveals nothing about camera ids. The stdio server
supplies the operator explicitly.mcpserver.Newwithout a caller source refuses
tools/callandresources/read. - The policy resource on HTTP reports
read-onlywith no gated tools and no advice to pass
--enable-control, identically for every caller. - Handler panics in HTTP tool calls are converted to an internal tool error instead of
ending the process. - With control on,
tools/listnow has thirteen tools (was eleven). With control off it is unchanged. - The server reports version
v0.2.0from source.
Not included
Continuous and absolute PTZ moves, a speed parameter, a cumulative travel budget for relative moves,
and verification on physical cameras. A looping model can still issue up to 30 capped steps a minute
per device.
Motion or any other state-changing tool over HTTP (write tools are stdio-only in this
release), OAuth or mTLS, browser clients, token or certificate hot-reload, and a per-token
camera allowlist.
onvif-mcp v0.1.0
First release: a Model Context Protocol server (Go, stdio transport) that exposes ONVIF IP camera
inspection and guarded PTZ control as typed tools for an LLM agent.
Added
- Eleven tools, ten of them read-only:
camera.list,camera.discover,camera.describe,
media.profiles,media.stream_uri,stream.inspect(RTSP OPTIONS and DESCRIBE: codecs, tracks,
best-effort resolution),camera.snapshot(one JPEG or PNG inline, capped by
-snapshot-max-bytes, default 1 MiB),imaging.settings,ptz.status,ptz.presets, and
ptz.goto_preset, the only tool that changes a camera. - The
onvif://server/policyresource, which describes the current mode, the gated write tool and
each camera's grant. - Read-only by default: a real move needs
-enable-controland a camera that opts in with
"control": true; with control off,ptz.goto_presetis not intools/list. Writes default to a dry
run, are rate limited per device (one per 2 seconds), are sent once and never retried, and every
attempt is audited to stderr. - Credentials come from the mode-600 config file or
ONVIF_MCP_<ID>_PASSWORD, never from tool
arguments, results, logs or errors. - Camera-supplied addresses are treated as hostile: service, stream and snapshot hosts must match
the configured endpoint unlesstrust_advertised_hostsis set, and credentials are never sent to
another host. - Errors are a typed envelope (
code,retryable,message,hint) instead of raw SOAP. make demoruns a virtual ONVIF camera (onvif-go's server package) on 127.0.0.1:18080 together
with the server, so the tools can be tried without hardware.- An opt-in
hardwarebuild-tag test tier for real cameras (make hardware); CI never runs it. -versionprints the version and exits; release binaries report the tag they were built from.- Release process: static cross-builds for linux, darwin and windows on amd64 and arm64, a
SHA256SUMSfile and build provenance attestations (seeRELEASING.md).
Not included
ONVIF events, continuous, relative or absolute PTZ, imaging or device writes, video serving, any
transport but stdio, and verification on physical cameras.