---
name: tryonanytime
description: Virtual try-on as a tool — put any garment onto any person photo and get the result image back. Use this whenever a user asks to see how clothing looks on someone, preview an outfit, or try a garment from any store, via the TryOnAnytime MCP server (preferred) or its REST API.
---

# TryOnAnytime — Virtual Try-On for Agents

TryOnAnytime turns two public image URLs — a person photo and a garment image —
into one generated image of that person wearing that garment. It is exposed as
an MCP server (preferred) and as a plain REST API. Every call is authenticated
with the user's Bearer API key and charged in credits.

Base URL for all examples below: `https://tryonanytime.com`

## When to use

- The user asks how a piece of clothing would look on a person ("would this
  jacket suit me?", "show this dress on my photo").
- The user wants to preview an outfit before buying: a garment from any store
  or product page plus a person photo.
- A shopping / fashion assistant flow needs a generated try-on image to show
  the user, not just text advice.
- Any time you hold two public image URLs (person + garment) and need the
  composite try-on image back.

## When NOT to use

- General image editing — background removal, face swap, style transfer,
  upscaling. The model only transfers a garment onto a person.
- Video or live-camera try-on. This interface is strictly photo in, photo out,
  one image per call.
- Images the server cannot fetch or must not process: non-public URLs
  (localhost, private networks, cloud metadata addresses — rejected by the
  SSRF guard), or photos you have no right to use.
- Textual sizing / fit / measurement answers. The tool returns an image, not
  measurements or advice.

## Setup

1. The user signs in at `https://tryonanytime.com` and opens
   `/settings/apikeys` to create a **secret key** (it starts with `sk_`).
   The full key is shown at creation — store it immediately in the agent's
   secret/config store. (It is also kept server-side, encrypted at rest when
   the deployment sets `CONFIG_ENCRYPTION_KEY`, so the user can copy it again
   from the same page.)
2. New accounts receive free initial credits when the deployment enables them;
   verify with `get_credit_balance` (MCP) or `GET /api/v1/me` (REST).

**Security red lines** — the `sk_` key is the user's credential and spends the
user's money:

- Never print it in chat output, logs, or error messages.
- Never commit it to a repository or paste it into source code.
- Never send it anywhere except `https://tryonanytime.com`
  over HTTPS, as an `Authorization: Bearer sk_…` header.
- In config files, reference an environment variable (e.g.
  `${TRYONANYTIME_API_KEY}`), never the literal key.
- Publishable `pk_` keys are refused by this API — only `sk_` works.

## Preferred: MCP server

**Endpoint:** `POST https://tryonanytime.com/api/mcp`

Transport: JSON-RPC 2.0 over a single HTTP POST (Streamable-HTTP style,
request/response only). **There is no SSE stream** — `GET` and `DELETE` answer
`405`. A single request object gets a single response object; a batch array
gets an array; a body of only notifications gets `202 Accepted` with an empty
body. Send `Content-Type: application/json` and no browser `Origin` header
(this endpoint is server-to-server only).

Protocol surface: `initialize` (protocol version `2025-03-26`, serverInfo
`tryonanytime/1.0.0`), `ping`, `tools/list`, `tools/call`, and the
`notifications/initialized` notification. Auth is
`Authorization: Bearer sk_…` on every POST; a missing or bad key is the one
non-200 transport failure: HTTP `401 {"error":"invalid_api_key"}`. Every
JSON-RPC-level failure stays HTTP 200 with an `error` object.

Client config snippet (MCP servers JSON):

```json
{
  "mcpServers": {
    "tryonanytime": {
      "type": "http",
      "url": "https://tryonanytime.com/api/mcp",
      "headers": {
        "Authorization": "Bearer ${TRYONANYTIME_API_KEY}"
      }
    }
  }
}
```

### Tools

Call them with `{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"<tool>","arguments":{…}}}`.
A successful call returns `result.content`: an array of `text` and `image`
content items (images are base64).

**`virtual_tryon`** — run a try-on. Charges 10 credits to the key owner.

- Arguments (`additionalProperties: false`):
  - `person_url` (string, uri, **required**) — public http(s) URL of the
    person photo.
  - `garment_url` (string, uri, **required**) — public http(s) URL of the
    garment image.
  - `prompt` (string, optional) — extra instruction for the model; trimmed to
    500 characters. Default: put the garment onto the person, keep person,
    pose and background unchanged.
- Returns `content`: a `text` item with
  `{"task_id":"…","status":"succeeded","demo":false}` and — when the result
  image is already stored (the normal case, generation is synchronous) — an
  `image` item `{ "type": "image", "data": "<base64>", "mimeType": "image/png" }`.

**`get_tryon_result`** — status and result of a task this key created.

- Arguments: `task_id` (string, **required**) — the id returned by
  `virtual_tryon`.
- Returns `content`: a `text` item with
  `{"status":"processing|succeeded|failed","created_at":"<ISO-8601>"}`; when
  `succeeded`, also the `image` item with the result as base64; when
  `processing`, a text hint to call again in a few seconds.
- A task owned by another key is indistinguishable from a missing one:
  JSON-RPC error `-32602` with `data.code: "not_found"`.

**`get_credit_balance`** — credit balance of the account that owns the API key.

- Arguments: none.
- Returns `content`: a `text` item with `{"credits": 123}`.

## Fallback: REST API v1

Same base URL, same `Authorization: Bearer sk_…` header. All JSON responses
use the envelope `{ "code": 0, "message": "ok", "data": … }`; errors are
`{ "code": -1, "message": "<error_id>" }` with a matching HTTP status.

**`POST /api/v1/tryon`** — run a try-on (10 credits). Two input modes:

- `application/json`: `{"person_url": "…", "garment_url": "…", "prompt"?}` —
  the server fetches both images (SSRF-guarded).
- `multipart/form-data`: `person=<file>`, plus `garment=<file>` **or**
  `garment_url=<https url>`; optional `prompt`.

Success: `200` with `data: {"id", "status": "succeeded", "result_url": "/api/v1/tryon/{id}/result", "demo"}`.

**`GET /api/v1/tryon/{id}`** — task status. `200` with
`data: {"id", "status": "processing|succeeded|failed", "result_url", "created_at"}`.
Only the key that created the task can read it — anything else is `404 not_found`.

**`GET /api/v1/tryon/{id}/result`** — the result image bytes
(`Content-Type: image/png` or the sniffed type, `inline` disposition). `404`
when the task is not yours or has no stored result.

**`GET /api/v1/me`** — key owner identity and balance:
`data: {"email", "credits"}`.

## Rules

- **Images:** jpeg, png or webp only, detected by magic bytes — the declared
  file extension or Content-Type is ignored. Maximum 10 MB per image.
- **URL mode:** the server fetches the URLs itself. Only `http:`/`https:`
  URLs; loopback, private, link-local and reserved IPs (e.g.
  `169.254.169.254`) and internal hostnames (`localhost`, `*.internal`,
  `*.local`) are rejected by the SSRF guard → `400 invalid_image`. Redirects
  are followed manually, re-validated at every hop, max 5 hops.
- **Rate limits:** try-on calls — 5 s minimum interval per API key owner and
  2 s per IP. Read calls (status / result / me) — 500 ms per key owner, 1 s
  per IP. On `429`, honor the `Retry-After` header (seconds) before retrying;
  never retry in a tight loop.
- **Cost:** each try-on costs **10 credits**, charged atomically to the
  account that owns the API key; if generation fails after the charge, the
  credits are revoked automatically. On `402 insufficient_credits`, tell the
  user to top up at `https://tryonanytime.com/pricing`.
- **Ownership:** status and result are visible only to the key that created
  the task. A foreign or unknown id returns `404 not_found` — treat it as
  final, never as a transient error worth retrying.
- **Typical workflow:** `get_credit_balance` → `virtual_tryon` → if the image
  content came back, done; otherwise poll `get_tryon_result` (or
  `GET /api/v1/tryon/{id}`) every 2 s until `succeeded`, and give up after
  ~90 s.
- **`demo: true`** means the deployment ran in demo mode (no model key
  configured) and echoed the person image as the result — the call is still
  charged. Do not present a demo result as a real try-on.

## Errors

HTTP layer (REST, and MCP transport):

| Status | `message` / body                    | Meaning / action                                                        |
| ------ | ----------------------------------- | ----------------------------------------------------------------------- |
| 401    | `invalid_api_key`                   | Missing, wrong, revoked or `pk_` Bearer key. Fix the key; do not retry. |
| 400    | `invalid_image`                     | Bad input: unsupported format, >10 MB, unfetchable URL, SSRF-blocked host. |
| 402    | `insufficient_credits`              | Not enough credits — user tops up at `/pricing`.                        |
| 404    | `not_found`                         | Task unknown or owned by another key. Final.                            |
| 429    | `too_many_requests`                 | Rate limited — wait `Retry-After` seconds.                              |
| 503    | `unavailable`                       | Try-on not available in this deployment; retry later.                   |
| 500    | `tryon_failed` / `server_error`     | Generation or server failure; charged credits are revoked.              |
| 405    | (MCP only, empty)                   | `GET`/`DELETE` on `/api/mcp` — no SSE stream; use POST.                 |
| 403    | `origin_not_allowed` (MCP only)     | Browser `Origin` header refused — call server-to-server.                |
| 415    | `unsupported_content_type` (MCP)    | Body must be `application/json`.                                        |
| 413    | `payload_too_large` (MCP)           | JSON-RPC body over 1 MB.                                                |

JSON-RPC layer (MCP, always HTTP 200):

| Code    | `message`         | When / `data.code`                                                              |
| ------- | ----------------- | ------------------------------------------------------------------------------- |
| -32700  | `parse_error`     | Body is not valid JSON.                                                         |
| -32600  | `invalid_request` | Not a valid JSON-RPC 2.0 request.                                               |
| -32601  | `method_not_found`| Unknown method or unknown tool (`data.code: "unknown_tool"`).                   |
| -32602  | `invalid_params`  | Bad tool arguments — `missing_arguments`, `invalid_url`, `invalid_prompt`, `missing_task_id`, or `not_found` (foreign/unknown task). Also `invalid_image` when a fetched URL fails the image rules (`data.code`: `invalid_url`, `blocked_host`, `fetch_failed`, `too_large`, `too_many_redirects`, `invalid_image`). |
| -32603  | internal          | `insufficient_credits` (top up at `/pricing`), `tryon_unavailable` (retry later), `tryon_failed` (credits revoked). |
