Ars Nova Media (Remote Upload)
Admin-only REST endpoint that lets the Ars Nova WordPress MCP connector create media library attachments without shell or filesystem access.
by Ars Nova (Jonathan Raabe) + Claude · github.com/arsnovasingers/ars-nova-media
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/arsnovasingers/ars-nova-media/archive/refs/heads/main.zipReadme
ars-nova-media
Ars Nova Singers — custom WordPress plugin. Canonical source (private use; not distributed).
Lets the Ars Nova WordPress MCP connector put files into the media library. Before this
existed there was no path: wp_upload_media reads file_path from the connector's own
container, which is neither the Claude sandbox nor anyone's PC, and there was no
sideload-from-URL. Images had to be uploaded by hand through wp-admin.
Endpoints
Registered into the ans-ops/v1 namespace — not a new one. The connector's ans_rest_call
is restricted by design to ars-nova/v1, ans-ops/v1, ans-notes/v1, ansg/v1, so a new
namespace would be unreachable. WordPress permits any plugin to register into any namespace.
GET ans-ops/v1/media/status
Sanity check. Reports version, site, is_production, whether the Google connector is available,
the host allow-list, and the size ceiling.
POST ans-ops/v1/media/upload
Creates an attachment. Provide exactly one source:
| Param | Notes |
|---|---|
b64 |
Base64 file bytes. Requires sha256. Practical only for small files, and see "Integrity" below before reaching for it. |
url |
Must be on the ars-nova-ops allow-list (the site itself, GitHub, Google Drive hosts). |
drive_file_id |
Google Drive file id. Fetched privately via the Ars Nova Google Connector's service-account token — the file never has to be made public, it only has to be shared with the service-account email. |
Optional: filename (required for b64; inferred otherwise), title, alt_text, caption,
description, post (attach to a post/page id), confirm_production.
Production guard. On arsnovasingers.org the call is refused unless
confirm_production=true, matching ars-nova-ops' posture and PROJECT_RULES §"LIVE is
read-only unless explicitly approved".
The response always includes site and is_production, because a connector's name is not
proof of which install it reached.
Integrity — read this before using b64
Found the hard way on 2026-08-14, during this plugin's own first test.
A 3,231-byte PNG was uploaded twice via b64 by an agent relaying the base64 in a tool call.
It "succeeded" both times. It arrived as 1,527 bytes once and 1,341 bytes the next —
truncated, differently each time, with no error at any layer. The truncated file still had a
valid PNG header, so WordPress still reported 600x400, and the only outward sign was an empty
sizes array where thumbnails should have been.
The same agent transmitted an 8,656-character base64 zip perfectly in the same session. The difference is entropy, not length: a zip of text files compresses to recognisable, patterned base64; PNG pixel data does not. High-entropy base64 is exactly what a language model reproduces badly, and nothing in the HTTP or REST layer notices.
Hence:
sha256is mandatory forb64and checked before anything is written. Mismatch → HTTP 422, no file, no attachment, both hashes in the error.sha256is optional but honoured forurlanddrive_file_id.- The response always returns the
sha256of what was stored, plussha256_verified, so a caller that forgot to pre-declare can still check afterwards.
Prefer url or drive_file_id. Neither routes the bytes through the caller, so neither can
suffer this. b64 exists for small, verifiable payloads and for environments with no other path.
Why a separate plugin
ars-nova-ops is the plugin installer and runs on a live storefront. Folding an unrelated
capability into it would mean every media change redeploys the component that can install and
delete plugins. Kept apart, this can be updated — or removed outright — without touching it.
It reuses ans_ops_is_production(), ans_ops_allowed_hosts() and ans_ops_host_allowed() when
ars-nova-ops is active, so there is one definition of those rules. Fallback copies exist only
so this plugin is never silently unguarded.
Known limits
- Google-native files (Docs/Sheets/Slides) have no raw bytes and are refused with a clear error. Export them first.
- Size ceiling 64 MB, filterable via
ansm_max_bytes. PHP/nginx limits still apply and are usually lower. wp_upload_bits()always writes to the current month's folder and mints a new attachment id — the same behaviour as any other upload.
Read the full README on GitHub →
Releases
| Tag | Published |
|---|---|
| v1.1.0 | Aug 20, 2026 |
These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.