Closes #1220: support media lookup by filename or URL in MCP media abilities - #1225
Closes #1220: support media lookup by filename or URL in MCP media abilities#1225Miraeld wants to merge 2 commits into
Conversation
The optimize-media, restore-media and get-media-status abilities only accepted a numeric attachment ID, so a caller had to open the Media Library and copy an ID out of the URL before asking for anything. Add two interchangeable inputs, media_filename and media_url, resolved by MediaResolver::resolve_id(). Filenames are matched on the exact base name of _wp_attached_file, so hero.jpg no longer also matches my-hero.jpg. Next-gen and thumbnail URLs peel their suffixes off and resolve to the original attachment. An ambiguous filename returns the matching IDs instead of acting on the first match.
Not up to standards ⛔🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 25 |
| Duplication | 0 |
🔴 Coverage 11.48% diff coverage · +0.01% coverage variation
Metric Results Coverage variation ✅ +0.01% coverage variation (-0.10%) Diff coverage ❌ 11.48% diff coverage (50.00%) Coverage variation details
Coverable lines Covered lines Coverage Common ancestor commit (6c28250) 20312 1540 7.58% Head commit (93740a6) 20396 (+84) 1548 (+8) 7.59% (+0.01%) Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch:
<coverage of head commit> - <coverage of common ancestor commit>Diff coverage details
Coverable lines Covered lines Diff coverage Pull request (#1225) 122 14 11.48% Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified:
<covered lines added or modified>/<coverable lines added or modified> * 100%
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Codacy ingests coverage from the unit suite only, so the integration tests for the resolver are invisible to it. Unit-cover the paths that do not need a database: identifier precedence, the URL lookup, and every unusable-input shape.
|
Note on the one red check — Codacy Diff Coverage.
I deliberately did not add a query-factory injection point just to make the DB-bound branch So this is a gate mismatch rather than untested code. Happy to either wire the integration suite into |
Description
Fixes #1220
The MCP media actions only accepted a numeric attachment ID, so
Optimize hero-banner.jpgwas notpossible: the user had to open the Media Library, edit the image and copy the ID out of the browser
URL first.
imagify/optimize-media,imagify/restore-mediaandimagify/get-media-statusnow alsoaccept
media_filenameandmedia_url, so an assistant can act on the identifier the user actuallyhas.
Type of change
Detailed scenario
What was tested
Automated — integration (
--group MediaResolver, 20 tests / 48 assertions), against a realdatabase and real
WP_Query: resolution by ID, by file name, case-insensitively, by relative path(
2026/08/hero.jpg), by URL, and by derivative URLs (hero.jpg.webp,hero.jpg.avif,hero-300x200.jpg,hero-300x200.jpg.webp). Plus the failure paths: unknown file name, unknown URL,ambiguous file name (asserting the candidate IDs appear in the message), and every unusable-input
shape (
[],media_id: 0,media_id: -5, blank URL, blank file name). One test specifically pinsthat
hero.jpgdoes not resolve tomy-hero.jpgorhero.jpg.bak.Automated — integration, per ability: all three abilities register
media_filenameandmedia_urland no longer mark any input as required.imagify/get-media-statusexecuted withmedia_filenamereturns output identical to the same media bymedia_id.imagify/restore-mediareaches its restore logic on a resolvable file name and reports the unknown one explicitly.
Automated — unit: 490 unit tests pass, including the existing media-ability tests updated for the
new error message.
Manual, on a real site (WP 7.0.2, Local, populated Media Library): for three real attachments,
media_filename,media_urland a next-gen…png.webpURL each resolved to the correct attachmentID, and
imagify/get-media-statusreturned byte-identical output whether called withmedia_filenameormedia_id.composer phpcsandcomposer run-stanare clean.How to test
example
hero-banner.jpg.imagify/get-media-statuswith{"media_filename": "hero-banner.jpg"}and then with{"media_id": <that attachment's ID>}— the two responses must be identical.{"media_url": "<the attachment URL>"}, then again with.webpappended to that URL— both must resolve to the same media.
name both attachment IDs.
{}: the error must list the three accepted inputs.imagify/optimize-mediaandimagify/restore-media.Affected Features & Quality Assurance Scope
The three MCP media abilities.
media_idbehaviour is unchanged, includingimagify/restore-media'sexisting fallback to the
custom-folderscontext. No non-MCP code path is touched: the Media LibraryUI, bulk optimization and the upload flow are untouched.
Technical description
Documentation
Imagify\Abilities\MediaResolver::resolve_id( array $args )returns an attachment ID or aWP_Error.Precedence is
media_id, thenmedia_url, thenmedia_filename; amedia_idof0or less countsas absent, so the next identifier is tried. Each ability calls it first and converts a
WP_Errorintoits own
status: "error"output shape, so no output schema changed.media_urlgoes throughattachment_url_to_postid(). Because a front-end URL often points at aderivative rather than the original, a miss retries as a file-name lookup after peeling off the
next-gen extension and the
-300x200dimension suffix, sohero-300x200.jpg.webpstill findshero.jpg.media_filenamenarrows candidates with aLIKEon_wp_attached_file, then keeps only the rowswhose own base name matches exactly and case-insensitively. That second pass is the important part: a
bare
LIKE '%hero.jpg%'also matchesmy-hero.jpgandhero.jpg.bak, which would mean optimizing orrestoring the wrong file. A full relative path is accepted and reduced to its base name.
Ambiguity is never resolved silently. When several attachments share a name the error lists their IDs
so the caller can retry with
media_id.The two new schema properties come from
MediaResolver::get_input_schema_properties()so the threeabilities cannot drift apart.
docs/api/mcp.mdgains an "Identifying a media" section documentingprecedence, the derivative-URL fallback and the three error codes.
New dependencies
None. Uses
attachment_url_to_postid()andWP_Query.Risks
The file-name lookup adds one
WP_Querywith a postmetaLIKE, which cannot use an index on thevalue. It is bounded:
posts_per_pageis capped at 20 viaMAX_CANDIDATES,no_found_rowsskips thecount query, and it only runs on an explicit one-shot MCP call — never in a page render or a bulk
loop. It is also skipped entirely whenever
media_idis supplied, so existing callers pay nothing.Dropping
media_idfromrequiredmeans a caller can now send no identifier at all. That is thepoint of the change, and the resolver answers with an explicit message naming the three accepted
inputs rather than falling through to a vague failure.
The derivative-URL fallback is a deliberate best-effort guess and only runs after
attachment_url_to_postid()has already failed, so it cannot change the result for a URL thatresolves normally. If the guess is itself ambiguous, the ambiguity error applies as usual.
Mandatory Checklist
Code validation
Code style
Unticked items justification
All mandatory items apply and are done.
Additional Checks