Tools
The server registers five tools for searching and generating images, and four for Scry Snip captures. All of them run as the authenticated user, and every search is filtered to the projects that user can read.
search_components
Text search over the component index, combining semantic (dense vector) and keyword (BM25 sparse) matching.
| Parameter | Type | Default | Notes |
|---|---|---|---|
query | string | — | Required. 1–500 characters |
project_id | string | — | Restrict to one project. Max 128 characters |
scope | project | org | project | org requires project_id |
limit | number | 10 | 1–50 |
page | number | 1 | Pagination |
versions | latest | all | latest | latest shows each screen once, as its newest indexed copy. all lists every indexed copy of every screen |
Scope is the important parameter. With project_id and the default scope: "project", the search never widens: an empty result means that project has no match, not that it fell back elsewhere. With scope: "org" it also returns components from other projects in the same organisation — but only those whose owners opted in to discovery and that the account can read; each is flagged crossProject: true.
Omitting project_id searches every project the account can read, which spans unrelated codebases. Results from those are not safe to import.
search_by_image
The same search, anchored on an image — a screenshot, a mockup, a Figma export.
| Parameter | Type | Default | Notes |
|---|---|---|---|
image | string | — | Required. Base64 PNG/JPG, max 10MB. Data-URI prefix optional |
query | string | — | Optional text to combine with the image for a hybrid match |
project_id | string | — | As above |
limit | number | 10 | 1–50 |
page | number | 1 | Pagination |
versions | latest | all | latest | As for search_components |
One result per screen
Every build that is indexed adds a new copy of each of its screens, and older copies stay in the index. Without project_id the search spans all of them, so one screen could come back once per deploy, with the assistant fetching the same screenshot several times.
By default both searches return one result per screen: the newest indexed copy. A screen is the same project, the same capture source (web, iOS or Android) and the same story. A screen is never dropped: one that exists only in an older build is still returned once, marked freshness: "stale".
Each result says how many copies it stands for, and the summary says how many older copies were folded:
Found 12 results (7 older versions of the same screens folded; pass versions: "all" to see them)
…
Versions: 4 indexed (showing newest, build a1b2c3d)In the structured result the count is versionCount. Pass versions: "all" to get every copy back, for example to compare how a screen looked across deploys. Searching with a project_id is already limited to that project's current build, so it rarely has copies to fold.
get_component_screenshot
Fetches a result's screenshot and returns it as an image block, so the assistant can actually look at the component rather than at a URL.
| Parameter | Type | Notes |
|---|---|---|
screenshot_url | string | Required. The screenshotUrl from a search result |
component_name | string | Optional, for labelling the response |
Screenshot URLs are presigned and expire after an hour, so fetch through this tool rather than storing the URL.
generate_image
Generates a reference image from a prompt, optionally guided by reference images for style transfer.
| Parameter | Type | Notes |
|---|---|---|
prompt | string | Required. Description of the image |
aspect_ratio | enum | Optional, default 1:1. One of 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9 |
quality | fast | quality | Optional. fast (Gemini 3.1 Flash) or quality (Gemini 3 Pro) |
reference_images | string[] | Optional. Base64 images for img2img |
reference_image | string | Deprecated — use reference_images |
Each image uses credits: 40 for fast, 150 for quality (see who pays). If the wallet can't cover the price, the tool returns INSUFFICIENT_CREDITS (with the balance and a link to the Credits page) and no image is generated.
whoami
No parameters. Returns the authenticated user's uid, email and display name — the quickest way to confirm which account a client is connected as.
Capture tools
These four tools work with screenshots taken with Scry Snip. A capture is private to the person who took it until they share it, so the tools only ever return captures the signed-in user took or that were shared with them.
| Tool | Use it for |
|---|---|
latest_capture | The signed-in user's own newest capture ("fix the screenshot I just took") |
get_capture | One capture by id, their own or one shared with them |
list_captures | Recent captures as text, newest first |
delete_capture | Permanently delete one of the user's own captures |
latest_capture
| Parameter | Type | Default | Notes |
|---|---|---|---|
project_id | string | — | Needed only when the user snips into more than one project |
maxAgeMinutes | number | 15 | 1–1440. Accept a capture up to this old. Raise it only after the user confirms an older one is the one they mean |
Returns a text block first (capture id, age, who took it, size), then the picture as a WebP image (long edge at most 1280 px) when it fits the inline size budget, and a link to the full-resolution original that expires after one hour. It never returns captures other people shared; use list_captures with scope: "shared" for those.
An assistant should call it once per request, not in a loop. On CAPTURE_STALE or CAPTURE_NOT_FOUND it should ask the user instead of retrying.
get_capture
| Parameter | Type | Notes |
|---|---|---|
capture_id | string | Required. The id, for example cap_7k3f (a UUID is also accepted) |
project_id | string | Optional. The project the capture lives in |
Same result as latest_capture, plus the note if the owner added one. Works for the user's own captures, and for captures shared with them while they stay shared. A capture that does not exist and one the user may not see give the same CAPTURE_NOT_FOUND. A note is returned quoted and labelled as untrusted text from the person who wrote it, so an assistant must not follow instructions inside it.
list_captures
| Parameter | Type | Default | Notes |
|---|---|---|---|
scope | mine | shared | mine | shared lists captures other people shared with the user |
project_id | string | — | Stay inside one project |
limit | number | 10 | 1–50 |
before | number | — | Page cursor: the nextBefore value of the previous page |
Text only (id, project, age, who took it, size), so it works in clients that do not show images. Without project_id, shared captures are read from at most 50 of the user's projects. When that limit applies the result says so (projectsTruncated and a note), and passing project_id reads the rest. Use get_capture to see a picture.
delete_capture
| Parameter | Type | Notes |
|---|---|---|
capture_id | string | Required |
project_id | string | Optional |
Permanently deletes a capture the user took: the original, the pictures, the record and any share link. This cannot be undone, and only the owner can do it. Deleting a capture that is already gone succeeds. The result is a confirmation naming the capture id. An assistant should call it only when the user explicitly asked to delete that capture.
Example prompts
Fix the screenshot I just took. (
latest_capture)
Look at Scry capture cap_7k3f and tell me what is wrong with the header. (
get_capture)
List the snips my teammates shared with me this week. (
list_captures)
Delete Scry capture cap_7k3f. (
delete_capture)
Capture errors
Capture tools return the same error shape as the other tools (error, message, retryable), with these codes:
| Code | Meaning |
|---|---|
CAPTURE_NOT_FOUND | No such capture, or the user may not see it. Do not retry or guess other ids |
CAPTURE_STALE | The newest capture is older than maxAgeMinutes. The error carries its id and age. Ask the user whether to use it |
AMBIGUOUS_PROJECT | The user has recent captures in several projects. The error lists them. Ask which one, then pass project_id |
CAPTURE_NOT_READY | The upload has not finished yet. Wait about five seconds and try once more |
CAPTURE_NOT_OWNER | delete_capture on a capture that was only shared with the user. Nothing changed |
RATE_LIMITED | Over 60 requests per minute for this user |
WRITE_RATE_LIMITED | Over 10 deletes per minute for this user |
UPSTREAM_RATE_LIMITED | The Scry API is busy. Wait and retry |
INVALID_ARGUMENT | A parameter failed validation, for example a malformed capture id |
TIMEOUT, DASHBOARD_UNREACHABLE | The Scry API did not answer. Retry once |
SERVER_MISCONFIGURED | A problem on Scry's side. Report it with the request_id |
Result shape
Both searches return results, plus a summary, the scope used, and widenedToOrg. Each result carries a versionCount.
{
"name": "StorybookConnection",
"score": 19.457698822021484,
"description": "This is an onboarding/connect panel for linking a Storybook instance…",
"searchableText": "…the text the row was indexed on…",
"tags": ["Connect Storybook Panel", "React", "Onboarding", "Form"],
"sourcePath": "src/features/settings/StorybookConnection.tsx",
"storyPath": "src/features/settings/StorybookConnection.stories.tsx",
"storyTitle": "Features/Settings/StorybookConnection",
"variant": "Disconnected",
"projectId": "BlJfujLJOl8AJ8Vkkjou",
"crossProject": false,
"buildId": "mz2P7qU3uZpHoJHGSSbR",
"buildSha": "ea816a7ae69b5d31f5268aaea9b365fef71bf9e2",
"storyId": "features-settings-storybookconnection--disconnected",
"indexedAt": "2026-09-11T21:52:19.231Z",
"versionCount": 3,
"latestBuildId": "mz2P7qU3uZpHoJHGSSbR",
"freshness": "fresh",
"freshnessReason": "matches_current_build",
"screenshotUrl": "https://…presigned…"
}| Field | Why it matters |
|---|---|
sourcePath | The component to import. storyPath is the .stories file it was captured from — importing that instead is a common mistake |
crossProject | true means the row came from another project via scope: "org", and may not be importable here |
freshness | fresh when buildId equals latestBuildId. Otherwise the index is behind the code, and freshnessReason says why |
buildSha | The commit the build came from, when the deployer reported one |
indexedAt | When the row was written, not when the build was deployed |
versionCount | How many indexed copies of this screen the search found, including this one. 1 means the screen has a single copy. Older copies are not listed unless you pass versions: "all" |
Rows indexed before build tracking shipped report freshness: "unknown" until their project is re-indexed.
Failure modes
Tools return a typed error rather than an empty result, so a client can branch on it.
| Code | Meaning |
|---|---|
RATE_LIMITED | Over 60 requests per minute for this user. Wait and retry |
SEARCH_API_5xx | Upstream search error. Retry once |
VALIDATION_ERROR | Input failed schema validation, including a versions value other than latest or all. Fix parameters |
INVALID_SCOPE | scope was not project or org |
PROJECT_REQUIRED | scope: "org" without a project_id |
PROJECT_HAS_NO_ORG | scope: "org" on a project with no organisation |
The error comes back as JSON text in a result with isError: true. It has error (the code above), message, retryable and request_id, the id of this tool call. Quote request_id if you report the failure; see Request ids and support references.
{ "error": "VALIDATION_ERROR", "message": "…", "retryable": false, "request_id": "01M3EQG44Y0J8F2K6ZP9RX1T7C" }Input limits are enforced by Zod schemas: 500-character queries, 10MB images, 128-character project ids, and a 30-second timeout on upstream calls.