joelio/stocky

๐Ÿ”Ž Search & Data Extraction
0 Views
0 Installs

๐Ÿ โ˜๏ธ ๐Ÿ  - An MCP server for searching and downloading royalty-free stock photography from Pexels and Unsplash. Features multi-provider search, rich metadata, pagination support, and async performance for AI assistants to find and access high-quality images.

Quick Install

One-Click IDE Configuration
claude_desktop_config.json
{
  "mcpServers": {
    "joelio-stocky": {
      "command": "npx",
      "args": [
        "-y",
        "joelio-stocky"
      ]
    }
  }
}
Or

Using an AI coding agent (Claude Code, Cursor, etc.)? Copy a ready-made prompt that tells it to fetch the setup instructions and install this server for you.

Documentation Overview

Stocky Logo
Stocky
Find beautiful royalty-free stock images ๐Ÿ“ธ

CI Python 3.10+ MCP Compatible License: MIT

Stocky is an MCP server that lets an AI assistant search, inspect and download royalty-free stock photography from Pexels and Unsplash.

๐Ÿ†• Version 2.0 โ€” already using Stocky? Read this

Your MCP client config keeps working. If it points at stocky_mcp.py, that file is still there and still starts the server. Nothing breaks on upgrade.

But pip install -r requirements.txt will fail โ€” that file is gone, and so is setup.py. From a source checkout, install with uv sync or pip install -e . instead. See Upgrading from 1.x for the two-minute version, or the CHANGELOG for everything.

The headline changes: pycurl is gone (so no more C build errors), searches actually run concurrently, failed providers now report why instead of returning nothing, and Python 3.10+ is required.

โœจ Features

  • ๐Ÿ” Multi-provider search โ€” queries Pexels and Unsplash concurrently
  • ๐Ÿ“Š Rich metadata โ€” dimensions, photographer, tags and every image size
  • โš–๏ธ Licence-aware โ€” generates the attribution each provider requires, and reports Unsplash downloads as their API guidelines demand
  • โฑ๏ธ Search caching โ€” a short TTL cache keeps repeat queries off your quota
  • ๐Ÿ›ก๏ธ Honest errors โ€” a failing provider is reported, not silently returned as "no results"
  • ๐ŸŽฏ Filters โ€” orientation, colour and sort, validated per provider

Photography Example

Beautiful stock photography at your fingertips Example image used for demonstration purposes

Mountain Landscape Stunning landscapes available through multiple providers

Photo by Simon Berger on Unsplash

๐Ÿš€ Quick start

1. Get API keys

Both are free:

ProviderWhereNotes
Pexels ๐Ÿ“ทpexels.com/api200 requests/hour by default
Unsplash ๐ŸŒ…unsplash.com/developers50/hour in demo mode, 1000/hour once your app is approved

You only need one โ€” Stocky runs with whichever providers are configured.

2. Add it to your MCP client

The recommended configuration needs no installation at all:

{
  "mcpServers": {
    "stocky": {
      "command": "uvx",
      "args": ["stocky-mcp"],
      "env": {
        "PEXELS_API_KEY": "your_pexels_key",
        "UNSPLASH_ACCESS_KEY": "your_unsplash_key"
      }
    }
  }
}
Other ways to run it

Installed with pip or uv:

uv tool install stocky-mcp     # or: pipx install stocky-mcp
{
  "mcpServers": {
    "stocky": {
      "command": "stocky-mcp",
      "env": { "PEXELS_API_KEY": "your_pexels_key" }
    }
  }
}

From a source checkout โ€” still supported so existing configurations keep working:

{
  "mcpServers": {
    "stocky": {
      "command": "python",
      "args": ["/path/to/stocky/stocky_mcp.py"],
      "env": { "PEXELS_API_KEY": "your_pexels_key" }
    }
  }
}

Stocky reads its configuration from the environment its client starts it in. It does not load a .env file of its own, so keys must go in the env block above.

โš™๏ธ Configuration

VariableDefaultPurpose
PEXELS_API_KEYโ€”Enables the Pexels provider
UNSPLASH_ACCESS_KEYโ€”Enables the Unsplash provider
ENABLE_ATTRIBUTION_LINKSfalseInclude attribution details in every result
STOCKY_CACHE_TTL300Search cache lifetime in seconds. 0 disables caching
STOCKY_HTTP_TIMEOUT15Per-request timeout in seconds
STOCKY_MAX_DOWNLOAD_BYTES26214400Refuse downloads larger than this (25 MiB)
STOCKY_DOWNLOAD_ROOTโ€”If set, confine all downloads to this directory
STOCKY_LOG_LEVELINFODEBUG, INFO, WARNING, ERROR
STOCKY_USER_AGENTstocky-mcpUser-Agent sent to providers

STOCKY_DOWNLOAD_ROOT is worth setting. Download paths come from model-generated tool calls, and this confines writes to one directory. Stocky always refuses a non-image file extension, and validates that image URLs from a provider are public HTTP(S) addresses before fetching them, but a download root is the strongest control available.

๐Ÿ“– Usage

Stock Photography Example

Find the perfect image for your project

Stocky exposes three MCP tools. You don't call them directly โ€” your client invokes them when you ask in natural language.

Search stock photos for a misty pine forest at dawn.

Find 30 landscape-orientation mountain photos from Pexels.

Download pexels_123456 at medium size to ~/Pictures/mountain.jpg

๐Ÿ› ๏ธ Tools

search_stock_images

ParameterTypeDefaultNotes
querystrrequiredSearch terms
providerslistall configured["pexels", "unsplash"]
per_pageint20Clamped per provider: Pexels 80, Unsplash 30
pageint11-based
sortstrrelevantrelevant or latest. Unsplash only โ€” Pexels has no sort option
orientationstrโ€”landscape, portrait, square
colorstrโ€”A colour name; Pexels also accepts hex codes
include_attributionboolโ€”Overrides ENABLE_ATTRIBUTION_LINKS

Returns results grouped by provider, plus total_results and โ€” if any provider failed โ€” an errors map explaining which and why.

An invalid orientation or color is dropped rather than forwarded: Pexels silently ignores bad filters and still returns 200, which would otherwise hide the mistake, while Unsplash rejects them outright with HTTP 400.

get_image_details

ParameterTypeDefaultNotes
image_idstrrequiredPrefixed id, e.g. pexels_123456
include_attributionboolโ€”Overrides ENABLE_ATTRIBUTION_LINKS

download_image

ParameterTypeDefaultNotes
image_idstrrequiredPrefixed id
sizestroriginalthumbnail, small, medium, large, original
output_pathstrโ€”Omit to receive base64 data instead

If a provider doesn't offer the requested size, the closest available one is used rather than failing.

Resource

stock-images://help โ€” a usage guide the assistant can read on demand.

โš–๏ธ Licence and attribution

Images are free to use, but both providers require attribution as a condition of API access. This is not optional, and the previous version of this document was wrong to imply otherwise for Pexels.

Pexels requires a prominent link back to Pexels wherever API results are shown, and asks that you credit the photographer where possible.

Unsplash requires that you credit both the photographer and Unsplash, with UTM parameters on the links, and that images are hotlinked from the URLs the API returns rather than re-hosted.

Set ENABLE_ATTRIBUTION_LINKS=true and each result gains an attribution block with ready-made text and html in the correct format for its provider.

Note on download_image. Saving Unsplash bytes to disk or returning them base64-encoded is in tension with Unsplash's hotlinking requirement. Stocky fires the mandatory download-report endpoint whenever you download an Unsplash image, but if you are displaying images in an application, use the hotlinked URLs from the search results instead of downloading them.

Read the full terms: Pexels ยท Unsplash

โฌ†๏ธ Upgrading from 1.x

Version 2.0 restructured the project. Existing MCP client configurations keep working โ€” the root stocky_mcp.py is deliberately retained as a launcher โ€” but installation changed, and a few behaviours are intentionally different.

What you need to do

If you run it via uvx or an installed console script, nothing. Upgrade and carry on.

If you run it from a source checkout, reinstall. requirements.txt, setup.py and setup.cfg were replaced by pyproject.toml:

git pull
uv sync                     # or: pip install -e .

pip install -r requirements.txt now fails โ€” the file no longer exists.

Check your Python version. 2.0 requires Python 3.10+. The old setup.py claimed 3.8 support, but the mcp SDK has always required 3.10, so that claim was never actually satisfiable.

Consider switching to uvx. It needs nothing installed and avoids the whole class of "which interpreter did my editor launch?" startup failures:

{
  "mcpServers": {
    "stocky": {
      "command": "uvx",
      "args": ["stocky-mcp"],
      "env": { "PEXELS_API_KEY": "your_pexels_key" }
    }
  }
}

What changed underneath

ChangeWhy it matters to you
pycurl โ†’ httpxNo C extension to compile, so no more build failures on install. httpx was already required by the mcp SDK, so this removed a dependency.
Searches now run concurrentlyThe old code was async in name only โ€” it made blocking calls inside async def. Searching both providers now costs roughly one provider's latency.
Failed providers report whyPreviously an invalid API key returned an empty list, identical to a search that genuinely matched nothing.
per_page clamped per providerUnsplash silently falls back to 10 above its maximum of 30, so asking for 50 used to return fewer images than asking for 30.
Attribution generated for youBoth providers require it as a condition of API access. Set ENABLE_ATTRIBUTION_LINKS=true and every result carries ready-made text and HTML.
New configuration optionsCaching, timeouts, download size caps and a download root โ€” see Configuration.

The one thing that might affect your code

Only if you parse Stocky's responses programmatically. A search that fails now returns an errors map naming the provider and the reason, where it used to return an empty result list:

{
  "query": "mountains",
  "results": { "pexels": [], "unsplash": [ /* ... */ ] },
  "errors": { "pexels": "pexels rejected the API key (HTTP 401)..." }
}

If you treated "empty" as "no matches", check for errors too โ€” otherwise a broken key looks like a query with no results. This is the fix, not a regression, but it is a visible change.

Nothing else in the tool surface changed: search_stock_images, get_image_details and download_image take the same arguments they always did, so existing prompts and workflows are unaffected.

๐Ÿง‘โ€๐Ÿ’ป Development

git clone https://github.com/joelio/stocky.git
cd stocky
uv sync --group dev
uv run pytest -m "not integration"   # unit tests, no network
uv run pytest -m integration         # needs real API keys
uv run ruff check . && uv run ruff format --check .
uv run mypy

See CONTRIBUTING.md for more.

๐Ÿ› Troubleshooting

Client reports a timeout on startup Almost always a launch problem rather than a Stocky bug. GUI-launched clients often have a minimal PATH, so uvx or a virtualenv python may not be found โ€” use an absolute path, or uvx with uv installed system-wide. You can reproduce the handshake yourself:

printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
  | stocky-mcp

A JSON response means the server is fine. Note that stdout carries the protocol, so Stocky sends all logging to stderr โ€” anything else printed there would corrupt the stream.

"No image providers are configured" No keys reached the server. They must be in the env block of your client config; names are case-sensitive.

A provider returns an error Check errors in the response. Auth failures, rate limits and timeouts are now reported explicitly rather than looking like an empty result set.

Pexels seems to accept an invalid key It does. Pexels returns 200 for a malformed key, so a typo can look like it works. If results seem wrong, verify the key directly.

Rate limits

ProviderLimitOn exhaustion
Pexels200/hour (default, per key)HTTP 429
Unsplash50/hour demo, 1000/hour productionHTTP 403 with X-Ratelimit-Remaining: 0

๐Ÿค Contributing

Contributions are welcome โ€” see CONTRIBUTING.md. For major changes, please open an issue first to discuss the approach.

๐Ÿ™ Acknowledgments


Made with ๐Ÿ’œ by the Stocky Team

Related MCP Servers

linxule/mineru-mcp

๐Ÿ“‡ โ˜๏ธ - MCP server for MinerU document parsing API. Parse PDFs, images, DOCX, and PPTX with OCR (109 languages), batch processing (200 docs), page ranges, and local file upload. 73% token reduction with structured output.

๐Ÿ”Ž Search & Data Extraction1 views
0xdaef0f/job-searchoor

๐Ÿ“‡ ๐Ÿ  - An MCP server for searching job listings with filters for date, keywords, remote work options, and more.

๐Ÿ”Ž Search & Data Extraction0 views
Aas-ee/open-webSearch

๐Ÿ ๐Ÿ“‡ โ˜๏ธ - Web search using free multi-engine search (NO API KEYS REQUIRED) โ€” Supports Bing, Baidu, DuckDuckGo, Brave, Exa, and CSDN.

๐Ÿ”Ž Search & Data Extraction0 views
ac3xx/mcp-servers-kagi

๐Ÿ“‡ โ˜๏ธ - Kagi search API integration

๐Ÿ”Ž Search & Data Extraction0 views

Engagement

Views
0
Installs
0
Upvotes
0

Views and upvotes are unique per visitor network (hashed IP). Installs count copy actions.

Status

Health: Not checked yet

We have not completed a health check for this listing yet.

No check timestamp yet.

Unclaimed listing (imported or pending owner verification). Claim it โ†’
โ˜… Spotlight Slot

Feature Your MCP Server

Get maximum visibility for your server across our directory, search results, and detail pages.

Spotlight Your Server

Own this project?

This directory is pre-filled from public sources. Claim via GitHub README, site badge, or DNS TXT to get the verified badge and attach your website.

Claim this listing

Promote this listing

Optional paid placement. Free listings stay free forever.

Share & Embed

Add our SVG badge (dark/light directory styles) or embeddable widget to your site.