Skip to main content
Glama
Pexafy Stock Photos

Pexafy Stock Photos

Official

pexafy-mcp

CI License: MIT

AI 어시스턴트를 위한 스톡 포토 검색. 이 MCP 서버는 Claude Desktop, ChatGPT 또는 모든 MCP 클라이언트가 로열티 프리 이미지 라이브러리를 검색할 수 있게 해 줍니다. 자연어로 장면을 설명하거나, 예시 이미지를 주거나, "이런 느낌"으로 요청할 수 있으며, 결과를 대화 안에서 썸네일 그리드로 렌더링합니다.

원격 MCP, OAuth, 붙여넣을 API 키 없음, 3가지 도구, 이미지 인라인 렌더링.

The Pexafy result grid, rendered inline in a Claude conversation


사용법 (설치할 필요 없음)

호스팅된 서버가 다음 주소에서 실행됩니다:

https://mcp.pexafy.com/mcp

streamable-http 전송을 지원하며 OAuth 2.1로 인증합니다 — 브라우저 창에서 Pexafy에 로그인하면 커넥터가 자체 자격 증명을 받습니다. 생성해서 JSON 파일에 붙여넣거나 나중에 회전해야 할 API 키가 없습니다.

Claude (웹 및 데스크톱)

  1. Settings → Connectors를 엽니다. (Team/Enterprise에서는 소유자가 Organization settings → Connectors에서 한 번만 추가합니다.)

  2. Add custom connector를 클릭합니다.

  3. https://mcp.pexafy.com/mcp를 붙여넣고 확인합니다.

  4. 열린 창에서 Pexafy에 로그인합니다. 완료 — Claude에게 사진을 요청하면 됩니다.

Claude Code

claude mcp add --transport http pexafy https://mcp.pexafy.com/mcp

다른 MCP 클라이언트

같은 URL에 streamable-http 전송으로 연결하세요. OAuth를 구현하지 않는 클라이언트는 Pexafy API 키를 Authorization: Bearer <key> 또는 x-api-key: <key>로 보내 인증할 수 있습니다. 키는 대시보드에서 발급받으세요.

활성 상태 확인: GET /health (공개, 인증 불필요).

또한 공식 MCP 레지스트리에 com.pexafy/pexafy-mcp로 등록되어 있으며, Smithery에도 있습니다 — 호스트된 게이트웨이 URL을 선호하는 클라이언트를 위한 옵션입니다.

비용

무료 플랜은 커넥터 1개로 월 5,000회 검색을 지원하며 — 일반적인 사용에 충분하고 카드가 필요 없습니다. 더 높은 등급은 가격 페이지에 있습니다. 한도에 도달하면 불명확한 오류로 실패하는 대신, 어시스턴트가 채팅에서 알려줍니다.


Related MCP server: Wikimedia-Search-Images

도구

읽기 전용 도구 3가지입니다. 쓰기 권한이나 계정 변경 기능은 없습니다.

search_photos — 의미 기반 텍스트 검색

장면을 완전한 문장으로 설명하세요. Pexafy는 의미 기반이므로, 키워드보다 문장이 더 좋습니다. 모든 매개변수는 선택 사항이지만, q 또는 필터를 하나 이상 전달해야 합니다.

매개변수

타입

설명

q

string

장면을 자연어로 표현. 최대 500자.

color_name

string

다음 중 하나: red, orange, yellow, green, blue, purple, pink, brown, black, white, gray, teal, beige, gold, navy. color_hex와 함께 사용 불가.

color_hex

string

예: #1E90FF. color_name과 함께 사용 불가.

color_tolerance

integer

0(정확) ~ 255(느슨함). 기본값 20. color_hex와 함께만 사용.

orientation

string[]

landscape, portrait, square.

source

string[]

Unsplash, Pexels, Pixabay, Kaboompics, Burst, StockSnap, Picjumbo, Skitterphoto, NegativeSpace.

license_type

string[]

free, cc0.

photographer

string

정확한 사용자 이름.

after_date

string

YYYY-MM-DD. 해당 날짜 이후 게시된 사진.

cursor

string

이전 응답의 pagination.next_cursor.

search_photos_by_image — 예시 이미지로 시각 검색

참조 이미지와 비슷한 사진을 찾습니다. "이 사진 느낌인데 밤에"처럼 텍스트로 추가 조정할 수 있습니다.

매개변수

타입

설명

image_url

string

참조 이미지의 공개 http(s) URL.

image_file

object

업로드를 지원하는 호스트가 자동으로 채움 (예: ChatGPT).

image_base64

string

프로그래밍 방식 클라이언트용 원본 base64 바이트.

q

string

이미지와 함께 쓸 텍스트 ("손을 들은 모습으로").

text_alpha

number

이미지 대비 q의 가중치.

orientation, source, color_name, license_type, photographer, after_date

string

위와 동일한 필터.

cursor

string

페이지 네이션 토큰.

image_url, image_file 또는 image_base64 중 하나는 필수입니다. 이미지는 서버 측에서 가져옵니다. 최대 20MB.

get_similar_photos — 더 비슷한 사진

매개변수

타입

설명

photo_id

string

필수. 이전 결과에서 가져온 사진의 UUID.

cursor

string

페이지 네이션 토큰.

반환되는 것

모든 사진에는 id, 여러 크기 URL, 크기, 주요 색상, 방향, 출처, 라이선스, 작가, 그리고 크레딧을 위한 attribution 문자열이 포함됩니다 — 어시스턴트가 결과를 단순히 나열하지 않고, 그 결과를 바탕으로 추론할 수 있을 만큼의 정보를 갖습니다.

결과는 #1, #2, …로 번호가 매겨져서, 대화에서 말하듯 사진을 가리킬 수 있습니다. 복사할 id가 없습니다:

Asking for more photos like #1, and the assistant reasoning over the new set

MCP Apps를 지원하는 클라이언트에서는 썸네일을 클릭하면 전체 메타데이터가 포함된 세부 패널이 열립니다. 추가로 호출할 필요 없이 — 모든 정보가 이미 도구 결과에 들어 있습니다:

The detail panel: photographer, source, resolution, licence, dominant colour, orientation and description


자체 호스팅

그럴 필요는 없습니다 — 호스팅된 서버가 의도된 진입 방법입니다. 다만 이 서버는 Pexafy API의 얇고 단순한 클라이언트이므로, 자체 키로 직접 실행할 수도 있습니다.

Python 3.12+ 필요.

git clone https://github.com/Pexafy/pexafy-mcp.git && cd pexafy-mcp
./run.sh setup        # venv + editable install + seed .env
# edit .env — set PEXAFY_API_KEY
./run.sh dev          # stdio, for Claude Desktop / Claude Code

설치된 콘솔 스크립트(pip install .)를 사용:

pexafy-mcp                             # stdio (default)
PEXAFY_MCP_TRANSPORT=http pexafy-mcp   # remote Streamable HTTP

Claude Desktop / Claude Code를 stdio로 사용:

{
  "mcpServers": {
    "pexafy": {
      "command": "pexafy-mcp",
      "env": { "PEXAFY_API_KEY": "pexafy_api_…" }
    }
  }
}

Docker를 HTTP로 — docker-compose.example.yml 참고:

docker compose -f docker-compose.example.yml up -d
curl localhost:8765/health

이미지 자체의 기본값은 stdio인데, 이는 MCP 클라이언트가 컨테이너를 구동하는 데 쓰는 전송 방식이라 바로 사용할 수도 있습니다:

docker run -i --rm pexafy-mcp

이 방식은 API 키나 네트워크 없이 initialize와 tools/list에 응답합니다. 도구 정의는 내장된 OpenAPI 스냅샷에서 가져옵니다. 키는 검색을 실행할 때만 필요합니다. HTTP 서비스는 전송만 바꾸면 되는데, 두 compose 파일 모두 그렇게 합니다.

설정

모든 설정은 환경 변수이며, 모두 선택 사항입니다. 아무것도 설정하지 않으면 pexafy-mcp는 stdio로 시작하고 오프라인 상태에서 initialize와 tools/list 에 응답합니다. 두 가지만 알면 됩니다.

변수

기본값

용도

PEXAFY_MCP_TRANSPORT

stdio

로컬 클라이언트는 stdio, 원격 서비스는 http

PEXAFY_API_BASE_URL

http://localhost:8000

Pexafy API 루트 — https://api.pexafy.com 또는 자체 배포를 지정.

나머지는 컨테이너를 실행하는 사람보다 배포 환경에 더 관련된 것이며 [env.example]... (원문: .env.example)에 있습니다: 클라이언트가 키를 보내지 않을 때 stdio에서 쓸 기본 PEXAFY_API_KEY, 인라인 그리드 썸네일 서명에 PEXAFY_THUMB_BASE_URL와 PEXAFY_THUMB_HMAC_SECRET, 그리고 OAuth 리소스 서버로 HTTP 전송을 실행하기 위한 MCP_RESOLVE_SECRET와 함께 PEXAFY_OAUTH_*. 어느 것도 서버를 시작하는 데 필요하진 않습니다.


동작 원리

src/pexafy_mcp/
├── server.py     # entry point: builds the server, wires hooks, custom tools, /health
├── tooling.py    # tunes the OpenAPI-derived tools for an LLM (descriptions, value sets)
├── widget.py     # MCP Apps UI resource — the inline result grid (self-contained HTML)
├── previews.py   # signs the thumbnail URLs injected into each result
├── limits.py     # turns plan-limit (429) responses into in-chat upgrade nudges
├── auth.py       # per-user auth: OAuth Resource Server or forwarded API key
└── assets/       # vendored, shipped with the package:
    ├── openapi.json          # OpenAPI snapshot the tools are generated from
    ├── facets.json           # evolving source/license value sets
    └── ext_apps_bundle.js    # @modelcontextprotocol/ext-apps SDK (inlined in the widget)
  • 도구는 FastMCP.from_openapi()로 Pexafy OpenAPI 스펙에서 생성됩니다. 그래서 API가 유일한 소스가 되며, tooling.py가 이를 LLM용으로 다듬습니다 — 검색 핵심만 남기고 모델을 오도할 수 있는 매개변수를 제거하고, 닫힌 값 집합을 인라인해서 패싯 조회(facet lookup)가 전혀 필요가 없게 합니다.

  • build_server()가 모든 것을 조립합니다. 패키지를 가져오는 것은 부작용이 없고 네트워크 I/O를 하지 않습니다: 내장된 assets/openapi.json과 assets/facets.json을 읽기만 합니다. prepare.sh는 이 파일들을 다시 생성합니다.

  • search_photos_by_image는 손수 작성했습니다. 채팅 어시스턴트는 MCP 도구에 이진 파일을 업로드할 수 없기 때문에, 이 도구는 이미지 URL을 받아 서버 측에서 받아옵니다.

  • 인라인 그리드는 MCP Apps UI 리소스입니다. 호스트의 샌드박스 iframe은 런타임에 외부 스크립트를 가져올 수 없으므로, ext-apps 클라이언트는 번들로 묶여 인라인되어 있습니다.

개발

./run.sh test         # offline test suite (pytest)
./run.sh inspect      # MCP Inspector
./prepare.sh          # maintainers: regenerate the vendored assets/

기여는 언제나 환영합니다 — CONTRIBUTING.md 참고.

라이선스

MIT — LICENSE 참고.

이 패키지는 서드파티 자산도 재배포합니다 (Inter 글꼴, @modelcontextprotocol/ext-apps 브라우저 번들 및 그 안에 포함된 라이브러리). 각각은 자체 라이선스를 따릅니다 — THIRD_PARTY_NOTICES.md 참고.

Available Tools

3 tools
connect_accountConnect a Pexafy accountA
Read-onlyIdempotent
Inspect

Asks the host to offer its own flow for connecting a Pexafy account, when none is attached. Use it when the person asks to connect, link or sign in to a Pexafy account, or wants more searches than the daily allowance available without an account.

When no account is attached, it returns an error result that carries an account-connection request for the host: the error is how that request is delivered, not a failure. A host that supports it shows its own account-connection prompt, which the person completes or dismisses; other hosts show only the result's text. Calling it again returns the same request. When an account is already attached, it says so and starts nothing.

It does not search, and searching does not require calling it. check_only reads the state without opening a prompt; leave it unset when the person wants to connect or sign in.

NameRequiredDescriptionDefault
check_onlyNo`true` reports whether an account is connected — and the allowance in force — without opening the connection prompt. Leave it unset when the person wants to connect or sign in.

Output Schema

NameRequiredDescription
budgetNoWhat is left of the current allowance, or null when it is not worth saying.
connectedNoWhether an account is attached to this conversation.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnly/idempotent annotations: it discloses that the connection request is delivered as an error result ('the error is how that request is delivered, not a failure'), that behavior is host-dependent (prompt vs. text-only), that repeat calls return the same request, and that an already-attached account makes it a no-op. This is exactly the kind of non-obvious behavior an agent could otherwise misread as a failure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads purpose and usage, then layers behavioral caveats in three compact paragraphs. Some restatement occurs (idempotency and the already-attached no-op are covered twice), but every sentence carries operational information and nothing is padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be documented, yet the description still explains the error-result contract that is the crux of using this tool correctly. With one optional parameter, complete annotations, and host-variation disclosed, an agent has everything needed to invoke it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single parameter is fully documented in the schema, so the baseline is 3. The description restates the `check_only` semantics ('reads the state without opening a prompt') but adds no format or edge-case detail beyond the schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Asks the host to offer its own flow for connecting a Pexafy account') and immediately distinguishes it from siblings by declaring 'It does not search, and searching does not require calling it.' An agent can tell what this does and does not do without inspecting any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit trigger conditions ('when the person asks to connect, link or sign in... or wants more searches than the daily allowance') and an explicit non-use case ('searching does not require calling it'). It also carves out the `check_only` alternative for state reads versus opening a prompt.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_photosSearch free stock photosA
Read-only
Inspect

Use this when the user asks for a real photograph, or states a need that a photograph fills, and the picture is not already in the conversation.

Call it when they ask outright: “find me photos of…”, “show me images of…”, “I need a picture of…”, “do you have a photo of…”, “where can I find free photos of…”, in any language. Call it when they describe the need without asking to search: “I'm writing an article and need a header image”, “my landing page needs a hero image”, “I need something to illustrate this”. Call it when they ask for something meant to carry photographs: “write an article with pictures”, “a deck with images”. Call it when they name only the picture, with no reason given: “a photo of a white mug”, “a train in Japan”. If the user is working on something that usually carries photographs — an article, a page, a deck, a post — without asking for any, you may offer to look for some; call it only once they accept.

This covers requests for photographs for articles, blog posts, websites, landing pages, products, slides, documents, reports, social posts, ads, newsletters, banners, thumbnails, covers, posters, backgrounds, wallpapers and mood boards; photographs that illustrate a concept, explain a subject or accompany a piece of writing; and any request for free, stock, royalty-free, commercially usable, real or documentary photographs.

A request for a visual is not always a request for a photograph: this tool finds photographs that already exist — not illustrations, drawings, logos, icons, diagrams or screenshots — and it does not generate, edit or upscale an image, nor look for a named person.

It searches Pexafy's own index of millions of free-to-use photographs from stock libraries such as Unsplash, Pexels and Pixabay, matched to the scene described. Each result carries a rank, a photo_id, its licence, the credit line to display and an image link.

NameRequiredDescriptionDefault
english_search_sentenceYesRequired. Describe the desired photograph in one concise English sentence, focusing on its visible subject and scene. Be specific: “Two colleagues laughing in a bright open-plan office” is more effective than “People office”. Keywords work too — send the user's own words, in English, rather than inventing detail they did not ask for. Maximum 250 characters. Describe one coherent scene, focusing on what should appear in the photograph rather than its intended use. Specify the requested photo shape or orientation in explicit_orientation_filter, not here. Example: “An old man sitting at a café table he has visited every morning for thirty years”.
explicit_orientation_filterNoOmit this parameter unless the user explicitly asks for a photo shape or orientation in their own words, or names a format whose shape is part of its definition — a vertical Short, a banner, a square post, a phone wallpaper. This is a hard filter, not a preference: it removes photos with other shapes before ranking, so using a shape the user did not request can exclude the best matches. Never infer the shape from a word inside `english_search_sentence` — “landscape” in “a natural landscape” names the scene, not the format — nor from the subject, the composition, or a purpose that fixes no shape (“a photo for my blog post”, “something for my landing page”). If you are choosing a value rather than repeating one explicitly requested by the user, omit it. Allowed values: `landscape`, `portrait`, `square`. Multiple values are allowed: two shapes return more than one alone, though any filter still returns less than none. For example, “anything but portrait” means `['landscape', 'square']`.

Output Schema

NameRequiredDescription
dataNo
errorNo
budgetNoPresent only when the caller is near the end of an allowance — the daily one without an account, the monthly one with. What is left of it, and, for a caller with no account, the one sentence that says what lifts the limit. Also carried by a refusal, where it describes the wall itself.
noticeNoWhy this answer is empty, when it is. A plan limit was reached: the allowance and when it returns. There is nothing to retry until then.
successNo

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint/openWorldHint/idempotentHint(false)/destructiveHint(false), so the safety profile is covered. The description adds genuinely useful context beyond them: the index is Pexafy's own corpus of free-to-use photos sourced from Unsplash, Pexels and Pixabay, and results carry a licence and a credit line that must be displayed. It slightly overlaps the output schema by listing result fields, and mentions no rate limits or failure behavior, keeping it at 4 rather than 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the primary condition ('Use this when...') and organized into triggers, scope and exclusions. It is long and several quoted example requests are near-duplicates, costing some efficiency, but every section serves routing or scope-setting rather than padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 2-parameter, read-only search tool whose safety annotations and output schema are both present, the description supplies everything an agent needs to decide to call it: triggers, exclusions, entity scope, and the fact that it is a hard-filtered, externally sourced search. Nothing required for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and both parameters carry rich guidance (max length, specificity advice, explicit orientation rules, multi-value semantics), so the baseline is 3. The description body itself adds only the loose framing that results are 'matched to the scene described' and carries no parameter-level detail of its own.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (finds/searches) and resource (existing free stock photographs matched to a described scene) and bounds it sharply: not illustrations, logos, icons, diagrams or screenshots, and no generation, editing, upscaling or named-person lookup. Combined with the parameter name `english_search_sentence`, an agent can distinguish this from the text-query sibling `search_photos_by_image` without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Enumerates concrete trigger phrasings, covers indirect needs ('my landing page needs a hero image'), and explicitly states when NOT to use it (illustrations, drawings, logos, icons, diagrams, screenshots, image generation/editing, named persons). It even specifies an offer-first-then-call-once protocol for working contexts that usually carry photos.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_photos_by_imageFind photos that look like an image or a Pexafy photoA
Read-only
Inspect

Use this when the user wants photographs that look like a picture, rather than ones described in words: a picture they link to, one the host passes to the tool as a file, or a Pexafy photo already found.

Call it for “find images like this”, “something similar to this photo”, “find a free alternative to this image”, “I need this but royalty-free”, “photos in this style”, “more like the second one”, whether or not they say “search by image”.

Give exactly one reference: photo_id for a Pexafy photo from an earlier result; image_url for a picture at a public URL, such as a link the user pasted; image_file when the host passes the user's uploaded image to the tool; image_base64 for image bytes a client already holds. Sending two is refused. A picture that is only visible in the conversation — not linked, not passed to the tool — is not a reference this tool can receive; its scene can be searched in words with search_photos. Words are optional, in english_search_sentence: with a photo_id, the words that photograph was found under; with an image, only what the user wants changed or kept (“like this but at night”).

It finds free stock photographs in Pexafy's index that visually match the reference. The reference image is sent to Pexafy only to run the search; it is not stored. Each result carries a rank, a photo_id, its licence, the credit line to display and an image link.

NameRequiredDescriptionDefault
photo_idNoThe photo_id of the Pexafy reference photo, copied from the result the user or assistant is referring to. It is a UUID such as `019e1ecb-0039-7da6-b1ca-987ee4d337c0`. Use it instead of an image when the reference is a Pexafy photo returned in an earlier search, and pass the words of that search in `english_search_sentence` if needed.
image_urlNoPublic http(s) URL pointing directly to the reference image file, including a URL the user pasted in the conversation. For a photograph Pexafy returned, send its `photo_id` instead.
image_fileNoThe user's uploaded image, as a host that passes uploads to tools provides it: the upload's `download_url` and `file_id`. A picture that is only visible in the conversation has neither.
image_base64NoReference image as base64-encoded bytes, optionally as a data: URL. Use for programmatic clients that already hold the image data.
english_search_sentenceNoOptional, and usable with any reference. Send the words that say what the user wants from the reference: with a `photo_id`, the words that photograph was found under, so the results stay on the subject they asked for; with an image, what they want changed or kept from it (“like this but at night”). The reference stays the main signal and these words adjust it. With an image, send the change alone (“at night”) rather than the whole scene rewritten, which would outweigh the picture instead of adjusting it; with a `photo_id`, the full sentence that photograph was found under is what to send. In English, like the sentence of a text search. The words a reference photograph was found under are the best ones. When it is unclear whether they want the same subject or simply the same look, send the words: staying on the subject is the safer default. Omit them only when they plainly want photographs that look like the picture itself.
explicit_orientation_filterNoOmit this parameter unless the user explicitly asks for a photo shape or orientation in their own words, or names a format whose shape is part of its definition — a vertical Short, a banner, a square post, a phone wallpaper. This is a hard filter, not a preference: it removes photos with other shapes before ranking, so using a shape the user did not request can exclude the best matches. Never infer the shape from the reference image the user gave — its shape is a property of the picture they had, not a constraint on the ones they want — nor from the subject, the composition, or a purpose that fixes no shape (“a photo for my blog post”, “something for my landing page”). If you are choosing a value rather than repeating one explicitly requested by the user, omit it. Allowed values: `landscape`, `portrait`, `square`. Multiple values are allowed: two shapes return more than one alone, though any filter still returns less than none. For example, “anything but portrait” means `['landscape', 'square']`.

Output Schema

NameRequiredDescription
dataNo
errorNo
budgetNoPresent only when the caller is near the end of an allowance — the daily one without an account, the monthly one with. What is left of it, and, for a caller with no account, the one sentence that says what lifts the limit. Also carried by a refusal, where it describes the wall itself.
noticeNoWhy this answer is empty, when it is. A plan limit was reached: the allowance and when it returns. There is nothing to retry until then.
successNo

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, openWorldHint, non-idempotent and non-destructive, so the safety profile is covered. The description adds real value beyond that: exactly one reference is accepted and 'sending two is refused', and the reference image 'is sent to Pexafy only to run the search; it is not stored'. It stops short of describing ranking behaviour or failure modes for unreachable URLs, so not a full 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loading is excellent — the when-to-use triggers come first — but the body is roughly 400 words and the guidance for english_search_sentence and explicit_orientation_filter largely restates the schema descriptions almost verbatim, which is padding rather than added value. The orientation paragraph in particular repeats the schema's own caution about it being a hard filter.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need no explanation (the brief mention of rank/licence/credit is a bonus, not a requirement), and annotations carry the safety profile. The description still covers the privacy handling of the reference image and the single-reference constraint, so nothing an agent needs to invoke this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter semantics the schema cannot express per-field: the mutually exclusive 'exactly one reference' rule across photo_id/image_url/image_file/image_base64, and the relationship between the reference and english_search_sentence ('the reference stays the main signal and these words adjust it', 'the full sentence that photograph was found under is what to send').

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — finds free stock photographs in Pexafy's index that visually match a reference image — and explicitly distinguishes itself from the sibling search_photos by noting that an unlinked picture's scene 'can be searched in words with search_photos'. An agent can route between the two tools without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit trigger phrases ('find images like this', 'more like the second one'), the condition under which the tool is inapplicable (a picture only visible in the conversation, not linked or passed), and names the alternative tool for that case. When-to-use, when-not-to-use, and the alternative are all present.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv1.0.1
    • Changedsearch_photos11 fields changed
      • addedOutput schema / properties / data / items / properties / alt_description / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / alt_description / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / data / items / properties / height / anyOf
        Added value: +[
        +  {
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / height / type
        Removed value: -[
        -  "integer",
        -  "null"
        -]
      • addedOutput schema / properties / data / items / properties / photographer_full_name / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / photographer_full_name / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / data / items / properties / source_image_url / anyOf
        Added value: +[
        +  {
        +    "format": "uri",
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / source_image_url / format
        Removed value: -"uri"
      • removedOutput schema / properties / data / items / properties / source_image_url / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / data / items / properties / width / anyOf
        Added value: +[
        +  {
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / width / type
        Removed value: -[
        -  "integer",
        -  "null"
        -]
    • Changedsearch_photos_by_image11 fields changed
      • addedOutput schema / properties / data / items / properties / alt_description / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / alt_description / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / data / items / properties / height / anyOf
        Added value: +[
        +  {
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / height / type
        Removed value: -[
        -  "integer",
        -  "null"
        -]
      • addedOutput schema / properties / data / items / properties / photographer_full_name / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / photographer_full_name / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / data / items / properties / source_image_url / anyOf
        Added value: +[
        +  {
        +    "format": "uri",
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / source_image_url / format
        Removed value: -"uri"
      • removedOutput schema / properties / data / items / properties / source_image_url / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / data / items / properties / width / anyOf
        Added value: +[
        +  {
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / data / items / properties / width / type
        Removed value: -[
        -  "integer",
        -  "null"
        -]
  2. 4 tool updatesv1.0.0
    • Addedconnect_account
    • Removedget_similar_photos
    • Changedsearch_photos39 fields changed
      • removedInput schema / properties / after_date
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Only return photos published on or after this date, formatted `YYYY-MM-DD`.",
        -  "example": "2025-01-01",
        -  "title": "After Date"
        -}
      • removedInput schema / properties / color_hex
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Keep only photos close to this hex color (e.g. `#1E90FF`). Cannot be combined with `color_name`. Use `color_tolerance` to widen or tighten the match.",
        -  "title": "Color Hex"
        -}
      • removedInput schema / properties / color_name
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Keep only photos whose dominant color matches one of: red, orange, yellow, green, blue, purple, pink, brown, black, white, gray, teal, beige, gold, navy. Cannot be combined with color_hex.",
        -  "example": "blue",
        -  "title": "Color Name"
        -}
      • removedInput schema / properties / color_tolerance
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "maximum": 255,
        -      "minimum": 0,
        -      "type": "integer"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "How far a photo's color may be from `color_hex` and still match, from `0` (exact match) to `255` (very loose). Defaults to `20`. Only applies when `color_hex` is set.",
        -  "title": "Color Tolerance"
        -}
      • removedInput schema / properties / cursor
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Token used to fetch the next page. Take the `pagination.next_cursor` value from a previous response and pass it back here. See the [Pagination](/pagination) guide.",
        -  "title": "Cursor"
        -}
      • addedInput schema / properties / english_search_sentence
        Added value: +{
        +  "description": "Required. Describe the desired photograph in one concise English sentence, focusing on its visible subject and scene.\n\nBe specific: “Two colleagues laughing in a bright open-plan office” is more effective than “People office”. Keywords work too — send the user's own words, in English, rather than inventing detail they did not ask for.\n\nMaximum 250 characters. Describe one coherent scene, focusing on what should appear in the photograph rather than its intended use.\n\nSpecify the requested photo shape or orientation in explicit_orientation_filter, not here.\n\nExample: “An old man sitting at a café table he has visited every morning for thirty years”.",
        +  "examples": [
        +    "two people sharing a bench in comfortable silence"
        +  ],
        +  "maxLength": 250,
        +  "minLength": 1,
        +  "type": "string"
        +}
      • addedInput schema / properties / explicit_orientation_filter
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "enum": [
        +          "landscape",
        +          "portrait",
        +          "square"
        +        ],
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Omit this parameter unless the user explicitly asks for a photo shape or orientation in their own words, or names a format whose shape is part of its definition — a vertical Short, a banner, a square post, a phone wallpaper.\n\nThis is a hard filter, not a preference: it removes photos with other shapes before ranking, so using a shape the user did not request can exclude the best matches.\n\nNever infer the shape from a word inside `english_search_sentence` — “landscape” in “a natural landscape” names the scene, not the format — nor from the subject, the composition, or a purpose that fixes no shape (“a photo for my blog post”, “something for my landing page”). If you are choosing a value rather than repeating one explicitly requested by the user, omit it.\n\nAllowed values: `landscape`, `portrait`, `square`. Multiple values are allowed: two shapes return more than one alone, though any filter still returns less than none. For example, “anything but portrait” means `['landscape', 'square']`."
        +}
      • removedInput schema / properties / license_type
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "items": {
        -        "type": "string"
        -      },
        -      "type": "array"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Keep only photos with these license types: free, cc0. 'free' means the photo can be used freely and attribution is appreciated. Repeat the parameter to pass several.",
        -  "example": "free",
        -  "title": "License Type"
        -}
      • removedInput schema / properties / orientation
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "items": {
        -        "type": "string"
        -      },
        -      "type": "array"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Keep only photos with these shapes: landscape, portrait, square. Repeat the parameter to pass several.",
        -  "example": "landscape",
        -  "title": "Orientation"
        -}
      • removedInput schema / properties / photographer
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Only return photos from this photographer's username. Use `GET /api/v1/facets/photographers/suggest` to find usernames.",
        -  "example": "nasa",
        -  "title": "Photographer"
        -}
      • removedInput schema / properties / q
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "maxLength": 500,
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Your search query as a full natural-language sentence describing the scene you want — Pexafy is semantic, so sentences beat keywords. Up to 500 characters. Optional if you provide at least one filter instead. Example: 'an old man sitting at a café table he has visited every morning for thirty years'.",
        -  "example": "two people sharing a bench in comfortable silence",
        -  "title": "Q"
        -}
      • removedInput schema / properties / source
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "items": {
        -        "type": "string"
        -      },
        -      "type": "array"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Keep only photos from these providers: Unsplash, Pexels, Pixabay, Kaboompics, Burst, StockSnap, Picjumbo, Skitterphoto, NegativeSpace. Repeat the parameter to pass several.",
        -  "example": "Unsplash",
        -  "title": "Source"
        -}
      • changedInput schema / required
        Previous value: -[]New value: +[
        +  "english_search_sentence"
        +]
      • addedOutput schema / properties / budget
        Added value: +{
        +  "description": "Present only when the caller is near the end of an allowance — the daily one without an account, the monthly one with. What is left of it, and, for a caller with no account, the one sentence that says what lifts the limit. Also carried by a refusal, where it describes the wall itself.",
        +  "properties": {
        +    "limit": {
        +      "description": "Searches allowed in that period.",
        +      "type": "integer"
        +    },
        +    "message": {
        +      "description": "The whole notice in words, for a host with no UI.",
        +      "type": "string"
        +    },
        +    "remaining": {
        +      "description": "Searches left in that period.",
        +      "type": "integer"
        +    },
        +    "scope": {
        +      "description": "Which allowance this counts: the daily one or the monthly one. Both exist with or without an account, so `signed_in` does not tell them apart.",
        +      "enum": [
        +        "day",
        +        "month"
        +      ],
        +      "type": "string"
        +    },
        +    "signed_in": {
        +      "description": "Whether the caller has an account. False is the anonymous allowance.",
        +      "type": "boolean"
        +    },
        +    "state": {
        +      "description": "`warning` while there is room left, `exhausted` once there is none.",
        +      "enum": [
        +        "warning",
        +        "exhausted"
        +      ],
        +      "type": "string"
        +    },
        +    "used": {
        +      "description": "Searches spent in that period.",
        +      "type": "integer"
        +    }
        +  },
        +  "type": "object"
        +}
      • changedOutput schema / properties / data / items / description
        Previous value: -"A photo result. Fields returned can be narrowed with the `fields` parameter and may depend on your plan."New value: +"One photograph of this answer."
      • changedOutput schema / properties / data / items / properties / attribution / description
        Previous value: -"Ready-to-display credit for the photographer/source."New value: +"The credit line to display, already written."
      • removedOutput schema / properties / data / items / properties / attribution / properties / html
        Removed value: -{
        -  "description": "HTML attribution snippet.",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / blur_hash
        Removed value: -{
        -  "description": "BlurHash placeholder string.",
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • removedOutput schema / properties / data / items / properties / color_hex
        Removed value: -{
        -  "description": "Dominant color hex code.",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / color_name
        Removed value: -{
        -  "description": "Dominant color name.",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / description
        Removed value: -{
        -  "description": "AI-generated caption.",
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • removedOutput schema / properties / data / items / properties / image_url
        Removed value: -{
        -  "description": "Canonical source image URL.",
        -  "format": "uri",
        -  "type": "string"
        -}
      • addedOutput schema / properties / data / items / properties / orientation / description
        Added value: +"The shape of this photograph — `landscape`, `portrait` or `square`, the values `explicit_orientation_filter` takes."
      • removedOutput schema / properties / data / items / properties / photographer_url
        Removed value: -{
        -  "format": "uri",
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • removedOutput schema / properties / data / items / properties / photographer_username
        Removed value: -{
        -  "type": "string"
        -}
      • addedOutput schema / properties / data / items / properties / rank
        Added value: +{
        +  "description": "This photograph's place in this answer, from 1. A person may name a result by position (“the second one”); the tools take that result's `photo_id`, not its rank.",
        +  "type": "integer"
        +}
      • removedOutput schema / properties / data / items / properties / relevance_score
        Removed value: -{
        -  "description": "Match score 0–1 (higher is better). Only on search results.",
        -  "type": [
        -    "number",
        -    "null"
        -  ]
        -}
      • removedOutput schema / properties / data / items / properties / source_description
        Removed value: -{
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • changedOutput schema / properties / data / items / properties / source_image_url / description
        Previous value: -"URL of the photo's page on the provider."New value: +"The photograph's page on the library it came from — where to send someone who wants the source."
      • removedOutput schema / properties / data / items / properties / uploaded_on
        Removed value: -{
        -  "description": "Publication date (YYYY-MM-DD).",
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • changedOutput schema / properties / data / items / properties / urls / description
        Previous value: -"Ready-to-use image links in five sizes."New value: +"The photograph's image file, 1280 pixels wide, for a page, a document or a download. `urls` carries this one variant and no other."
      • removedOutput schema / properties / data / items / properties / urls / properties / full
        Removed value: -{
        -  "format": "uri",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / urls / properties / large
        Removed value: -{
        -  "format": "uri",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / urls / properties / small
        Removed value: -{
        -  "format": "uri",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / urls / properties / thumb
        Removed value: -{
        -  "format": "uri",
        -  "type": "string"
        -}
      • changedOutput schema / properties / error / anyOf
        Previous value: -[
        -  {
        -    "properties": {
        -      "code": {
        -        "description": "Machine-readable error code (e.g. `MISSING_PARAMS`, `PHOTO_NOT_FOUND`).",
        -        "type": "string"
        -      },
        -      "message": {
        -        "description": "Human-readable error message.",
        -        "type": "string"
        -      },
        -      "request_id": {
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "code",
        -      "message"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "properties": {
        +      "code": {
        +        "description": "Machine-readable error code (e.g. `MISSING_PARAMS`, `PHOTO_NOT_FOUND`).",
        +        "type": "string"
        +      },
        +      "message": {
        +        "description": "Human-readable error message.",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "code",
        +      "message"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / meta
        Removed value: -{
        -  "properties": {
        -    "request_id": {
        -      "description": "Unique id for this request (quote it in support tickets).",
        -      "type": "string"
        -    },
        -    "took_ms": {
        -      "description": "Server processing time in milliseconds.",
        -      "type": "number"
        -    }
        -  },
        -  "type": "object"
        -}
      • addedOutput schema / properties / notice
        Added value: +{
        +  "description": "Why this answer is empty, when it is. A plan limit was reached: the allowance and when it returns. There is nothing to retry until then.",
        +  "type": "string"
        +}
      • removedOutput schema / properties / pagination
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "properties": {
        -        "has_more": {
        -          "description": "Whether another page exists.",
        -          "type": "boolean"
        -        },
        -        "next_cursor": {
        -          "description": "Pass back as `cursor` for the next page; `null` when `has_more` is false.",
        -          "type": [
        -            "string",
        -            "null"
        -          ]
        -        },
        -        "per_page": {
        -          "description": "Number of items per page.",
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ]
        -}
    • Changedsearch_photos_by_image41 fields changed
      • removedInput schema / properties / after_date
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Only return photos published on or after this date, formatted YYYY-MM-DD."
        -}
      • removedInput schema / properties / color_name
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Keep only photos whose dominant color matches one of: red, orange, yellow, green, blue, purple, pink, brown, black, white, gray, teal, beige, gold, navy. Cannot be combined with color_hex."
        -}
      • removedInput schema / properties / cursor
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Token to fetch the next page. Take `pagination.next_cursor` from a previous response and pass it back here — no need to re-upload the image. See the Pagination guide."
        -}
      • addedInput schema / properties / english_search_sentence
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maxLength": 250,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Optional, and usable with any reference. Send the words that say what the user wants from the reference: with a `photo_id`, the words that photograph was found under, so the results stay on the subject they asked for; with an image, what they want changed or kept from it (“like this but at night”). The reference stays the main signal and these words adjust it. With an image, send the change alone (“at night”) rather than the whole scene rewritten, which would outweigh the picture instead of adjusting it; with a `photo_id`, the full sentence that photograph was found under is what to send. In English, like the sentence of a text search. The words a reference photograph was found under are the best ones. When it is unclear whether they want the same subject or simply the same look, send the words: staying on the subject is the safer default. Omit them only when they plainly want photographs that look like the picture itself."
        +}
      • addedInput schema / properties / explicit_orientation_filter
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "enum": [
        +          "landscape",
        +          "portrait",
        +          "square"
        +        ],
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Omit this parameter unless the user explicitly asks for a photo shape or orientation in their own words, or names a format whose shape is part of its definition — a vertical Short, a banner, a square post, a phone wallpaper.\n\nThis is a hard filter, not a preference: it removes photos with other shapes before ranking, so using a shape the user did not request can exclude the best matches.\n\nNever infer the shape from the reference image the user gave — its shape is a property of the picture they had, not a constraint on the ones they want — nor from the subject, the composition, or a purpose that fixes no shape (“a photo for my blog post”, “something for my landing page”). If you are choosing a value rather than repeating one explicitly requested by the user, omit it.\n\nAllowed values: `landscape`, `portrait`, `square`. Multiple values are allowed: two shapes return more than one alone, though any filter still returns less than none. For example, “anything but portrait” means `['landscape', 'square']`."
        +}
      • changedInput schema / properties / image_base64 / description
        Previous value: -"The reference image as base64 bytes, optionally as a `data:` URL. For a client that already holds the bytes; prefer `image_url` when a link exists."New value: +"Reference image as base64-encoded bytes, optionally as a data: URL. Use for programmatic clients that already hold the image data."
      • changedInput schema / properties / image_file / description
        Previous value: -"Filled in by the host when the user uploads an image, not by the caller. Carries the upload's `download_url` and `file_id`."New value: +"The user's uploaded image, as a host that passes uploads to tools provides it: the upload's `download_url` and `file_id`. A picture that is only visible in the conversation has neither."
      • changedInput schema / properties / image_url / description
        Previous value: -"Public http(s) URL of the reference image. Reuse the `image_url` of a photo from a previous search result, or any public URL the user provides."New value: +"Public http(s) URL pointing directly to the reference image file, including a URL the user pasted in the conversation. For a photograph Pexafy returned, send its `photo_id` instead."
      • removedInput schema / properties / license_type
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Keep only photos with these license types: free, cc0. 'free' means the photo can be used freely and attribution is appreciated. Repeat the parameter to pass several."
        -}
      • removedInput schema / properties / orientation
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Keep only photos with these shapes: landscape, portrait, square. Repeat the parameter to pass several."
        -}
      • addedInput schema / properties / photo_id
        Added value: +{
        +  "anyOf": [
        +    {
        +      "examples": [
        +        "019e1ecb-0039-7da6-b1ca-987ee4d337c0"
        +      ],
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "The photo_id of the Pexafy reference photo, copied from the result the user or assistant is referring to. It is a UUID such as `019e1ecb-0039-7da6-b1ca-987ee4d337c0`. Use it instead of an image when the reference is a Pexafy photo returned in an earlier search, and pass the words of that search in `english_search_sentence` if needed."
        +}
      • removedInput schema / properties / photographer
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Only return photos from this photographer's exact username."
        -}
      • removedInput schema / properties / q
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Your search query as a full natural-language sentence describing the scene you want — Pexafy is semantic, so sentences beat keywords. Up to 500 characters. Optional if you provide at least one filter instead. Example: 'an old man sitting at a café table he has visited every morning for thirty years'."
        -}
      • removedInput schema / properties / source
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Keep only photos from these providers: Unsplash, Pexels, Pixabay, Kaboompics, Burst, StockSnap, Picjumbo, Skitterphoto, NegativeSpace. Repeat the parameter to pass several."
        -}
      • removedInput schema / properties / text_alpha
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "number"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Balance between your text and the image when both are provided, from `0` to `10`. `0` ignores the text (pure visual search), `1.7` (the default) is balanced, and higher values give your words more weight. Has no effect without `q`."
        -}
      • addedOutput schema / properties / budget
        Added value: +{
        +  "description": "Present only when the caller is near the end of an allowance — the daily one without an account, the monthly one with. What is left of it, and, for a caller with no account, the one sentence that says what lifts the limit. Also carried by a refusal, where it describes the wall itself.",
        +  "properties": {
        +    "limit": {
        +      "description": "Searches allowed in that period.",
        +      "type": "integer"
        +    },
        +    "message": {
        +      "description": "The whole notice in words, for a host with no UI.",
        +      "type": "string"
        +    },
        +    "remaining": {
        +      "description": "Searches left in that period.",
        +      "type": "integer"
        +    },
        +    "scope": {
        +      "description": "Which allowance this counts: the daily one or the monthly one. Both exist with or without an account, so `signed_in` does not tell them apart.",
        +      "enum": [
        +        "day",
        +        "month"
        +      ],
        +      "type": "string"
        +    },
        +    "signed_in": {
        +      "description": "Whether the caller has an account. False is the anonymous allowance.",
        +      "type": "boolean"
        +    },
        +    "state": {
        +      "description": "`warning` while there is room left, `exhausted` once there is none.",
        +      "enum": [
        +        "warning",
        +        "exhausted"
        +      ],
        +      "type": "string"
        +    },
        +    "used": {
        +      "description": "Searches spent in that period.",
        +      "type": "integer"
        +    }
        +  },
        +  "type": "object"
        +}
      • changedOutput schema / properties / data / items / description
        Previous value: -"A photo result. Fields returned can be narrowed with the `fields` parameter and may depend on your plan."New value: +"One photograph of this answer."
      • changedOutput schema / properties / data / items / properties / attribution / description
        Previous value: -"Ready-to-display credit for the photographer/source."New value: +"The credit line to display, already written."
      • removedOutput schema / properties / data / items / properties / attribution / properties / html
        Removed value: -{
        -  "description": "HTML attribution snippet.",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / blur_hash
        Removed value: -{
        -  "description": "BlurHash placeholder string.",
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • removedOutput schema / properties / data / items / properties / color_hex
        Removed value: -{
        -  "description": "Dominant color hex code.",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / color_name
        Removed value: -{
        -  "description": "Dominant color name.",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / description
        Removed value: -{
        -  "description": "AI-generated caption.",
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • removedOutput schema / properties / data / items / properties / image_url
        Removed value: -{
        -  "description": "Canonical source image URL.",
        -  "format": "uri",
        -  "type": "string"
        -}
      • addedOutput schema / properties / data / items / properties / orientation / description
        Added value: +"The shape of this photograph — `landscape`, `portrait` or `square`, the values `explicit_orientation_filter` takes."
      • removedOutput schema / properties / data / items / properties / photographer_url
        Removed value: -{
        -  "format": "uri",
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • removedOutput schema / properties / data / items / properties / photographer_username
        Removed value: -{
        -  "type": "string"
        -}
      • addedOutput schema / properties / data / items / properties / rank
        Added value: +{
        +  "description": "This photograph's place in this answer, from 1. A person may name a result by position (“the second one”); the tools take that result's `photo_id`, not its rank.",
        +  "type": "integer"
        +}
      • removedOutput schema / properties / data / items / properties / relevance_score
        Removed value: -{
        -  "description": "Match score 0–1 (higher is better). Only on search results.",
        -  "type": [
        -    "number",
        -    "null"
        -  ]
        -}
      • removedOutput schema / properties / data / items / properties / source_description
        Removed value: -{
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • changedOutput schema / properties / data / items / properties / source_image_url / description
        Previous value: -"URL of the photo's page on the provider."New value: +"The photograph's page on the library it came from — where to send someone who wants the source."
      • removedOutput schema / properties / data / items / properties / uploaded_on
        Removed value: -{
        -  "description": "Publication date (YYYY-MM-DD).",
        -  "type": [
        -    "string",
        -    "null"
        -  ]
        -}
      • changedOutput schema / properties / data / items / properties / urls / description
        Previous value: -"Ready-to-use image links in five sizes."New value: +"The photograph's image file, 1280 pixels wide, for a page, a document or a download. `urls` carries this one variant and no other."
      • removedOutput schema / properties / data / items / properties / urls / properties / full
        Removed value: -{
        -  "format": "uri",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / urls / properties / large
        Removed value: -{
        -  "format": "uri",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / urls / properties / small
        Removed value: -{
        -  "format": "uri",
        -  "type": "string"
        -}
      • removedOutput schema / properties / data / items / properties / urls / properties / thumb
        Removed value: -{
        -  "format": "uri",
        -  "type": "string"
        -}
      • changedOutput schema / properties / error / anyOf
        Previous value: -[
        -  {
        -    "properties": {
        -      "code": {
        -        "description": "Machine-readable error code (e.g. `MISSING_PARAMS`, `PHOTO_NOT_FOUND`).",
        -        "type": "string"
        -      },
        -      "message": {
        -        "description": "Human-readable error message.",
        -        "type": "string"
        -      },
        -      "request_id": {
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "code",
        -      "message"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "properties": {
        +      "code": {
        +        "description": "Machine-readable error code (e.g. `MISSING_PARAMS`, `PHOTO_NOT_FOUND`).",
        +        "type": "string"
        +      },
        +      "message": {
        +        "description": "Human-readable error message.",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "code",
        +      "message"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / meta
        Removed value: -{
        -  "properties": {
        -    "request_id": {
        -      "description": "Unique id for this request (quote it in support tickets).",
        -      "type": "string"
        -    },
        -    "took_ms": {
        -      "description": "Server processing time in milliseconds.",
        -      "type": "number"
        -    }
        -  },
        -  "type": "object"
        -}
      • addedOutput schema / properties / notice
        Added value: +{
        +  "description": "Why this answer is empty, when it is. A plan limit was reached: the allowance and when it returns. There is nothing to retry until then.",
        +  "type": "string"
        +}
      • removedOutput schema / properties / pagination
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "properties": {
        -        "has_more": {
        -          "description": "Whether another page exists.",
        -          "type": "boolean"
        -        },
        -        "next_cursor": {
        -          "description": "Pass back as `cursor` for the next page; `null` when `has_more` is false.",
        -          "type": [
        -            "string",
        -            "null"
        -          ]
        -        },
        -        "per_page": {
        -          "description": "Number of items per page.",
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ]
        -}
  3. 1 tool updatev0.4.9
    • Changedsearch_photos_by_image19 fields changed
      • addedInput schema / properties / after_date / description
        Added value: +"Only return photos published on or after this date, formatted YYYY-MM-DD."
      • addedInput schema / properties / color_name / description
        Added value: +"Keep only photos whose dominant color matches one of: red, orange, yellow, green, blue, purple, pink, brown, black, white, gray, teal, beige, gold, navy. Cannot be combined with color_hex."
      • addedInput schema / properties / cursor / description
        Added value: +"Token to fetch the next page. Take `pagination.next_cursor` from a previous response and pass it back here — no need to re-upload the image. See the Pagination guide."
      • addedInput schema / properties / image_base64 / description
        Added value: +"The reference image as base64 bytes, optionally as a `data:` URL. For a client that already holds the bytes; prefer `image_url` when a link exists."
      • addedInput schema / properties / image_file / additionalProperties
        Added value: +false
      • removedInput schema / properties / image_file / anyOf
        Removed value: -[
        -  {
        -    "additionalProperties": true,
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • removedInput schema / properties / image_file / default
        Removed value: -null
      • addedInput schema / properties / image_file / description
        Added value: +"Filled in by the host when the user uploads an image, not by the caller. Carries the upload's `download_url` and `file_id`."
      • addedInput schema / properties / image_file / properties
        Added value: +{
        +  "download_url": {
        +    "type": "string"
        +  },
        +  "file_id": {
        +    "type": "string"
        +  },
        +  "file_name": {
        +    "type": "string"
        +  },
        +  "mime_type": {
        +    "type": "string"
        +  }
        +}
      • addedInput schema / properties / image_file / required
        Added value: +[
        +  "download_url",
        +  "file_id"
        +]
      • addedInput schema / properties / image_file / type
        Added value: +"object"
      • addedInput schema / properties / image_url / description
        Added value: +"Public http(s) URL of the reference image. Reuse the `image_url` of a photo from a previous search result, or any public URL the user provides."
      • addedInput schema / properties / license_type / description
        Added value: +"Keep only photos with these license types: free, cc0. 'free' means the photo can be used freely and attribution is appreciated. Repeat the parameter to pass several."
      • addedInput schema / properties / orientation / description
        Added value: +"Keep only photos with these shapes: landscape, portrait, square. Repeat the parameter to pass several."
      • addedInput schema / properties / photographer / description
        Added value: +"Only return photos from this photographer's exact username."
      • addedInput schema / properties / q / description
        Added value: +"Your search query as a full natural-language sentence describing the scene you want — Pexafy is semantic, so sentences beat keywords. Up to 500 characters. Optional if you provide at least one filter instead. Example: 'an old man sitting at a café table he has visited every morning for thirty years'."
      • addedInput schema / properties / source / description
        Added value: +"Keep only photos from these providers: Unsplash, Pexels, Pixabay, Kaboompics, Burst, StockSnap, Picjumbo, Skitterphoto, NegativeSpace. Repeat the parameter to pass several."
      • addedInput schema / properties / text_alpha / description
        Added value: +"Balance between your text and the image when both are provided, from `0` to `10`. `0` ignores the text (pure visual search), `1.7` (the default) is balanced, and higher values give your words more weight. Has no effect without `q`."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "data": {
        +      "items": {
        +        "description": "A photo result. Fields returned can be narrowed with the `fields` parameter and may depend on your plan.",
        +        "properties": {
        +          "alt_description": {
        +            "description": "Accessibility-friendly text.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "attribution": {
        +            "description": "Ready-to-display credit for the photographer/source.",
        +            "properties": {
        +              "html": {
        +                "description": "HTML attribution snippet.",
        +                "type": "string"
        +              },
        +              "plain": {
        +                "description": "Plain-text attribution.",
        +                "type": "string"
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "blur_hash": {
        +            "description": "BlurHash placeholder string.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "color_hex": {
        +            "description": "Dominant color hex code.",
        +            "type": "string"
        +          },
        +          "color_name": {
        +            "description": "Dominant color name.",
        +            "type": "string"
        +          },
        +          "description": {
        +            "description": "AI-generated caption.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "height": {
        +            "type": [
        +              "integer",
        +              "null"
        +            ]
        +          },
        +          "image_url": {
        +            "description": "Canonical source image URL.",
        +            "format": "uri",
        +            "type": "string"
        +          },
        +          "license_type": {
        +            "description": "License type (e.g. `free`).",
        +            "type": "string"
        +          },
        +          "orientation": {
        +            "enum": [
        +              "landscape",
        +              "portrait",
        +              "square"
        +            ],
        +            "type": "string"
        +          },
        +          "photo_id": {
        +            "description": "Unique Pexafy identifier (UUID).",
        +            "type": "string"
        +          },
        +          "photographer_full_name": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "photographer_url": {
        +            "format": "uri",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "photographer_username": {
        +            "type": "string"
        +          },
        +          "relevance_score": {
        +            "description": "Match score 0–1 (higher is better). Only on search results.",
        +            "type": [
        +              "number",
        +              "null"
        +            ]
        +          },
        +          "source": {
        +            "description": "Provider (e.g. `Pexels`, `Unsplash`, `Pixabay`).",
        +            "type": "string"
        +          },
        +          "source_description": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "source_image_url": {
        +            "description": "URL of the photo's page on the provider.",
        +            "format": "uri",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "uploaded_on": {
        +            "description": "Publication date (YYYY-MM-DD).",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "urls": {
        +            "description": "Ready-to-use image links in five sizes.",
        +            "properties": {
        +              "full": {
        +                "format": "uri",
        +                "type": "string"
        +              },
        +              "large": {
        +                "format": "uri",
        +                "type": "string"
        +              },
        +              "regular": {
        +                "format": "uri",
        +                "type": "string"
        +              },
        +              "small": {
        +                "format": "uri",
        +                "type": "string"
        +              },
        +              "thumb": {
        +                "format": "uri",
        +                "type": "string"
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "width": {
        +            "type": [
        +              "integer",
        +              "null"
        +            ]
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "error": {
        +      "anyOf": [
        +        {
        +          "properties": {
        +            "code": {
        +              "description": "Machine-readable error code (e.g. `MISSING_PARAMS`, `PHOTO_NOT_FOUND`).",
        +              "type": "string"
        +            },
        +            "message": {
        +              "description": "Human-readable error message.",
        +              "type": "string"
        +            },
        +            "request_id": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "code",
        +            "message"
        +          ],
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ]
        +    },
        +    "meta": {
        +      "properties": {
        +        "request_id": {
        +          "description": "Unique id for this request (quote it in support tickets).",
        +          "type": "string"
        +        },
        +        "took_ms": {
        +          "description": "Server processing time in milliseconds.",
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "pagination": {
        +      "anyOf": [
        +        {
        +          "properties": {
        +            "has_more": {
        +              "description": "Whether another page exists.",
        +              "type": "boolean"
        +            },
        +            "next_cursor": {
        +              "description": "Pass back as `cursor` for the next page; `null` when `has_more` is false.",
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "per_page": {
        +              "description": "Number of items per page.",
        +              "type": "integer"
        +            }
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ]
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "type": "object",
        +  "x-fastmcp-top-level-schema": "PhotoListResponse"
        +}
  4. 2 tool updatesv0.4.0
    • Addedget_similar_photos
    • Removedphoto_similar
  5. 3 tool updatesv0.2.0
    • First observedphoto_similar
    • First observedsearch_photos
    • First observedsearch_photos_by_image

TDQS

A4.4/5.0

Scored across 3 tools

Disambiguation4/5

The three tools have clearly distinct purposes: text-described photo search, image-reference search, and account connection. The two search tools could superficially be confused, but the descriptions sharply delineate when to use each (words vs. a concrete reference image).

Naming Consistency5/5

All names follow a consistent snake_case verb_noun pattern (search_photos, search_photos_by_image, connect_account). The shared search_photos prefix is predictable and reinforces the relationship between the two search tools.

Tool Count4/5

Three tools is slightly lean but well-scoped for a photo search service, with each tool earning its place. No filler tools are present, though a small set like this has little redundancy to mask gaps.

Completeness4/5

For a stock photo search domain, text search, image-reference search, and account connection cover the core lifecycle. Minor gaps exist (e.g., no dedicated photo-detail or collection retrieval), but results already carry photo_id, licence, and credit info, so core workflows are unblocked.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides access to the Unsplash API, enabling AI assistants to search and retrieve high-quality photos, browse collections, view photographer profiles, and get photo statistics with advanced filtering options.
    83 npm
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    This MCP server enables AI assistants to search for images on Wikimedia Commons, providing detailed metadata and optional thumbnail combinations to assist AI models in visual comparisons.
    1
    2
    Apache 2.0
  • A
    license
    A
    quality
    D
    maintenance
    An MCP server that provides image search capabilities via the Brave Image Search API, allowing AI assistants to search images with various filters and perform batch queries.
    2
    1
    MIT