Pexafy MCP Server
Officialpexafy-mcp
Поиск стоковых фотографий для ИИ-ассистентов. MCP — сервер, который позволяет Claude, ChatGPT или любому MCP‑клиенту искать изображения в библиотеке без лицензионных отчислений: по описанию сцены естественным языком, по фотографии‑примеру или по запросу «ещё похожие» — и отображать результаты в виде сетки миниатюр прямо в диалоге.
Удалённый MCP, OAuth, никаких API‑ключей для вставки, 3 инструмента, изображения прямо в ответе.

Использование (ничего устанавливать не нужно)
Готовый сервер работает по адресу:
https://mcp.pexafy.com/mcpОн работает с транспортом Streamable HTTP и аутентифицируется через OAuth 2.1.
Вы входите в Pexafy в окне браузера, а коннектор получает индивидуальные учётные данные. Не нужно генерировать API‑ключ, вставлять его в JSON‑файл и обновлять позже.
Claude (web и десктоп)
Откройте «Настройки → Коннекторы» (на планах Team/Enterprise владелец добавляет их один раз в разделе «Настройки организации → Коннекторы»).
Нажмите Добавить пользовательский коннектор.
Вставьте
https://mcp.pexafy.com/mcpи подтвердите.Войдите в Pexafy в открывшемся окне. Готово — можно просить Claude подобрать фото.
Claude Code
claude mcp add --transport http pexafy https://mcp.pexafy.com/mcpЛюбой другой MCP‑клиент
Укажите тот же самый URL с транспортом streamable-http. Клиенты, не поддерживающие роrypto, могут вместо этого использовать API‑ключ Pexafy в заголовке Authorization: Bearer <key> либо x-api-key: <key> — ключ выдаётся в панели управления.
Проверка живости: GET /health (публичный адрес, без авторизации).
Сервер также есть в официальном реестре MCP как com.pexafy/pexafy-ност_str и на Smithery — там можно запустить «жив» шлюз для клиентов, которым удобнее через него.
Сколько это стоит
Бесплатный план — 5 000 поисковых запросов в месяц на один коннектор: для обычного использования его достаточно, карта не требуется. Другие тарифы — на странице с ценами. Когда заканчивается лимит, доуствует ассистент в чате, а не непонятная ошибка.
Related MCP server: brave-image-mcp
Инструменты
Три инструмента только для чтения. Без прав на запись и изменения аккаунта.
search_photos — семантический текстовый поиск
Опишите сцену в свободной форме; Pexafy семантический, поэтому фразы работают лучше отдельных ключевых слов. Все параметры необязательны,но что‑либо передавать не обязательно — достаточно q или хотя бы одного фильтра.
Параметр | Тип | Комментарий |
| string | Описание сцены в естественной языке. Не более 500 символов. |
| string | Одно из: red, orange, yellow, green, blue, purple, pink, brown, black, white, gray, teal, beige, gold, navy. Исключается |
| string | Например |
| integer | 0 (точно) … 255 (прибл). По умолчанию 20. Только в сочетании с |
| string[] |
|
| string[] | Unsplash, Pexels, Pixabay, Kaboompics, Burst, StockSnap, Picjumbo, Skitterphoto, NegativeSpace. |
| string[] |
|
| string | Точное имя (username). |
| string |
|
| string |
|
search_photos_by_image — визуальный поиск по примеру
Находит фотографии, похожие на референс‑изображение, при необходимости с уточнением словами («как на этом фото, но ночью»).
Параметр | Тип | Комментарий |
| string | Публичный http(s)-URL референс‑изображения. |
| object | Заполняется автоматически на платформах с поддержкой загрузки (например, ChatGPT). |
| string | Сырые байты в Base64, для бесплатного посетителя. |
| string | Дополнительный тест к изображению («например, с поднятыми руками»). |
| number | Вес |
| string | Те же фильтры, что и выше. |
| string | Пагинационный токен. |
Один из image_url, image_file или image_base64 обязателен. Изображения скачиваются на стороне сервера; максимум 20 МБ.
get_similar_photos — «ещё похожие»
Параметр | Тип | Комментарий |
| string | Обязательный. UUID фото, из результата предыдущего запроса. |
| string | Пагинационный токен. |
Что возвращается
Каждый фото имеет: id, ссылки в нескольких размерах, размеры, доминирующий цвет, ориентация, источник, лицензию, фотографа и обязательную attribution‑ строку для показа в словами — так ассистен достаточно данных, чтобы анализировать результаты, а не просто (просто) вывести их списком.
Результаты нумеруются #1, #2, … — так фотографию можно указать, как в обычном разговоре, без копирования id:

В клиентах, поддерживающих MCP MCP, ходип по миниатюре открывает панель с полной metadata — без лишних запросов: они уже в выводе инструмента.

Самостоятельное размещение
Вам это не обязательно — вышеуказанный размещённый сервер — обычный вход в бой. Однако файл является тонким обычным клиентом Pexafy API, поэтому способен запустить свой собьенный экземпляр с тем же своим 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 HTTPClaude 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Сервер отвечает на initialize и tools/list, не полагаясь на API‑ключ и даже на сеть: инструменты приходят из встроенного снапшота OpenAPI. Ключ нужен только для фактического поиска. HTTP‑экспонент требует, поднять transport, как в обоих compose‑файлах.
Конфигурация
Каждая настройка — это переменная окружения, и все они предусмотрены. Если ничего не выставить, pexafy-mcp запустится на stdio и отвечает на initialize и tools/list автономно. О двух стоит знать.
Variable | Default | Purpose |
|
|
|
|
| Корень Pexafy API — ставить |
Остальное — это конструкция для deployment, а не для отдельного пользователя, запускающего контейнер. Настройки живут в .env.example: fallback-ключ PEXAFY_API_KEY для stdio‑клиентов, не имеющих собственного ключа; PEXAFY_THUMBINA_BASE_URL и PEXAFY_THUMB_HMAC_SECRET для подписи миниатюр в « инлайновой » сетке, и PEXAFY_OAUTH_* вместе с MCP_RESOLVE_SECRET для работы HTTP‑транспорта как 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)Инструменты автоматически генерируются из спеку OpenAPI Pexafy через
FastMCP.from_openapi(); подлинная api остаётся единственным источником правды. Затемtooling.pyпереоформляет их для LLM — сужает до ядра поиска, убирает параметры, сосущие ли вниманием модели, и заворачивает закрытые наборы значений, чтобы не понадобилось никакого точечного справочника.build_server()собирает всё. Использование модуля не имеет побочных эффектов и не работает по сети: он читает вхоженные вместе assets/overview(обвиниают script).prepare.shобновляет их.search_photos_by_image— манн‑писнная: чат‑ассистент не может отправить бинарный файл прямо в MCP‑инструмент, поэтому инструмент сайта принимает URL изображения и сам загружает его на сервере.«Мозаика» (инлайновая сетка) — это графический ресурс MCP Apps. Браузерный клиент из
ext-appsвстроен прямо внутрь, потому что между периметром других. внешние скрипты in runtime недоступны.
Разработка
./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 toolsget_similar_photosFind similar photosARead-onlyIdempotentInspect
Use this tool when the user says 'find something similar', 'show me more like this', or 'I need a visually consistent set'. Requires a photo_id obtained from a previous search result. A person normally refers to a photo by the rank shown on the result grid rather than by its identifier; each search result carries both, in the same object. Each result carries: rank, its position on this page (1, 2, 3, …), which is also the number drawn on the inline grid and the handle a person naturally uses to refer to one photo among several; photo_id, the identifier the similar-photos tool takes, present in the same object as the rank; attribution, the credit line to display with the photo; and urls, the image at several sizes, urls.regular being the one to link to. Inline thumbnails are attached to this tool's result as an MCP App resource. Some clients, claude.ai on the web among them, render that resource only inside an expandable tool panel rather than in the reply itself; where it is not rendered, the photos remain reachable through their URLs.
| Name | Required | Description | Default |
|---|---|---|---|
| cursor | No | Token to fetch the next page. Take `pagination.next_cursor` from a previous response and pass it back here. See the Pagination guide. | |
| photo_id | Yes | The reference photo's unique Pexafy identifier (a UUID). |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | No | |
| meta | No | |
| error | No | |
| success | No | |
| pagination | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint, openWorldHint, idempotentHint, and non-destructive behavior, so the safety profile is covered. The description adds useful behavioral context: the result includes inline thumbnails as an MCP App resource, and warns that some clients (like claude.ai on web) render them only in an expandable panel, with URLs still available. This goes beyond annotations and is valuable for the agent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is relatively lengthy but every paragraph serves a purpose: it explains when to use, prerequisites, how users refer to photos (critical for agent understanding), and display behavior. It is front-loaded with the primary usage trigger and then provides necessary details. Slightly verbose but justified by the need to explain the rank vs. photo_id distinction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has an output schema, so return values are covered there. The description compensates for the complexity of the tool by explaining the relationship between rank and photo_id, which is not obvious from the schema. It also addresses pagination and resource rendering behavior. Given the moderate complexity and presence of output schema, this is adequately complete, though more details on what 'similar' entails could be added.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, with both parameters (photo_id and cursor) described in the schema. The description reinforces the use of photo_id (requires it from a prior search) and explains the cursor's role (pass pagination.next_cursor), but adds minimal additional semantics beyond the schema. Baseline 3 is appropriate given full schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it finds similar photos based on a photo_id, distinct from sibling search tools by focusing on similarity rather than keywords or image upload. It explicitly ties to user phrases like 'find something similar', making its purpose actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly explains when to use the tool ('when the user says...'), specifies the prerequisite (photo_id from a previous search), and details how a person refers to photos (by rank) versus the identifier, which prevents misuse. It also clarifies how to use the cursor for pagination.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_photosSearch photos by descriptionARead-onlyIdempotentInspect
Use this tool whenever the user needs an image, photo, or visual — for a presentation, blog, website, social-media post, mood board, or any creative project. Pexafy is a SEMANTIC search engine: describe the scene in full natural-language sentences, not keywords. Rich descriptions return far better results than tag-like queries. Good queries: 'a melancholy portrait of an old person sitting under a soft light'; 'two people sharing a bench in comfortable silence'; 'the last sunlight of the day hitting a dusty windowsill'; 'a child discovering snow for the first time'. Prefer this tool over search_photos_by_image when the user describes what they want in words. BUT if they want photos LIKE a specific image that has a URL — a photo from a previous result, or a public URL they gave — use search_photos_by_image instead (pass that URL, plus a q for any change like 'but with hands raised'). Only use THIS text tool for a reference image with NO URL (a file pasted/uploaded in the chat): describe what you see in rich detail — Pexafy is semantic, so a good description finds visually similar photos. Each result carries: rank, its position on this page (1, 2, 3, …), which is also the number drawn on the inline grid and the handle a person naturally uses to refer to one photo among several; photo_id, the identifier the similar-photos tool takes, present in the same object as the rank; attribution, the credit line to display with the photo; and urls, the image at several sizes, urls.regular being the one to link to. Inline thumbnails are attached to this tool's result as an MCP App resource. Some clients, claude.ai on the web among them, render that resource only inside an expandable tool panel rather than in the reply itself; where it is not rendered, the photos remain reachable through their URLs.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | 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'. | |
| cursor | No | 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. | |
| source | No | Keep only photos from these providers: Unsplash, Pexels, Pixabay, Kaboompics, Burst, StockSnap, Picjumbo, Skitterphoto, NegativeSpace. Repeat the parameter to pass several. | |
| color_hex | No | 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. | |
| after_date | No | Only return photos published on or after this date, formatted `YYYY-MM-DD`. | |
| color_name | No | 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. | |
| orientation | No | Keep only photos with these shapes: landscape, portrait, square. Repeat the parameter to pass several. | |
| license_type | No | 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. | |
| photographer | No | Only return photos from this photographer's username. Use `GET /api/v1/facets/photographers/suggest` to find usernames. | |
| color_tolerance | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | No | |
| meta | No | |
| error | No | |
| success | No | |
| pagination | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare a safe, read-only, idempotent operation, so the description's job is to add context beyond that. It does: semantic search behavior, result-field semantics (rank, photo_id, attribution, urls), the inline-thumbnail MCP resource, and the rendering caveat on claude.ai. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but it is front-loaded with the primary use case and every section earns its place: query style, examples, sibling distinction, result fields, and rendering behavior. A few example queries could be trimmed without losing meaning, which keeps it from a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 10 optional parameters, an output schema, and two siblings, the description is complete: it explains semantic querying, when to use each sibling, what each result field means, and how the inline resource may render. The output schema covers return values, so the description correctly focuses on selection and invocation behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3 and the schema already documents every parameter. The description adds real value by teaching the core q semantics, showing strong example queries, and explaining how photo_id connects to the similar-photos sibling, but it does not need to repeat the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific use case ('user needs an image, photo, or visual') and names the resource being searched. It clearly differentiates this text-query tool from search_photos_by_image, which is the main sibling it could be confused with.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance ('Prefer this tool over search_photos_by_image when the user describes what they want in words') and names the alternative with the exact input it needs. It also handles the edge case of a reference image with no URL, telling the agent to describe it in rich detail instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_photos_by_imageSearch photos by example imageARead-onlyIdempotentInspect
Find visually similar stock photos from an EXAMPLE IMAGE, optionally TWEAKED with words. This is the right tool for 'find photos LIKE THIS but ' (e.g. 'like this but with their hands raised', 'the same scene but at night'). Give the reference image one of three ways: (1) image_url — a public http(s) link: a photo from a PREVIOUS search result (reuse its image_url/urls.regular), or any public URL the user provides; (2) image_file — auto-filled by the host when the user UPLOADS an image (e.g. ChatGPT) — it is populated by the host, not by the caller; (3) image_base64 — raw base64 image bytes, for a programmatic client that already holds the file. A chat assistant has no access to the exact bytes of an image it was shown, so image_base64 is not available to it. Put any change in q; raise text_alpha to weight the text more. If the reference image has no URL and the host did not auto-provide image_file (e.g. a file pasted into a chat that can't be forwarded), you cannot send it — describe what you see and use search_photos instead. Every result carries an attribution you show.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | 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'. | |
| cursor | No | 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. | |
| source | No | Keep only photos from these providers: Unsplash, Pexels, Pixabay, Kaboompics, Burst, StockSnap, Picjumbo, Skitterphoto, NegativeSpace. Repeat the parameter to pass several. | |
| image_url | No | 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. | |
| after_date | No | Only return photos published on or after this date, formatted YYYY-MM-DD. | |
| color_name | No | 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. | |
| image_file | No | Filled in by the host when the user uploads an image, not by the caller. Carries the upload's `download_url` and `file_id`. | |
| text_alpha | No | 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`. | |
| orientation | No | Keep only photos with these shapes: landscape, portrait, square. Repeat the parameter to pass several. | |
| image_base64 | No | 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. | |
| license_type | No | 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. | |
| photographer | No | Only return photos from this photographer's exact username. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | No | |
| meta | No | |
| error | No | |
| success | No | |
| pagination | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly and idempotent, so the description doesn't need to repeat that. It adds meaningful context beyond annotations: the image_file is host-populated rather than caller-set, image_base64 is unavailable to chat assistants, and every result carries an attribution to display. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every section earns its place—it covers usage, input methods, edge cases, and attribution. The numbered list of image-providing options is clear and well-structured. It could be slightly trimmed, but the density is justified by the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the 12 parameters, 100% schema coverage, and an output schema, the description provides all necessary behavioral context: how to provide the reference image, the host-filling behavior of image_file, the text weighting mechanism, and the fallback to search_photos. It also mentions the attribution requirement from results, which is not in the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents each parameter. The description adds practical nuance beyond the schema, such as how text_alpha weights text against image, and the guidance to put any modification in q. This exceeds the baseline for fully covered schemas.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool finds visually similar stock photos from an example image, optionally tweaked with words. It explicitly distinguishes from siblings by providing a usage scenario ('find photos LIKE THIS but <change>') and names the alternative (search_photos) when the image can't be sent.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance with examples and a concrete fallback: when the image has no URL and no auto-provided file, use search_photos instead. It also explains the three ways to supply the reference image and which is appropriate for different clients.
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. Dates show when Glama detected each change.
1 tool update
v0.4.9- Changed
search_photos_by_image19 fields changed- added
Input schema / properties / after_date / descriptionAdded value: +"Only return photos published on or after this date, formatted YYYY-MM-DD." - added
Input schema / properties / color_name / descriptionAdded 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." - added
Input schema / properties / cursor / descriptionAdded 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." - added
Input schema / properties / image_base64 / descriptionAdded 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." - added
Input schema / properties / image_file / additionalPropertiesAdded value: +false - removed
Input schema / properties / image_file / anyOfRemoved value: -[ - { - "additionalProperties": true, - "type": "object" - }, - { - "type": "null" - } -] - removed
Input schema / properties / image_file / defaultRemoved value: -null - added
Input schema / properties / image_file / descriptionAdded 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`." - added
Input schema / properties / image_file / propertiesAdded value: +{ + "download_url": { + "type": "string" + }, + "file_id": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "mime_type": { + "type": "string" + } +} - added
Input schema / properties / image_file / requiredAdded value: +[ + "download_url", + "file_id" +] - added
Input schema / properties / image_file / typeAdded value: +"object" - added
Input schema / properties / image_url / descriptionAdded 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." - added
Input schema / properties / license_type / descriptionAdded 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." - added
Input schema / properties / orientation / descriptionAdded value: +"Keep only photos with these shapes: landscape, portrait, square. Repeat the parameter to pass several." - added
Input schema / properties / photographer / descriptionAdded value: +"Only return photos from this photographer's exact username." - added
Input schema / properties / q / descriptionAdded 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'." - added
Input schema / properties / source / descriptionAdded value: +"Keep only photos from these providers: Unsplash, Pexels, Pixabay, Kaboompics, Burst, StockSnap, Picjumbo, Skitterphoto, NegativeSpace. Repeat the parameter to pass several." - added
Input schema / properties / text_alpha / descriptionAdded 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`." - changed
Output 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" +}
2 tool updates
v0.4.0- Added
get_similar_photos - Removed
photo_similar
3 tool updates
v0.2.0- First observed
photo_similar - First observed
search_photos - First observed
search_photos_by_image
TDQS
Each tool has a clearly documented input type (text query vs. image/file vs. previous photo_id), and the descriptions are explicit about which phrase or condition triggers each tool. However, search_photos_by_image and get_similar_photos both produce visually similar photos, and their boundary (one tweaks by text, the other just fetches similar) could occasionally mislead an agent even with the detailed guidance.
All names follow a snake_case verb_noun pattern (search_photos, search_photos_by_image, get_similar_photos), and the shared 'search_photos' prefix on two tools is helpful. The slight deviation is 'get' in get_similar_photos versus 'search' elsewhere for the same core concept, but the pattern is otherwise uniform and predictable.
Three tools is a lean but sensible footprint for a dedicated photo-search server, covering the natural query modalities (text, image, similar-by-id). While each tool does earn its place, the set feels slightly minimal—no dedicated tool for fetching individual photo details, but results already carry URLs and attribution, so it works.
The core workflow is complete: text query → results → similar-by-photo_id, and image query → results with tweakable text, covering the main stock-photo search use cases with no dead ends. Minor gaps exist (no downloadable/collections/curated feed support, no orientation/filter parameters), but agents can work around these with richer natural-language calls to search_photos.
Maintenance
Related MCP Connectors
MCP server for Qwen Image 3 AI image generation
MCP server for Wan AI video generation
MCP server for Midjourney AI image generation and editing
MCP server for Flux AI image generation
Related MCP Servers
- AlicenseBqualityDmaintenanceThis 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.12Apache 2.0
- FlicenseAqualityDmaintenanceAn 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.21-
- AlicenseAqualityCmaintenanceMCP server that allows AI coding agents to search, download, and convert stock photos from Pexels, Unsplash, and Pixabay into WebP format for direct use in web development projects.3253MIT
- AlicenseAqualityFmaintenanceAn MCP server that enables AI assistants to search for royalty-free images from Pexels and Unsplash using natural language, returning structured results with metadata.559MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Pexafy/pexafy-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server