Unsplash MCP Server

io.github.hanoak由 GitHub 账号 hanoak 发布v1.0.1更新于 Oct 28, 2026

MCP server for the Unsplash API: search & fetch photos with attribution + download tracking.

概览

AI 生成的概览

本地运行的 Unsplash API MCP 服务器,让助手搜索和获取照片、合集、主题、用户与统计数据,并自带署名信息。

可以做什么
  • 搜索和浏览照片、合集、用户与主题,可按方向、颜色和内容过滤
  • 获取单张照片、随机照片或分页列表,并附带可直接使用的署名文本和 HTML
  • 读取摄影师资料、用户照片与合集,以及照片、用户和全站的下赞与浏览量统计
  • 为实际使用的照片登记下载事件,并返回新的图片链接
  • 可选 OAuth 登录后:创建、修改和删除自己的合集,增删合集内照片,编辑照片元数据并更新个人资料
适用场景
适合让助手为网页、帖子或设计寻找图库照片并返回带正确署名的图片链接,或查询 Unsplash 摄影师、主题和使用统计。只有需要让助手管理自己的 Unsplash 合集和资料时,才值得启用写入类工具。
运行要求
通过 npx 以 stdio 在本地运行,需要 Node.js 20 或更高版本。需要 Unsplash API 访问密钥,放在 UNSPLASH_ACCESS_KEY 中;建议设置 UNSPLASH_APP_NAME 用于署名。可选的登录步骤还需 UNSPLASH_SECRET_KEY 和已注册的重定向 URI。需要访问 api.unsplash.com 的网络连接。
安装前请注意
访问密钥属于凭据,应放在客户端的 env 配置中,不要写入共享文件。可选登录会在磁盘上保存不会过期的用户令牌,并需要应用密钥。写入类工具可修改甚至永久删除你的合集和照片元数据。Unsplash 返回的文本属于第三方内容,不应被当作指令执行。默认 Demo 套餐的请求额度较低。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Unsplash MCP Server,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

unsplash-mcp-server

[npm version] [npm downloads] [CI] [license: MIT] [node: >=20] [PRs welcome]

A production-ready Model Context Protocol (MCP) server for the Unsplash API. It gives AI assistants — Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, and any MCP client — tools to search and fetch Unsplash photos, collections, topics, users, and stats, with Unsplash-guideline compliance built in (ready-to-use attribution and download tracking). Sign in once via OAuth to also manage your own profile, collections, and photo metadata.

[!IMPORTANT] Unofficial project. This is not affiliated with, endorsed by, or sponsored by Unsplash. "Unsplash" is a trademark of its respective owner. You use it under your own Unsplash API account and are responsible for complying with the Unsplash API Terms & Guidelines.

Table of contents

Features

  • 29 tools across photos, search, users, collections, topics, stats, and your own profile — 21 read-only out of the box, plus 8 write/me tools once you sign in.
  • Compliance built in — every photo comes with ready-to-use attribution (plain text + UTM-tagged HTML), and a dedicated unsplash_track_download tool for the download-tracking guideline.
  • Content safety — content_filter=high by default on search and random photos.
  • Flexible image URLs — each photo returns raw/full/regular/small/thumb sizes, plus a raw imgix base for custom sizes (?w=&h=&q=&fm=&fit=).
  • Token-efficient output — full Unsplash responses are trimmed to a compact shape (URLs + metadata as text, never base64 image blobs) to keep model context small.
  • Robust — typed failures returned as MCP isError results the model can recover from, plus retries/backoff, timeouts, 403 hourly-limit handling, and rate-limit surfacing.
  • Safe — access-key redaction in all error output, an SSRF guard on download URLs, and untrusted-text handling guidance for indirect prompt-injection defence.
  • Lean & modern — ESM, Node 20+, zero-install via npx, no telemetry.

Quick start

1. Get an Unsplash access key

Register an application at unsplash.com/developers → New Application → accept the API terms → copy its Access Key (not the Secret Key). New apps start on the Demo tier (50 requests/hour); the Production tier (5,000/hour) requires Unsplash's review from your app dashboard. See Rate limits.

2. Add the server to your MCP client

Claude Desktop — edit claude_desktop_config.json:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
json
{  "mcpServers": {    "unsplash": {      "command": "npx",      "args": ["-y", "@hanoak/unsplash-mcp-server"],      "env": {        "UNSPLASH_ACCESS_KEY": "your_access_key",        "UNSPLASH_APP_NAME": "your_registered_app_name"      }    }  }}

Restart the client. See Configuration for every supported variable.

Other clients (Claude Code, Cursor, VS Code, Windsurf, generic stdio)

Claude Code (CLI):

bash
claude mcp add unsplash \  --env UNSPLASH_ACCESS_KEY=your_access_key \  --env UNSPLASH_APP_NAME=your_registered_app_name \  -- npx -y @hanoak/unsplash-mcp-server

Cursor — ~/.cursor/mcp.json (global) or .cursor/mcp.json (per-project): use the exact same mcpServers block as Claude Desktop above.

Windsurf — ~/.codeium/windsurf/mcp_config.json: same mcpServers block as Claude Desktop above.

VS Code — .vscode/mcp.json (note the top-level key is servers, not mcpServers):

json
{  "servers": {    "unsplash": {      "command": "npx",      "args": ["-y", "@hanoak/unsplash-mcp-server"],      "env": {        "UNSPLASH_ACCESS_KEY": "your_access_key",        "UNSPLASH_APP_NAME": "your_registered_app_name"      }    }  }}

Any other MCP client — run the server over stdio with:

bash
UNSPLASH_ACCESS_KEY=your_access_key npx -y @hanoak/unsplash-mcp-server

Point your client's stdio transport at command: npx, args: ["-y", "@hanoak/unsplash-mcp-server"], and pass the key via env.

3. Try it

Restart your client and ask:

"Find me a photo of mountains on Unsplash and show the attribution."

Example interaction

A typical flow: the model calls unsplash_search_photos, picks a result, presents the image with its attribution, and calls unsplash_track_download when it actually uses the photo.

You: Find a landscape photo of a foggy pine forest and credit the photographer.

Assistant: (calls unsplash_search_photos with query: "foggy pine forest", orientation: "landscape", picks the best result, then calls unsplash_track_download with its download_location) Here's a great match — Photo by Jane Doe on Unsplash — along with a ready-to-embed credit line and the image URL.

Each tool returns a compact JSON payload. Here's the shape of a single photo result (illustrative values):

Example tool output
json
{  "photo": {    "id": "Dwu85P9SOIk",    "description": "brown rocky mountain under blue sky during daytime",    "width": 6000,    "height": 4000,    "color": "#734940",    "blur_hash": "L6Pj0^i_.AyE_3t7t7R**0o#DgR4",    "urls": {      "raw": "https://images.unsplash.com/photo-1465…?ixid=…",      "full": "https://images.unsplash.com/photo-1465…?ixid=…&q=85",      "regular": "https://images.unsplash.com/photo-1465…?ixid=…&w=1080",      "small": "https://images.unsplash.com/photo-1465…?ixid=…&w=400",      "thumb": "https://images.unsplash.com/photo-1465…?ixid=…&w=200"    },    "photo_page": "https://unsplash.com/photos/Dwu85P9SOIk",    "download_location": "https://api.unsplash.com/photos/Dwu85P9SOIk/download?ixid=…",    "photographer": {      "name": "Jane Doe",      "username": "janedoe",      "profile": "https://unsplash.com/@janedoe"    },    "attribution": {      "text": "Photo by Jane Doe on Unsplash",      "html": "Photo by <a href=\"https://unsplash.com/@janedoe?utm_source=your_app&utm_medium=referral\">Jane Doe</a> on <a href=\"https://unsplash.com/?utm_source=your_app&utm_medium=referral\">Unsplash</a>",      "photographerName": "Jane Doe",      "photographerUrl": "https://unsplash.com/@janedoe?utm_source=your_app&utm_medium=referral",      "unsplashUrl": "https://unsplash.com/?utm_source=your_app&utm_medium=referral"    }  },  "rate_limit": { "limit": 50, "remaining": 49 }}

Every tool result includes a rate_limit object (limit, remaining) read from the Unsplash response headers. List/search tools wrap results in photos/collections/users/topics arrays with pagination fields (total, total_pages, count, page, per_page).

Configuration

Configuration is entirely via environment variables — no config files, no flags for secrets.

Environment variableRequiredDescription
UNSPLASH_ACCESS_KEYyesYour Unsplash API access key. The server exits at startup with a clear message if it is missing or blank.
UNSPLASH_APP_NAMErecommendedYour registered Unsplash app name, used as the attribution utm_source. Defaults to a generic value (with a startup warning) if unset.
UNSPLASH_SECRET_KEYonly for loginYour Unsplash app's secret key. Only needed to run login — see OAuth sign-in.
UNSPLASH_OAUTH_REDIRECT_URIonly for loginOverride the default http://localhost:8734/callback used by login. Must match the redirect URI registered on your Unsplash app.
LOG_LEVELnodebug | info | warn | error (default info). All logs go to stderr; stdout carries only the MCP protocol.

CLI flags: --version and --help are supported (e.g. npx @hanoak/unsplash-mcp-server --version). login/logout are subcommands, not flags — see below.

OAuth sign-in (optional)

The 21 core tools work out of the box with just UNSPLASH_ACCESS_KEY. To also use the 8 write/me tools (update your profile, manage collections, edit photo metadata), sign in once via OAuth:

  1. On your app's page at unsplash.com/oauth/applications, add http://localhost:8734/callback as a redirect URI, and copy the Secret key.

  2. Set both UNSPLASH_ACCESS_KEY and UNSPLASH_SECRET_KEY in your shell (not just the MCP client config — login runs from your terminal).

  3. Run:

    bash
    npx @hanoak/unsplash-mcp-server login

    This opens your browser to Unsplash's consent screen, captures the redirect on a short-lived local server, exchanges the code for a user access token, and saves it to ~/.config/unsplash-mcp-server/credentials.json (owner-only file permissions). Unsplash user access tokens don't expire, so this is a one-time step — no periodic re-auth.

  4. Restart your MCP client. The 8 write/me tools are now available; the 21 read-only tools are unaffected either way.

Run npx @hanoak/unsplash-mcp-server logout at any time to remove the stored token. To revoke it server-side, regenerate your app's secret key from the Unsplash dashboard.

Tools

All tools are namespaced unsplash_*. Most are read-only (annotated readOnlyHint: true); the exceptions are unsplash_track_download (registers a download event) and the 8 write/me tools below, all marked non-read-only and gated behind OAuth sign-in. Parameters map to the Unsplash API; per_page and stats quantity are clamped to a max of 30, and page is 1-based.

DomainTools
Photosrandom_photo, list_photos, get_photo, photo_statistics, track_download, update_photo 🔒
Searchsearch_photos, search_collections, search_users
Usersget_user, user_photos, user_collections, user_statistics
Collectionslist_collections, get_collection, collection_photos, related_collections, create_collection 🔒, update_collection 🔒, delete_collection 🔒, add_photo_to_collection 🔒, remove_photo_from_collection 🔒
Topicslist_topics, get_topic, topic_photos
Statstotal_stats, month_stats
Meget_my_profile 🔒, update_my_profile 🔒

🔒 = requires OAuth sign-in (login) first.

Tool reference

Photos
ToolParametersDescription
unsplash_random_photoquery?, orientation? (landscape|portrait|squarish), content_filter? (low|high, default high), collections?, topics?, username?A single random photo, optionally filtered.
unsplash_list_photospage? (default 1), per_page? (default 10, max 30)The latest featured photos, paginated.
unsplash_get_photoid (required)A single photo by ID or slug, full detail.
unsplash_photo_statisticsid (required), quantity? (days, default 30, max 30)Download/view totals for a photo over N days.
unsplash_track_downloaddownload_location (required) — the download_location URL from a prior photo result (must be an https://api.unsplash.com URL)Registers a download on real use; returns a fresh, usable download URL.
unsplash_update_photo 🔒id (required), show_on_profile?, description?, tags? (comma-separated), location? (city/country/name/latitude/longitude), exif? (make/model/exposure_time/aperture_value/focal_length/iso_speed_ratings)Update metadata on a photo you own. Only the fields you pass are changed.
Search
ToolParametersDescription
unsplash_search_photosquery (required), page?, per_page?, order_by? (latest|editorial|relevant), orientation?, color? (11 named colors, e.g. blue), content_filter? (default high), collections?, lang?Keyword photo search with rich filters.
unsplash_search_collectionsquery (required), page?, per_page?Keyword collection search.
unsplash_search_usersquery (required), page?, per_page?Keyword user search.
Users
ToolParametersDescription
unsplash_get_userusername (required)A user's public profile.
unsplash_user_photosusername (required), page?, per_page?, order_by? (latest|oldest|popular|views|downloads), orientation?A user's photos, paginated.
unsplash_user_collectionsusername (required), page?, per_page?A user's collections, paginated.
unsplash_user_statisticsusername (required), quantity? (days, default 30, max 30)A user's download/view totals over N days.
Collections
ToolParametersDescription
unsplash_list_collectionspage?, per_page?The latest featured collections.
unsplash_get_collectionid (required)A single collection by ID.
unsplash_collection_photosid (required), page?, per_page?, orientation?Photos within a collection, paginated.
unsplash_related_collectionsid (required)Collections related to a given one.
unsplash_create_collection 🔒title (required), description?, private?Create a new collection you own.
unsplash_update_collection 🔒id (required), title?, description?, private?Update a collection you own. Only the fields you pass change.
unsplash_delete_collection 🔒id (required)Permanently delete a collection you own. Cannot be undone.
unsplash_add_photo_to_collection 🔒id (required), photo_id (required)Add a photo to a collection you own.
unsplash_remove_photo_from_collection 🔒id (required), photo_id (required)Remove a photo from a collection you own.
Me (requires OAuth sign-in)
ToolParametersDescription
unsplash_get_my_profile 🔒(none)Your own profile, including private fields (email, uploads remaining).
unsplash_update_my_profile 🔒username?, first_name?, last_name?, email?, url?, location?, bio?, instagram_username?Update your own profile. Only the fields you pass are changed.
Topics & stats
ToolParametersDescription
unsplash_list_topicspage?, per_page?, order_by? (featured|latest|oldest|position), ids? (comma-separated)Curated topics, paginated.
unsplash_get_topicid (required) — ID or slug (e.g. nature, wallpapers)A single topic.
unsplash_topic_photosid (required), page?, per_page?, orientation?, order_by? (latest|oldest|popular)Photos within a topic, paginated.
unsplash_total_stats(none)Unsplash-wide totals (photos, downloads, …).
unsplash_month_stats(none)Unsplash-wide totals for the past 30 days.

Output shape

Tools return trimmed, token-efficient JSON rather than raw Unsplash responses:

  • Photos → id, description, width/height, color, blur_hash, urls (raw/full/regular/small/thumb), photo_page, download_location, photographer, and a ready-to-use attribution object. See Example interaction.
  • Users → id, username, name, bio, location, profile_url, profile_image, total_photos, total_collections.
  • Collections / Topics → title, description, counts, page link, curator/owners, and a compact cover_photo.
  • Every result carries a rate_limit (limit, remaining); lists/searches add pagination fields.

Resources & prompts

Beyond tools, the server also exposes:

  • Resources — compact guides your client can pull in as context:

    • unsplash://guides/attribution — attribution, download tracking, hotlinking, content safety.
    • unsplash://guides/oauth-setup — how to sign in for the 8 write/me tools.
    • unsplash://guides/prompts — which of the prompts below to use for a given task.
  • Prompts — ready-made tasks your client can surface directly; each expands into a guided, multi-step tool-calling task:

    PromptArgumentsWhat it does
    find_photosubject (required), orientation?Search for one photo and present it with attribution.
    photo_gallerytheme (required), count?, orientation?, color?Build a themed set of photos (up to 10), each with attribution.
    topic_spotlighttopic (required), count?Showcase a curated topic's best photos.
    photographer_spotlightusername (required), count?A photographer's profile + their most popular work.
    platform_pulse(none)A quick Unsplash-wide stats briefing.
    curate_collection 🔒theme (required), count?, collection_id?Search, then build (or extend) a real collection from the matches.
    describe_photo 🔒id (required), description?, tags?Add a description/tags to a photo you own.
    refresh_profile 🔒bio?, location?, url?Update your own bio, location, or portfolio URL.

    🔒 = requires OAuth sign-in first.

Example prompts

Natural-language asks that map cleanly onto the tools:

  • "Find a photo of a foggy forest at sunrise and give me the HTML attribution."
  • "Search Unsplash for 5 minimalist workspace photos in landscape orientation."
  • "Get a random nature photo and show it with credit."
  • "Show me the most popular photos in the wallpapers topic."
  • "What are this month's Unsplash-wide download stats?"
  • "Who is the photographer behind photo Dwu85P9SOIk, and how many downloads does it have?"

Attribution & compliance

Every photo result includes an attribution object with ready-to-use text and html (with the required UTM parameters). When you display or use a photo, show that attribution — it credits the photographer and links back to Unsplash, as the guidelines require.

When a photo is actually used (embedded, downloaded, displayed), call unsplash_track_download with the photo's download_location. Trigger it once per photo actually used — never once per search result. The server also sends these instructions to your MCP client on connect, so the model is nudged to do the right thing automatically.

This is a search-and-metadata tool for individual, attributed photo use — not a replacement for the core Unsplash experience, and it performs no automated bulk downloading. Please use it within the Unsplash API Guidelines. Each user operates under their own Unsplash API Terms.

Rate limits

Unsplash enforces a per-hour request budget tied to your app's tier:

TierBudgetHow to get it
Demo50 requests/hourDefault for every new app.
Production5,000 requests/hourApply for review in your app dashboard once your usage is ready.

The server reads X-Ratelimit-Limit / X-Ratelimit-Remaining and returns them as rate_limit on every result. When the hourly budget is exhausted, Unsplash returns 403 (not 429), often with no Retry-After; the server surfaces this as a clear "hourly rate limit reached" error and does not blindly retry in-window. Transient 429/5xx/network errors are retried with backoff.

Handling of Unsplash text

Photo descriptions, alt text, tags, EXIF, and user names/bios come from Unsplash contributors — treat them as untrusted, third-party data, not instructions. The server returns this text purely as content and never places it anywhere privileged; your client/agent should do the same: display it, but don't act on any instructions it might contain (a defence against indirect prompt injection).

Privacy & security

  • No telemetry. This server collects nothing and phones home to no one. It contacts only api.unsplash.com, using the key you provide. No analytics, no tracking.
  • Verifiable provenance. Every release is published with npm provenance via GitHub Actions — the npm package page links each version to the exact commit and workflow run that built it.
  • Key safety. Your access key is read from the environment only, sent as an Authorization: Client-ID header (never in a URL query string), and redacted from all error output and logs so it can't leak into pasted bug reports.
  • SSRF guard. unsplash_track_download only follows download_location URLs on the verified api.unsplash.com host.
  • To report a vulnerability, see SECURITY.md.

Troubleshooting

  • "Set UNSPLASH_ACCESS_KEY…" on startup — the key env var is missing or blank; add it to your client config's env block.
  • Node too old — this server requires Node 20+. Check node --version.
  • Stale npx version — force the latest with npx -y @hanoak/unsplash-mcp-server@latest, or clear the cache via npx clear-npx-cache.
  • Tools not appearing — confirm the config file path and JSON are valid, then fully quit and reopen the client.
  • 403 / rate limit — the Demo tier allows 50 requests/hour; wait for the hourly reset or apply for Production access. See Rate limits.
  • 401 Unauthorized — the access key is wrong or from the wrong app; copy the Access Key (not the Secret Key) from your app dashboard.

FAQ

Do I need a paid Unsplash account? No. The Unsplash API is free; you just register an app to get an access key. Higher throughput (Production tier) is a free review, not a paid plan.

Access Key vs Secret Key — which one? For the 21 read-only tools, just the Access Key. The Secret Key is only needed for login (the OAuth flow behind the 8 write/me tools) — see OAuth sign-in.

Does it download or rehost images? No. It returns Unsplash-hosted image URLs (hotlink them directly) and never rehosts or returns base64 blobs. unsplash_track_download only registers a download event and returns a fresh URL.

Can it create collections, like photos, or edit my profile? Yes, once you sign in — unsplash_create_collection and friends, and unsplash_update_photo/unsplash_update_my_profile. (Liking photos is not currently exposed as a tool.)

Does it work outside Claude? Yes — it's a standard stdio MCP server. See the client setup section for Claude Code, Cursor, VS Code, Windsurf, and generic stdio.

Requirements

  • Node.js >= 20 (Node 18 is end-of-life).
  • An Unsplash API access key.

Compatibility

ComponentSupported
Node.js20 and 22, tested in CI; >=20 required (enforced by engines and a runtime guard).
OSLinux, macOS, and Windows (all tested in CI).
MCP SDK@modelcontextprotocol/sdk ^1.29; the protocol version is negotiated with your client on connect.
Transportstdio (HTTP/SSE may be added in a future release).

Roadmap

Full detail lives in docs/ROADMAP.md. In short:

  • v1 (shipped) — the 21 read-only tools, attribution + download-tracking compliance, the attribution resource, and the find_photo prompt.
  • v2 (shipped) — the 8 OAuth write / me endpoints (profile, collections, photo metadata) via a login/logout CLI and the Unsplash authorization-code flow.
  • v3 (current) — 7 more MCP prompts covering every tool domain (see Resources & prompts). A .mcpb Desktop Extension was considered for this phase but dropped to unscheduled future scope — npx already works across every supported client.

Changes are tracked in CHANGELOG.md; the project follows Semantic Versioning.

Contributing

Contributions are welcome — see CONTRIBUTING.md and our Code of Conduct. It covers local setup, the test suite, testing tools by hand with the MCP Inspector, and the versioning/deprecation policy. To report a vulnerability, see SECURITY.md.

Contact & community

Maintained by Hanoak S. The fastest way to get help or propose a feature is to open an issue — it's public, searchable, and helps the whole community. For anything else, reach out:

[LinkedIn] [X]

If this project helps you, a ⭐ on GitHub is appreciated — it aids discoverability for others looking for an Unsplash MCP server.

License

MIT © Hanoak S. Not affiliated with Unsplash.

来源:README.md,提交 de8c3e3

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v1.0.1最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分