Skip to main content

Match Images to Text (Research)

researchmcp-research_match_images_to_text

Find images that match arbitrary text by meaning — "find me more of this".

Two jobs, not one:

  • Illustration: you have an answer and want images that visually support it.
  • Discovery expansion: feed a synthesis paragraph back in (with report_ids omitted) and the hits point at adjacent reports you did not retrieve — co-occurring themes a keyword-shaped query would miss. Read the report_id and tags on each hit, not just the picture: this is a cheap way to widen a trend read after your first search round.

Differs from research_image_search in two ways:

  • The input is text (typically longer than a query — e.g. an already-generated answer or a paragraph of synthesis).
  • No date filters. Use report_ids to constrain scope, or omit it to let the search reach the whole corpus (what you want when expanding).

Match-only: the response is the raw search hits. The caller is responsible for any perceptual-hash dedup or LLM relevance filtering if desired.

Before presenting results, resolve the report_id values you cite — including any report this expansion surfaced — with research_report_links (one batched call) so the user gets clickable EDITED Research URLs. This follow-up is expected, not optional; use research_read_report when the user wants the article text rather than a link.

Ranking blends relevance with recency, plus the optional intent axes (article_types, topics, gender, season, year, moments) when supplied — none is required and unknown slugs are ignored. No date window is applied here, so year/moments bias ranking only and report_ids is the one thing that constrains scope. The score on each hit is this composite value.

Try queries like

"Oversized tailoring dominated the autumn runways.", "Retailers are leaning into minimalist, recyclable packaging this season.".

Parameters​

NameTypeRequiredConstraintsDescription
textstringyes—Free-form text — an answer, paragraph, or anything — to find images that match its meaning. Typically longer than a search query.
report_idsinteger[]no—Constrain the search to images from specific reports. Pass the report_id values from a previous search. Omit to search the full corpus.
kintegernodefault: 5 · 1–10Number of images to return.
article_typesstring[]no—Editorial format of the reports to favour; research_analyze_query can supply these.
topicsstring[]no—Subject matter to favour.
genderstring[]no—Audience to favour.
seasonstring[]no—Retail season to favour. A ranking signal only — never converted to a date filter.
yearstring[]no—Four-digit year(s) the query targets, e.g. ['2025'].
momentsstring[]no—Named retail moments the query targets.

Returns​

  • On success: {"result": {"total": int, "images": list[ImageHit]}} where each hit
  • carries report_id, title, date, score, url, caption,
  • copyright (image attribution string or null), format, image_id,
  • and tags — the parent report's taxonomy tags (article_types,
  • topics, gender, season, year, moments), so you can check the
  • audience/season of an expanded hit before grounding a claim in it.
  • total is the number of images returned (<= k).
  • On retrieval failure or invalid k: {"error": {code, message}}
  • code is invalid_input, upstream_unavailable,
  • internal (our own bad data; a retry returns the same error), or
  • not_entitled (authorization refused).
  • image_id is opaque and session-scoped: redeem it with research_get_image
  • in the same session, and never persist it — re-ingestion reassigns ids.

Try it​

Loading interactive widget…

Code examples​

curl -s https://mcp.edited.com/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-06-18" \
-H "x-api-key: $MCP_API_KEY" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "researchmcp-research_match_images_to_text",
"arguments": {
"text": "example",
"k": 5
}
}
}'

Input schema​

{
"type": "object",
"properties": {
"text": {
"description": "Free-form text — an answer, paragraph, or anything — to find images that match its meaning. Typically longer than a search query.",
"type": "string"
},
"report_ids": {
"description": "Constrain the search to images from specific reports. Pass the `report_id` values from a previous search. Omit to search the full corpus.",
"items": {
"type": "integer"
},
"type": "array"
},
"k": {
"default": 5,
"description": "Number of images to return.",
"maximum": 10,
"minimum": 1,
"type": "integer"
},
"article_types": {
"description": "Editorial format of the reports to favour; `research_analyze_query` can supply these.",
"items": {
"enum": [
"trend-analysis",
"trends-by-season",
"trends-by-city",
"street-style",
"retailer-messaging",
"buyers-guide",
"consumer-moments",
"hindsighting",
"consumer-strategy",
"forecasting",
"product-newness",
"collections-by-city",
"events-opportunities",
"messaging-calendars",
"assortment-pricing",
"site-merchandising-promotion"
],
"type": "string"
},
"type": "array"
},
"topics": {
"description": "Subject matter to favour.",
"items": {
"enum": [
"footwear",
"accessories",
"pricing-discounting",
"denim-fabric",
"color",
"bottoms",
"active-sports",
"subcultures",
"print-licensing",
"intimates-swimwear",
"tops",
"outerwear",
"details-trims",
"diversity-inclusion",
"dresses",
"sustainability",
"generation-demographic",
"knitwear",
"tailoring"
],
"type": "string"
},
"type": "array"
},
"gender": {
"description": "Audience to favour.",
"items": {
"enum": [
"men",
"women",
"children",
"mid-mature"
],
"type": "string"
},
"type": "array"
},
"season": {
"description": "Retail season to favour. A ranking signal only — never converted to a date filter.",
"items": {
"enum": [
"spring-summer",
"fall-winter",
"pre-spring",
"pre-fall"
],
"type": "string"
},
"type": "array"
},
"year": {
"description": "Four-digit year(s) the query targets, e.g. ['2025'].",
"items": {
"pattern": "^20[0-9]{2}$",
"type": "string"
},
"type": "array"
},
"moments": {
"description": "Named retail moments the query targets.",
"items": {
"enum": [
"black-friday",
"lunar-new-year",
"easter",
"halloween",
"international-womens-day",
"mothers-day-uk",
"mothers-day-us",
"4th-july-us",
"earth-month",
"festival-season",
"holiday-season",
"ramadan",
"valentines-day",
"back-to-college",
"back-to-school",
"fathers-day",
"occasion-season",
"spring-break-us"
],
"type": "string"
},
"type": "array"
}
},
"required": [
"text"
]
}

Output schema​

Describes both branches of the envelope — result on success, error on a refusal. Validate against this rather than pattern-matching the prose above; see Concepts → Response shape.

{
"type": "object",
"properties": {
"result": {
"properties": {
"total": {
"type": "integer"
},
"images": {
"items": {
"properties": {
"report_id": {
"type": "integer"
},
"title": {
"type": "string"
},
"date": {
"format": "date",
"type": "string"
},
"score": {
"type": "number"
},
"url": {
"type": "string"
},
"caption": {
"type": "string"
},
"copyright": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"format": {
"anyOf": [
{
"enum": [
"png",
"jpeg",
"webp",
"jpg"
],
"type": "string"
},
{
"type": "null"
}
]
},
"image_id": {
"type": "string"
},
"tags": {
"description": "The parent report's curated taxonomy tags, keyed by the public intent axes.\n\nPopulated on every search hit, browse row and report lookup so a consumer can\nsee a report's audience, season and subject matter *at discovery time* — a\ntitle alone does not say whether a \"SS27 Buyers' Guide\" is menswear or\nwomenswear, and grounding a womenswear claim in a menswear report is the\nfailure this prevents.\n\nThe values are drawn from the vocabulary the search tools' intent params\naccept, so a tag read off a hit can usually be threaded straight back in to\nnarrow a follow-up search. Two limits on that, both worth knowing before\ntreating a threaded tag as a guaranteed narrowing:\n\n- **The vocabularies are not identical.** These are curated report-side\n labels, so a value can exist here that the params do not recognise (prod\n carries ``moments: [\"pride\"]``, which is outside ``MomentsParam``'s enum).\n Threading such a value is ignored by ``normalise_intent`` or rejected by the\n param schema — never a silent narrowing.\n- **``year`` and ``moments`` are not pure ranking axes.** Unlike the other\n four, they also derive the publish-date window when no explicit dates are\n given (see ``retrieval/date_window.py``), so threading one back moves the\n filter as well as the boost.\n\nEvery axis is always present on the model; an untagged axis is an empty list.\nCoverage is uneven and heavily skewed by report class — buyers' guides are\nclose to fully tagged for ``gender``, while a large minority of the corpus\ncarries no taxonomy at all — so an empty axis is common and means \"not\ncurated\", never \"out of scope\". (One near-exception: a topic tag that exists\nonly as an all-encompassing label is dropped rather than surfaced, matching\nwhat the scorer sees — see ``canonicalise_topics``.)\n\nTwo exceptions, both id re-fetches: the chunk and image indices carry no\ntaxonomy tags and ``get_chunk`` / ``get_image`` do not spend a second query\nresolving them, so the field stays at its empty default there — and the\nresearch_get_chunk / research_get_image tools serve ``ChunkRow`` / ``ImageRow``,\nwhich have no ``tags`` field at all, rather than let an empty axis read as\n\"this report is untagged\". So `tags` is absent from those two tools' published\nschemas; wherever it *is* published, an empty axis does mean untagged.",
"properties": {
"article_types": {
"items": {
"type": "string"
},
"type": "array"
},
"topics": {
"items": {
"type": "string"
},
"type": "array"
},
"gender": {
"items": {
"type": "string"
},
"type": "array"
},
"season": {
"items": {
"type": "string"
},
"type": "array"
},
"year": {
"items": {
"type": "string"
},
"type": "array"
},
"moments": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
}
},
"required": [
"report_id",
"title",
"date",
"score",
"url",
"caption",
"copyright",
"format",
"image_id"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"total",
"images"
],
"type": "object"
},
"error": {
"type": "object",
"description": "Present instead of `result` when the call was rejected. Branch on `code`; never string-match `message`.",
"properties": {
"code": {
"type": "string",
"enum": [
"invalid_input",
"not_entitled",
"upstream_unavailable",
"internal"
]
},
"message": {
"type": "string"
},
"field": {
"type": "string",
"description": "Offending parameter, when the failure is attributable to one."
},
"details": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"code",
"message"
]
}
},
"oneOf": [
{
"required": [
"result"
]
},
{
"required": [
"error"
]
}
],
"x-fastmcp-wrap-result": true
}