spektrafilm-mcp
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., "@spektrafilm-mcpOpen portrait.CR3, develop with Portra 400, warm it up, and export a JPEG."
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.
spektrafilm-mcp
An MCP server that turns spektrafilm's spectral film simulation into tools an agent can use to develop photos: open a RAW or image, pick film and paper, set exposure, dial in enlarger filtration, compose, compare stocks side by side, inspect grain at 100%, and export. Every call returns a preview, so the agent sees the result, and so do you in the conversation.
Credits
Spectral film modeling powered by spektrafilm, by Andrea Volpato. All the film science lives there: the spectral pipeline, the film and paper profiles digitised from manufacturer datasheets, DIR couplers, grain and halation. This project only wraps it as MCP tools for agents and adds framing, tiled export and RAW loading around it.
This is an independent project, not affiliated with or endorsed by spektrafilm. It installs spektrafilm as a dependency, pinned to commit 3bb2c2d (see pyproject.toml). If you find it useful, star spektrafilm, cite it in academic work (its repository has a CITATION.cff), and consider supporting its author.
Related MCP server: imagegrain MCP
Install
The only prerequisites are uv and git. uv fetches Python 3.13 and every dependency (including spektrafilm itself) into an isolated, cached environment, so there is nothing else to install.
curl -LsSf https://astral.sh/uv/install.sh | shClaude Code
claude mcp add spektrafilm -- uvx --from git+https://github.com/BangyiZhang/spektrafilm-mcp spektrafilm-mcpClaude Desktop or another MCP client (claude_desktop_config.json or your client's equivalent)
{
"mcpServers": {
"spektrafilm": {
"command": "uvx",
"args": ["--from", "git+https://github.com/BangyiZhang/spektrafilm-mcp", "spektrafilm-mcp"]
}
}
}First run is slow. uv downloads about 2 GB of dependencies (spektrafilm pulls in its desktop GUI libraries, which this server doesn't use), then numba compiles spektrafilm's kernels for about 2 minutes. Both happen once; later starts take a few seconds. The server warms up in the background, and calls made before warm-up finishes wait for it.
Pass --image /path/x.ARW to load an image at startup.
From a local checkout
git clone https://github.com/BangyiZhang/spektrafilm-mcp
cd spektrafilm-mcp
uv sync
claude mcp add spektrafilm -- uv run --project "$PWD" spektrafilm-mcpEnd-to-end smoke test: uv run python tests/smoke_mcp.py [image] calls every tool over stdio the way an agent would and writes the images to out/.
Tools
Tool | What it does |
| Load a RAW (CR3/NEF/ARW/RAF/DNG…) or a JPEG/PNG/TIFF/EXR, return a preview and a geometry check (horizon tilt, vertical lean, keystone) |
| List the available films and papers |
| Change the recipe (only the fields you pass) and return the developed preview. |
| Compose: crop aspect ratio, zoom and position, straighten, vertical and horizontal perspective correction, 90° rotation, flips. Only the fields you pass change; |
| Render a labelled contact sheet of up to 12 variations; the axes you pass are combined |
| Full-resolution crop for inspecting grain and halation |
| Full-resolution export, rendered in tiles to keep memory bounded (about 73 s for 40 MP). Writes jpg, png, 16-bit tif or exr |
| Show the current image and recipe |
| Revert the last change |
Recipe fields
Field | Meaning |
| Film stock / print paper. Changing the film without naming a paper switches to that film's recommended paper. Slide films skip the print stage |
| Camera exposure compensation, in stops |
| Automatic print compensation, on by default. Like a lab printer, it adjusts print exposure to the negative's density, so |
| Enlarger exposure; above 1 prints darker |
| Enlarger filtration in Kodak CC units: +Y cooler, −Y warmer; +M greener, −M more magenta |
| Paper preflash: lowers contrast and opens up the shadows |
| Gamma of the film's characteristic curve; works like push or pull processing |
| DIR coupler strength; affects saturation and sharpness |
| Grain on/off and particle size in µm² (about 0.1 ≈ ISO 100, 0.4 ≈ ISO 400) |
| Halation and in-emulsion scatter. A |
| Reconstructs highlights the camera clipped, so light sources glow more realistically |
| Pro-Mist style diffusion filter |
| Long edge of the film frame; smaller formats make grain and halation larger relative to the picture |
| Scan the developed film itself instead of printing it |
| Straighten from −45° to 45°; positive rotates clockwise. Zooms in automatically so no empty corners show |
| Vertical / horizontal perspective correction, −100 to 100. Positive vertical widens the top, straightening verticals that converge upward when shooting up at buildings; positive horizontal widens the right side |
| Crop aspect ratio (original, 3:2, 4:5, 1:1, 16:9, XPan 65:24, cinema 2.39:1 and more), crop zoom (1 = the largest crop at that ratio) and the crop's centre |
| Clockwise 90° rotation, horizontal / vertical flip |
Ranges and descriptions for every field live in RECIPE_FIELDS in src/spektrafilm_mcp/recipe.py; the MCP tool parameter docs are generated from it.
Implementation notes
Framing happens before the film (
geometry.py). Crop, straighten and perspective apply to the scene before it reaches the film, like enlarging only part of the negative: a tighter crop makes grain and halation larger relative to the picture, and the effective film format shrinks with the crop. All transforms compose into one 3×3 homography that maps output pixels back to the source for bilinear sampling. When only 90° rotations, flips and crops are involved, the image is sliced directly with no interpolation.Previews sample from the most suitable level of an image pyramid (up to 4096 px, halving each level), so a framing change previews in about 0.25 s.
Export and the loupe sample each tile straight from the full-resolution source through the framing, so cropped or straightened images still render in tiles with bounded memory.
Auto exposure is metered once on the uncropped frame, so cropping never changes exposure.
Geometry check and auto upright (
upright.py). A 1–2° tilt is easy to miss on a preview, so the agent gets measurements instead: line segments are detected (Canny + probabilistic Hough at 1600 px, each refined to sub-pixel angle by a least-squares fit to its edge pixels), and the straighten/perspective values that make verticals plumb and parallel are solved through the same homography the framing uses. Real photos are full of lines that aren't level or plumb, so the results are guarded:Leveling trusts verticals (gravity) first. Horizontals are used only when they agree with each other (spread ≤ 1°); receding roof lines in a street disagree and are reported as perspective lines, not a horizon.
Near-vertical segments that don't fit a lean-vs-position line (water jets, eaves, branches) are dropped before solving; real keystone, where lines lean more toward the edges, survives.
A solution at the edge of the search range, or verticals that still disagree after correction, means "ambiguous": auto makes no change and says why.
On synthetic distortions, tilt and single-axis keystone are recovered to within about 0.1°. Strong vertical and horizontal keystone together leaves about 1° of residual, because the correction applies the two tilts in a fixed order.
Exposure, print exposure and Y/M filtration are applied through spektrafilm's
Simulator.soft_update(), without rebuilding the pipeline or its spectral LUTs, so changing them is fast. Any other change rebuilds the pipeline; the first frame then takes about 1–2 s.Tiled export. spektrafilm needs about 0.9 GB per megapixel (about 2.7 GB without LUTs), so rendering a 40 MP RAW in one pass would need tens of GB or more. Tiles are sized to 25% of the machine's RAM (override with the
SPEKTRAFILM_TILE_MPenvironment variable). Tiles overlap by the physical reach of halation and coupler diffusion, so seams don't show. Auto exposure is metered once for the whole frame and applied to every tile. The highlight boost normalizes by the frame's maximum and can't be tiled, so with it on the export is downscaled to fit in memory.Export speed-ups (
accel.py). Profiling showed about 40% of the time in spektrafilm's output gamut compression (colour-science CAM16-UCS round trips, float64, single core). It is a per-pixel transform with no numba inside, so it runs on a thread pool over row bands; the result is bit-identical to the single-threaded call and about 3× faster. spektrafilm's numba grain RNG (use_fast_stats) is also enabled at export resolution: it is about 2× faster there and gives each tile independent grain. It is slower at preview resolution, so previews keep scipy's samplers. Running tiles in parallel processes was tried and dropped: memory bandwidth is the bottleneck, and two processes made each tile twice as slow.Two-stage RAW decoding. On open, LibRaw's
half_sizemode bins pixels instead of demosaicing: about 0.2 s for a 40 MP Fujifilm X-Trans file, used for previews. The full demosaic (about 15 s, single-threaded, releases the GIL) runs in the background; the loupe and export wait for it.Measured on an M2 Pro with a 40 MP Fujifilm RAF: opening takes 2.2 s (was 20 s); a full-resolution export takes 73 s in 6 tiles with 8.2 GB peak memory (was 122 s and about 13 GB).
numba's default threading layer aborts the process if parallel kernels run from two threads at once, so every render goes through one global lock.
Non-linear input (JPEG/PNG) is converted to linear on load. spektrafilm's auto exposure applies its gain before sRGB decoding, so passing encoded data makes images far too bright.
License
GPLv3, like spektrafilm. This project calls spektrafilm's code directly, which makes it a derivative work that must be released under GPLv3, as spektrafilm's author asks. The film profile data is CC BY-SA 4.0. The sample image samples/portrait_leaves_32bit_linear_prophoto_rgb.tif comes from the spektrafilm repository.
Available Tools
9 toolscompareA
Render a labelled contact sheet of variations of the current recipe (up to 12 tiles).
Axes you pass are combined (e.g. 3 films x 2 exposures = 6 tiles). Does not change the recipe.
| Name | Required | Description | Default |
|---|---|---|---|
| films | No | Film stocks to compare. Each uses its recommended paper unless prints is given. | |
| prints | No | Print papers to compare. | |
| exposures_ev | No | Camera exposure compensations to compare, e.g. [-1, 0, 1, 2]. | |
| print_exposures | No | Enlarger print exposures to compare, e.g. [0.8, 1.0, 1.25]. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are supplied, so the description carries the full burden; it does usefully disclose that the operation 'does not change the recipe' (a read-only, non-mutating guarantee). However, it omits what happens when the combined axes exceed 12 tiles (error vs. truncation), what the return value is (an image? a reference?), and whether all axes are optional and what happens with none supplied.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the primary action and scope, followed by the combinatorial rule and the non-mutation guarantee. Every sentence adds information an agent needs; nothing is padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter, all-optional tool with no annotations and no output schema, the description should say what is produced and how the 12-tile cap is enforced. It covers the input composition well but leaves the output side and the over-cap behavior undocumented, which is a meaningful gap when no output schema exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so parameter meaning is already documented, but the description adds genuinely new semantics the schema lacks: axes are combined as a cartesian product, and there is a hard ceiling of 12 tiles that constrains how many values per axis are sensible. The example ('3 films x 2 exposures = 6 tiles') makes the combinatorics unambiguous.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb and resource — 'Render a labelled contact sheet of variations of the current recipe' — plus a concrete scope (up to 12 tiles) and the combinatorial behavior of the axes. It is clearly distinct from siblings like develop, frame, or loupe, though it never explicitly names a sibling to differentiate against.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent can infer you call this when you want to visually compare film/paper/exposure variations, and the example '3 films x 2 exposures = 6 tiles' hints at how to compose the call. There is no explicit when-to-use, when-not-to-use, or pointer to an alternative (e.g. single-variation tools) if the comparison exceeds the tile cap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
developC
Change the film recipe (only the fields you pass) and return the developed preview.
| Name | Required | Description | Default |
|---|---|---|---|
| film | No | Camera film stock (negative, slide/positive or cine). See list_stocks. Default 'kodak_portra_400'. Changing film also switches to its recommended paper unless print is given. | |
| grain | No | Stochastic silver-halide grain. Only rendered in quality='full' (previews skip it for speed). Default True. | |
| No | Print paper or cine print film. Ignored for slide films and when scan_negative is on. Default 'kodak_portra_endura'. | ||
| reset | No | Start from the default recipe before applying the given fields. | |
| quality | No | 'preview' (fast, no grain) or 'full' (grain and all spatial effects, slower). | preview |
| contrast | No | Multiplier on the film's characteristic-curve gamma. >1 like push processing, <1 like pull. Range 0.5 to 1.6. Default 1.0. | |
| couplers | No | Strength of DIR inhibitor couplers (inter-layer effects). Higher = more saturation and local contrast/acutance, 0 = off. Range 0.0 to 2.0. Default 1.0. | |
| halation | No | Red/orange glow around highlights from light bouncing off the film base, plus in-emulsion scatter. Default True. | |
| preflash | No | Uniform pre-exposure of the paper. Lowers print contrast and opens up the shadows. Try 0.005-0.05. Range 0.0 to 0.1. Default 0.0. | |
| diffusion | No | Strength of a Pro-Mist style diffusion filter on the camera, in filter grades (0, 1/8, 1/4, 1/2, 1, 2). 0 = no filter. Range 0.0 to 2.0. Default 0.0. | |
| grain_size | No | Grain particle area in µm². Roughly 0.1 ≈ ISO 100, 0.4 ≈ ISO 400; bigger = coarser. Range 0.02 to 2.0. Default 0.2. | |
| exposure_ev | No | Exposure compensation of the virtual camera in stops, on top of auto exposure. +1 = one stop over. Negative film holds highlights gracefully; underexposure gets muddy and grainy. Range -4.0 to 4.0. Default 0.0. | |
| auto_exposure | No | Meter the scene like a camera so midgray lands on the film's midgray before compensation. Default True. | |
| scan_negative | No | Scan the developed film itself instead of printing it (orange-masked negative, or the slide for positive films). Default False. | |
| film_format_mm | No | Long edge of the film frame in mm (35 = 135, 60/120 = medium format). Smaller formats = bigger grain and halation relative to the frame. Range 8.0 to 120.0. Default 35.0. | |
| m_filter_shift | No | Magenta filtration shift from neutral, Kodak CC units. +M makes the print greener, -M makes it more magenta. Range -40.0 to 40.0. Default 0.0. | |
| print_exposure | No | Enlarger exposure multiplier (like print time). >1 = darker print, <1 = lighter print. Range 0.2 to 3.0. Default 1.0. | |
| scatter_amount | No | In-emulsion light scatter (softness). 1 = physical default, 0 = none. Range 0.0 to 2.0. Default 1.0. | |
| y_filter_shift | No | Yellow filtration shift from neutral, Kodak CC units. +Y makes the print bluer/cooler, -Y makes it yellower/warmer. Range -40.0 to 40.0. Default 0.0. | |
| halation_amount | No | Multiplier on halation strength (1 = physical default for this stock). Try 3-8 for a CineStill-like glow. Range 0.0 to 10.0. Default 1.0. | |
| diffusion_family | No | Diffusion filter family. Default 'black_pro_mist'. | |
| highlight_boost_ev | No | Reconstructs clipped highlights before they hit the film, so light sources bloom and halate more realistically. Range 0.0 to 4.0. Default 0.0. | |
| print_compensation | No | Like a lab printer: compensate print exposure for exposure_ev, so exposure_ev mostly changes tonality (highlight/shadow latitude, grain, saturation) rather than brightness. Turn off to make exposure_ev brighten/darken the print directly. Default True. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It states that only passed fields are changed and that a developed preview is returned, but it does not say whether changes persist, whether they are undoable, what permissions are needed, or how costly a 'full' quality render may be. For a state-changing tool with 23 parameters, this is a significant gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no filler. It states the action, the partial-update rule, and the return value efficiently.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 23 parameters, no annotations, and no output schema, the description is too thin. It does not explain what the developed preview contains, how it relates to the current state, or when to use this tool rather than its siblings. The rich parameter schema helps, but the description remains incomplete for a tool of this complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents each parameter in detail. The description adds only the useful partial-update clarification that 'only the fields you pass' are changed, which slightly extends beyond the schema but does not provide parameter-specific meaning. The baseline of 3 is appropriate when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'Change the film recipe' and 'return the developed preview.' It is clear what the tool does, but it does not distinguish itself from siblings such as frame, compare, or list_stocks, leaving the agent to infer when this tool is preferred.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use or when-not-to-use guidance, and no alternatives are named. The phrase 'only the fields you pass' describes partial-update behavior but does not tell the agent under what circumstances to call develop versus frame, compare, or list_stocks.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
exportB
Render the current recipe at full resolution and save it. Renders in tiles to bound memory; ~75 s for a 40 MP RAW.
| Name | Required | Description | Default |
|---|---|---|---|
| max_size | No | Optional long-edge size in pixels; default keeps the full resolution. | |
| output_path | Yes | Absolute output path ending in .jpg, .png, .tif (16-bit) or .exr. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and it does contribute real behavior: tiled rendering to bound memory and a concrete timing estimate (~75 s for 40 MP RAW). It still omits consequential traits for a write operation — whether an existing file at output_path is overwritten, what happens on partial/failed renders, and whether the render reflects uncommitted state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the action before the performance caveat; nothing is padded. It could arguably be tightened further by folding the timing note into the same sentence, but no wording is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter tool with no annotations and no output schema, the description covers the action and its performance profile, which is a reasonable minimum. It falls short on the side effects an agent needs to call it safely — overwrite behavior, failure handling, and relationship to other rendering siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both max_size (long-edge pixels, default full resolution) and output_path (absolute path, allowed extensions) are already documented in the schema. The description adds no parameter-level meaning beyond that, which is the baseline 3 case.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
"Render the current recipe at full resolution and save it" gives a specific verb (render/save) and object (current recipe, full resolution), so the agent knows this is a file-producing operation rather than a viewer. It does not, however, distinguish itself from siblings like develop or frame, which could plausibly also produce rendered output.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: "the current recipe" signals this operates on already-applied edits rather than raw input, but there is no statement of when to choose export over develop, frame, or loupe, and no prerequisites or exclusions are given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
frameA
Compose the picture: straighten, perspective correction, crop, rotate and flip.
Only the fields you pass change. Every call measures the straight lines under the new framing and reports any remaining tilt or keystone, with the auto correction that would fix it. Returns the framed preview. Call with reset_framing=true and no other fields to look at the whole uncropped photo.
| Name | Required | Description | Default |
|---|---|---|---|
| auto | No | Measure straight lines and correct automatically, like Lightroom Upright: 'level' straightens the horizon (or the verticals if there is no horizon); 'vertical' also fixes converging verticals (architecture); 'full' also fixes horizontal perspective. Fields you pass explicitly win. | |
| grid | No | Overlay a grid on the returned preview, to check level and verticals by eye. | |
| aspect | No | Crop aspect ratio (width:height). 'original' keeps the photo's own ratio. 65:24 is XPan panoramic. Default 'original'. One of: original, 3:2, 2:3, 4:3, 3:4, 1:1, 4:5, 5:4, 16:9, 9:16, 65:24, 1.85:1, 2.39:1. | |
| crop_x | No | Horizontal centre of the crop, 0 = as far left as possible, 1 = as far right. Range 0.0 to 1.0. Default 0.5. | |
| crop_y | No | Vertical centre of the crop, 0 = as far up as possible, 1 = as far down. Range 0.0 to 1.0. Default 0.5. | |
| flip_h | No | Mirror left-right. Default False. | |
| flip_v | No | Mirror top-bottom. Default False. | |
| rotate | No | Rotate in 90° steps, clockwise. Default '0'. One of 0, 90, 180, 270. | |
| crop_zoom | No | How tight the crop is: 1 = the largest crop of that aspect ratio, 2 = half the width and height. Range 1.0 to 5.0. Default 1.0. | |
| straighten | No | Level the horizon. Positive rotates the picture clockwise. The frame zooms in automatically so no empty corners show. Range -45.0 to 45.0. Default 0.0. | |
| perspective_h | No | Positive widens the right side: corrects a subject shot at an angle from the left. Range -100.0 to 100.0. Default 0.0. | |
| perspective_v | No | Keystone correction. Positive widens the top: straightens verticals that converge upward when shooting up at buildings. Range -100.0 to 100.0. Default 0.0. | |
| reset_framing | No | Clear all framing (back to the full, unrotated photo) before applying the given fields. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations at all, the description carries the full behavioral burden and mostly succeeds: it discloses the partial-update semantics, that every call re-measures straight lines and reports residual tilt or keystone with a suggested auto correction, and that it returns a framed preview. It does not say whether the framing is non-destructive to the source image or what error conditions look like, which are notable gaps for a 13-parameter mutation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four short sentences, front-loaded with purpose, then behavior, then the reset recipe. The measurement sentence is the densest but earns its place because the returned diagnostic report is not obvious from the name. Minor redundancy in restating that every call re-measures under the new framing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 13-parameter tool with no annotations and no output schema, the description covers what changes, what the response contains (framed preview plus a tilt/keystone report), and one important invocation pattern. It falls short only on non-destructiveness and failure behavior, which an agent would want before chain-calling with undo and export.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already explains every parameter in detail, including ranges, defaults, and what auto levels mean. The description adds little beyond restating that only passed fields change and describing the reset_framing-plus-nothing invocation pattern. Baseline 3 is appropriate when the schema does this much work.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource set: 'Compose the picture: straighten, perspective correction, crop, rotate and flip.' That enumerates exactly which geometric operations the tool owns, which implicitly separates it from siblings like develop (tone/color) and loupe (viewing). It never explicitly names those siblings, so the differentiation is inferred rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Only the fields you pass change' establishes this as a partial-update tool, and 'Call with reset_framing=true and no other fields to look at the whole uncropped photo' gives a concrete invocation recipe. What is missing is any explicit routing against alternatives (e.g., when to use this versus develop or compare), so an agent must infer the boundary from the operation list.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_stateC
Current image and recipe.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden and discloses almost nothing: it does not confirm the call is read-only and side-effect free, whether it fails when no image is loaded, or how the state is represented. Only the 'get_' naming convention implies a safe read.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four words with zero waste, but the brevity crosses into under-specification: it is a noun fragment rather than a statement, so there is nothing to front-load and no structure to speak of.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read of editor state with no output schema and no annotations, the description should at least say it returns the currently open image and its applied recipe/settings, and what happens when no image is open. It gestures at the return contents but leaves the critical edge case and format unexplained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate or document; the baseline for a parameterless tool applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The fragment 'Current image and recipe' names the two pieces of data returned, which does add content beyond the tool name 'get_state', but it contains no verb and never states that it retrieves/returns anything. It is distinguishable from siblings like open_image or develop only by inference from the noun phrase.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to call this versus open_image, loupe, or compare, nor any precondition (e.g., must an image already be open). Usage is left entirely to the agent's inference from the name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_stocksA
List the film stocks and print papers available, with type (negative/positive) and use (still/cine).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are supplied, so the description carries the disclosure burden. It does reveal the shape of what comes back (type: negative/positive, use: still/cine), which is genuinely useful, but it never states that the call is read-only with no side effects, nor whether the catalog is static or reflects current darkroom state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence, front-loaded with the verb and resource, with the returned fields trailing as supporting detail. Nothing is redundant or padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description is the only source of return-value information, and it covers only two fields. It does not say what the stock identifiers look like or whether the names returned are the values that develop and export expect, which is the key downstream question for a zero-parameter catalog tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, which is the baseline-4 case; there is nothing for the description to disambiguate. It correctly avoids inventing parameter talk and instead spends its words on output semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb ("List") and a concrete resource ("film stocks and print papers"), and even names the sub-fields exposed. It reads clearly against the action-oriented siblings (develop, frame, export, undo), though it never explicitly contrasts itself with them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent can infer this is the discovery step before develop or export, but the description states no when-to-use condition, no prerequisites, and names no alternative. A single clause like "call before develop to obtain valid stock names" would have lifted this to 4-5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
loupeA
Render a full-resolution crop of the framed picture with grain and halation, to inspect fine detail at 100%.
| Name | Required | Description | Default |
|---|---|---|---|
| size | No | Crop size as a fraction of the frame, 0.02-1. | |
| center_x | No | Horizontal center of the crop, 0 = left edge, 1 = right edge. | |
| center_y | No | Vertical center of the crop, 0 = top, 1 = bottom. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the behavioral load. It usefully discloses that the crop includes grain and halation (i.e. the processed look, not raw source pixels) and that it renders at full resolution, but it never states that the operation is non-destructive, whether a full-res render is expensive/slow, or what the response actually contains.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence with the verb and resource front-loaded and no wasted wording; nothing to trim.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple (three optional, defaulted numeric parameters), and the description covers what is produced. However, with no annotations and no output schema, the return value (image payload vs. reference) and the cost of a full-resolution render are left unexplained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and each of size/center_x/center_y is documented with defaults and coordinate conventions, so the schema does the heavy lifting. The description adds only the notion of cropping from 'the framed picture', not any meaning about how size or center interact.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb ('Render') plus a precise resource ('a full-resolution crop of the framed picture') and an explicit rationale ('to inspect fine detail at 100%'). It is clearly distinct from develop, frame, export and undo, though it never names a sibling it could be confused with (compare is the nearest candidate).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The trailing clause gives a clear use context: inspect fine detail at 100% magnification. It stops short of exclusions or alternatives, so an agent is not told when to prefer this over compare or export, but the intended situation is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
open_imageA
Load an image or RAW into the film lab and return a preview with the current recipe.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Absolute path to a RAW file or a JPEG/PNG/TIFF/EXR image. | |
| temperature | No | RAW only, with white_balance='custom': color temperature in K. | |
| white_balance | No | RAW only: 'as_shot', 'daylight', 'tungsten' or 'custom'. | as_shot |
| input_is_linear | No | Override whether non-RAW input is linear. Default: float TIFF/EXR linear, 8/16-bit images sRGB-encoded. | |
| input_color_space | No | Override the input color space (e.g. 'sRGB', 'ProPhoto RGB', 'ACES2065-1'). Auto-detected by default. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description must carry behavioral disclosure. It reveals that the operation loads into the film lab and returns a preview with the current recipe, but it does not clarify whether loading replaces the current image, mutates state, requires permissions, or how errors are handled.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no redundant wording. It communicates the core action and return value efficiently.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has five parameters but a rich schema that fully documents them, and the description covers the main purpose and a partial return value. However, with no annotations and no output schema, it leaves state effects and preview/recipe output structure underspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% description coverage, documenting path, temperature, white_balance, input_is_linear, and input_color_space. The description does not add any parameter-level meaning beyond the schema, so the baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: loading an image or RAW into the film lab and returning a preview with the current recipe. This is clear enough to distinguish the operation from develop/export-type siblings, but it does not explicitly name alternatives or scope boundaries.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the film-lab context and preview return, suggesting it should be used when opening an image before further processing. However, it gives no explicit when-to-use guidance, prerequisites, or comparison to siblings such as develop, loupe, or compare.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
undoB
Revert the last recipe change and return the preview.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It discloses that a change is reverted and a preview is returned, but not whether the revert is destructive or reversible, whether repeated calls step further back, or how failure with an empty history is signalled. For a state-mutating tool with zero annotation coverage this is a significant gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single short sentence with the action front-loaded and the return value appended. No wasted wording, no redundancy with the name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with no output schema, saying it returns the preview is genuinely useful. However, the missing details on undo depth, history preconditions, and failure behaviour leave real gaps for an agent that must decide when this call is safe.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes no parameters, which is the baseline-4 case. The description correctly implies nothing needs supplying and that the target is implicit (the last change).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb ('Revert') and a specific resource ('the last recipe change'), and adds that it returns the preview. An agent can distinguish this from siblings like develop, frame, or get_state. The only weakness is that 'recipe change' is domain jargon that doesn't pin down which edits count as undoable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to call undo instead of inspecting state with get_state, or on whether undo is single-level or repeatable. Nothing says what happens when there is no prior change to revert, so the agent must guess at preconditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
9 tool updates
v0.1.0- First observed
compare - First observed
develop - First observed
export - First observed
frame - First observed
get_state - First observed
list_stocks - First observed
loupe - First observed
open_image - First observed
undo
TDQS
Scored across 9 tools
Each tool maps to a distinct stage of the film-lab pipeline (load, recipe, framing, preview variants, detail inspection, export, state, undo), so misselection is unlikely. Minor overlap exists between develop and frame (both mutate state and return a preview with 'only the fields you pass change' semantics), and between loupe and export (both render full-resolution), but the crop-only vs save distinction is stated.
The set mixes verb_noun names (open_image, list_stocks, get_state) with bare verbs (develop, frame, compare, export, undo) and a lone noun (loupe). The darkroom vocabulary keeps it readable, but there is no predictable pattern an agent can extrapolate from.
Nine tools cover the workflow with no filler: one per operation, each earning its place. Well within the ideal 3-15 range for a scoped image-processing server.
The surface covers the full lifecycle: load, inspect stocks, adjust recipe and framing, compare variants, inspect detail, export, and undo. Gaps are minor: no redo/redo-history, no way to save or reload a named recipe preset, and no documented way to reset framing outside a frame() call.
Maintenance
Related MCP Connectors
AI film lab for filmmakers: generate and review images/clips with input provenance, cost preflight
AI-agent image generation: image sets, character sets & illustration sets. Edit, extend, export.
Image processing for AI agents: resize, convert, compress, crop, and web-ready AI-generated images.
Build and run AI product photography workflows: on-model shots, catalog images, prints and edits.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceMCP server enabling AI agents to perform autonomous photographic color science and screenshot annotation with deterministic computer vision, film emulation, and real-time live preview.1-

imagegrain MCPofficial
AlicenseNot gradedqualityCmaintenanceEnables applying deterministic film grain and analog adjustments to photos locally via natural language, with no uploads.MIT- AlicenseAqualityAmaintenanceEnables AI agents to perform deterministic, non-generative image transformations—straightening, cropping, masking, layering, color adjustments, and encoding—as reproducible recipes while preserving immutable originals.13MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to inspect images, compare and apply 41 dithering algorithms with 256 curated or custom palettes, and produce stills, animations, video, and print separations locally while saving reusable recipes. It exposes these image-processing workflows through typed MCP tools, resources, and prompts, keeping source files and artifacts in a user-controlled workspace.MIT