WP Manifestindependent plugin directory
manifest / media / ars-nova-media

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

0stars
0forks

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.zip

Readme

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:

  • sha256 is mandatory for b64 and checked before anything is written. Mismatch → HTTP 422, no file, no attachment, both hashes in the error.
  • sha256 is optional but honoured for url and drive_file_id.
  • The response always returns the sha256 of what was stored, plus sha256_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

TagPublished
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.