---
title: "import_screenshot"
description: "Turn attachments, URLs or local paths into account-scoped refs the render tools accept."
canonical: "https://shotops.dev/docs/mcp/reference/import_screenshot"
last_updated: "2026-09-07"
shotops_mcp_version: "0.9.6"
result_contract_version: "2.0.0"
---
# import_screenshot

Turn attachments, URLs or local paths into account-scoped refs the render tools accept.

## What it does

- Writes
- No account needed

How screenshots that exist only in this conversation get into ShotOps. One call takes several sources in `screenshots` — PNG/JPEG URLs, or local file paths on the stdio server — and hands each back as a private ref. The bytes move server to server, so they never enter model context. Results come back one per entry in input order, and a failed entry reports its own error without cancelling the rest. A ChatGPT attachment is the exception: it arrives as the top-level `file` parameter, which carries one attachment, so N attached screenshots are N calls.

You can ask: “Import these two Tidebook screenshot URLs for later rendering.”

The published schema accepts any shape inside `screenshots` so a two-source entry comes back as a sentence instead of three stacked union errors. The real element shapes are below.

## Input

- `screenshots`arrayoptionalat least 1 itemat most 10 items The screenshots to import, 1–10, in the order you want them back. Each entry names EXACTLY ONE source: { "url": "https://…" } (this server fetches a PNG or JPEG, converts JPEG to PNG, and stores it under your account), { "file": { "download_url": "…", "file_id": "…" } } (a ChatGPT attachment descriptor), or (local stdio server only) { "path": "/abs/shot.png" }. Add "name" to any entry to keep its original filename. One result per entry, in this exact order — a failed entry reports its own error and never cancels the others. In ChatGPT, an attachment arrives as the top-level `file` parameter instead, one attachment per call — batch URL sources here and import each attachment in its own call; one call may carry both. each item any The published schema does not constrain this element, on purpose — a malformed entry comes back as a sentence instead of a stacked union error. What the server re-validates against is below.
- `file`objectoptional A PNG or JPEG attached by the user in ChatGPT — the top-level ChatGPT file parameter. It holds exactly ONE attachment, so N attached screenshots are N import_screenshot calls; collect a ref from each before rendering. JPEGs are converted to PNG before storage. Other clients put their sources in `screenshots` instead.
  - `download_url`stringrequired Short-lived HTTPS download URL supplied by ChatGPT.
  - `file_id`stringrequired ChatGPT file identifier.
  - `mime_type`stringoptional File MIME type supplied by ChatGPT.
  - `file_name`stringoptionalat most 200 characters Original attachment filename, when available.
- `name`stringoptionalat most 200 characters Override the filename stored in the returned ref for the top-level `file`. Defaults to file.file_name. Entries in `screenshots` carry their own "name".
- `locale`stringoptional Optional bookkeeping tag for the App Store locale these screenshots belong to.

## What the server actually accepts

The published schema above deliberately accepts a wider shape for `screenshots`, `file`, so a malformed entry comes back as a sentence rather than a stack of union errors. The server re-validates against these shapes and refuses anything else.

- `screenshots`arrayoptionalat least 1 itemat most 10 items The screenshots to import, 1–10, in the order you want them back. Each entry names EXACTLY ONE source: { "url": "https://…" } (this server fetches a PNG or JPEG, converts JPEG to PNG, and stores it under your account), { "file": { "download_url": "…", "file_id": "…" } } (a ChatGPT attachment descriptor), or (local stdio server only) { "path": "/abs/shot.png" }. Add "name" to any entry to keep its original filename. One result per entry, in this exact order — a failed entry reports its own error and never cancels the others. In ChatGPT, an attachment arrives as the top-level `file` parameter instead, one attachment per call — batch URL sources here and import each attachment in its own call; one call may carry both. each item one of 3 shapes 1`url` - `url`stringrequiredat least 1 character An https URL to a PNG or JPEG. This server fetches it and converts JPEG to PNG — https only, no redirects, 20MB cap. - `name`stringoptionalat most 200 characters The screenshot's original filename, stored in the returned ref (e.g. "03_stats.png"). 2`file` A ChatGPT attachment descriptor: { "file": { "download_url": "…", "file_id": "…" } }. - `file`objectrequired - `download_url`stringrequiredformat uri Short-lived HTTPS download URL supplied by ChatGPT. - `file_id`stringrequiredat least 1 character ChatGPT file identifier. - `mime_type`stringoptional File MIME type supplied by ChatGPT. - `file_name`stringoptionalat most 200 characters Original attachment filename, when available. - `name`stringoptionalat most 200 characters The screenshot's original filename, stored in the returned ref (e.g. "03_stats.png"). 3`path` - `path`stringrequiredat least 1 character A local filesystem path to a PNG, read straight off disk — ONLY on the local stdio server (npx shotops-mcp); the hosted server rejects it. - `name`stringoptionalat most 200 characters The screenshot's original filename, stored in the returned ref (e.g. "03_stats.png").
- `file`objectoptional
  - `download_url`stringrequiredformat uri Short-lived HTTPS download URL supplied by ChatGPT.
  - `file_id`stringrequiredat least 1 character ChatGPT file identifier.
  - `mime_type`stringoptional File MIME type supplied by ChatGPT.
  - `file_name`stringoptionalat most 200 characters Original attachment filename, when available.

## Result

- `ok`booleanoptional
- `count`integeroptionalmin 0
- `screenshots`arrayoptional each item object - `ok`booleanrequired - `source`stringrequired one of`url``file``path` - `name`stringoptional - `ref`stringoptional - `url`stringoptional - `path`stringoptional - `size`integeroptionalmin 0 - `error`stringoptional
- `instructions`stringoptional
- `disclosure`objectoptional Stated because this call moved caller bytes into ShotOps storage. Relay it before or with the result; do not paraphrase the retention window.
  - `moves`stringrequired The bytes that left the caller’s control, in plain words.
  - `why`stringrequired What the move buys — never a restatement of the mechanism.
  - `retentionDays`integerrequiredgreater than 0 How long ShotOps keeps it without an explicit retaining action. Read from the retention policy, never typed.
  - `retainedBy`stringrequired The explicit action that keeps it past that window.
  - `statement`stringrequired The same four facts as one sentence, safe to relay verbatim.

## Example

That request maps to this call:

```
{
  "name": "import_screenshot",
  "arguments": {
    "screenshots": [
      {
        "url": "https://example.com/tidebook/01-today.png",
        "name": "01-today.png"
      },
      {
        "url": "https://example.com/tidebook/02-forecast.png",
        "name": "02-forecast.png"
      }
    ]
  }
}
```

One result per entry, in the order you sent them, each with a ref or its own error.

## Access and cost

- One of the few tools a hosted caller may use with no account at all — getting your own screenshots in is part of the free taste.
- Free. Importing spends no cloud credits.
- A local filesystem path only works on the local stdio server; the hosted server refuses it, and there is no URL to hand it for a file on your own disk. Use request_screenshot_upload there instead.
- An import that produced refs put the bytes in ShotOps storage, and the reply discloses what moved, why, how long it is kept and what retains it. A local path import moves nothing and discloses nothing.

## When it refuses

- **One entry failed and the rest succeeded.**
  That is by design. Re-import just the failed entry; the refs you already got stay valid.

- **An entry names two sources at once.**
  Every entry names exactly one of url, file or path. The reply says which entry and what to drop.

- **A path was refused on the hosted server.**
  Hosted has no access to your filesystem. Either give a URL it can fetch, or mint a signed upload.

## Where this fits

- [Bringing the screenshots in](https://shotops.dev/docs/mcp/workflows/first-strip)
- [Importing one batch per locale](https://shotops.dev/docs/mcp/workflows/localization)

## What the agent is told

The title and description the server publishes on `tools/list` — this is the copy a model chooses between, reproduced verbatim.

Import screenshots

How screenshots that exist only in THIS conversation get into ShotOps so they can be rendered: one call takes several — an attached PNG or JPEG, or a PNG/JPEG URL — and hands each back as a private, account-scoped `ref` the render tools accept, with the image bytes moving server-to-server so they never enter model context. Every entry of `screenshots` names EXACTLY ONE source: { "url": "https://…" } (this server fetches PNG or JPEG and converts JPEG to PNG — works on any client), { "file": … } (a ChatGPT attachment descriptor; in ChatGPT it arrives as the top-level `file` parameter instead), or (local stdio server only) { "path": "/abs/shot.png" }. Results come back one per entry in input order, and a failed entry reports its own error without cancelling the rest — re-import just that one. Then call render_strip with the preferred flat input { "screenshots": [{ "ref": "THE_REF" }] }; the same refs work in emit_bundle, and with their filenames in render_project. If the screenshots are only on the user's machine and this is the hosted server, there is no URL to give — use request_screenshot_upload instead. An import that produced refs puts the bytes in ShotOps storage and says so in a `disclosure` block: what moved, why, how long it is kept and the explicit action that retains it. A local { "path" } import moves nothing and discloses nothing.
