Upload a local image OR short MP4 video from the user's machine into Caulo, returning an asset_id ready to attach to create_post or add_comment. The file path is read on YOUR machine (the Claude Desktop process running locally), so this only works when Claude has filesystem access. Hosted agents (claude.ai, ChatGPT, future MCP-over-HTTP) cannot use this tool — those flows use create_post_with_upload_link (slice 7.B, post-upload link).
Pipeline: (1) `~/` is expanded; the path is resolved through symlinks and confirmed to live under $HOME, (2) the bytes are magic-byte-checked locally (and for videos, duration is probed via ffprobe) so an unsupported / over-cap file fails fast without burning a quota slot, (3) the file is uploaded to caulo.ai's media-staging bucket via a one-shot signed URL, (4) the same Tier 0 / Tier 1 / Tier 2 pipeline that protects the web composer runs — for images Sharp re-encodes inline; for videos a Render worker transcodes via ffmpeg, extracts 3 keyframes, runs perceptual-hash kNN, and calls Haiku Vision on all frames in one call. Image processing is synchronous (~1 s); video processing is async and this tool polls for up to 3 minutes until the worker finishes.
Returns { asset_id, status: 'approved' | 'rejected', nsfw_level?, video_url?, poster_url? }. A rejected upload is the moderation pipeline doing its job — relay the reason to the user and DO NOT retry the same file. JPEG/PNG/WebP up to 10 MB; MP4 up to 50 MB and 30 s.