atx-mcp
A deterministic, non-generative image/asset transformation MCP server that applies declarative recipes to produce immutable, reproducible revisions without ever modifying the original.
Import & inspect assets —
import_asset(single or batch up to 64, sha256-idempotent, also LUTs/SVGs/fonts) andinspect_image(dimensions, EXIF, ICC, GPS, luma stats, sharpness, perceptual hash).Detect geometry & text —
detect_tilt(roll angle),detect_document(page/screen quad for perspective),detect_text_blocks(blocks in reading order + legibility/OCR crop bands).Apply transforms —
apply_transformruns 29 ops (rotate, crop, resize, curves, levels, hsl, blur, clone/heal, svg_overlay, encode to jpeg/png/webp/avif, strip metadata, etc.) at full resolution, single or batched, fully idempotent.Preview before committing —
render_previewrenders small/low-res previews (long edge up to 1568) with grid/thirds/horizon/mask overlays and vision-token estimate.Local (masked) edits —
generate_maskbuilds deterministic gradient/luminosity/hue masks to restrict tone/filter ops to part of the image.Compare & verify —
compare_revisionsfor side-by-side, stacked, or diff heatmaps with SSIM, mean/max abs diff, and perceptual-hash distance.Manage revisions —
list_assetsreads the immutable revision ledger,export_assetwrites copies out of the workspace (batch, filename templates, overwrite guard).Discover the vocabulary —
list_operations(op catalog + preset names by category) andexplain_operation(full op/preset reference with params, examples, gotchas).Use built-in presets — 30+ named looks (eyecatch, film, mono, editorial, social sizes, OCR) that hash identically to their expanded recipes.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@atx-mcpStraighten this photo, crop it to 16:9, and export as WebP."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
atx-mcp
A deterministic (non-generative) asset transformation MCP server for general-purpose AI agents, written in Rust.
It executes editing intent — "level the horizon, crop to 16:9, brighten it up a touch" — as a declarative transform recipe, and tracks every result as an immutable revision. The original asset is never modified.
Tilt correction + auto levels + a look applied (a fully deterministic recipe) — left: input / right: output.
See docs/DESIGN.md for the full design.
Use cases
Eye-catch image for an article
"Straighten this photo and crop it to a 16:9, 1600px eye-catch. WebP."
import_asset→detect_tilt(the AI skips correction when it's already near-level) →apply_transform(rotate → crop → resize → encode) →export_asset. The original is never touched, and the same recipe reproduces the same result every time.Multiple sizes for social/CMS
"Generate the OGP, Instagram square, and thumbnail versions of this photo." One original fans out into OGP 1200×630, Instagram 1080 square, and a 400px thumbnail in parallel. The same-recipe-same-revision idempotency means re-running never double-creates output; a one-word preset name works too.
Safe to publish
"Strip the location data for sure, but don't touch the colors."
strip_metadata(exif) removes EXIF including GPS while keeping the ICC profile intact. The AI can also warn ahead of time by checkinghas_gpsfrominspect_image.Color and look adjustments
"Make just the sky bluer, leave everything else alone." Covers
curves/levels/hsl/white_balance, thefilm_softpreset, and importing your own.cubeLUT withimport_assetthen applying it withlut.Local (masked) adjustments
"Darken just the sky a bit, keep the ground as is."
generate_maskbuilds a mask (gradient, luminosity range, or hue range); after wiring it into the adjustment,render_previewwithoverlay:"mask"shows exactly where it will bite before you commit.Layer compositing
"Blur a copy of this photo and blend it in at 50% screen for a soft glow." The
layersstack combines 16 blend modes, opacity, and masks to build reproducible composites like soft focus.Watermarks, retouching, and perspective
"Stamp my logo in the corner, remove the power lines, and fix the converging verticals."
svg_overlayburns in a logo,clone/healremove blemishes or wires by compositing both texture and tone, andperspectivecorrects converging verticals.Reading documents (OCR pre-processing)
"Read this receipt photo for me." / "What does this slide say?"
detect_documentfinds the page or screen and returns aperspectivequad ready to paste, theocr_documentpreset (grayscale, auto levels, light sharpen) andtrimconcentrate the pixel budget on the text, andrender_previewwithlong_edge:1568hands the model an image it can actually read. No OCR engine is bundled: the model does the reading, atx only makes the pixels legible and reproducible.detect_text_blocksanswers the question that decides the rest — "will this text survive the downscale, and where do I cut?" — by returning the text blocks in reading order plus ready-to-pastecropbands.threshold(Otsu / Sauvola) andocr_binarizeexist for external OCR engines.Verification and accountability
"Show me this image before and after the edits, side by side."
compare_revisionsplaces before/after side by side, or returns a difference heatmap with stats likemean_abs_diff. Every revision keeps its lineage, so the full edit history behind any image used in an article can be traced and reproduced — byte-identical on any machine.Splitting one scan into separate images
"Two mounted sheets were scanned on one page. Save them as two files, keeping a little of the mount around each." There is no split operation: the model runs one
apply_transformper piece against the same source revision (cropto the left/right half, thentrimto snap to the paper's edges withpaddingto keep some mount), andexport_assetwrites both at once.render_previewwith no recipe andoverlay:"grid"is how it reads the coordinates first.
What atx doesn't do — generative editing, RAW development, ML-based auto-cropping, OCR itself — is out of scope; see docs/DESIGN.md for the roadmap.
Related MCP server: roundcut-mcp
Install
atx-mcp is a single self-contained binary with no runtime dependencies. Pick one of the following.
1. cargo binstall (prebuilt binary, no compilation)
cargo binstall atx-mcp
claude mcp add --scope user asset-transform -- atx-mcp --workspace /path/to/asset-workspacecargo-binstall downloads the
release archive built by this repository's CI instead of compiling, so this is
the fastest route for anyone who already has a Rust toolchain. (--scope user
makes the server available in every project; omit it for the current project
only.)
2. cargo install (builds from source)
cargo install atx-mcpWorks on any platform a Rust toolchain supports, including ones without a prebuilt binary. Needs a C compiler as well (libwebp is built from its vendored source).
3. Prebuilt binary (no Rust toolchain)
Installer scripts (default install location is ~/.local/bin, or
%LOCALAPPDATA%\Programs\atx-mcp on Windows; the archive is verified against
SHA256SUMS before extraction):
# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/gridhra/atx-mcp/main/scripts/install.sh | sh# Windows
irm https://raw.githubusercontent.com/gridhra/atx-mcp/main/scripts/install.ps1 | iexTo download manually, grab atx-mcp-<version>-<target>.tar.gz (.zip on
Windows) from Releases.
Supported targets:
Platform | Target triple |
macOS (Apple Silicon) |
|
macOS (Intel) |
|
Linux x86_64 |
|
Linux arm64 |
|
Windows x86_64 |
|
claude mcp add asset-transform -- ~/.local/bin/atx-mcp --workspace /path/to/asset-workspace4. Docker
ghcr.io/gridhra/atx-mcp is a FROM scratch image holding the statically
linked binary and nothing else (linux/amd64 and linux/arm64).
claude mcp add asset-transform -- \
docker run -i --rm -v "$PWD:/workspace" ghcr.io/gridhra/atx-mcp:0.6.7Two things to keep in mind. -i is required: the server speaks the MCP
stdio transport and needs stdin to stay open. And paths are container
paths: the directory you bind-mount appears as /workspace inside the
container, so import_asset and export_asset take paths like
/workspace/photos/shot.jpg, not host paths.
5. npx (Node.js 18+, nothing to install)
The prebuilt native binary for your platform is pulled in automatically via
optionalDependencies.
claude mcp add --scope user asset-transform -- npx -y atx-mcp --workspace /path/to/asset-workspaceOr add it directly to your MCP client config:
{
"mcpServers": {
"asset-transform": {
"command": "npx",
"args": ["-y", "atx-mcp", "--workspace", "/path/to/asset-workspace"]
}
}
}--workspace (env: ATX_WORKSPACE) is the directory used as the asset store.
It is created automatically if it doesn't exist.
--max-concurrent-tools (env: ATX_MAX_CONCURRENT_TOOLS) caps how many
pixel-processing tool calls (import / inspect / detect_* / apply / preview /
compare / mask) run at the same time; the default is the CPU count. Lower it on
memory-constrained hosts. Tool work runs on dedicated threads, so the stdio
transport (ping, cancellation, the vocabulary tools) stays responsive while
images are being processed.
Tools (13)
Tool | Role |
| Compact catalog of the recipe vocabulary: every operation with a one-line description and terse parameter hints, plus the built-in preset names. Optional |
| Full reference for one operation: parameter table (type, range, required/default, semantics), ready-to-paste JSON examples and gotchas. A built-in preset name works too and returns its full operation list. An unknown name returns the valid operations and presets, grouped (read-only) |
| Import a local image into the workspace (sha256-idempotent). Takes |
| Inspect dimensions, EXIF summary, ICC profile, presence of GPS data, luma statistics, a |
| Estimate tilt angle via Canny+Hough (coarse) plus a projection profile (sub-0.1° refinement). Also returns horizontal/vertical family estimates; the full score curve is opt-in via |
| Find the dominant quadrilateral (page, screen, whiteboard, sign) via Canny + contours and return it as a |
| Find the text-like blocks (headline, paragraphs, table, caption) via Otsu binarization + run-length smearing + connected components, in reading order, each with |
| Generate a deterministic grayscale mask ( |
| Apply a recipe (or a |
| Apply a recipe (or a |
| Downscale two revisions to long edge ≤640 and return them composited into a single inline image, arranged via |
| Read the revision ledger (read-only) |
| Write revisions out of the workspace: |
Recipe example
{
"operations": [
{ "op": "rotate", "angle_degrees": -1.8 },
{ "op": "crop", "aspect_ratio": "16:9" },
{ "op": "resize", "width": 1600 },
{ "op": "encode", "format": "webp", "quality": 82 }
]
}Supported ops (29): auto_orient / rotate / perspective / crop (crop,
pad) / trim / resize (cover, contain, fill) / adjust / color_matrix /
curves / levels / lut / white_balance / hsl / blur / median /
unsharp_mask / convolve / threshold / clone / heal / svg_overlay /
flip / vignette / grain / gradient_map / pixelate / auto_levels /
encode (jpeg, png, webp, avif) / strip_metadata.
The operation vocabulary is deliberately kept out of the tool schemas: call
list_operations for the up-to-date catalog and explain_operation for one
operation's full schema, examples and gotchas.
LUT (.cube)
A .cube 3D/1D LUT is an asset, not an image: import it first, then point a
recipe at the revision it produced.
import_assetthe.cubefile. It is stored as an immutable revision withmime_type: "application/x-cube"(inspect_imagerefuses it on purpose — it is not an image).Reference the returned
revision_idfrom a recipe:
{ "op": "lut", "lut_revision_id": "rev_...", "strength": 0.8 }strength (0..1, default 1.0) blends linearly with the original. Because
revisions are immutable, including the referenced id in the recipe_hash keeps
the transform fully deterministic — but it also means the recipe is only
reproducible inside a workspace that holds that LUT, so move the .cube
alongside the recipe when you move a look between machines. Referencing an
unknown id fails with a structured error before any pixel work happens.
SVG overlays (logos and watermarks)
An .svg is a vector asset, like a .cube LUT: import it first, then stamp
it onto a raster image from a recipe.
import_assetthe.svgfile. It is stored as an immutable revision withmime_type: "image/svg+xml", and the summary reports the SVG's intrinsic size (0x0means it has none — noviewBoxand no absolutewidth/heighton the root<svg>).inspect_imagerefuses it on purpose: it is a vector asset, not a raster image.Reference the returned
revision_idfrom a recipe:
{ "op": "svg_overlay", "svg_revision_id": "rev_...",
"x": 24, "y": 24, "width": 320, "opacity": 0.25, "blend_mode": "normal" }x/y are the overlay's top-left corner in the coordinates of the image
at that point in the pipeline (so put the overlay after your resize/crop);
negative values are allowed and the overflow is clipped. Omit width and
height to rasterize at the SVG's intrinsic size, give one to scale while
preserving the aspect ratio, or give both to stretch to an exact box — an SVG
with no intrinsic size is a structured error unless you give both. Compositing
uses the same W3C formula and the same 16 blend_mode values as
layers.
Text in an SVG
atx never reads system fonts: the installed fonts differ from machine to machine
and would break byte-for-byte reproducibility. <text> is therefore skipped
unless you ask for it:
{ "op": "svg_overlay", "svg_revision_id": "rev_...", "x": 120, "y": 80,
"width": 48, "render_text": true, "font_revision_ids": ["rev_..."] }render_textdefaults tofalse, which keeps the old behaviour exactly (the shapes render, the glyphs do not, and the result carries a warning). Converting text to paths in your vector editor still works and needs no font at all.render_text:truedraws the text with one bundled font, Roboto Regular (embedded in the binary), plus any fonts you pass infont_revision_ids(up to 4). Nothing else is ever loaded, so the output is identical on every machine.A font is an asset like a LUT or an SVG:
import_asseta.ttf/.otffile, and the summary reports the family names to write infont-family. Japanese and other CJK text needs an imported font — Roboto has no CJK glyphs, and characters missing from every loaded font render as boxes and are counted in a warning.inspect_imagerefuses a font revision on purpose: it is an asset, not an image.
Masks (local adjustments)
A mask is a grayscale image revision: its BT.709 luma is the weight, so white
means "apply this operation at full strength" and black means "leave the pixel
alone". Any of the 14 tone/filter ops (adjust, color_matrix, curves,
levels, hsl, lut, white_balance, blur, median, unsharp_mask,
convolve, grain, gradient_map, auto_levels) accepts one.
generate_maskbuilds one deterministically against a reference image, with exactly that image's dimensions:
| Parameters | What it selects |
|
| A graduated filter (skies, foregrounds) |
|
| A vignette or a subject spotlight |
|
| Highlights, midtones or shadows |
|
| One hue family (sky blue, foliage green) |
You can also import_asset your own grayscale image instead.
Attach the returned
revision_idto an operation:
{ "op": "curves", "master": [[0,0],[128,168],[255,255]],
"mask": { "revision_id": "rev_...", "invert": false, "feather_px": 8.0 } } invert (default false) flips the weight to 1-w; feather_px (default
0.0) blurs the mask edge by that gaussian sigma in pixels of the current
image.
render_previewwithoverlay:"mask"andmask_revision_idtints the preview red where the weight exceeds 0.5 and dims it elsewhere, so the coverage can be checked before committing.
Masks are referenced by revision id exactly like LUTs, so the same caveat applies: the recipe hash includes the id, and the recipe only reproduces inside a workspace that holds that mask.
Layers
A recipe may carry a layers stack instead of (or in addition to) a flat
operations list. Layers composite bottom-to-top, each layer's ops run
against its own source before it is blended onto the running composite:
{
"layers": [
{ "source": "base", "ops": [] },
{
"source": { "revision_id": "rev_..." },
"ops": [{ "op": "blur", "sigma": 8 }],
"blend_mode": "multiply",
"opacity": 0.6
}
],
"operations": [
{ "op": "resize", "width": 1600 },
{ "op": "encode", "format": "webp", "quality": 82 }
]
}sourceis either"base"(the input revision passed toapply_transform/render_preview) or{"revision_id": "rev_..."}(any other revision already in the workspace). Every layer's source must match the base image's dimensions exactly, or the recipe fails with a structured error before any pixel work happens.opsis a normal operations list, applied to that layer's source alone.mask,blend_mode(default"normal") andopacity(default1.0) control how the layer composites onto the layers below it.Blend mode is one of 16 W3C modes: the 12 separable modes
normal,multiply,screen,overlay,darken,lighten,color_dodge,color_burn,hard_light,soft_light,difference,exclusion, plus the 4 non-separable modeshue,saturation,color,luminosity.When
layersis present, the top-leveloperationsbecomes the finishing pass, applied once to the composited result — this is whereresizeand the finalencodebelong (encodemust still be last and appear at most once).Call
explain_operation {"operation":"layers"}for the full reference.
Presets
A preset can also be inlined inside a recipe as a single operation —
{"op": "preset", "name": "ocr_document"} — so a named look can be combined with
your own ops. The macro expands in place before anything runs, so the recipe
hashes exactly as if you had written the preset's operations out by hand, and a
preset that carries layers cannot be inlined (that is a structured error).
Call explain_operation {"operation":"preset"} for the rules.
apply_transform and render_preview take either recipe (the raw DSL) or
preset (a built-in named recipe from crates/atx-mcp/presets/) — exactly one of
the two:
Set | Preset | What it does |
basics |
| Center-crop to 16:9, resize to 1600px wide, WebP q82 |
basics |
| Soft film look: gentle S-curve plus a 15% pull towards luma |
basics |
| Clean product shot: near-neutral white balance, levels lift, light sharpen |
basics |
| Center-crop to 1:1, resize to 800x800, WebP q80 |
basics |
| Fit inside 2000x2000 without upscaling, WebP q80 |
basics |
| Black and white via a BT.709 luma |
basics |
| Classic sepia tone via |
film |
| Warm film stock: amber white balance, soft S-curve, light grain |
film |
| Cool film stock: blue-leaning white balance, soft S-curve, light grain |
film |
| Faded matte: lifted blacks via |
film |
| Heavy, coarse grain over a gentle S-curve (pushed/high-ISO look) |
film |
| Teal-and-orange cinematic grade via targeted |
mono |
| Neutral black and white via a BT.709 luma |
mono |
| High-contrast black and white: luma conversion plus a strong S-curve |
mono |
| B&W through a simulated red filter (classic sky darkener) |
mono |
| Soft, low-contrast black and white (matte curve) |
mono |
| Navy-to-cream duotone via |
editorial |
| Auto levels stretch, neutral white balance, final sharpen |
editorial |
| Warm orange/yellow saturation boost plus a contrast lift |
editorial |
| Soft matte curve, light desaturation, subtle vignette |
editorial |
| Contrast + saturation lift plus a light vignette |
editorial |
| Auto levels, sharpen, slight desaturation (pair with a manual |
social |
| Open Graph share image: crop 1200:630, resize to 1200 wide, WebP q82 |
social |
| X (Twitter) wide card: crop 16:9, resize to 1600 wide, WebP q82 |
social |
| Instagram square post: crop 1:1, resize to 1080x1080, WebP q85 |
social |
| Instagram portrait post: crop 4:5, resize to 1080x1350, WebP q85 |
social |
| YouTube thumbnail: crop 16:9, resize to 1280x720, WebP q85 |
social |
| Large hero/banner image: fit inside 2400px, WebP q85 |
building block |
| Subtle vignette on its own, for stacking after other looks |
building block |
| Light, fine, deterministic grain on its own, for stacking |
ocr |
| Make a page/slide/whiteboard photo legible for a vision model: grayscale, auto levels, light sharpen (no binarization) |
ocr |
| Noisy or faded receipts: grayscale, median denoise, stronger auto levels, sharpen |
ocr |
| Sauvola adaptive binarization for external OCR engines (prefer |
ocr |
| Dark-mode screenshots: |
A preset is pure sugar: it resolves to its recipe and flows through the normal
pipeline, and the recipe_hash (the idempotency key) is computed on the
resolved recipe — so a preset call and the equivalent raw recipe land on the
same revision.
Guarantees
Deterministic: the same input + the same recipe always produces byte-identical output (regression-checked with golden tests)
Idempotent: recipes are normalized (keys sorted,
f64values quantized to a 1e-6 grid) and hashed with sha256. If(input revision, recipe hash)matches an existing pair, the existing revision is returned instead of a new oneOriginals are protected:
objects/is an append-only, content-addressed store — there is no delete or overwrite API
Development
cargo test --workspace # unit + integration + property (proptest) tests
cargo clippy --workspace --all-targets -- -D warningsCrate layout: atx-core (recipe/transform engine) / atx-geometry (tilt
detection) / atx-store (immutable asset store) / atx-mcp (rmcp stdio server).
The three libraries are published on crates.io under longer names, because
atx-core there is an unrelated project:
Directory | Published as | Library name in code |
|
| |
|
| |
|
| |
|
|
So to use the transform engine as a library, depend on
asset-transform-core = "0.6.7" and write use atx_core::….
Name
"atx" stands for Asset Transform; the trailing x follows the
familiar shorthand for "transform" (as in xform / tx). It was chosen as a
short, easy-to-type binary name and directory prefix (crates/atx-core,
etc.), and it has no relation to the PC ATX form factor or Markdown ATX-style
headings. The crates.io packages spell the name out in full
(asset-transform-core, and so on).
License
MIT. See LICENSE.
Non-crate material shipped inside the binary (the bundled Roboto Regular font) is listed with its source and license in THIRD_PARTY_NOTICES.md.
If atx-mcp saves you time, you can buy me a coffee ☕
Available Tools
13 toolsapply_transformAIdempotent
Apply a transform recipe at full resolution and issue a new revision.
Pass either recipe ({"operations": [...]}, applied in order, at most one
"encode" and it must be last) or preset (a built-in named recipe) - exactly
one of the two - and either revision_id (one image) or revision_ids (the same
recipe over a batch of up to 64; one failure does not abort the rest). Call list_operations for the operation catalog and the preset
names, explain_operation for one operation's full schema.
Idempotent: the same (revision_id, resolved recipe) returns the existing derived
revision; a preset hashes identically to the equivalent raw recipe.
Note: if the recipe's encode format is png, webp, or avif, any ICC color profile
on the source is dropped (embedding is only supported for jpeg output); this is
reported as a warning, not an error.
| Name | Required | Description | Default |
|---|---|---|---|
| preset | No | ビルトインプリセット名(`list_operations` の presets セクション参照)。 指定するとそのプリセットのレシピが使われる。`recipe` とは排他。 | |
| recipe | No | 変換レシピ。`{"operations": [...]}`。`preset` とはどちらか一方のみ指定する。 | |
| revision_id | No | 入力 revision ID("rev_...")。`revision_ids` とは排他で、どちらか一方が必須。 | |
| revision_ids | No | 同じレシピを複数 revision に適用する(1..=64 件)。`revision_id` とは排他。 1件が失敗してもバッチは中断せず、その要素に error が入る。 |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
compare_revisionsAIdempotent
Scale two revisions to a long edge of 640px each and compose them on one canvas
(side by side, or stacked) with an 8px gap, returned inline as a JPEG. A is placed
left/top, B is placed right/bottom. Useful for before/after or A/B visual checks.
layout="diff" instead requires A and B to share the exact same dimensions and returns
a single pixel-difference heatmap plus mean_abs_diff/max_abs_diff/changed_pixel_ratio
and an ssim score (structural similarity, 1.0 = identical).
Every layout also reports perceptual_hash_distance: the Hamming distance between
the two dHash values (0..=64), where 5 or less usually means the same picture
re-encoded or resized and 20 or more means two different pictures.
| Name | Required | Description | Default |
|---|---|---|---|
| layout | No | `"side_by_side"`(既定、水平に並べる)| `"stacked"`(垂直に並べる)| `"diff"`(並べる代わりに1枚の差分ヒートマップを作る。A/B の寸法が完全一致している必要がある)。 | side_by_side |
| revision_id_a | Yes | 比較対象 A の revision ID("rev_...")。合成画像の左(または上)に置かれる。 | |
| revision_id_b | Yes | 比較対象 B の revision ID("rev_...")。合成画像の右(または下)に置かれる。 |
Output Schema
| Name | Required | Description |
|---|---|---|
| a | Yes | |
| b | Yes | |
| ssim | No | Structural similarity (SSIM, 0..=1) over the two images' luma. Present only with `layout: "diff"` and equal dimensions. 1.0 is pixel-identical; above ~0.98 the difference is usually invisible; below ~0.9 it is a visible change of content. |
| width | Yes | 合成画像(比較プレビュー)の寸法・容量。 |
| height | Yes | |
| layout | Yes | |
| byte_size | Yes | |
| mime_type | Yes | |
| a_position | Yes | A が合成画像のどこに置かれるか("left" | "top")。 |
| b_position | Yes | B が合成画像のどこに置かれるか("right" | "bottom")。 |
| max_abs_diff | No | `layout: "diff"` のときだけ載る: 画素ごとのチャンネル最大絶対差 d の、全画素中の最大値。 |
| preview_path | Yes | 比較プレビュー画像の絶対パス。 |
| mean_abs_diff | No | `layout: "diff"` のときだけ載る: 全チャンネル・全画素平均の絶対差(0..255 スケール)。 |
| changed_pixel_ratio | No | `layout: "diff"` のときだけ載る: d > 2 の画素が全体に占める割合(0.0..=1.0)。 |
| perceptual_hash_distance | No | Hamming distance (0..=64) between the two revisions' perceptual hashes (dHash), on every layout. 0 means the thumbnails are identical; 5 or less usually means the same picture re-encoded or resized; 20 or more means different pictures. Null when either side's pixels could not be hashed. |
detect_documentARead-onlyIdempotent
Detect the dominant quadrilateral (a sheet of paper, a screen, a whiteboard, a sign)
in a revision with a contour-based search, and return it as a ready-to-paste
perspective operation. This is to perspective what detect_tilt is to rotate:
read-only, it never modifies the image, and a null quad means "do not correct".
The quad is in post-EXIF-orientation pixel coordinates, ordered tl, tr, br, bl, and
output_size_hint is exactly the size perspective will produce from it.
Optional min_area_ratio (0.05..=1.0, default 0.2) is the smallest fraction of the
frame a candidate may cover. When quad is null the reason is the first warning:
no_quad_found, already_rectified (the page already fills the frame) or low_confidence.
| Name | Required | Description | Default |
|---|---|---|---|
| revision_id | Yes | 対象 revision ID("rev_...")。 | |
| min_area_ratio | No | 画像面積に対する候補四角形の最小面積比(0.05..=1.0)。省略時は 0.2。 |
Output Schema
| Name | Required | Description |
|---|---|---|
| detection | Yes | 検出結果。`quad` が null なら「補正しない」で、理由は `warnings` の先頭に入る。 |
| revision_id | Yes |
detect_text_blocksARead-onlyIdempotent
Find the text-like blocks (headline, paragraphs, table, caption) of a revision with
a binarize + run-length smearing + connected-components pass, and report whether the
text will survive a preview downscale. Read-only: it never modifies the image.
Blocks come back in reading order (top to bottom, then left to right) in
post-EXIF-orientation pixel coordinates, each with its line_count,
median_line_height_px and ink_ratio. legibility.line_height_at_1568_px is the
median line height once the whole image is shrunk to long edge 1568 (below ~16 the
text is usually unreadable), and legibility.recommended_bands splits the image into
crops that clear that bar - each entry is already a crop operation, so paste one into
a recipe before render_preview long_edge: 1568 and read them in order.
legibility.strategy says how it split: "whole" (no split needed), "bands"
(full-width horizontal bands) or "blocks" (per-block crops, used when the image is too
wide for full-width bands to help). Optional max_blocks (1..=128, default 32) and min_block_area_ratio
(0.0..=1.0, default 0.00005) bound how many and how small the blocks may be.
No block found means the image has no text-like structure, not an error.
| Name | Required | Description | Default |
|---|---|---|---|
| max_blocks | No | 返すブロック数の上限(1..=128)。省略時は 32。 | |
| revision_id | Yes | 対象 revision ID("rev_...")。 | |
| min_block_area_ratio | No | 作業画像面積に対する 1 ブロックの最小面積比(0.0..=1.0)。 これ未満の成分はノイズとして捨てる。省略時は 0.00005。 |
Output Schema
| Name | Required | Description |
|---|---|---|
| detection | Yes | 検出結果。ブロックが無ければ `blocks` は空で `warnings` に理由が入る。 |
| revision_id | Yes |
detect_tiltARead-onlyIdempotent
Detect the tilt (roll) of a revision: Canny + Hough dominant lines for coarse candidates, refined below 0.1 degree with an edge projection-profile search. Horizontal-only and vertical-only estimates are reported separately so a disagreement can be read as perspective/camera position rather than roll, Set include_score_curve=true to also get the whole search range as a score curve (omitted by default to keep the answer small). Read-only: it never modifies the image. A null recommended angle means "do not correct".
| Name | Required | Description | Default |
|---|---|---|---|
| revision_id | Yes | 対象 revision ID("rev_...")。 | |
| max_abs_angle | No | 探索する最大傾き角(度、0.5..=45)。省略時は 15。 | |
| include_score_curve | No | 探索範囲全体のスコア曲線(最大 300 点)を結果に含めるか。既定 false。 ピークの鋭さ・多峰性を自分で読みたいときだけ true にする(既定では省かれる)。 |
Output Schema
| Name | Required | Description |
|---|---|---|
| detection | Yes | 検出結果。`score_curve` は `include_score_curve: true` のときだけ載る。 |
| revision_id | Yes |
explain_operationARead-onlyIdempotent
Full reference for one recipe operation: every parameter with its type, range, required/default status and semantics, one or two ready-to-paste JSON examples, and the gotchas worth knowing before using it. A built-in preset name works too and returns its full operation list, so a preset can be read and copied as a raw recipe. An unknown name returns a structured error listing every valid operation and preset. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| operation | Yes | 説明したい op 名(`{"op": "..."}` に書く名前)。 |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
export_assetADestructive
Copy revisions' bytes out of the workspace. Pass either revision_id + dest_path
(one file) or revision_ids + dest_dir (a batch of up to 64 into one existing
directory; one failure does not abort the rest) - exactly one of the two forms.
For a batch, filename_template (default "{revision_id}.{ext}") builds each file
name from {revision_id} / {index} (1-based, zero-padded) / {ext} (from the MIME type) /
{stem} (the file name the lineage was imported from); it must be a plain file name,
and if two entries would collide nothing is written.
Refuses to overwrite an existing file unless overwrite=true (ask the user first),
and never writes inside the workspace store or through a symbolic link.
| Name | Required | Description | Default |
|---|---|---|---|
| dest_dir | No | 書き出し先ディレクトリ(ワークスペース外、既存のディレクトリ)。 `revision_ids`(複数)専用。 | |
| dest_path | No | 書き出し先パス(ワークスペース外)。`revision_id`(単数)専用。 | |
| overwrite | No | 既存ファイルを上書きしてよいか。既定 false(既存なら失敗する)。 | |
| revision_id | No | 書き出す revision ID("rev_...")。`revision_ids` とは排他で、どちらか一方が必須。 | |
| revision_ids | No | 複数 revision を1回で書き出す(1..=64 件)。`revision_id` とは排他。 このときは `dest_path` ではなく `dest_dir` を渡す。 1件が失敗してもバッチは中断せず、`failed` に理由が入る。 | |
| filename_template | No | `dest_dir` 内のファイル名の組み立て方。既定 `"{revision_id}.{ext}"`。 `revision_ids`(複数)専用。 |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
generate_maskAIdempotent
Generate a deterministic grayscale mask as a new PNG revision, with exactly the
dimensions of reference_revision_id. kind is "linear_gradient" (angle_degrees,
start, end), "radial_gradient" (center_x, center_y, radius, feather),
"luminosity_range" (min, max, feather) or "color_range" (hue_center, hue_width,
feather); the gradients use only the reference's dimensions, the other two compute
weights from its pixels. White = the masked operation applies fully, black = not at
all. Reference the returned revision_id from any tone/filter operation as
"mask": {"revision_id": "rev_...", "invert": false, "feather_px": 0}, or visualise it
with render_preview overlay="mask". Idempotent: the same params over the same
reference produce byte-identical PNG bytes and return the existing revision.
| Name | Required | Description | Default |
|---|---|---|---|
| end | No | linear_gradient: 軸上で重みが 0.0 に達する位置(0..1、`start` 以上)。既定 1.0。 | |
| max | No | luminosity_range: 完全に選択される輝度域の上限(0..255、`min` 以上)。既定 255。 | |
| min | No | luminosity_range: 完全に選択される輝度域の下限(0..255)。既定 0。 | |
| kind | Yes | `"linear_gradient"` | `"radial_gradient"` | `"luminosity_range"` | `"color_range"`。 | |
| start | No | linear_gradient: 軸上で重みが 1.0 のままでいる終端位置(0..1)。既定 0.0。 | |
| radius | No | radial_gradient: 重みが 1.0 の内円の半径(対角線の半分に対する比 0..1)。既定 0.5。 | |
| feather | No | 減衰帯の幅。radial_gradient では対角線の半分に対する比(0..1、既定 0.25)、 luminosity_range では輝度単位(0..255、既定 16)、 color_range では色相の度数(0..180、既定 15)。linear_gradient では使わない (`start`/`end` の間隔がフェザそのもの)。 | |
| center_x | No | radial_gradient: 中心の X(画像幅に対する相対値 0..1)。既定 0.5。 | |
| center_y | No | radial_gradient: 中心の Y(画像高に対する相対値 0..1)。既定 0.5。 | |
| hue_width | No | color_range: 中心からの**片側**幅(度、1..180)。既定 30。 | |
| hue_center | No | color_range: 中心色相(度、0..360)。必須。 | |
| angle_degrees | No | linear_gradient: グラデーション軸の角度(度)。0 = 上が白で下へ向かって黒、 90 = 左が白で右へ向かって黒(正の角度で時計回り)。既定 0。 | |
| reference_revision_id | Yes | 寸法(と、輝度/色域マスクでは画素)の供給元になる画像 revision ID。 |
Output Schema
| Name | Required | Description |
|---|---|---|
| kind | Yes | 生成した種別(`linear_gradient` 等)。 |
| next | Yes | 次の一手(op への参照の仕方)。 |
| width | Yes | |
| height | Yes | |
| reused | Yes | 同じマスクが既に生成済みで既存 revision を返した場合 true(冪等ヒット)。 |
| revision | Yes | |
| generator | Yes | 既定値まで解決したパラメータの正規化 JSON(origin の generator と同一文字列)。 |
| mean_weight | Yes | マスクの平均重み(0..1)。1 に近いほど広く、0 に近いほど狭い被覆。 |
| reference_revision_id | Yes | 参照した画像 revision(寸法・画素の供給元)。 |
import_assetAIdempotent
Import a local image file into the workspace and issue an immutable revision.
Pass either path (one file) or paths (a batch of up to 64; one bad file does not
abort the batch, it lands in failed) - exactly one of the two.
Idempotent: importing the same bytes again returns the existing revision.
If the imported bytes are already the output of a recipe held in this workspace,
the result carries a warning plus already_derived_from so the same recipe is not
applied twice.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | 取り込むローカルファイルの絶対パス(または cwd からの相対パス)。 `paths` とは排他で、どちらか一方が必須。 | |
| paths | No | 複数ファイルを1回で取り込む(1..=64 件)。`path` とは排他。 1件が失敗してもバッチは中断せず、`failed` に理由が入る。 |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
inspect_imageARead-onlyIdempotent
Inspect a revision: dimensions, MIME type, byte size, alpha/ICC presence,
EXIF orientation and summary, whether GPS (PII) metadata is present, luma
statistics, a sharpness score (variance of the Laplacian - relative, so compare
it against a known-good capture rather than an absolute; for documents below ~30
usually means motion blur or defocus) and a perceptual_hash (dHash, 16 hex
digits) for telling "is this the same picture?" without comparing pixels.
Set include_exif=true to also get every EXIF field as {ifd, tag, value} entries;
it is off by default because the full dump can carry GPS coordinates and names.
| Name | Required | Description | Default |
|---|---|---|---|
| revision_id | Yes | 対象 revision ID("rev_...")。 | |
| include_exif | No | EXIF 全フィールドを `info.exif` に載せるか。既定 false。 既定で落としているのはプライバシーのため: 全量には GPS 座標や 個人名が入りうるので、明示的に要求されたときだけ返す (`exif_summary` と `has_gps` は従来どおり常に載る)。 |
Output Schema
| Name | Required | Description |
|---|---|---|
| info | Yes | |
| path | Yes | |
| revision_id | Yes |
list_assetsBRead-onlyIdempotent
List revisions in the workspace ledger (lineage, recipe hash, dimensions, sizes).
| Name | Required | Description | Default |
|---|---|---|---|
| asset_id | No | 指定した asset_id の revision だけに絞る。省略時は全件。 |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| revisions | Yes |
list_operationsARead-onlyIdempotent
Compact catalog of the recipe vocabulary: every operation with a one-line
description and its parameter names with terse type/range hints, plus the
built-in preset names. Optional category ("geometry" | "color" | "filter" |
"output") narrows the list. Call explain_operation for the full schema of one
operation. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| category | No | 絞り込む分類(`"geometry"` | `"color"` | `"filter"` | `"output"`)。省略時は全件。 |
Output Schema
| Name | Required | Description |
|---|---|---|
| ops | Yes | op の名前と分類だけ。要約・パラメータはテキスト側 / `explain_operation` 側。 |
| count | Yes | |
| presets | Yes | ビルトインプリセット名(`apply_transform` / `render_preview` の `preset` に渡せる)。 説明はテキストサマリ側にだけ載せる。 |
render_previewAIdempotent
Render a recipe as a small JPEG preview (long edge <= 768 by default) and return it
inline plus a file path, so the composition can be checked before committing to
apply_transform. Takes either recipe or preset, exactly like apply_transform - or
NEITHER, which previews the revision as it is (use this with overlay="grid" to read
crop coordinates off an image before writing a recipe).
Optional long_edge (256..=1568) sets the preview size: raise it to 1568 when the
point of the preview is to READ text in the image; 768 is too small for that.
Optional overlay ("grid" | "thirds" | "horizon") draws semi-transparent composition
guide lines on the returned preview only (never on the apply_transform output).
overlay="mask" instead visualises a mask: pass mask_revision_id (required for this
overlay and rejected for the others) and the preview is tinted red where the mask
weight exceeds 0.5 and dimmed elsewhere, so the coverage can be eyeballed.
Note: if the recipe's encode format is png, webp, or avif, any ICC color profile
on the source is dropped (embedding is only supported for jpeg output); this is
reported as a warning, not an error.
| Name | Required | Description | Default |
|---|---|---|---|
| preset | No | ビルトインプリセット名。`recipe` とは排他。 | |
| recipe | No | 変換レシピ。`{"operations": [...]}`。`preset` とはどちらか一方のみ指定する。 | |
| overlay | No | 構図確認用のガイド線。`"grid"`(1/8 刻みの格子)| `"thirds"`(三分割法)| `"horizon"`(1/12 刻みの水平線のみ、傾き目視用)| `"mask"`(マスクの被覆可視化。 `mask_revision_id` が必須)。省略時はオーバレイなし。 | |
| long_edge | No | プレビューの長辺(256..=1568)。省略時は 768。 文字を読む用途では 1568 まで上げる(DESIGN.md §9.12)。 | |
| revision_id | Yes | 入力 revision ID("rev_...")。 | |
| mask_revision_id | No | `overlay: "mask"` で可視化するマスク画像 revision ID。 `overlay` が `"mask"` のときのみ指定でき、そのときは必須。 |
Output Schema
| Name | Required | Description |
|---|---|---|
| width | Yes | |
| height | Yes | |
| overlay | No | 適用した overlay。未指定なら null。 |
| warnings | Yes | |
| byte_size | Yes | |
| mime_type | Yes | |
| recipe_hash | Yes | |
| preview_path | Yes | プレビュー画像の絶対パス。 |
| engine_version | Yes | |
| mask_revision_id | No | `overlay: "mask"` で可視化したマスクの revision ID。それ以外では null。 |
| source_revision_id | Yes | |
| estimated_vision_tokens | Yes | Rough token cost of reading this preview inline: a rule of thumb for Anthropic vision models (pixels / 750, rounded up); other hosts differ. Use it to decide whether a larger `long_edge` (or reading a long document in bands) is worth it. |
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.6.4- Changed
compare_revisions2 fields changed- added
Output schema / properties / perceptual_hash_distanceAdded value: +{ + "description": "Hamming distance (0..=64) between the two revisions' perceptual hashes (dHash),\non every layout. 0 means the thumbnails are identical; 5 or less usually means\nthe same picture re-encoded or resized; 20 or more means different pictures.\nNull when either side's pixels could not be hashed.", + "format": "uint32", + "minimum": 0, + "type": [ + "integer", + "null" + ] +} - added
Output schema / properties / ssimAdded value: +{ + "description": "Structural similarity (SSIM, 0..=1) over the two images' luma. Present only with\n`layout: \"diff\"` and equal dimensions. 1.0 is pixel-identical; above ~0.98 the\ndifference is usually invisible; below ~0.9 it is a visible change of content.", + "format": "double", + "type": [ + "number", + "null" + ] +}
- Added
detect_text_blocks - Changed
export_asset12 fields changed- added
Input schema / properties / dest_dirAdded value: +{ + "description": "書き出し先ディレクトリ(ワークスペース外、既存のディレクトリ)。\n`revision_ids`(複数)専用。", + "type": [ + "string", + "null" + ] +} - changed
Input schema / properties / dest_path / descriptionPrevious value: -"書き出し先パス(ワークスペース外)。"New value: +"書き出し先パス(ワークスペース外)。`revision_id`(単数)専用。" - changed
Input schema / properties / dest_path / typePrevious value: -"string"New value: +[ + "string", + "null" +] - added
Input schema / properties / filename_templateAdded value: +{ + "description": "`dest_dir` 内のファイル名の組み立て方。既定 `\"{revision_id}.{ext}\"`。\n`revision_ids`(複数)専用。", + "type": [ + "string", + "null" + ] +} - changed
Input schema / properties / revision_id / descriptionPrevious value: -"書き出す revision ID(\"rev_...\")。"New value: +"書き出す revision ID(\"rev_...\")。`revision_ids` とは排他で、どちらか一方が必須。" - changed
Input schema / properties / revision_id / typePrevious value: -"string"New value: +[ + "string", + "null" +] - added
Input schema / properties / revision_idsAdded value: +{ + "description": "複数 revision を1回で書き出す(1..=64 件)。`revision_id` とは排他。\nこのときは `dest_path` ではなく `dest_dir` を渡す。\n1件が失敗してもバッチは中断せず、`failed` に理由が入る。", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] +} - removed
Input schema / requiredRemoved value: -[ - "revision_id", - "dest_path" -] - added
Output schema / $defsAdded value: +{ + "BatchError": { + "description": "バッチ要素の失敗理由。単一呼び出しが返す構造化エラーと同じ code / message。", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" + }, + "BatchFailure": { + "description": "バッチの1件分の失敗(パスと構造化エラーの要点)。", + "properties": { + "error": { + "$ref": "#/$defs/BatchError" + }, + "path": { + "description": "入力で与えられたパス(または revision ID)。", + "type": "string" + } + }, + "required": [ + "path", + "error" + ], + "type": "object" + }, + "ExportAssetOutput": { + "description": "`export_asset` の structuredContent。", + "properties": { + "byte_size": { + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "overwritten": { + "description": "既存ファイルを上書きした場合 true。", + "type": "boolean" + }, + "path": { + "description": "実際に書き出した絶対パス。", + "type": "string" + }, + "revision_id": { + "type": "string" + } + }, + "required": [ + "revision_id", + "path", + "byte_size", + "overwritten" + ], + "type": "object" + }, + "ExportBatchOutput": { + "description": "`export_asset`(バッチ)の structuredContent。", + "properties": { + "count": { + "description": "書き出しに成功した件数(= `exported` の長さ)。", + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "dest_dir": { + "description": "書き出し先ディレクトリの絶対パス。", + "type": "string" + }, + "exported": { + "description": "入力順の成功結果。", + "items": { + "$ref": "#/$defs/ExportAssetOutput" + }, + "type": "array" + }, + "failed": { + "description": "失敗した revision(1件の失敗でバッチは中断しない)。", + "items": { + "$ref": "#/$defs/BatchFailure" + }, + "type": "array" + }, + "filename_template": { + "description": "実際に使ったファイル名テンプレート(既定を含む)。", + "type": "string" + } + }, + "required": [ + "count", + "exported", + "failed", + "dest_dir", + "filename_template" + ], + "type": "object" + } +} - added
Output schema / anyOfAdded value: +[ + { + "$ref": "#/$defs/ExportAssetOutput" + }, + { + "$ref": "#/$defs/ExportBatchOutput" + } +] - removed
Output schema / propertiesRemoved value: -{ - "byte_size": { - "format": "uint64", - "minimum": 0, - "type": "integer" - }, - "overwritten": { - "description": "既存ファイルを上書きした場合 true。", - "type": "boolean" - }, - "path": { - "description": "実際に書き出した絶対パス。", - "type": "string" - }, - "revision_id": { - "type": "string" - } -} - removed
Output schema / requiredRemoved value: -[ - "revision_id", - "path", - "byte_size", - "overwritten" -]
- Changed
import_asset2 fields changed- added
Output schema / $defs / ImportEntry / properties / font_familiesAdded value: +{ + "description": "Font family names this asset provides, present only when the imported file is a\nfont. Use one of these verbatim in the SVG's `font-family` and reference this\nrevision from `svg_overlay.font_revision_ids`.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] +} - added
Output schema / $defs / ImportOutput / properties / font_familiesAdded value: +{ + "description": "Font family names this asset provides, present only when the imported file is a\nfont. Use one of these verbatim in the SVG's `font-family` and reference this\nrevision from `svg_overlay.font_revision_ids`.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] +}
- Changed
inspect_image7 fields changed- added
Input schema / properties / include_exifAdded value: +{ + "default": false, + "description": "EXIF 全フィールドを `info.exif` に載せるか。既定 false。\n\n既定で落としているのはプライバシーのため: 全量には GPS 座標や\n個人名が入りうるので、明示的に要求されたときだけ返す\n(`exif_summary` と `has_gps` は従来どおり常に載る)。", + "type": "boolean" +} - added
Output schema / $defs / ExifEntryAdded value: +{ + "description": "One EXIF field, rendered as text.", + "properties": { + "ifd": { + "description": "Which IFD the field came from: primary, exif, gps, interop or thumbnail.", + "type": "string" + }, + "tag": { + "description": "Tag name, or `Tag(0x____)` when the tag is not in the standard set.", + "type": "string" + }, + "value": { + "description": "Formatted value including its unit. Long values are cut to 256 characters\nfollowed by `...`; maker notes and binary blobs over 1 KiB read `<N bytes>`.", + "type": "string" + } + }, + "required": [ + "ifd", + "tag", + "value" + ], + "type": "object" +} - added
Output schema / $defs / ImageInfo / properties / exifAdded value: +{ + "description": "Every EXIF field found in the input, one entry per field. Present only when\nthe caller explicitly asks for it, because the full dump can include GPS\ncoordinates and other personal data.", + "items": { + "$ref": "#/$defs/ExifEntry" + }, + "type": [ + "array", + "null" + ] +} - added
Output schema / $defs / ImageInfo / properties / perceptual_hashAdded value: +{ + "description": "Perceptual hash (dHash) as 16 lowercase hex digits, or null when the pixels\ncould not be decoded. Compare two hashes bit by bit: a Hamming distance of\n5 or less usually means the same picture re-encoded or resized.", + "type": [ + "string", + "null" + ] +} - changed
Output schema / $defs / ImageInfo / properties / stats / descriptionPrevious value: -"輝度ヒストグラム統計(黒点 / 白点 / 中央値 / チャンネル平均)。\n\n**決定論的なサブサンプル**上で計算する(`crate::stats` 参照)。\n262144 画素(512x512 相当)以下の画像は全画素を使うため厳密値、\nそれより大きい画像はグリッド間引き(x, y とも k 刻み)による近似値。\nメタデータは読めたが画素デコードに失敗した場合は `None`(inspect 自体は成功させる)。"New value: +"輝度ヒストグラム統計(黒点 / 白点 / 中央値 / チャンネル平均)。\n\nヒストグラムとチャンネル平均は**決定論的なグリッド間引き**の上で計算する\n(`crate::stats` 参照。262144 画素以下は全画素なので厳密値)。`sharpness` だけは\n間引かず全画素を輝度化し、長辺 1024 へ縮小してから測る(隣接差分が要るため)。\nメタデータは読めたが画素デコードに失敗した場合は `None`(inspect 自体は成功させる)。" - added
Output schema / $defs / ImageStats / properties / sharpnessAdded value: +{ + "description": "Variance of the 3x3 Laplacian response over the BT.709 luma, downscaled to a\nlong edge of at most 1024 px (higher = crisper edges; rounded to 1 decimal).\nScene-dependent, so treat it as a relative measure: for documents, below ~30\nusually means motion blur or defocus; compare against a known-good capture\nrather than an absolute.", + "format": "double", + "type": "number" +} - changed
Output schema / $defs / ImageStats / requiredPrevious value: -[ - "luma_p0_2", - "luma_p1", - "luma_p50", - "luma_p99", - "luma_p99_8", - "mean_r", - "mean_g", - "mean_b" -]New value: +[ + "luma_p0_2", + "luma_p1", + "luma_p50", + "luma_p99", + "luma_p99_8", + "mean_r", + "mean_g", + "mean_b", + "sharpness" +]
- Changed
render_preview2 fields changed- added
Output schema / properties / estimated_vision_tokensAdded value: +{ + "description": "Rough token cost of reading this preview inline: a rule of thumb for Anthropic\nvision models (pixels / 750, rounded up); other hosts differ. Use it to decide\nwhether a larger `long_edge` (or reading a long document in bands) is worth it.", + "format": "uint32", + "minimum": 0, + "type": "integer" +} - changed
Output schema / requiredPrevious value: -[ - "source_revision_id", - "recipe_hash", - "engine_version", - "preview_path", - "width", - "height", - "byte_size", - "mime_type", - "warnings" -]New value: +[ + "source_revision_id", + "recipe_hash", + "engine_version", + "preview_path", + "width", + "height", + "byte_size", + "mime_type", + "estimated_vision_tokens", + "warnings" +]
12 tool updates
v0.5.2- First observed
apply_transform - First observed
compare_revisions - First observed
detect_document - First observed
detect_tilt - First observed
explain_operation - First observed
export_asset - First observed
generate_mask - First observed
import_asset - First observed
inspect_image - First observed
list_assets - First observed
list_operations - First observed
render_preview
TDQS
Scored across 13 tools
Each tool targets a distinct resource or action: cataloging operations, listing assets, comparing revisions, detecting text/document/tilt, exporting/importing, inspecting, previewing, applying transforms, and generating masks. The only pair with adjacent concepts is list_operations and explain_operation, but their descriptions clearly separate catalog-level discovery from single-operation schema lookup.
All 13 tools use a consistent snake_case verb_noun convention (list_assets, detect_tilt, apply_transform, etc.). There are no mixed casing styles or vague one-word names.
13 tools is well-scoped for an image workspace and processing server, with each tool covering a meaningful capability. No tool appears redundant or trivial; the surface stays within a reasonable range for both discovery and operation.
The set covers import/export, inspection, preview, full-resolution transforms, masking, comparison, and several detection workflows, which is strong lifecycle coverage for the domain. The main gap is revision/workspace cleanup—there is no delete or remove tool for ledger entries—so some housekeeping workflows require external handling.
Maintenance
Related MCP Connectors
Image processing for AI agents: resize, convert, compress, crop, and web-ready AI-generated images.
Video, audio, and image processing for AI agents: convert, transcribe, upscale - 150+ operations.
Image & PDF tools for AI agents: compress, convert, resize, PDF, AI vision, pipeline.
Design, save, and run outcome-aligned AI workflows and verifiers, with reliable image output.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to process images locally via file paths—converting, resizing, removing backgrounds, smart cropping, upscaling, reading or stripping metadata, and batch processing—without files ever leaving the device.MIT
- AlicenseAqualityCmaintenanceEnables AI agents to perform local image operations such as circle cropping, cropping, resizing, compressing, converting, and inspecting images without uploading files or making network calls.786 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to perform local computer-vision-based image recognition and editing, including cropping, resizing, rotating, background removal, drawing text and shapes, object detection, OCR, overlays, and metadata inspection without sending images to external servers.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to run real image and PDF processing remotely — compressing, converting, resizing, cropping, watermarking, and chaining multiple operations in a single call — with no local install and zero-retention handling.9MIT