Picsha AI

Picsha AI MCP Server

Overview

Picsha AI ships an official Model Context Protocol (MCP) server. This allows your custom AI agents, assistants, and LLM workflows to directly interface with your Picsha instance.

By connecting to the Picsha MCP Server, your AI agents can instantly search, retrieve, and process your organization's digital assets without you having to build custom API wrappers or prompt definitions.

Connection Details

Connect through the published @picsha-ai/mcp-server package. It runs locally over stdio transport (the model every MCP client supports natively — Claude Desktop, Cursor, custom agents), authenticates with your standard Picsha API key, and talks to the Picsha REST API under the hood.

PICSHA_API_KEY="<YOUR_PICSHA_API_KEY>" npx -y @picsha-ai/mcp-server

Don't have an API key? Generate one from your Picsha Admin Dashboard.

Hosted Remote MCP (Streamable HTTP)

Web and cloud agents that cannot spawn a local subprocess — hosted assistants, cloud-deployed agent frameworks, workflow platforms — can connect directly to Picsha's hosted MCP endpoint instead:

  • Endpoint: https://api.picsha.ai/v1/mcp (MCP Streamable HTTP transport)
  • Authentication: OAuth 2.0 (sign in with your Picsha account — recommended for AI assistants like Claude) or Authorization: Bearer <YOUR_PICSHA_API_KEY> for programmatic clients
  • Rate limits: requests are limited per credential (default 120 requests/minute)

Connect from Claude (OAuth — no API key needed)

The hosted endpoint is a full OAuth 2.0 protected resource: it supports standards-based discovery, dynamic client registration, and PKCE. In Claude (claude.ai, Desktop, or mobile):

  1. Go to Settings → Connectors → Add custom connector.
  2. Enter https://api.picsha.ai/v1/mcp.
  3. You'll be redirected to sign in to your Picsha account and approve access. That's it — the Picsha tools appear in your chats, and the session is automatically scoped to your Picsha organization.

Any other MCP client that implements the MCP authorization specification can connect the same way; discovery metadata is served at https://api.picsha.ai/.well-known/oauth-protected-resource/v1/mcp.

Connect with an API key (programmatic clients)

For CLIs, agent frameworks, and server-to-server integrations, authenticate with a Picsha API key instead:

  • Header: Authorization: Bearer <YOUR_PICSHA_API_KEY> — the same API key as the REST API
  • Multi-tenancy: send an x-external-user-id: <end-user-id> header to scope the session to a single end user (see Multi-Tenancy & User Isolation below). This header applies to API-key sessions only — OAuth sessions are automatically scoped to the signed-in user and ignore it.

The endpoint is stateless — every JSON-RPC message is an individual POST — so it needs no sticky sessions and works with any Streamable HTTP MCP client. For example, connecting Claude Code:

claude mcp add --transport http picsha-ai https://api.picsha.ai/v1/mcp --header "Authorization: Bearer <YOUR_PICSHA_API_KEY>"

Or a remote server entry in Cursor's .cursor/mcp.json:

{
  "mcpServers": {
    "picsha-ai": {
      "url": "https://api.picsha.ai/v1/mcp",
      "headers": {
        "Authorization": "Bearer <YOUR_PICSHA_API_KEY>"
      }
    }
  }
}

NOTE

The hosted endpoint exposes the same core toolset as the local package, with transport-appropriate substitutions for uploading and rendering. It also adds paging and filters to search and listing, tools for similar assets, collections, moderation, face naming and image generation, image previews the agent can see (view_asset, contact_sheet), and an interactive gallery in clients that support MCP Apps, such as Claude — see Hosted Endpoint Tools at the end of this page. Use the local stdio package when your agent should upload files from the local filesystem in a single step or preview a generative render inline.

Multi-Tenancy & User Isolation (For B2B2C Apps)

If you are building an AI agent that serves multiple users (for example, a Slack bot or a customer-facing SaaS application), you must ensure the agent only accesses assets belonging to the specific user making the request.

Use the hosted endpoint for this. It supports strict multi-tenant isolation at the transport layer: connect to https://api.picsha.ai/v1/mcp with an Organization API key and send x-external-user-id: <end-user-id> on your MCP requests. You do not need to build mapping logic or modify your LLM prompts.

When the header is present:

  1. Isolated Sandboxing: The Picsha backend applies OpenSearch and Postgres filters so the AI agent only searches, views, or modifies files owned by that specific user, and files what it uploads under that user.
  2. Invisible Enforcement: The LLM tools functionally operate the same — the scoping happens at the transport layer, not in your prompts.

If the header is omitted, the session operates with the full organization scope of the API key. That is appropriate for internal tools and org-wide automations — but for customer-facing multi-user deployments, always send it so one user's agent can never touch another user's assets. Hosted OAuth sessions don't use this header: they are automatically and non-overridably scoped to the signed-in Picsha user.

The local @picsha-ai/mcp-server package does not confine a session. You can start it with an end user's id:

PICSHA_API_KEY="..." PICSHA_EXTERNAL_USER_ID="user_123" npx @picsha-ai/mcp-server

It then sends x-external-user-id on the REST calls it makes, which records that user as the owner of what it uploads. Its search, list and asset tools still reach the whole Organization, because the REST API does not narrow reads by the header. Use the local package for single-user and internal agents, and the hosted endpoint when one user's agent must not see another user's assets.

Personal API keys

A personal API key — one that does not belong to an organization — is always scoped to its owner's own assets, the same as GET /v1/assets and POST /v1/search. This holds on the hosted endpoint and for the local @picsha-ai/mcp-server alike.

The x-external-user-id header is ignored for personal keys, so setting PICSHA_EXTERNAL_USER_ID has no effect with one: uploads are filed under the key's owner, and every tool reaches only the owner's assets. To isolate your own end users from each other, use an API key that belongs to an organization with the hosted endpoint, as described above.

Collections in an isolated session

A session scoped to a single end user sees only the collections that session's user created through the hosted tools. It does not see collections shared across the organization. Sessions with full organization scope see every collection in the organization.

Integrating with AI Agents

Using the MCP TypeScript / Python SDKs

If you are building your own AI agent (for example, using LangChain, defining custom LLM tool chains, or a custom Node/Python application), you can connect to the Picsha MCP server using the official MCP client SDKs. The client spawns the server as a subprocess over stdio:

TypeScript Example:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const transport = new StdioClientTransport({
  command: "npx",
  args: ["-y", "@picsha-ai/mcp-server"],
  env: {
    PICSHA_API_KEY: process.env.PICSHA_API_KEY
  }
});

const mcpClient = new Client(
  { name: "my-ai-agent", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

await mcpClient.connect(transport);

// View available Picsha tools for your LLM
const tools = await mcpClient.request({ method: "tools/list" });
console.log(tools);

Agent Framework Recipes

Every major agent framework can now consume MCP servers natively, so there is no Picsha-specific package to install — you point the framework's MCP adapter at @picsha-ai/mcp-server and the full toolset appears as regular framework tools. The recipes below cover the most common Python frameworks; multi-tenancy works identically in all of them (add PICSHA_EXTERNAL_USER_ID to the env block).

LangChain / LangGraph

# pip install langchain-mcp-adapters langgraph "langchain[anthropic]"
import asyncio, os
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent

async def main():
    client = MultiServerMCPClient({
        "picsha": {
            "transport": "stdio",
            "command": "npx",
            "args": ["-y", "@picsha-ai/mcp-server"],
            "env": {"PICSHA_API_KEY": os.environ["PICSHA_API_KEY"]},
        }
    })
    tools = await client.get_tools()
    agent = create_react_agent("anthropic:claude-sonnet-5", tools)
    result = await agent.ainvoke({
        "messages": "Find photos of people wearing red hats and tag the top result 'campaign-hats'"
    })
    print(result["messages"][-1].content)

asyncio.run(main())

Building in TypeScript? The equivalent adapter is @langchain/mcp-adapters with the same server config.

LlamaIndex

# pip install llama-index llama-index-tools-mcp llama-index-llms-anthropic
import os
from llama_index.tools.mcp import BasicMCPClient, McpToolSpec
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.anthropic import Anthropic

mcp_client = BasicMCPClient(
    "npx",
    args=["-y", "@picsha-ai/mcp-server"],
    env={"PICSHA_API_KEY": os.environ["PICSHA_API_KEY"]},
)
tools = await McpToolSpec(client=mcp_client).to_tool_list_async()

agent = FunctionAgent(tools=tools, llm=Anthropic(model="claude-sonnet-5"))
response = await agent.run("Summarize the most recently uploaded document")

CrewAI

# pip install crewai "crewai-tools[mcp]"
import os
from crewai import Agent
from crewai_tools import MCPServerAdapter
from mcp import StdioServerParameters

server_params = StdioServerParameters(
    command="npx",
    args=["-y", "@picsha-ai/mcp-server"],
    env={"PICSHA_API_KEY": os.environ["PICSHA_API_KEY"]},
)

with MCPServerAdapter(server_params) as picsha_tools:
    media_specialist = Agent(
        role="Media Specialist",
        goal="Search, organize, and transform the team's digital assets",
        backstory="Expert curator of the organization's Picsha AI asset library",
        tools=picsha_tools,
    )
    # Add media_specialist to any Crew alongside your other agents

Claude Desktop Integration

The easiest way to use Picsha in Claude Desktop (and claude.ai) is the hosted connector — Settings → Connectors → Add custom connector → https://api.picsha.ai/v1/mcp, then sign in with your Picsha account. No API key, no config file. See Connect from Claude above.

In Claude the hosted connector also shows search, listing, similar-asset and collection results as an interactive gallery of thumbnails inside the chat, where you can open, select and send assets back to Claude. See Interactive gallery below.

Alternatively, run the local stdio server when you specifically need its local-machine tools — uploading files straight from your filesystem (upload_asset) or previewing a generative render inline in the chat (render_asset_preview):

  1. Open Claude Desktop.
  2. Go to Settings > Developer and click Edit Config.
  3. Add the following entry to your claude_desktop_config.json file:
{
  "mcpServers": {
    "picsha-ai": {
      "command": "npx",
      "args": ["-y", "@picsha-ai/mcp-server"],
      "env": {
        "PICSHA_API_KEY": "<YOUR_PICSHA_API_KEY>"
      }
    }
  }
}

Don't have an API key? Generate one from your Picsha Admin Dashboard.

  1. Save the file and restart Claude Desktop. You should now see the Picsha hammer icon 🔨 in your prompt bar, indicating the tools are successfully connected!

The Agentic Workflow (How to talk to Claude)

Now that Claude is connected, you can use natural language to search, generate, and organize your files.

Recommended Context Prompt To get the best results from Claude, start a new chat with the following context prompt. This ensures Claude knows exactly how to utilize the Picsha tools you've provided.

"You are my Picsha Media Assistant. You have direct access to my organization's Digital Asset Management library via MCP tools. When I ask for images, use search_assets with mode: \"ai\" to find them semantically. When I ask for alterations (like background removal or MIMI edits), use render_asset_preview to perform the transformation and show me the resulting image preview inline while also providing the final cdnUrl. Keep my workspace organized by using create_dam_group and link_assets for variations."

Example Prompts to Try

1. Semantic Search & Discovery

  • You: "Find me photos of people wearing red hats enjoying the outdoors, then summarize what's happening in the top 3 results."
  • What Claude Does: Calls search_assets(query: "people wearing red hats outdoors", mode: "ai"), reads the rich AI descriptions attached to the results, and intelligently summarizes them.

2. On-the-fly Image Manipulation

  • You: "Grab that sunset photo we just uploaded, remove the background, and crop it to a 16:9 aspect ratio."
  • What Claude Does: First searches to find the sunset photo ID. Then calls render_asset_preview(id: "550e8400-e29b-41d4-a716-446655440000", params: "bg_rem=true&ar=16:9") to dynamically generate the asset. Claude visually returns the finished image directly into the chat interface for your review, and provides the finalized 4K delivery CDN link.

3. Intelligent Organization

  • You: "Gather all photos related to the Q3 Marketing Campaign and put them into a new folder."
  • What Claude Does: Executes a semantic search for related campaign assets. Calls create_dam_group(name: "Q3 Campaign"), adds the retrieved IDs to the group, and confirms the organization structure.

4. Re-analysis & Summarization

  • You: "Re-run the AI analysis on that blurry document, then give me a summary of it."
  • What Claude Does: Calls reanalyze_asset on the document to re-trigger the ingestion pipeline, checks back with get_asset for the refreshed analysis, and reads the resulting text summary with summarize_asset.

5. Safe Cleanup with the Trash

  • You: "Clean up the duplicate screenshots from last week's testing."
  • What Claude Does: Searches for the assets and calls delete_asset on each — which moves them to the Trash rather than destroying them. They stay recoverable for 30 days (via restore_asset or the dashboard), so a misunderstood instruction never causes permanent data loss.

Cursor Integration

Add the server to .cursor/mcp.json in your project root (or ~/.cursor/mcp.json to make it available in every project):

{
  "mcpServers": {
    "picsha-ai": {
      "command": "npx",
      "args": ["-y", "@picsha-ai/mcp-server"],
      "env": {
        "PICSHA_API_KEY": "<YOUR_PICSHA_API_KEY>"
      }
    }
  }
}

Cursor lists the Picsha tools under Settings → MCP once the file is saved. In Agent mode, just ask in natural language — "find our hero images with transparent backgrounds and crop them to 1:1" — and Cursor will call the tools directly.

VS Code Integration

VS Code (1.99+) supports MCP servers in agent mode via .vscode/mcp.json. The inputs block below prompts for your API key the first time the server starts and stores it securely, so the key never lives in a file you might commit:

{
  "inputs": [
    {
      "type": "promptString",
      "id": "picsha-api-key",
      "description": "Picsha AI API Key",
      "password": true
    }
  ],
  "servers": {
    "picsha-ai": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@picsha-ai/mcp-server"],
      "env": {
        "PICSHA_API_KEY": "${input:picsha-api-key}"
      }
    }
  }
}

Open Copilot Chat in Agent mode and the Picsha tools appear in the tools picker.

Available Tools

The published @picsha-ai/mcp-server package (v2.3.x) exposes the following tools:

NOTE

The hosted Streamable HTTP endpoint (https://api.picsha.ai/v1/mcp — see Connection Details) exposes the same core toolset with substitutions for transport reasons: instead of upload_asset it offers get_presigned_upload_url and complete_upload (a remote server cannot read your local files, so the client uploads the file itself), instead of get_rendered_asset_url it offers generate_render_url, and instead of render_asset_preview / poll_render it offers view_asset and contact_sheet (plain renders only; a generative render is previewed with the local package). On the hosted endpoint search_assets and list_recent_assets also accept filters, sorting and paging, and there are additional tools for similar assets, collections, the moderation queue, face naming and image generation. All of these are documented under Hosted Endpoint Tools at the end of this page.

Every hosted tool also declares MCP tool annotations (readOnlyHint / destructiveHint), so clients like Claude can distinguish safe read-only operations from writes and apply the right permission prompts automatically.

1. search_assets

Search for assets in the Picsha AI platform. Natively supports semantic/vector search for natural language queries.

  • query (string, required): The search query to find assets.
  • mode (string, optional): "ai" (default) is hybrid semantic vector search using natural language (e.g., "people wearing red hats"). "standard" is text search over tags, file names, metadata and AI fields with the query syntax (apple -peach, "118th congress", wing*).
  • threshold (number, optional): Minimum relevance score threshold (default: 0.6).
  • sort (string, optional): "relevance" (default), "newest" or "oldest".

The local package returns the top 5 matches. On the hosted endpoint this tool pages through every match and accepts filters; its mode defaults to "standard" there. See Hosted Endpoint Tools.

2. get_asset

Retrieve detailed metadata and AI analysis results for a specific asset.

  • id (string, required): The unique ID of the asset to fetch metadata for.

3. list_recent_assets

Returns the most recently uploaded assets, newest first. Assets in the Trash are not listed.

  • limit (number, default: 10): Number of assets to retrieve. The hosted endpoint accepts 1–100.

On the hosted endpoint this tool also filters, sorts and pages. See Hosted Endpoint Tools.

4. reanalyze_asset

Re-runs the Picsha AI processing pipeline on an existing asset (face/object detection, tagging, summaries, embeddings).

  • id (string, required): The unique ID of the asset.

5. upload_asset

Uploads a local file directly to the Picsha platform. The tool fetches a pre-signed S3 URL and executes the PUT automatically, then triggers the asynchronous ingestion pipeline — so the asset is initially pending; use get_asset a few seconds later for the processed result.

  • filePath (string, required): Absolute path to the local file.
  • filename (string, optional): Original filename to associate; defaults to the file's basename.

6. trigger_url_ingest

Ingests a file from a public web URL. The Picsha ingest worker downloads the file to S3 and processes it automatically.

  • url (string, required): Public URL for the worker to download the asset from.
  • filename (string, optional): Explicit filename to save.
  • config (object, optional): Processing configuration (e.g. { auto_summarize: true }).

7. get_rendered_asset_url

Generates a delivery URL for an asset with applied transformations or AI overrides — immediate access to Picsha's visual manipulation engine without duplicating the underlying asset.

  • id (string, required): The unique ID of the asset.
  • params (string, optional): A query string of transformation parameters. Supports standard parameters (e.g. w, h, ar, fmt, forced downloads via download=true) and generative AI parameters (e.g. bg_rem=true, mimi=remove person and add a sunset scene, mimi_mode=lite, upscale=4k, mimi_bg=misty alpine lake).
  • When params includes generative parameters, the returned URL is automatically minted with a delivery signature (?sig=) so it is fetchable without authentication — anonymous unsigned generative URLs are rejected with 401. Always obtain generative URLs from this tool (or sign-delivery) rather than hand-constructing them.

8. update_asset

Allows agents to act as automated curators by updating asset metadata and tags.

  • id (string, required): The unique ID of the asset.
  • tags (array, optional): Array of strings to append as tags. To remove tags, prefix the string with a hyphen (e.g. ["-old_tag", "new_tag"]).
  • metadata (object, optional): Custom key-value dictionary to attach to the asset.

9. delete_asset

Moves an asset into a 30-day Trash state (soft delete). The asset immediately disappears from search results and delivery endpoints (cached CDN copies are evicted), but remains restorable via restore_asset or the dashboard until its purge date. This fail-safe protects against prompt injection or misunderstood instructions causing permanent data loss.

  • id (string, required): The unique ID of the asset.
  • force (boolean, optional): If true, skips the Trash and permanently deletes immediately (database, search indexes, and storage). Agents should pass this only when the user explicitly asks for permanent deletion.

10. restore_asset

Restores a trashed asset to its previous state, making it deliverable and searchable again. No re-processing is needed — the asset returns exactly as it was.

  • id (string, required): The unique ID of the trashed asset.

11. moderate_asset

Allows specialized AI agents to handle moderation workflows by approving or rejecting assets that are held for review. Only an asset whose status is pending_moderation can be moderated; any other status returns an error. Approval sets the asset's status to completed; rejection sets it to rejected (blocking delivery).

  • id (string, required): The unique ID of the asset.
  • action (string, required): The moderation action. Must be "approve" or "reject".

12. create_dam_group

Creates a collection or folder to structurally organize assets.

  • name (string, required): The name of the collection/folder.
  • description (string, optional): A description for the group.
  • assetIds (string[], optional, hosted endpoint only): Existing assets to place in the group. An asset has one collection, so adding it moves it out of any other. The response lists addedAssetIds and notFoundAssetIds.

Explicitly defines a relationship between two assets, useful for linking AI-generated variants to their originals.

  • sourceId (string, required): The asset ID of the parent or source asset.
  • targetId (string, required): The asset ID of the variation, derived, or correlated asset.
  • relationshipType (string, required): Description of the link (e.g., "REPLACES", "VARIATION", "CONVERTED").

14. summarize_asset

Returns the AI text summary produced for a document asset at ingest. If the asset has no summary yet, analysis is queued and the tool says so — check back with get_asset.

  • id (string, required): The unique ID of the asset.

15. escalate_to_support

Use this tool ONLY when you need to log a feature request, report a documentation gap, or escalate an issue to the engineering team. This will actually send an email to support@picsha.ai.

  • subject (string, required): The subject of the escalation email.
  • headline (string, required): A short, punchy headline for the email.
  • message (string, required): The full summary of the request, formatted nicely.

16. render_asset_preview

Generates a dynamic preview of an asset with applied transformations or AI overrides, returning the visual image bytes natively to the agent. Non-blocking: if a cold generation takes too long, it returns pending with a jobId for poll_render.

  • id (string, required): The unique ID of the asset.
  • params (string, optional): A query string of transformation parameters (e.g. bg_rem=true&mimi=sunset, or mimi=sunset&mimi_mode=lite for a faster 1K preview render).
  • maxDim (number, optional): The maximum dimension in pixels to cap the preview at (default: 768).
  • timeoutMs (number, optional): Max time to block waiting for cold generation.

17. poll_render

Retrieves the result of an asynchronous generation started by render_asset_preview when the initial request timed out and returned pending.

  • jobId (string, required): The async job ID returned by a pending render response.
  • timeoutMs (number, optional): Bounded wait to check for completion.

Hosted Endpoint Tools

This section covers what is different on the hosted endpoint (https://api.picsha.ai/v1/mcp):

  • Uploading: get_presigned_upload_url and complete_upload replace upload_asset.
  • Rendering: generate_render_url replaces get_rendered_asset_url.
  • Seeing the images: view_asset and contact_sheet return pixels the agent can look at, and the list tools take contactSheet: true.
  • Interactive gallery: in clients that support MCP Apps, such as Claude, search, listing, similar-asset and collection results also render as a gallery the user can browse and select from.
  • Searching and listing: search_assets and list_recent_assets accept filters, sorting and paging, and find_similar_assets is added.
  • Collections: list_dam_groups, get_dam_group, add_assets_to_dam_group and remove_assets_from_dam_group are added.
  • Moderation: list_pending_moderation is added.
  • Faces: list_asset_faces, name_face and clear_face_name are added.
  • Generating an image: generate_image is added.

Result format

search_assets, list_recent_assets, find_similar_assets, get_dam_group and list_pending_moderation return assets in the same format.

Lists come back as an object with the results and the paging position:

{
  "results": [ ... ],
  "pagination": { "page": 1, "limit": 20, "total": 57 }
}

Request the next page with page while page * limit is less than total.

Each asset is a summary by default: id, originalName, mimeType, size, status, width, height, captureDate, createdAt, tags, description, labels, metadata (your custom metadata, without raw EXIF), matchScore and thumbnail_url. A summary is typically under 1 KB.

  • detail (string, optional): "summary" (default) or "full". "full" returns every stored field, including raw EXIF and face geometry. That is about 25 KB per asset, so keep limit small with it. For one asset, get_asset always returns every field.

Uploading a file

A hosted server cannot read files from your machine, so an upload takes three steps:

  1. Call get_presigned_upload_url to create a pending asset and receive an upload URL.
  2. Send the file to that URL with an HTTP PUT, using the exact Content-Type you declared.
  3. Call complete_upload with the assetId to start processing.

Until complete_upload is called the asset stays in pending_upload status and is not processed, searchable or deliverable.

# Step 2: upload the file to the URL returned by get_presigned_upload_url
curl -X PUT -H "Content-Type: image/jpeg" --data-binary "@photo.jpg" "<uploadUrl>"

NOTE

Step 2 is an ordinary HTTP request made by the client, not an MCP tool call. Agents that can run commands or make HTTP requests (Claude Code, agent frameworks, your own backend) can do it. Chat assistants that cannot should use trigger_url_ingest with a public URL instead.

get_presigned_upload_url

Creates a pending asset and returns a short-lived URL for uploading its file directly to storage.

  • filename (string, required): Original filename (e.g. photo.jpg).
  • contentType (string, required): MIME type of the file (e.g. image/jpeg).

Returns assetId, uploadUrl, method (PUT), the headers to send with the upload, and expiresInSeconds (3600).

complete_upload

Finishes an upload started with get_presigned_upload_url. It verifies that the file reached storage, records its size, and queues the asset for AI processing. Follow the asset's status with get_asset.

  • id (string, required): The assetId returned by get_presigned_upload_url.
  • config (object, optional): Processing configuration (e.g. { auto_summarize: true, auto_tag: true }).

Calling it before the file has been uploaded returns an error and leaves the asset in pending_upload. Calling it again after it has succeeded changes nothing.

Rendering

generate_render_url

Builds a CDN delivery URL for an asset with transformation parameters. It does not modify the asset.

  • id (string, required): The unique ID of the asset.
  • transformations (object, required): Transformation parameters as key-value pairs (e.g. { "w": "800", "h": "600", "bg_rem": "true" }).

URLs that include generative parameters are signed automatically, the same as get_rendered_asset_url.

Seeing the images

Every hosted tool describes assets in text and points at a thumbnail_url. A chat client such as Claude does not render an external image URL, and the model cannot fetch one, so on their own those results are something the agent can read but not look at. The two tools below return the pixels themselves, as an MCP image content block next to the JSON, so the model sees the picture and the client can show it.

view_asset

Returns one asset's rendered pixels as an image, together with the CDN URL of that same render.

  • id (string, required): The asset to look at.
  • transformations (object, optional): Plain delivery parameters applied before the preview, the same keys as generate_render_url (w, h, fit, crop, pos, rotate, ...). Use it to check a crop before using its URL. Generative parameters (gen_fill, gen_rem, bg_rem, mimi) are refused here; mint their URL with generate_render_url instead.
  • maxDim (number, default: 768): Longest edge of the returned preview in pixels, 128–1024. The preview is webp and is never enlarged.

Returns { assetId, originalName, mimeType, status, url, preview: { width, height, bytes } } followed by the image. A video shows a frame and a document its first page. An asset that is still uploading or processing comes back as an error that names its status.

contact_sheet

Returns up to 16 assets as one image: their thumbnails tiled four to a row in the order given, each cell numbered from 1.

  • assetIds (string[], required): The assets to tile, 1–16, in the order to number them.

Returns { cells: [{ index, id, originalName }], columns, rows, width, height, missing, notFound } followed by the image. missing lists assets whose thumbnail could not be rendered (their cell is blank but still numbered); notFound lists ids outside your library. Use it on ids from any tool, such as a collection, the moderation queue or similar assets, when the agent needs to compare or pick images.

contactSheet on the list tools

search_assets, list_recent_assets and find_similar_assets accept contactSheet (boolean, default false). When set, the response carries the same numbered sheet of its first 16 results, each of those results gains a sheetIndex matching its number, and the JSON gains contactSheet: { cells, columns, rows, width, height, missing }. The results still come back even if the sheet could not be built; then contactSheet holds an error instead. Nothing is fetched from the CDN unless the sheet is requested, so a search that only needs text costs the same as before.

A 16-cell sheet is about 1,044 × 1,044 pixels, which is a single image for the model whatever the page size.

search_assets, list_recent_assets, find_similar_assets and get_dam_group carry an MCP Apps view. In a client that supports MCP Apps, such as Claude on claude.ai, Claude Desktop and Claude mobile, their results render as a gallery inside the chat as well as the usual JSON: a grid of thumbnails, each with the asset's name, tags and, for a search, its match score. The JSON text result is identical with or without the gallery, so the model reads exactly what it always did, and a client without MCP Apps, such as Claude Code, Cursor or the local stdio package, simply shows the text.

In the gallery the user can:

  • Open any asset in a larger view with its description, tags and facts, and step through the results with the arrow keys.
  • Select assets and send them back to the chat. The gallery offers Add to collection, Render URLs, Find similar and Send to chat; each one posts a message to the conversation that names the selected asset ids, and the agent carries on with the matching tool (add_assets_to_dam_group, generate_render_url, find_similar_assets, or whatever the user asks next).
  • Show all results when a long page was folded (the inline gallery starts with 12 cards), load the next page, or expand the gallery to full screen.

The gallery is served by the endpoint as the resource ui://picsha/gallery, a single self-contained HTML page with no external scripts. It only ever receives the result the tool already returned to the session, so the same tenant and user scoping applies to it as to the text, and it never calls Picsha on its own except through the host, with the session's own credentials. Thumbnails come from cdn.picsha.ai, which the page declares for the client's sandbox; where a client blocks that domain, the gallery fetches previews through view_asset instead.

NOTE

Claude caches a connector's tool list. If you added the Picsha connector before the gallery shipped, remove it and add it again under Settings → Connectors so that the four tools are picked up as interactive.

Searching and listing

search_assets

Searches the asset library with the same filters, sorting and paging as POST /v1/search.

  • query (string, required): What to find. May be empty when filters alone select the assets.
  • mode (string, optional): "standard" (default) matches text and tags with the query syntax: words are ANDed, | ORs, a leading - excludes, quotes make a phrase, a trailing * a prefix. "ai" is hybrid semantic search and understands natural language, including dates and places.
  • threshold (number, optional): Minimum relevance score for "ai" matches, 0–1. Defaults to the server's setting (0.45).
  • limit (number, default: 20): Results per page, 1–50.
  • page (number, default: 1): Page number.
  • sort (string, optional): "relevance" (default), "added-desc", "added-asc", "created-desc", "created-asc", "name-asc" or "name-desc".
  • filters (object, optional): Structured filters, all of which must match: addedAfter, addedBefore, capturedAfter, capturedBefore, face, object, place, ocr, type, style, minRating, metadata, metadataField / metadataValue, and anyOf for alternative filter blocks. These are the Advanced Filters described in the Search docs.
  • timezone (string, optional): IANA zone (e.g. "America/New_York") that anchors "today" and "this week" to the user's day.
  • assetIds (string[], optional): Only search within these assets.
  • excludeAssetIds (string[], optional): Assets to leave out of the results.
  • facets (object[], optional): Distinct values with counts over the matching assets, e.g. [{ "field": "tags" }]. Fields: faces, labels, places, tags, mimeType, cameraMake, cameraModel. The response then carries a facets object.
  • detail (string, optional): See Result format.
  • contactSheet (boolean, optional): See Seeing the images.

In Claude the results also render as an interactive gallery.

{
  "query": "",
  "filters": { "type": "image", "capturedAfter": "2026-01-01", "minRating": 4 },
  "sort": "created-desc",
  "limit": 20,
  "page": 2
}

list_recent_assets

Lists assets, newest first unless sort says otherwise, with the same filters and paging as GET /v1/assets.

  • limit (number, default: 10): Assets per page, 1–100.
  • page (number, default: 1): Page number.
  • sort (string, optional): "added-desc" (default), "added-asc", "created-desc", "created-asc", "name-asc" or "name-desc".
  • type (string, optional): MIME type prefix, e.g. "image", "video" or "application/pdf".
  • tag (string, optional): Only assets carrying this tag.
  • search (string, optional): Only assets whose original filename contains this text.
  • status (string, optional): Only assets in this status, e.g. "completed" or "pending_upload". "trashed" lists the Trash, which is hidden otherwise.
  • albumId (string, optional): Only assets in this collection, by its id or name.
  • metadata (object, optional): Exact matches on custom metadata keys, e.g. { "asset_status": "Retired" }. Every key must match. A list of values for a key matches any of them.
  • anyOf (object[], optional): Alternative { "metadata": { ... } } blocks. An asset matches when it satisfies at least one.
  • detail (string, optional): See Result format.
  • contactSheet (boolean, optional): See Seeing the images.

In Claude the results also render as an interactive gallery.

find_similar_assets

Finds assets that look like a given asset, most similar first. The asset itself and trashed assets are never returned.

  • id (string, required): The asset to compare against.
  • limit (number, default: 12): How many matches to return, 1–50.
  • minScore (number, optional): Drop matches scoring below this, 0–1. When omitted, the nearest limit matches are returned whatever they score.
  • filters (object, optional): Structured filters the matches must satisfy, the same as search_assets.
  • excludeAssetIds (string[], optional): Assets never returned as matches.
  • detail (string, optional): See Result format.
  • contactSheet (boolean, optional): See Seeing the images.

Returns { assetId, results, total }. In Claude the results also render as an interactive gallery. When the response carries "reason": "no-embedding", the asset has not been vectorized yet; run reanalyze_asset on it and try again.

Collections

A collection is a folder, group or album. An asset belongs to one collection at a time. Each collection has a kind:

  • collection: made by a person or an agent, for example with create_dam_group.
  • session: made automatically for one upload session.
  • unregistered: an album id that assets carry but that was never created as a collection, typically an album that came from another system. It can be listed, opened and added to like any other.

list_dam_groups

Lists collections with the number of assets in each.

  • kind (string, optional): "all" (default), "collection", "session" or "unregistered".
  • search (string, optional): Only collections whose name contains this text.
  • limit (number, default: 50): Collections per page, 1–200.
  • page (number, default: 1): Page number.

Each result has id, name, kind, assetCount, and where available description and createdAt.

get_dam_group

Returns one collection and a page of the assets in it.

  • id (string, required): The collection's id or exact name.
  • limit (number, default: 20): Assets per page, 1–100.
  • page (number, default: 1): Page number.
  • sort (string, optional): Asset order. Defaults to newest upload first.
  • detail (string, optional): See Result format.

Returns { collection, assets: { results, pagination } }. In Claude the assets also render as an interactive gallery.

add_assets_to_dam_group

Puts existing assets in a collection. An asset has one collection, so adding it moves it out of any other.

  • groupId (string, required): The collection's id or exact name. The collection must already exist; create one with create_dam_group.
  • assetIds (string[], required): The assets to add, 1–100.

Returns addedAssetIds and notFoundAssetIds.

remove_assets_from_dam_group

Takes assets out of a collection. The assets themselves are not deleted.

  • groupId (string, required): The collection's id or exact name.
  • assetIds (string[], required): The assets to remove, 1–100.

Returns removedAssetIds, notInGroupAssetIds (assets that were not in this collection and were left alone) and notFoundAssetIds.

Moderation

list_pending_moderation

Lists the assets held for manual review (status pending_moderation), newest first. Resolve each one with moderate_asset.

  • limit (number, default: 20): Assets per page, 1–100.
  • page (number, default: 1): Page number.

Each result is an asset summary plus moderationLabels: the labels that flagged it, each with name, confidence and, where there is one, parent.

An asset is held for review only when it was ingested with content_moderation enabled and a label scored 80 or higher.

Faces

Faces are detected at ingest when auto_tag is on. Naming one works the same as PUT /v1/rekognition/faces/{faceId} in the API Reference: the name propagates to matching faces across the organization's library, and every asset showing the face becomes searchable by that name (search_assets with filters.face).

A session confined to one end user can only see and name faces on that user's assets.

list_asset_faces

Lists the faces detected on an asset.

  • id (string, required): The unique ID of the asset.

Returns { assetId, faces, total }. Each face has faceId, name (when one has been given), confidence and boundingBox. An asset with no detected faces returns an empty list.

name_face

Gives a detected face a person's name.

  • faceId (string, required): A faceId from list_asset_faces.
  • name (string, required): The person's name.

Sending a new name to an already-named face replaces it everywhere the face appears. The propagation to matching faces runs after the call returns.

clear_face_name

Removes the name from one face, for example to undo a wrong tag.

  • faceId (string, required): A faceId from list_asset_faces.

Only this face loses its name. Similar faces that received the same name keep theirs.

Generating an image

generate_image

Creates a new image from a text prompt and adds it to the library as a normal asset, tagged generated. It is the same operation as POST /assets/generate in the API Reference and is billed per image at the MIMI tier matching quality.

  • prompt (string, required): Text description of the image, up to 4,000 characters.
  • aspect_ratio (string, default: "1:1"): One of 1:1, 3:4, 4:3, 9:16, 16:9, 2:3, 3:2, 4:5 or 5:4.
  • quality (string, default: "standard"): "lite" (1K, cheapest), "standard" (2K) or "4k".
  • tags (string[], optional): Initial tags for the generated asset.

Returns assetId, a CDN render url and a thumbnail_url. The asset is then processed like an upload (thumbnails, analysis, search), so follow its status with get_asset and build delivery URLs with generate_render_url. A prompt the model refuses returns an error and nothing is billed.

The account needs a billing method on file. Without one the tool returns a payment-required error before anything is generated, and nothing is billed; add a billing method in the Picsha dashboard to continue. There is no free allowance for generation.

The tool's description asks the agent to confirm with the user before generating many images. Beyond the billing-method requirement, nothing caps how many an agent may request, so keep that in mind when connecting an autonomous agent with your API key.