Installable Agent Skill
$tryonanytime
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
SKILL.md
View raw---
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). |