JANCTION Render
JANCTION Render is a cloud GPU render farm for Blender that lets an agent inspect, preview, QA, and finally render 3D scenes or bpy scripts remotely, then deliver, share, and manage those results and their cost.
Inspect a scene without rendering:
scene_inforeads cameras, frame range, fps, objects, lights, materials, and missing files from a.blend, bpy script, or 3D file (no GPU time).Fast previews with a critic verdict:
render_previewreturns 1–4 tiled frames in seconds plus an ok/check/fix verdict (too dark, blown out, no light, out of frame) with fix code, a 1080p final estimate, and remaining daily quota.QA a model or scene from 4 sides:
render_reviewrenders 0/90/180/270° under studio light and returns pass/warning/fail, a score, the checks run, and fix code.Final renders of frames or video:
render_finalproduces png/exr frames or mp4/webm/prores/gif/webp (up to 3,000 frames, 1080p free / 4K paid) with job_id, estimate, spending caps (max_yen), idempotency keys, and optionalnotify_urlwebhook.Estimate before spending:
render_estimategives GPU seconds, queue wait, wall-clock time, and whether the job fits the free GPU time (no GPU used).Turntables, product shots, and presets:
orbit/preset='product'|'turntable'|'animation', multi-camera sheets, HDRI environments (studio/sunset/overcast/night/compare), transparent backgrounds, and Cycles or Eevee engines.Track and retrieve jobs:
render_statusreports progress, ETA, warnings, and failures (or lists recent jobs), andrender_downloadsaves files locally (mp4,frames,sheet,output, orall).Cancel a render:
render_cancelstops a queued/running job, keeps rendered frames, and returns unused GPU time.Find free CC0 assets:
asset_searchlocates Poly Haven models, textures, and HDRIs by keyword to use viapolyhaven:<id>specs.Share results publicly:
render_sharepublishes an image/video page (with optional script and request text, optionally gallery-listed) that outlives the 24-hour expiry;render_unsharetakes it down permanently.Check service health and quota:
render_infoshows worker availability, queue wait, gate status, and connection/key diagnosis;billingreports free GPU time, balance, price per GPU-second, and can create a top-up checkout link.
Provides tools for rendering Blender scenes on remote GPUs, including scene inspection (cameras, frame range, missing files), low-cost preview renders, and final MP4 renders, accepting .blend files or bpy Python scripts.
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., "@JANCTION Renderrender a 4-frame preview of my street.blend so I can check the camera framing"
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.
JANCTION Render
What is JANCTION Render?
JANCTION Render is a cloud GPU render farm for Blender that AI agents call as an MCP server or HTTP API. Send a .blend
file, a bpy script or a 3D file (glTF / FBX / USD), preview in seconds, and get frames or an MP4 back from JANCTION's
NVIDIA GPUs. Every preview also returns a verdict on what is wrong, such as too dark, blown out, no light or an object
out of frame, and the bpy code to fix it, so the agent can correct the scene before the final render. No local Blender
or GPU is needed. A 100 JPY welcome credit for a new key (one per network every 30 days; further keys on the same network share what is left) and 400 JPY more for saving a card that is not charged (once per card), then 0.1 JPY per GPU-second (previews free up to 2 GPU-minutes a day);
operated by JasmyLab Inc.
AI agents can render Blender projects on JANCTION GPUs without running Blender locally. Works from Claude, ChatGPT, Claude Code, Codex, Cursor or any MCP client: an MCP server (remote and stdio), an HTTP API and a CLI.
JANCTION Render is a separate product from SmartRender (JasmyLab's distributed rendering for people at a desktop) and is not affiliated with Render.com or the Render Network. The numbers, as of 2026-10-08 (the primary source is https://render.janction.jp/facts.json): a 100 JPY welcome credit (1,000 GPU-seconds, 14 days, no card) for a new key, one per network every 30 days (further keys on the same network share what is left), plus 400 JPY more once a card is saved on a page that charges nothing (once per card); after that, previews are free up to 2 GPU-minutes a day and finals cost 0.1 JPY per GPU-second from prepaid credit (paid use started on 2026-10-08); finals up to 3,000 frames per instruction (run as parts of 240 under one job id); 1080p on free time, up to 4K on prepaid credit; inputs and results deleted 24 hours after last use (7 days, normally, for keys that have paid 500 JPY or more in total).
Official site: https://render.janction.jp · MCP endpoint: https://render.janction.jp/mcp · Fact sheet: https://render.janction.jp/facts
Try it from a terminal in a few seconds, free and with nothing to sign up for (needs uv; or pip install janction-render and drop the uvx):
uvx janction-render try # renders a sample scene and shows the free GPU time of your new key
uvx janction-render preview scene.blend # your .blend, bpy script or 3D file
uvx janction-render render scene.blend --frames 1-48 --output mp4 --wait
uvx janction-render turntable model.glb # a 360-degree turntable MP4 in one command (a path or an https link)
uvx janction-render shots model.glb --transparent # four product shots (PNG)What one command makes (real outputs)
Rendered on 2026-10-10 on the production GPU (NVIDIA RTX PRO 6000 Blackwell) from Khronos's WaterBottle sample (public domain, CC0); copy any line and it runs as is. Prices are at 0.1 JPY per GPU-second and are covered by the welcome credit of a new key.
Command | GPU time | Price | |
|
| 13.9 s | about 1.4 JPY |
|
| 85.2 s | about 8.5 JPY |
|
| 54.1 s | about 5.4 JPY |
The shots are four 1920x1080 PNGs; the turntable and the animation are MP4s (shown here as GIFs). Your own .glb, .blend
or bpy script goes in place of the link. More, with their scripts: https://render.janction.jp/examples
Keep 3D files in a repository? Add a button that lets anyone render them from their own AI or terminal (links to file pages on GitHub and Hugging Face work as they are; make one at https://render.janction.jp/go):
[](https://render.janction.jp/go?scene=https://github.com/you/repo/blob/main/model.glb)Related MCP server: blender-render-mcp
Guides on the official site
From an agent: Claude Code + Blender · Codex + Blender · Cursor + Blender · ChatGPT + Blender · Blender MCP server for rendering
Best render farms for AI agents (2026): JANCTION, BlendSwap, Sceneplane, your own worker on Modal or RunPod and a local Blender MCP side by side
Headless Blender as an API · glTF, GLB or FBX to MP4 · 360-degree product video · What a Blender render costs
Why use it?
No GPU, no Blender install. The scene can be a bpy script the agent writes, a
.blend, or a 3D file; Blender runs on our GPUs.Built for agents. Preview first (1-4 frames in a few GPU seconds), an estimate with time and cost before the final, structured errors with the fix, spending caps per key, safe retries with
Idempotency-Key.3D files and turntables. glTF / GLB, FBX, USD, OBJ, STL, PLY, Alembic are imported into an empty scene with a camera and HDRI lighting;
orbit=Truemakes a turntable. Fixed-price outcomes:POST /v1/outcomes/turntableand/product-shot.Honest limits. a 100 JPY welcome credit for a new key with no card (14 days; one per network every 30 days) and 400 JPY more after saving a card that is not charged, then 0.1 JPY per GPU-second (previews free up to 2 GPU-minutes a day), finals up to 3,000 frames per instruction as parts of 240 (1080p on free time, up to 4K on prepaid credit), on JANCTION's own GPUs (NVIDIA RTX PRO 6000 Blackwell); when they are busy jobs wait in a queue and the ETA says so. Inputs and results are deleted 24 hours after last use (7 days, normally, for keys that have paid 500 JPY or more in total) and never used for training.
Not Render.com. Same word, different product.
Quick start
Claude.ai / ChatGPT (nothing to install). Settings → Connectors → add https://render.janction.jp/mcp → Connect (a free key
is created on the consent page). Then paste:
Render a preview of https://render.janction.jp/samples/cube_scene.py
A 4-frame preview comes back in a few seconds. A file on your computer: drop it at https://render.janction.jp/upload to get a 12-hour link, then "Render this file: ".
Claude Code.
claude mcp add --transport http janction-render https://render.janction.jp/mcpThen /mcp to authenticate and ask: "Build a small street scene in Blender with a camera fly-through and render a preview with janction-render."
HTTP (any language).
curl -s -X POST https://render.janction.jp/v1/keys # -> {"api_key": "jr_..."} no sign-up
curl -s -X POST https://render.janction.jp/v1/files -H "X-API-Key: $KEY" -F "file=@scene.blend" # -> {"scene_id": "f_..."}
curl -s -X POST https://render.janction.jp/v1/jobs -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"scene_id":"f_...","kind":"preview","frames":[1]}' # -> job_id; then GET /v1/jobs/{job_id}From your own agent or app
Python (pip install janction-render; the first call creates a free key and keeps it in ~/.janction-render.json):
from janction_render.client import Client
c = Client() # or Client(api_key="jr_...")
scene = c.upload("scene.py") # a bpy script, a .blend, or a .glb / .fbx / .usd / .obj file
job = c.submit(scene["scene_id"], kind="final", frame_start=1, frame_end=48, output="mp4")
job = c.wait(job["job_id"], timeout=1800)
print(job["status"], c.download(job["job_id"], "out", only="mp4"))Pay per use with prepaid credit; no subscription is needed (prices: https://render.janction.jp/pricing).
Instead of polling, pass notify_url (https) to POST /v1/jobs and get one JSON POST when the job finishes (job.done /
job.failed, with download links); an Idempotency-Key header makes retries safe. OpenAPI: https://render.janction.jp/openapi.json
A complete app example (a user uploads a GLB, the app orders fixed-price product shots, a webhook stores the images):
https://render.janction.jp/blender-render-api#embed
Agent frameworks connect to the remote MCP server at https://render.janction.jp/mcp with the key as a bearer token
(curl -s -X POST https://render.janction.jp/v1/keys gives one, no sign-up).
OpenAI Agents SDK (keep the longer timeout: render_preview waits for the image, longer than the 5-second default):
import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async def main():
async with MCPServerStreamableHttp(name="janction-render", client_session_timeout_seconds=120,
params={"url": "https://render.janction.jp/mcp",
"headers": {"Authorization": "Bearer jr_..."}}) as render:
agent = Agent(name="3D artist", instructions="Render Blender scenes with janction-render.", mcp_servers=[render])
result = await Runner.run(agent, "Render a preview of https://render.janction.jp/samples/cube_scene.py")
print(result.final_output)
asyncio.run(main())LangChain (pip install langchain-mcp-adapters):
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
async def main():
client = MultiServerMCPClient({"janction-render": {"url": "https://render.janction.jp/mcp", "transport": "streamable_http",
"headers": {"Authorization": "Bearer jr_..."}}})
tools = await client.get_tools() # render_preview, render_final, render_status, render_download, ...
print([t.name for t in tools])
asyncio.run(main())OpenAI Responses API (remote MCP; render_final keeps asking for approval because it spends credit):
from openai import OpenAI
client = OpenAI()
resp = client.responses.create(
model="gpt-5",
tools=[{"type": "mcp", "server_label": "janction_render", "server_url": "https://render.janction.jp/mcp",
"authorization": "jr_...",
"require_approval": {"never": {"tool_names": ["scene_info", "render_preview", "render_estimate",
"render_status", "render_download"]}}}],
input="Render a preview of https://render.janction.jp/samples/cube_scene.py and give me the image link.",
)
print(resp.output_text)Anthropic Messages API (MCP connector; fallbacks="default" reruns a declined request on a fallback model):
import anthropic
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
betas=["mcp-client-2025-11-20", "server-side-fallback-2026-07-01"],
fallbacks="default",
mcp_servers=[{"type": "url", "url": "https://render.janction.jp/mcp", "name": "janction-render",
"authorization_token": "jr_..."}],
tools=[{"type": "mcp_toolset", "mcp_server_name": "janction-render"}],
messages=[{"role": "user", "content": "Render a preview of https://render.janction.jp/samples/cube_scene.py"}],
)
for block in response.content:
if block.type == "text":
print(block.text)Google ADK:
import os
from google.adk.agents import Agent
from google.adk.tools.mcp_tool import MCPToolset, StreamableHTTPConnectionParams
agent = Agent(
name="renderer",
model="gemini-2.5-flash",
instruction="Render Blender scenes with JANCTION Render: preview first, ask before a final render.",
tools=[MCPToolset(connection_params=StreamableHTTPConnectionParams(
url="https://render.janction.jp/mcp",
headers={"Authorization": "Bearer " + os.environ["JANCTION_RENDER_API_KEY"]}))],
)Pydantic AI:
from pydantic_ai import Agent
from pydantic_ai.mcp import MCPToolset
render = MCPToolset("https://render.janction.jp/mcp", headers={"Authorization": "Bearer jr_..."})
agent = Agent("anthropic:claude-opus-5-5", toolsets=[render])Connect the AI apps on this computer in one command, and tell every coding agent in a project when and how to render:
uvx janction-render connect # lists Claude Code, Codex, Gemini CLI, Cursor, Windsurf, VS Code and the step for each
uvx janction-render connect claude-code # applies one (or: connect all --yes)
uvx janction-render init # writes a short section to AGENTS.md and adds the server to .mcp.jsoninit touches CLAUDE.md, GEMINI.md, .cursor/ and .vscode/ only when they already exist (or with --claude, --cursor,
--vscode); it keeps everything else in those files and backs up JSON it changes.
The local MCP server (stdio) also offers prompts that show up as slash commands, for example /mcp__janction-render__turntable_video
in Claude Code: try_janction_render, render_my_scene, turntable_video, product_shots, scene_from_description, blend_to_mp4.
MCP
Remote (Streamable HTTP, OAuth 2.1 with dynamic client registration; a raw key also works as Authorization: Bearer jr_...)
client | how |
Claude.ai (web, desktop, mobile) | Settings → Connectors → Add custom connector → paste the URL → Connect |
ChatGPT | Settings → Connectors → Advanced → Developer mode → Create → paste the URL (OAuth) |
Claude Code |
|
Codex |
|
Gemini CLI / Antigravity CLI |
|
Grok (grok.com) | Connectors → New Connector → Custom → paste the URL |
Perplexity (Pro / Max / Enterprise), Le Chat (workspace admin) | Add a custom remote MCP connector with the URL |
Notion Agent (Business / Enterprise) | Settings → Connections → Discover → Add Custom MCP → paste the URL, then sign in (the button shows when a workspace admin allows custom MCP servers) |
Cursor |
|
VS Code (Copilot agent mode) |
|
Windsurf, Cline, Goose, other MCP clients | Streamable HTTP at the URL above (OAuth, or a Bearer API key header). Installer notes: llms-install.md. Cursor project rule (when to use it, preview-first flow): integrations/cursor |
Claude Desktop and other stdio-only clients (npm) |
|
Remote tools take scene_script (bpy code as text), scene_url (an https link to a .blend, .py or a 3D file, or a link
from https://render.janction.jp/upload) or scene_id, plus asset_urls for textures or glTF .bin files. Results come back as
an inline image plus download links that need no key and work for about 24 hours (render_download(only="mp4") for just the video).
Claude Code plugin (the remote connector plus a skill with the workflow):
/plugin marketplace add JasmyLab-JANCTION/janction-render
/plugin install janction-render@janction-renderStdio (sends local files)
The package is on PyPI as janction-render (and the npm launcher janction-render runs it with npx -y janction-render --local when uv is installed).
# Claude Code (uvx runs it without a global install)
claude mcp add janction-render -e JANCTION_RENDER_SERVER=https://render.janction.jp -- uvx --from janction-render janction-render-mcp
# or with pip / pipx
pip install janction-render
claude mcp add janction-render -e JANCTION_RENDER_SERVER=https://render.janction.jp -- janction-render-mcpCodex: add to ~/.codex/config.toml
[mcp_servers.janction-render]
command = "uvx"
args = ["--from", "janction-render", "janction-render-mcp"]
env = { JANCTION_RENDER_SERVER = "https://render.janction.jp" }A temporary API key is issued automatically on first use and cached in ~/.janction-render.json; set
JANCTION_RENDER_API_KEY to pin one (quota and, later, credit belong to the key). The same key can be used from the
remote connector: paste it on the connect page.
Tools
tool | what |
`scene_info(scene_script | scene_url |
| up to 4 frames (720p budget) tiled with frame labels; returns the image inline |
| checks a 3D model or a generated scene from 4 sides (0/90/180/270 degrees) under studio light: pass / warning / fail, a score, the checks it ran (files, visibility, framing, exposure, lighting) with fix code, and the 4 views in one image. Stdio server; same GPU time as one preview |
| GPU seconds, queue wait, "about N minutes", fits the free GPU time left? No GPU time used |
| frames (png / exr) or video (mp4 / webm / prores / gif / webp); |
| progress and ETA ( |
| the free GPU time left (the welcome credit, then the free preview minutes today), the credit, a top-up link, and the auto top-up and monthly plan status |
| a public page |
| CC0 models / textures / HDRIs on Poly Haven by words; results carry |
| workers online or gated (GPU lent to another workload), queue, expected wait, supported inputs, environment presets, Blender versions |
Options on render_preview / render_final: environment (studio, sunset, overcast, night, compare on previews; environment_strength,
environment_visible=False for a flat grey backdrop with HDRI lighting), orbit / orbit_frames / orbit_elevation (turntable),
blender ("5.2"), and assets (stdio: local files sent with the scene, found in the script through os.environ["JR_ASSETS_DIR"];
a .blend's external files are collected automatically with pip install janction-render[blend]) or asset_urls (remote).
CLI: janction-render inspect|preview|render|status|download|share|unshare|cancel|jobs|balance|topup|limits|outcomes|info.
API
POST /v1/keys -> {api_key} (header X-API-Key afterwards)
POST /v1/files multipart "file" (.blend|.py|.glb|.fbx|.usd|.obj|...) -> {scene_id}
POST /v1/files/{id}/assets multipart "files" textures, glTF .bin ... GET /v1/files/lookup?sha256= reuse an upload
POST /v1/estimate {kind, frames|frame_start/frame_end, width, height, samples, scene_id?} -> seconds, wall_seconds, human, quota, currency, expires_at
POST /v1/jobs {scene_id, kind: info|preview|final, frames|frame_start/frame_end, width, height, samples, camera, fps, output (png|exr|mp4|webm|prores|gif|webp), transparent, notify_url (https), engine (cycles|eevee),
environment, environment_strength, environment_visible, blender, orbit, orbit_frames, orbit_elevation} header Idempotency-Key makes retries safe
GET /v1/jobs/{id} status, progress, eta, artifacts[], cost, warnings, info, failure (code + fix) DELETE /v1/jobs/{id} cancel
GET /v1/jobs/{id}/artifacts/{name} PNG / MP4
GET /v1/jobs/{id}/archive?only=png every file of the job in one zip (frame sequences: one request instead of hundreds)
GET /v1/outcomes fixed-price outcomes; POST /v1/outcomes/turntable {scene_id, size, frames} POST /v1/outcomes/product-shot {scene_id, size, transparent?}
POST /v1/try JSON {scene_script | scene_url, frames?, width?, height?, environment?} (no key) -> first 720p preview + an API key to continue, in one response (1 per network per 24h)
POST /v1/drops multipart "file" (no key) -> a 12-hour https link to pass as scene_url (the /upload page uses this)
POST /v1/jobs/{id}/share {title?, note?, include_script?, listed?} -> {share_id, url} DELETE /v1/jobs/{id}/share GET /v1/shares
GET /r/{share_id} public page (no key) GET /gallery
GET /v1/assets/search?q=&kind=models|textures|hdris CC0 assets (Poly Haven) -> spec polyhaven:<id>
POST /v1/files/{id}/assets/urls {urls: ["https://...", "polyhaven:<id>"]} fetch assets server-side
GET /v1/me (quota, balance, limits) POST /v1/me/limits {job_yen?, day_yen?} spending caps per key
POST /mcp remote MCP (Streamable HTTP; Bearer api key or OAuth)
GET /.well-known/oauth-protected-resource/mcp OAuth discovery
POST /v1/billing/checkout {amount_yen} -> {checkout_url} POST /v1/billing/sync GET /v1/ledger
GET /openapi.json /llms.txt /llms-full.txt /ja/llms.txt /facts.json /version.json /capabilities.json /pricing.json /status.jsonA job that goes past the free GPU time without enough credit gets 402 payment_required with checkout_url (show it to the
user), topup_yen and how much of the job the free time covers; a 400 beta_limit means the job is too big (split it). Every API
error carries retryable, possible_fix and docs_url; a failed job carries failure with a code and the recommended action.
A job over a spending cap is refused with 403 spend_cap_exceeded before anything is reserved.
Example
A scene script the agent can write (the service sets engine, resolution, samples and denoising; the script sets the scene, camera and frame range):
import bpy, math
for ob in list(bpy.data.objects):
bpy.data.objects.remove(ob, do_unlink=True)
scene = bpy.context.scene
scene.frame_start, scene.frame_end = 1, 24
bpy.ops.mesh.primitive_monkey_add(location=(0, 0, 1))
cam = bpy.data.objects.new("Camera", bpy.data.cameras.new("Camera"))
scene.collection.objects.link(cam); scene.camera = cam
cam.location = (6, -6, 4); cam.rotation_euler = (math.radians(60), 0, math.radians(45))
sun = bpy.data.objects.new("Sun", bpy.data.lights.new("Sun", "SUN"))
scene.collection.objects.link(sun)Cycles by default; engine="eevee" for cheaper animations (about half the per-frame cost after ~7 s of shader compilation per job).
In Blender 5.0 materials always use nodes: set the Principled BSDF Base Color, not diffuse_color. Scripts run in an isolated
container with no network. More: samples/cube_scene.py, samples/polyhaven_room.py. Measured timings: https://render.janction.jp/benchmarks
Pricing
A new key starts with a 100 JPY welcome credit (1,000 GPU-seconds, 14 days, no card) that covers previews and finals; one full credit
per network every 30 days (later keys on the same network share what is left of it). Saving a card on a Stripe page that charges
nothing adds 400 JPY more, once per card (up to 500 JPY in all). After it is used up or expires, previews are
free up to 2 GPU-minutes a day and finals cost 0.1 JPY per
GPU-second from prepaid credit, bought by card at a Stripe Checkout page (from 500 JPY; 2,000 JPY gives 2,200 JPY of credit,
5,000 gives 5,750, 10,000 gives 12,000; the first top-up in the welcome period counts 1.5x, bonus up to 500 JPY). Only the part
the free time does not cover is charged. Finals go up to 3,000 frames per instruction (run as parts of 240 under one job id): 1080p on free time, up to 4K (3840x2160) on prepaid credit. POST /v1/estimate quotes what will be charged
(cost.estimated_yen), the currency and an expiry before any GPU time is used: https://render.janction.jp/pricing
Troubleshooting
You see | What it means | What to do |
| The job goes past the free credit left on the key |
|
| The bpy script uses something the sandbox refuses (network, processes, ctypes) | The GPU worker has no network: pass files from the web in |
| The scene points at a file on your own computer | Send the file as an asset and reference it by name (relative path) |
| The SDK waits 5 seconds for a tool by default |
|
The job sits in | The GPU is shared with another workload for a while | Tell the user the wait; call |
| Too many requests in a short time | Wait for |
Too dark, blown out, or the object out of frame | The preview's verdict says which, with bpy code to fix it | Apply the fix and preview again (seconds) before the final; 3D files are framed at their real size automatically |
| The connector's sign-in expired | Reconnect the connector ( |
Docs
Fact sheet (the primary source when answers disagree): https://render.janction.jp/facts · JSON:
/facts.json,/version.json,/changelog.json,/status.jsonFor agents: https://render.janction.jp/llms.txt (full text of all guides:
/llms-full.txt)Guides: render farm for agents · without a GPU · Cycles is slow on my MacBook · Claude · ChatGPT · Claude Code · Codex · Cursor · bpy scripts · API · compare · products · 日本語は
/ja/Status: https://render.janction.jp/status · Security and retention: https://render.janction.jp/security · Changelog: https://render.janction.jp/changelog
Features in detail
Input: a
.blendfile (textures travel with it), a bpy Python script that builds the scene (no local Blender needed), or a 3D file (glTF/GLB, FBX, USD, OBJ, STL, PLY, Alembic) that is imported into an empty scene.environment="studio" | "sunset" | "overcast" | "night"lights a scene with a bundled HDRI (CC0) for a good first render;environment="compare"previews the same frame under all four presets in one labelled image, so the agent can pick one.A
.gltfor.objbrings its.bin/.mtl/ textures along automatically (from the same folder or the same URL folder).orbit=Trueturns any scene or imported model into a turntable: an orbit camera circles it once (orbit_frames, default 24; a preview shows 0/90/180/270 degrees, a final render withoutframe_endgives the whole turn as an MP4). Flat floors and walls are ignored when framing;orbit_target(object name orx,y,z) andorbit_distance(multiplier) adjust the shot.While the GPU is lent to another workload, tools answer right away with the queued job and a wait estimate (how long it has been out, how long it usually stays out, how much longer to expect;
eta.gate) instead of waiting.While a job renders,
render_statuscounts finished frames inside the running chunk, so the ETA updates every few seconds.In hosts that support MCP Apps (Claude web and desktop, among others), previews, progress and download links also appear as an interactive panel in the chat: the image, a progress bar, "Open MP4", and preset buttons after an
environment="compare"preview. Both the remote connector and the stdio package ship the panel (janction_render/mcp_app.html); setJR_MCP_APPS=0to turn it off.CC0 assets by name:
asset_search("wooden table")finds Poly Haven models, textures and HDRIs; passpolyhaven:<id>inasset_urls(remote) orassets(stdio) and the files land inJR_ASSETS_DIR/<id>/on the GPU, ready to import (bpy.ops.import_scene.gltf(filepath=os.path.join(os.environ['JR_ASSETS_DIR'], 'wooden_table_02', 'wooden_table_02_1k.gltf'))). Scene scripts canimport jr_assetsfor one-liners:jr_assets.import_model('wooden_table_02'),jr_assets.apply(floor, jr_assets.material('wood_floor_deck', scale=2))(diff / normal / roughness / metal wired into a Principled BSDF),jr_assets.world_hdri('studio_small_09'). Seesamples/polyhaven_room.py.A render you choose to share (
render_share) gets a public page (/r/<id>, OGP for X and Discord) that stays until you unshare it.Blender 5.0, Cycles on GPU (
blender="5.2"selects Blender 5.2 when the worker has it;render_infolists what is available).
Blender add-on and DCC tools (for people, not agents)
Blender extension (
addons/blender/, Blender 4.2+): a JANCTION Render panel under Properties → Render with Preview, Estimate and Final. A copy of the open file and the textures it references with relative paths are uploaded; results land next to the .blend. Install from the extension repositoryhttps://render.janction.jp/extensions/index.json(Preferences → Get Extensions → Repositories → + → Add Remote Repository), or install the zip fromhttps://render.janction.jp/extensions/janction_render-0.1.2.zip(python scripts/build_blender_addon.pybuilds it from this repository). Guide: https://render.janction.jp/blender-addonMaya, Houdini, Cinema 4D (
integrations/): export to USD / FBX / Alembic, upload with textures, render with Cycles, open the result; Preview, Final (MP4) and Turntable. Standard-library Python only;jr_submit.pyis the shared client. Guide: https://render.janction.jp/maya-houdini-cinema4d
Self-hosting
The server (FastAPI + SQLite, with the remote MCP endpoint) and the worker (Blender in disposable Docker containers,
--network none --cap-drop ALL) live in the internal repository and are not part of this package yet. This repository
holds the client side: stdio MCP server, CLI, HTTP client, samples, the Claude Code plugin, the Blender add-on and the
DCC tools.
MCP registry
This server is listed in the official MCP registry as io.github.JasmyLab-JANCTION/janction-render (remote: https://render.janction.jp/mcp).
mcp-name: io.github.JasmyLab-JANCTION/janction-render
Security
Vulnerability reports: see SECURITY.md. Retention, isolation and the external tests are summarised on the security page.
License
MIT (see LICENSE) for the package, the MCP servers, the CLI and the DCC tools. The Blender extension under
addons/blender/ is GPL-3.0-or-later, as Blender requires for add-ons. Operated by JasmyLab Inc.
Available Tools
13 toolsasset_searchARead-onlyIdempotentInspect
Find free CC0 3D models, PBR textures or HDRIs on Poly Haven by words ('wooden chair', 'forest hdri') to furnish a scene without uploading files; no GPU time. For product shots of something the user has no model for: asset_search(''), then render_preview(scene_script='import jr_assets; jr_assets.import_model("")', assets=['polyhaven:'], preset='product') gives 4 studio shots on a plain backdrop. Each result has a spec 'polyhaven:' to pass in assets, its size in metres and the entry file. In the scene script: import jr_assets, then jr_assets.import_model('', location=(x, y, z), scale=1.0) returns the objects, jr_assets.material('') builds a material and jr_assets.apply(obj, mat) assigns it, jr_assets.world_hdri('', strength=1.0) lights the world. 'polyhaven:@2k' fetches 2k textures (1k by default).
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | 'models' (default) | 'textures' | 'hdris'. | models |
| query | Yes | Words to search Poly Haven for (name, tags, category), e.g. 'wooden chair' or 'forest hdri'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, openWorld, non-destructive), so the description is free to add the operationally important context it does: results carry a 'polyhaven:<id>' spec, size in metres, and entry file, and the '@2k' suffix changes texture resolution (1k default). This is behavior an agent cannot infer from the annotations or schema.
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?
Front-loaded with purpose, then usage, then result shape, then the scene-script API in a logical order with zero filler. It is dense and runs long in a single block, and some of the jr_assets API detail arguably belongs with the consuming tools, but every part is actionable.
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?
An output schema exists, yet the description still summarizes the return contract (id spec, size in metres, entry file) and shows the full search-to-render workflow. An agent has everything needed to search and to use the results correctly.
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 the 'kind' and 'query' documentation baseline is 3; the description goes further by showing worked query examples and, more usefully, the '@2k' resolution modifier that governs how the returned id is consumed downstream. It adds real semantics but not per-parameter edge cases.
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?
Opens with a specific verb and resource: 'Find free CC0 3D models, PBR textures or HDRIs on Poly Haven by words'. It is plainly distinguishable from the render_* siblings, which produce output rather than locate input assets.
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?
Explicitly names the alternative runtime path: 'to furnish a scene without uploading files; no GPU time', and gives a concrete when-to-use scenario ('For product shots of something the user has no model for') with the exact follow-up call to render_preview. When-to-use and the alternative are both spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
billingARead-onlyIdempotentInspect
Check what this key can still render for free and its credit (no GPU time): the free GPU time left and when it expires or resets (each new key gets 100 JPY of GPU time free with no card (14 days) and 400 JPY more once the user saves a card on a page that charges nothing (once per card); after that, previews are free up to 2 GPU-minutes a day and finals cost 0.1 JPY per GPU-second (prepaid)), the balance in yen, the price per GPU-second, any pending checkout link, and card_offer / autocharge_offer when they apply. Relay tell_user to the user in their language when it is present.
| Name | Required | Description | Default |
|---|---|---|---|
| topup_yen | No | 0 = just report the quota / balance; > 0 (minimum 500) = create a Stripe checkout link for that amount in yen. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/openWorld/non-destructive, so the bar is lower, and the description still adds real behavioral context: credit expiry (14 days), the once-per-card 400 JPY grant, prepaid charging rates (previews free to 2 GPU-min/day, finals 0.1 JPY per GPU-second), and a tell_user relay directive that appears nowhere in structured fields. There is a mild tension between readOnlyHint=true and the topup_yen path that creates a Stripe checkout link, but the description frames it as link generation with no charge, so it is not a clear contradiction.
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 body is effectively one enormous sentence dense with nested parentheses enumerating the pricing ladder, expiry windows, and offer flags. It is front-loaded with the core purpose, but the run-on structure makes it hard to scan and several clauses (deposit mechanics, per-second rates) are crammed together rather than separated.
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 an output schema present, return values need not be explained, and the description still covers the operational rules an agent needs: what is reported, the topup threshold, credit expiry/reset, and the tell_user relay. It is close to complete for a one-parameter tool; only the read-only-vs-checkout tension is left unaddressed.
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 the single parameter is fully documented in the schema, so the baseline is 3. The description contextualises the topup path (creating a checkout link) but adds no syntax or format meaning beyond what the schema already states.
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+resource ('Check what this key can still render for free and its credit'), then catalogues exactly what is reported: free GPU time, balance in yen, price per GPU-second, pending checkout link, and card/autocharge offers. It is clearly a billing/quota tool rather than a renderer, so an agent can place it against the render_* siblings. It stops short of naming any sibling or exclusion explicitly.
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 rather than stated: checking credits/balance is the obvious 'use' and the schema's topup_yen documents the one conditional action. There is no explicit when-to-use vs when-not, no prerequisites, and no reference to alternatives, so guidance is only adequate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_cancelADestructiveIdempotentInspect
Stop a queued or running render job of this key (a j_... job, or a b_... job split into parts, which stops every part). Effects: queued frames are dropped; frames already rendered are charged and stay downloadable until the job expires; the rest of the GPU time held for the job is returned to the key at once. It cannot be undone or resumed: to render again, call render_final again (a new job). A job that is already done, failed or canceled is returned unchanged, and a job whose frames are all rendered and is being assembled into the video cannot be stopped (nothing more is charged). Only the key that started the job can stop it; any other job_id returns not_found. Ask the user before cancelling a render they asked for.
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | The job_id returned by render_preview or render_final (j_...; a long final split into parts is b_...). Only this key's jobs; in render_status, leave it empty to list them. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the destructiveHint/idempotentHint annotations: it details that queued frames are dropped, already-rendered frames are charged and stay downloadable until expiry, held GPU time is returned, cancellation is irreversible, terminal jobs return unchanged, and assembling jobs cannot be stopped. It also states the auth boundary (only the owning key; otherwise not_found), which annotations do not cover.
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?
Front-loads the action and scope, then presents effects and edge cases in a logical sequence. It is on the longer side with some mild redundancy around irreversibility, but nearly every clause carries an edge case the agent needs.
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 single-param destructive mutation with an output schema and annotations already present, the description fills every remaining gap: side effects, billing, reversibility, idempotent terminal-state behavior, and permission boundaries.
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% for the single job_id parameter, so the baseline is 3, but the description adds the authorization constraint ('Only the key that started the job can stop it; any other job_id returns not_found'), which is behavioral meaning the schema does not carry.
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?
States a specific verb and resource ('Stop a queued or running render job of this key') and immediately distinguishes scope by naming the job id formats (j_..., b_...) and the part-job behavior. An agent can tell this apart from render_final/render_status without opening any schema.
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?
Gives clear operating context: use it to stop a queued/running job, call render_final again to re-render, and 'Ask the user before cancelling a render they asked for.' It does not explicitly contrast against alternatives like render_status or render_final beyond the re-render note, so it stops short of full when/when-not routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_downloadARead-onlyIdempotentInspect
Download a job's files to out_dir (default ~/janction-render/<job_id>) and return the local paths. only picks the files: 'all' (default), 'mp4' (just the video; use it when the user wants the video, to skip hundreds of frames), 'frames', 'sheet' or 'output'. wait_seconds > 0 first waits that long for the job to finish.
| Name | Required | Description | Default |
|---|---|---|---|
| only | No | 'all' (every frame plus output.mp4 / sheet.png) | 'mp4' | 'frames' | 'sheet' | 'output' (the MP4 or the sheet only). | all |
| job_id | Yes | The job_id returned by render_preview or render_final (j_...; a long final split into parts is b_...). Only this key's jobs; in render_status, leave it empty to list them. | |
| out_dir | No | Local folder to save the files in (default ~/janction-render/<job_id>). | |
| wait_seconds | No | Wait up to this many seconds for the job to finish before downloading (0 = do not wait). |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds context the annotations cannot: artifacts are written to a local directory (~/janction-render/<job_id> by default), local paths are returned, and a positive wait_seconds blocks until the job finishes. It does not describe behavior when the job is still unfinished with wait_seconds=0, or download size/limits.
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?
Roughly three compact sentences, front-loaded with the action and destination before the option list; every sentence carries information. Minor awkwardness in the 'only picks the files' phrasing and the inline enumeration, but no filler.
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?
An output schema exists, so return values need not be spelled out, and the schema plus annotations cover parameters and safety. What remains thin is the unfinished-job case (behavior when wait_seconds=0 and the job is not done) — otherwise the definition is sufficient to call the tool correctly.
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 all four parameters are already documented — the baseline is 3. The description mostly restates the enum values and the out_dir/wait_seconds defaults, adding only the rationale for 'mp4' ('to skip hundreds of frames'), which is a modest value-add beyond the schema.
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?
States a specific verb+resource ('Download a job's files') plus the destination ('out_dir, default ~/janction-render/<job_id>') and the return ('local paths'). It is unambiguous among siblings such as render_preview, render_final and render_status, which produce or inspect jobs rather than retrieve their artifacts.
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?
Gives concrete selection guidance for the 'only' mode, including the condition for the most important case ('mp4' — use when the user wants the video, to skip hundreds of frames), and explains the wait_seconds timing tradeoff. It does not, however, state when to prefer this tool over render_status/render_preview for checking job state, so there is no explicit exclusion.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_estimateARead-onlyIdempotentInspect
Estimate a render before starting it (no GPU time): GPU seconds, queue wait, the wall-clock time as text (estimate.human, e.g. 'about 3 minutes') and whether it fits the free GPU time left (the welcome credit, or 2 preview GPU-minutes a day after it) and the size limits. Call it before render_final when the time or quota is uncertain, the job is longer than about 24 frames or above 720p, or the user asks how long it takes; tell the user the result. With scene_id the estimate uses measured render times and returns calibration.final_path_measured. If false, treat it as preliminary: run a short contiguous final-path test and estimate again before the full animation. It also says when to split a job.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | 'final' (frame_start..frame_end at width x height, samples) or 'preview' (frames like '1-24'). | final |
| width | No | Output width in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit). | |
| frames | No | Preview frames: '' = frame 1; '12' = one frame; '1,8,16,24' or '1-24' = up to 4 frames tiled in one labeled image (to judge camera motion and animation; same GPU time as one 720p frame). | |
| height | No | Output height in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit). | |
| preset | No | Purpose preset: 'product' (4 studio shots at 0/90/180/270 deg on a plain backdrop; the final gives 4 PNGs at 1440x1440), 'turntable' (one full turn as an MP4, studio light, plain backdrop), 'animation' (the scene's own camera, lights and frame range; the scene is read first when the range is unknown), or '' (none). It fills what you do not pass; environment, orbit_frames, width/height and the frames still win when given. | |
| cameras | No | Several camera object names (up to 8) rendered from the same frame in one job: the preview tiles them in one labeled sheet, the final returns one PNG/EXR per camera (camera_files maps names to files). For product shots from fixed angles. Not combinable with orbit, environment='compare' or camera. | |
| samples | No | Cycles samples per pixel (preview: up to 32, default 16; final: default 128). | |
| scene_id | No | scene_id returned by an earlier call (reuses the uploaded scene without sending it again). | |
| frame_end | No | Last frame to render (inclusive); equal to frame_start for a single image. With orbit=True and no frame_end, one full turn is rendered. | |
| frame_start | No | First frame to render (inclusive). |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint true and destructiveHint false. The description adds rich behavioral context: no GPU time consumed, measured vs preliminary estimates, calibration.final_path_measured semantics, splitting advice, and the consequence of a false preliminary flag. This far exceeds what annotations provide.
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 information-dense and front-loaded with the key outputs. It is a single long paragraph but every sentence carries useful signal. Slightly verbose but appropriate for the complexity of the tool.
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 10 parameters, no required params, full schema coverage, and an output schema present, the description covers the essential behavioral context: what the estimate returns, when to call it, how to interpret preliminary results, and when to split jobs. Nothing critical is missing.
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 all 10 parameters are already documented in the schema. The description adds marginal semantic value by explaining what the estimate does with scene_id and what calibration output means, but it doesn't add parameter syntax beyond what the schema already provides. Baseline 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?
States a specific verb (Estimate) and resource (a render) and immediately specifies the output signals: GPU seconds, queue wait, human-readable wall-clock text, and quota fit. Clearly distinguishable from render_final, render_preview, and the other siblings.
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?
Explicit when-to-use guidance: 'Call it before render_final when the time or quota is uncertain, the job is longer than about 24 frames or above 720p, or the user asks how long it takes.' It also names the alternative (render_final) and gives concrete thresholds and exclusion conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_finalAInspect
Render the final frames or video of a Blender scene on cloud GPUs, after the user approved a preview: 'render the final', 'make the video', 'export the turntable MP4'. Call render_estimate first when the time or the free GPU time left (the welcome credit, or 2 preview GPU-minutes a day after it) is uncertain, the job is longer than about 24 frames or above 720p, or the user asks how long it takes, and tell the user. For animations, keep the approved engine/look, test 10 contiguous final-path frames, then estimate again. Long jobs are rendered in chunks and joined into one video. Returns job_id and estimate.human at once; then render_status until done, and render_download(job_id, only='mp4') for the video. Use the same environment and blender as the approved preview. Limits: up to 3,000 frames per call (over 240 runs as parts under one job id); 1920x1080 on free time, 3840x2160 with prepaid credit; each new key gets 100 JPY of GPU time free with no card (14 days) and 400 JPY more once the user saves a card on a page that charges nothing (once per card); after that, previews are free up to 2 GPU-minutes a day and finals cost 0.1 JPY per GPU-second (prepaid); ask before a render the free time does not cover; files are deleted 24 h after last use (7 days once topped up). preset='product' | 'turntable' | 'animation' fills the settings for that purpose. Options per parameter: output (png, exr, mp4, webm, prores, gif, webp), orbit, cameras, transparent, engine, notify_url, assets ('polyhaven:'). Pass scene_id from render_preview, or scene_path / scene_script / scene_url; files are saved locally.
| Name | Required | Description | Default |
|---|---|---|---|
| fps | No | Frames per second of the video output (default 24). | |
| orbit | No | True: turntable; an orbit camera circles the scene once (the scene's own camera is not used). Flat floors and walls are ignored when framing. A preview without frames shows 0/90/180/270 degrees; a final without frame_end renders the whole turn as an MP4. Best for imported models and product videos. | |
| width | No | Output width in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit). | |
| assets | No | Extra files sent with the scene: local paths (textures, glTF .bin), https links, or 'polyhaven:<id>' CC0 assets from asset_search. A .blend finds them through relative paths (same folder); a script through os.environ['JR_ASSETS_DIR'] or the jr_assets helpers. | |
| camera | No | Name of the camera object to render from (default: the scene's active camera, or an automatic camera if there is none). Ignored with orbit=True; not combinable with cameras. | |
| engine | No | '' or 'cycles' (path tracing, default) | 'eevee' (real-time rasterizer: about half the per-frame cost on animations of a few dozen frames or more; each job pays about 7 s of shader compilation, so previews stay on Cycles). | |
| height | No | Output height in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit). | |
| output | No | 'auto' (mp4 for a frame range, png for one frame) | 'png' | 'exr' (16-bit linear frames for compositing) | 'mp4' (H.264) | 'webm' (VP9) | 'prores' (Apple ProRes 422 HQ .mov, for editing) | 'gif' | 'webp' (animated, at most 960 px wide; good for chat and web). | auto |
| preset | No | Purpose preset: 'product' (4 studio shots at 0/90/180/270 deg on a plain backdrop; the final gives 4 PNGs at 1440x1440), 'turntable' (one full turn as an MP4, studio light, plain backdrop), 'animation' (the scene's own camera, lights and frame range; the scene is read first when the range is unknown), or '' (none). It fills what you do not pass; environment, orbit_frames, width/height and the frames still win when given. | |
| blender | No | Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' (also '4.5', '4.2', '3.6'; a script that stops on Blender 5 because of an older API is rendered again on those automatically) to choose. | |
| cameras | No | Several camera object names (up to 8) rendered from the same frame in one job: the preview tiles them in one labeled sheet, the final returns one PNG/EXR per camera (camera_files maps names to files). For product shots from fixed angles. Not combinable with orbit, environment='compare' or camera. | |
| max_yen | No | Ceiling for this job in JPY (-1 = none): the job is refused before anything is reserved when its reservation would exceed it. 0 means 'only if it fits the free time'. | |
| samples | No | Cycles samples per pixel (preview: up to 32, default 16; final: default 128). | |
| scene_id | No | scene_id returned by an earlier call (reuses the uploaded scene without sending it again). | |
| frame_end | No | Last frame to render (inclusive); equal to frame_start for a single image. With orbit=True and no frame_end, one full turn is rendered. | |
| scene_url | No | https link to a .blend, a .py script or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc); the service fetches it (e.g. the sample https://render.janction.jp/samples/cube_scene.py, or a 12-hour /upload link). A .gltf or .obj also brings its .bin / .mtl / textures from the same folder. | |
| notify_url | No | Optional https URL that receives one JSON POST when the final render finishes (job.done / job.failed, with download links), so nobody has to poll. | |
| scene_path | No | Local path to the scene: a .blend file, a bpy .py script, or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc, imported into an empty scene). Uploaded once; reuse the returned scene_id afterwards. | |
| environment | No | Lighting preset: '' (the scene's own world), 'studio', 'sunset', 'overcast' or 'night' (bundled HDRIs; a good first render for scenes without lighting work), or 'compare' (preview only: all four in one labeled 2x2 image, to pick one). | |
| frame_start | No | First frame to render (inclusive). | |
| transparent | No | True renders on a transparent background (alpha) for product shots and overlays; png / exr / webm / gif / webp keep it, mp4 and prores cannot. The preview sheet shows it over dark grey. | |
| orbit_frames | No | Number of frames for one full turn (default 24). | |
| orbit_target | No | Object name or 'x,y,z' to centre the orbit on (default: the scene's main objects). | |
| scene_script | No | The scene as bpy Python code (text). No file and no local Blender needed; the service runs it in Blender 5.0 (5.2 with blender='5.2'). Blender 5.0 API notes: materials are node-based, so set a colour on the Principled BSDF (mat.node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value = (r, g, b, 1)); mat.diffuse_color only affects the viewport; do not set Material.use_nodes. Animate with obj.keyframe_insert() (action.fcurves no longer exists; set bpy.context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR' first for constant speed). Build the scene, camera and frame range only; leave resolution, samples and denoising to the service. Helpers: `import jr_assets`, then jr_assets.frame_camera(margin=1.1) points the camera at every object and fits them in the frame; jr_assets.import_model('<id>'), jr_assets.material('<id>') and jr_assets.world_hdri('<id>') use Poly Haven assets passed in assets. | |
| orbit_distance | No | Multiplier for the orbit camera distance (default 1.0). | |
| idempotency_key | No | Any string up to 128 characters that identifies this order: sending the same key again returns the first job instead of rendering twice (a different body with the same key is refused). | |
| orbit_elevation | No | Orbit camera elevation in degrees (default 18). | |
| environment_visible | No | False hides the HDRI from the camera (flat grey backdrop, HDRI lighting only). | |
| environment_strength | No | Multiplier for the HDRI preset's brightness (default 1.0). |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations mark this as non-read-only, non-idempotent, open-world, and the description adds far more: per-call frame limits (3,000; 240 as parts under one job id), resolution caps on free vs prepaid, pricing (100 JPY free credit, 400 JPY after card save, 0.1 JPY/GPU-second), file deletion windows, chunk-and-join behavior, and an explicit 'ask before a render the free time does not cover.' This is unusually rich behavioral context.
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?
Purpose and the estimate-first rule are front-loaded, and nearly every clause carries operational weight (limits, pricing, workflow). However, it is a single dense block that mixes workflow, pricing, and parameter notes; splitting into short labeled segments would improve scanability.
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 29-parameter, open-world, paid-render tool with an output schema, the description covers the full lifecycle: when to estimate, how to render, how to poll (render_status), how to download (render_download), cost limits, and deletion policy. Return values need not be explained since 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 the baseline is 3; the description earns an extra point by adding cross-parameter semantics not obvious from the schema, e.g. preset='product'|'turntable'|'animation' filling settings, output format list, assets ('polyhaven:<id>'), and using scene_id from render_preview vs scene_path/script/url. It mostly reinforces schema content rather than adding novel syntax detail.
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?
Opens with a specific verb+resource+context: 'Render the final frames or video of a Blender scene on cloud GPUs, after the user approved a preview.' This clearly distinguishes it from render_preview (which happens before approval) and from render_estimate. An agent can select it without opening the schema.
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?
Explicitly states when to call render_estimate first (uncertain time, >24 frames, >720p, user asks duration) and 'tell the user.' It also prescribes the animation workflow ('keep the approved engine/look, test 10 contiguous final-path frames, then estimate again') and routes to render_status / render_download afterwards. Alternatives and conditions are named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_infoARead-onlyIdempotentInspect
Check the connection and the service before rendering (read-only, no GPU time, no charge): the server URL, whether GPU workers are online, the queue length and expected wait, and this key's usage. Use it before the first render or when renders seem stuck, and to decide whether to route a render here: workers_available > 0 means a preview comes back in seconds; gated means the GPU is lent out and gate says how long the wait usually is. It uses the API key in JANCTION_RENDER_API_KEY or ~/.janction-render.json (a free key is made on first use). Every call is rate-limited per key; a 429 response carries retry_after_s, wait that long. diagnose.tell_user says in plain words whether the connection, the key, the free credit and the GPUs are fine and what the last failed render said; relay it when the user asks why something does not work.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (which already cover read-only/idempotent/non-destructive), it discloses cost and resource behavior ('no GPU time, no charge'), the auth source (JANCTION_RENDER_API_KEY or ~/.janction-render.json, auto-provisioned), and rate limiting with the concrete recovery action ('a 429 response carries retry_after_s, wait that long'). It also explains how to interpret the gated state and diagnose.tell_user.
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?
Front-loaded with the action and the cost reassurance, and nearly every clause carries information an agent needs. However, it is delivered as one dense ~120-word paragraph mixing purpose, routing logic, auth, rate limits and diagnosis, which would scan better broken into discrete statements.
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?
An output schema exists so return values need not be documented exhaustively, yet the description goes further and tells the agent how to read the important fields (workers_available, gated, gate) and how to relay diagnose.tell_user to the user. Nothing an agent needs to call this correctly is missing.
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 per the rubric the baseline is 4. There is no parameter surface for the description to clarify, and it correctly spends its words on return-value interpretation instead.
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?
States a specific verb and scope: 'Check the connection and the service before rendering', then enumerates exactly what is reported (server URL, GPU worker health, queue length, expected wait, key usage). It is clearly a service-level diagnostic rather than a job-level one, but it never names the adjacent sibling render_status, so an agent must infer the boundary with that tool.
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?
Gives explicit triggering conditions ('before the first render', 'when renders seem stuck') plus a decision rule ('to decide whether to route a render here'). It also names the alternative path implicitly by explaining that workers_available > 0 vs gated determines whether a preview is worthwhile. This is textbook when-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_previewAInspect
Show what a Blender scene looks like: a fast, low-cost preview render on a cloud GPU, back as an image in seconds. Typical requests: 'render this .blend', 'show me the scene', 'preview the turntable', 'I have no GPU', 'Blender is too slow on my laptop', 'Blenderでレンダリングして', 'GPUがない', 'ターンテーブル', '商品の360度動画'. Also use it after writing a bpy script, to see what it builds. Each preview returns a critic verdict (ok / check / fix) with fix code: apply the fixes and preview again before any final render. It also returns final_estimate (the same scene as a 1080p final) and quota_left_today; tell the user both before asking to go on. A script that raises comes back as a failure with the line (nothing is rendered). When not to use: editing a scene open in the user's Blender belongs to a local Blender MCP, and a single still on a machine with its own NVIDIA GPU usually renders faster locally. This service is for CPU-only machines, animations, and scripts with no Blender installed. Limits: up to 1280x720; 1-4 frames tiled in one labeled image for the GPU time of one 720p frame. preset='product' | 'turntable' | 'animation' sets camera, light, backdrop and frames for that purpose. Other options are explained on each parameter: environment presets and 'compare', orbit (turntable), cameras (several named cameras), engine, transparent, assets with 'polyhaven:' CC0 assets. Give the scene as scene_path (a local .blend, .py or 3D file), scene_script, scene_url or scene_id; the image is saved locally and shown.
| Name | Required | Description | Default |
|---|---|---|---|
| orbit | No | True: turntable; an orbit camera circles the scene once (the scene's own camera is not used). Flat floors and walls are ignored when framing. A preview without frames shows 0/90/180/270 degrees; a final without frame_end renders the whole turn as an MP4. Best for imported models and product videos. | |
| width | No | Output width in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit). | |
| assets | No | Extra files sent with the scene: local paths (textures, glTF .bin), https links, or 'polyhaven:<id>' CC0 assets from asset_search. A .blend finds them through relative paths (same folder); a script through os.environ['JR_ASSETS_DIR'] or the jr_assets helpers. | |
| camera | No | Name of the camera object to render from (default: the scene's active camera, or an automatic camera if there is none). Ignored with orbit=True; not combinable with cameras. | |
| engine | No | '' or 'cycles' (path tracing, default) | 'eevee' (real-time rasterizer: about half the per-frame cost on animations of a few dozen frames or more; each job pays about 7 s of shader compilation, so previews stay on Cycles). | |
| frames | No | Preview frames: '' = frame 1; '12' = one frame; '1,8,16,24' or '1-24' = up to 4 frames tiled in one labeled image (to judge camera motion and animation; same GPU time as one 720p frame). | |
| height | No | Output height in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit). | |
| preset | No | Purpose preset: 'product' (4 studio shots at 0/90/180/270 deg on a plain backdrop; the final gives 4 PNGs at 1440x1440), 'turntable' (one full turn as an MP4, studio light, plain backdrop), 'animation' (the scene's own camera, lights and frame range; the scene is read first when the range is unknown), or '' (none). It fills what you do not pass; environment, orbit_frames, width/height and the frames still win when given. | |
| blender | No | Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' (also '4.5', '4.2', '3.6'; a script that stops on Blender 5 because of an older API is rendered again on those automatically) to choose. | |
| cameras | No | Several camera object names (up to 8) rendered from the same frame in one job: the preview tiles them in one labeled sheet, the final returns one PNG/EXR per camera (camera_files maps names to files). For product shots from fixed angles. Not combinable with orbit, environment='compare' or camera. | |
| out_dir | No | Local folder to save the files in (default ~/janction-render/<job_id>). | |
| samples | No | Cycles samples per pixel (preview: up to 32, default 16; final: default 128). | |
| scene_id | No | scene_id returned by an earlier call (reuses the uploaded scene without sending it again). | |
| scene_url | No | https link to a .blend, a .py script or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc); the service fetches it (e.g. the sample https://render.janction.jp/samples/cube_scene.py, or a 12-hour /upload link). A .gltf or .obj also brings its .bin / .mtl / textures from the same folder. | |
| scene_path | No | Local path to the scene: a .blend file, a bpy .py script, or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc, imported into an empty scene). Uploaded once; reuse the returned scene_id afterwards. | |
| environment | No | Lighting preset: '' (the scene's own world), 'studio', 'sunset', 'overcast' or 'night' (bundled HDRIs; a good first render for scenes without lighting work), or 'compare' (preview only: all four in one labeled 2x2 image, to pick one). | |
| transparent | No | True renders on a transparent background (alpha) for product shots and overlays; png / exr / webm / gif / webp keep it, mp4 and prores cannot. The preview sheet shows it over dark grey. | |
| orbit_frames | No | Number of frames for one full turn (default 24). | |
| orbit_target | No | Object name or 'x,y,z' to centre the orbit on (default: the scene's main objects). | |
| scene_script | No | The scene as bpy Python code (text). No file and no local Blender needed; the service runs it in Blender 5.0 (5.2 with blender='5.2'). Blender 5.0 API notes: materials are node-based, so set a colour on the Principled BSDF (mat.node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value = (r, g, b, 1)); mat.diffuse_color only affects the viewport; do not set Material.use_nodes. Animate with obj.keyframe_insert() (action.fcurves no longer exists; set bpy.context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR' first for constant speed). Build the scene, camera and frame range only; leave resolution, samples and denoising to the service. Helpers: `import jr_assets`, then jr_assets.frame_camera(margin=1.1) points the camera at every object and fits them in the frame; jr_assets.import_model('<id>'), jr_assets.material('<id>') and jr_assets.world_hdri('<id>') use Poly Haven assets passed in assets. | |
| orbit_distance | No | Multiplier for the orbit camera distance (default 1.0). | |
| orbit_elevation | No | Orbit camera elevation in degrees (default 18). | |
| environment_visible | No | False hides the HDRI from the camera (flat grey backdrop, HDRI lighting only). | |
| environment_strength | No | Multiplier for the HDRI preset's brightness (default 1.0). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations give the safety profile (readOnly=false, openWorld=true, idempotent=false, non-destructive) but the description discloses behavior the annotations cannot: a critic verdict ok/check/fix with fix code, final_estimate and quota_left_today surfaced before asking to proceed, and a script that raises returning a failure with the offending line and nothing rendered. It also states resolution caps and the tiling economy (1-4 frames for the cost of one 720p frame).
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?
Front-loads the core purpose, then when-to-use, then when-not-to-use and limits, which is good ordering. It is on the long side and the multilingual trigger phrases add bulk, but nearly every sentence carries routing or behavioral information an agent needs, so little 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 24-parameter zero-required tool with no output schema, the description still explains the salient returns (critic verdict, final_estimate, quota_left_today, failure-with-line), the limits, the input-source options and the follow-up workflow. Nothing an agent needs to call it correctly appears missing.
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 baseline is 3; the description modestly exceeds it by mapping preset values to concrete behavior ('product' | 'turntable' | 'animation' set camera, light, backdrop and frames) and by summarizing the four scene-source channels (scene_path, scene_script, scene_url, scene_id). It otherwise defers to the per-parameter docs ('Other options are explained on each parameter'), which is appropriate but not additive.
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?
States a specific verb+resource+medium: 'Show what a Blender scene looks like: a fast, low-cost preview render on a cloud GPU, back as an image in seconds.' Scope, cost class and output form are all named, and the preview-vs-final distinction is drawn against the sibling render_final implicitly but clearly.
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?
Explicit when-to-use ('after writing a bpy script', 'I have no GPU') and when-not-to-use ('editing a scene open in the user's Blender belongs to a local Blender MCP', a single still on an NVIDIA machine renders faster locally), plus the intended workflow (apply fixes and preview again before any final render). Alternatives are named with the condition that selects them.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_reviewAInspect
Check a 3D model or a generated Blender scene by looking at it from 4 sides: a turntable preview (0, 90, 180 and 270 degrees) under neutral studio light on a cloud GPU, back with pass / warning / fail, a score, the checks it ran (texture files found, objects visible and not hidden behind a floor or wall, camera not inside an object, framing, exposure, light) with fix code for each problem, and the 4 views tiled in one image. Typical requests: 'check this model', 'does the scene look right', 'QA this GLB', 'モデルを確認して', '見た目をチェックして'. Use it after writing a bpy script, generating or converting a model (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc) or changing materials, before showing the result to the user or rendering a final; apply the fixes and call it again. The checks cannot judge whether the shape matches the request: look at the views for that. It costs the same as one preview. To check a shot through the scene's own camera and lights, use render_preview (its critic runs the same checks). Give the scene as scene_path, scene_script, scene_url or scene_id.
| Name | Required | Description | Default |
|---|---|---|---|
| assets | No | Extra files sent with the scene: local paths (textures, glTF .bin), https links, or 'polyhaven:<id>' CC0 assets from asset_search. A .blend finds them through relative paths (same folder); a script through os.environ['JR_ASSETS_DIR'] or the jr_assets helpers. | |
| blender | No | Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' (also '4.5', '4.2', '3.6'; a script that stops on Blender 5 because of an older API is rendered again on those automatically) to choose. | |
| out_dir | No | Local folder to save the files in (default ~/janction-render/<job_id>). | |
| scene_id | No | scene_id returned by an earlier call (reuses the uploaded scene without sending it again). | |
| scene_url | No | https link to a .blend, a .py script or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc); the service fetches it (e.g. the sample https://render.janction.jp/samples/cube_scene.py, or a 12-hour /upload link). A .gltf or .obj also brings its .bin / .mtl / textures from the same folder. | |
| scene_path | No | Local path to the scene: a .blend file, a bpy .py script, or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc, imported into an empty scene). Uploaded once; reuse the returned scene_id afterwards. | |
| environment | No | Light for the review: 'studio' (default; the same neutral light every time), 'sunset', 'overcast', 'night', or '' for the scene's own lights. | studio |
| orbit_target | No | Object name or 'x,y,z' to centre the orbit on (default: the scene's main objects). | |
| scene_script | No | The scene as bpy Python code (text). No file and no local Blender needed; the service runs it in Blender 5.0 (5.2 with blender='5.2'). Blender 5.0 API notes: materials are node-based, so set a colour on the Principled BSDF (mat.node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value = (r, g, b, 1)); mat.diffuse_color only affects the viewport; do not set Material.use_nodes. Animate with obj.keyframe_insert() (action.fcurves no longer exists; set bpy.context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR' first for constant speed). Build the scene, camera and frame range only; leave resolution, samples and denoising to the service. Helpers: `import jr_assets`, then jr_assets.frame_camera(margin=1.1) points the camera at every object and fits them in the frame; jr_assets.import_model('<id>'), jr_assets.material('<id>') and jr_assets.world_hdri('<id>') use Poly Haven assets passed in assets. | |
| orbit_distance | No | Multiplier for the orbit camera distance (default 1.0). | |
| orbit_elevation | No | Orbit camera elevation in degrees (default 18). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context beyond the annotations: it runs on a cloud GPU, costs the same as one preview, enumerates the concrete checks performed (texture files, hidden objects, camera inside geometry, framing, exposure, light), returns fix code per problem, and returns the 4 views tiled in one image. The annotations only carry the generic openWorld/non-idempotent profile, so the description is doing real work here.
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?
Front-loaded with the core action and output, then usage triggers, then the sibling routing rule. Every sentence carries information, but the block is long and the multi-language example strings ('モデルを確認して') add bulk without adding much decision value for an English-speaking agent.
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 an 11-parameter, zero-required tool with no output schema, the description covers what matters: the return shape (pass/warning/fail, score, checks, fix code, tiled image), the input alternatives, cost, and the boundary of what the checks can judge. Nothing an agent needs to invoke it correctly is missing.
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 the baseline is 3, but the description adds meaning the schema does not: it frames scene_path/scene_script/scene_url/scene_id as four alternative ways to supply the same scene ('Give the scene as ...'), which the flat schema never states. It also notes environment affects lighting choice. Minor gap: it does not explain orbit_distance/orbit_elevation 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?
States a specific verb (check/review) and resource (a 3D model or generated Blender scene), and specifies the exact method: a 4-view turntable under neutral studio light returning pass/warning/fail, a score, and the checks run. It explicitly distinguishes itself from the sibling render_preview by naming the camera/lights case that selects the other tool.
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?
Gives explicit when-to-use triggers ('after writing a bpy script, generating or converting a model ... before showing the result to the user or rendering a final'), the loop to follow ('apply the fixes and call it again'), an explicit limitation ('the checks cannot judge whether the shape matches the request'), and the alternative for camera-based shots (render_preview). Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_statusARead-onlyIdempotentInspect
Check a render job: status (queued / running / done / failed / canceled), frames done, the ETA (eta.human, including queue wait), files ready, Blender warnings, and the error with the log tail if it failed. Use it to report progress to the user; call render_download once status is done. Without job_id it lists this key's recent jobs and the scene_ids still kept, to pick up earlier work in a new conversation.
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | No | The job_id returned by render_preview or render_final (j_...; a long final split into parts is b_...). Only this key's jobs; in render_status, leave it empty to list them. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, openWorld), so the bar is lower. The description still adds genuine behavioral context: the enumerable status values, that eta.human includes queue wait, that Blender warnings and a log tail are surfaced on failure, and that omitting job_id switches to a listing mode.
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 dense sentences, front-loaded with the returned fields and then the usage/fallback modes; nothing is padding. The long field enumeration is justified but makes the second half slightly heavy.
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?
An output schema exists, so return-value documentation is not required, yet the description still sketches the response shape, covers the failure path and the no-argument listing path. Nothing an agent needs to invoke it correctly is missing.
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% for the single job_id parameter, and the schema already documents its origin (j_.../b_...), key scoping, and the empty-string listing behavior. The description restates the empty case without adding new syntax or constraints, so baseline 3 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?
States a specific verb and resource ('Check a render job') and enumerates exactly what is reported: status enum values, frames done, ETA, files ready, warnings, error tail. It is clearly distinguishable from siblings like render_download or render_estimate, which it names as the follow-up step.
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?
Explicitly says when to use it ('to report progress to the user'), what to do next ('call render_download once status is done'), and defines the no-argument case ('lists this key's recent jobs and the scene_ids still kept') including the motivating scenario of resuming work in a new conversation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scene_infoAIdempotentInspect
Read a Blender scene without rendering (a few seconds, no GPU time): cameras and the active one, frame range and fps, resolution, objects by type, lights, materials, whether it is animated, and missing linked files that would render pink. Use it for a .blend you did not write, or to check what a bpy script built. Returns scene_id; pass it to render_preview and render_final instead of sending the scene again.
| Name | Required | Description | Default |
|---|---|---|---|
| assets | No | Extra files sent with the scene: local paths (textures, glTF .bin), https links, or 'polyhaven:<id>' CC0 assets from asset_search. A .blend finds them through relative paths (same folder); a script through os.environ['JR_ASSETS_DIR'] or the jr_assets helpers. | |
| blender | No | Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' (also '4.5', '4.2', '3.6'; a script that stops on Blender 5 because of an older API is rendered again on those automatically) to choose. | |
| scene_id | No | scene_id returned by an earlier call (reuses the uploaded scene without sending it again). | |
| scene_url | No | https link to a .blend, a .py script or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc); the service fetches it (e.g. the sample https://render.janction.jp/samples/cube_scene.py, or a 12-hour /upload link). A .gltf or .obj also brings its .bin / .mtl / textures from the same folder. | |
| scene_path | No | Local path to the scene: a .blend file, a bpy .py script, or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc, imported into an empty scene). Uploaded once; reuse the returned scene_id afterwards. | |
| scene_script | No | The scene as bpy Python code (text). No file and no local Blender needed; the service runs it in Blender 5.0 (5.2 with blender='5.2'). Blender 5.0 API notes: materials are node-based, so set a colour on the Principled BSDF (mat.node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value = (r, g, b, 1)); mat.diffuse_color only affects the viewport; do not set Material.use_nodes. Animate with obj.keyframe_insert() (action.fcurves no longer exists; set bpy.context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR' first for constant speed). Build the scene, camera and frame range only; leave resolution, samples and denoising to the service. Helpers: `import jr_assets`, then jr_assets.frame_camera(margin=1.1) points the camera at every object and fits them in the frame; jr_assets.import_model('<id>'), jr_assets.material('<id>') and jr_assets.world_hdri('<id>') use Poly Haven assets passed in assets. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare idempotentHint=true and destructiveHint=false; the description adds genuinely useful cost/behavior context ('a few seconds, no GPU time') and clarifies that this is a non-rendering inspection step, which reconciles the readOnlyHint=false annotation with what the agent sees. It does not cover auth requirements, quotas, or failure modes, so 4 rather than 5.
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?
Three sentences, front-loaded with the verb and the no-render constraint, then the payload list, then the routing rule. Every clause carries information; nothing is padded or repeated.
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?
An output schema exists, so return values need no explanation, and the description still sketches what is inspected. Combined with the scene_id hand-off instruction, an agent has everything needed to call it and chain it into rendering, though it says nothing about what happens when the scene fails to load or has unresolvable assets.
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 the per-parameter text is unusually detailed (Blender API notes, asset resolution rules, jr_assets helpers), so the schema does the heavy lifting and baseline is 3. The description only restates the scene_id reuse rule that the schema already documents, adding no new parameter-level meaning.
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?
States a specific verb and resource ('Read a Blender scene') and enumerates exactly what is returned: cameras, frame range, fps, resolution, objects, lights, materials, animation state, missing linked files. That enumeration distinguishes it cleanly from render_preview/render_final, which produce images rather than metadata.
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?
Gives explicit when-to-use conditions ('a .blend you did not write', 'check what a bpy script built') and an explicit alternative/sequencing rule: pass the returned scene_id to render_preview and render_final instead of re-uploading. Nothing about tool selection is left to inference.
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.
4 tool updates
v0.4.120- Changed
render_final1 field changed- changed
Input schema / properties / blender / descriptionPrevious value: -"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' to choose."New value: +"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' (also '4.5', '4.2', '3.6'; a script that stops on Blender 5 because of an older API is rendered again on those automatically) to choose."
- Changed
render_preview1 field changed- changed
Input schema / properties / blender / descriptionPrevious value: -"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' to choose."New value: +"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' (also '4.5', '4.2', '3.6'; a script that stops on Blender 5 because of an older API is rendered again on those automatically) to choose."
- Changed
render_review1 field changed- changed
Input schema / properties / blender / descriptionPrevious value: -"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' to choose."New value: +"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' (also '4.5', '4.2', '3.6'; a script that stops on Blender 5 because of an older API is rendered again on those automatically) to choose."
- Changed
scene_info1 field changed- changed
Input schema / properties / blender / descriptionPrevious value: -"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' to choose."New value: +"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' (also '4.5', '4.2', '3.6'; a script that stops on Blender 5 because of an older API is rendered again on those automatically) to choose."
12 tool updates
v0.4.119- Changed
asset_search2 fields changed- added
Input schema / properties / kind / descriptionAdded value: +"'models' (default) | 'textures' | 'hdris'." - added
Input schema / properties / query / descriptionAdded value: +"Words to search Poly Haven for (name, tags, category), e.g. 'wooden chair' or 'forest hdri'."
- Changed
billing1 field changed- added
Input schema / properties / topup_yen / descriptionAdded value: +"0 = just report the quota / balance; > 0 (minimum 500) = create a Stripe checkout link for that amount in yen."
- Changed
render_cancel1 field changed- added
Input schema / properties / job_id / descriptionAdded value: +"The job_id returned by render_preview or render_final (j_...; a long final split into parts is b_...). Only this key's jobs; in render_status, leave it empty to list them."
- Changed
render_download4 fields changed- added
Input schema / properties / job_id / descriptionAdded value: +"The job_id returned by render_preview or render_final (j_...; a long final split into parts is b_...). Only this key's jobs; in render_status, leave it empty to list them." - added
Input schema / properties / only / descriptionAdded value: +"'all' (every frame plus output.mp4 / sheet.png) | 'mp4' | 'frames' | 'sheet' | 'output' (the MP4 or the sheet only)." - added
Input schema / properties / out_dir / descriptionAdded value: +"Local folder to save the files in (default ~/janction-render/<job_id>)." - added
Input schema / properties / wait_seconds / descriptionAdded value: +"Wait up to this many seconds for the job to finish before downloading (0 = do not wait)."
- Changed
render_estimate10 fields changed- added
Input schema / properties / camerasAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Several camera object names (up to 8) rendered from the same frame in one job: the preview tiles them in one labeled sheet, the final returns one PNG/EXR per camera (camera_files maps names to files). For product shots from fixed angles. Not combinable with orbit, environment='compare' or camera.", + "title": "Cameras" +} - added
Input schema / properties / frame_end / descriptionAdded value: +"Last frame to render (inclusive); equal to frame_start for a single image. With orbit=True and no frame_end, one full turn is rendered." - added
Input schema / properties / frame_start / descriptionAdded value: +"First frame to render (inclusive)." - added
Input schema / properties / frames / descriptionAdded value: +"Preview frames: '' = frame 1; '12' = one frame; '1,8,16,24' or '1-24' = up to 4 frames tiled in one labeled image (to judge camera motion and animation; same GPU time as one 720p frame)." - added
Input schema / properties / height / descriptionAdded value: +"Output height in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit)." - added
Input schema / properties / kind / descriptionAdded value: +"'final' (frame_start..frame_end at width x height, samples) or 'preview' (frames like '1-24')." - added
Input schema / properties / presetAdded value: +{ + "default": "", + "description": "Purpose preset: 'product' (4 studio shots at 0/90/180/270 deg on a plain backdrop; the final gives 4 PNGs at 1440x1440), 'turntable' (one full turn as an MP4, studio light, plain backdrop), 'animation' (the scene's own camera, lights and frame range; the scene is read first when the range is unknown), or '' (none). It fills what you do not pass; environment, orbit_frames, width/height and the frames still win when given.", + "title": "Preset", + "type": "string" +} - added
Input schema / properties / samples / descriptionAdded value: +"Cycles samples per pixel (preview: up to 32, default 16; final: default 128)." - added
Input schema / properties / scene_id / descriptionAdded value: +"scene_id returned by an earlier call (reuses the uploaded scene without sending it again)." - added
Input schema / properties / width / descriptionAdded value: +"Output width in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit)."
- Changed
render_final29 fields changed- added
Input schema / properties / assets / descriptionAdded value: +"Extra files sent with the scene: local paths (textures, glTF .bin), https links, or 'polyhaven:<id>' CC0 assets from asset_search. A .blend finds them through relative paths (same folder); a script through os.environ['JR_ASSETS_DIR'] or the jr_assets helpers." - added
Input schema / properties / blender / descriptionAdded value: +"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' to choose." - added
Input schema / properties / camera / descriptionAdded value: +"Name of the camera object to render from (default: the scene's active camera, or an automatic camera if there is none). Ignored with orbit=True; not combinable with cameras." - added
Input schema / properties / camerasAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Several camera object names (up to 8) rendered from the same frame in one job: the preview tiles them in one labeled sheet, the final returns one PNG/EXR per camera (camera_files maps names to files). For product shots from fixed angles. Not combinable with orbit, environment='compare' or camera.", + "title": "Cameras" +} - added
Input schema / properties / engine / descriptionAdded value: +"'' or 'cycles' (path tracing, default) | 'eevee' (real-time rasterizer: about half the per-frame cost on animations of a few dozen frames or more; each job pays about 7 s of shader compilation, so previews stay on Cycles)." - added
Input schema / properties / environment / descriptionAdded value: +"Lighting preset: '' (the scene's own world), 'studio', 'sunset', 'overcast' or 'night' (bundled HDRIs; a good first render for scenes without lighting work), or 'compare' (preview only: all four in one labeled 2x2 image, to pick one)." - added
Input schema / properties / environment_strength / descriptionAdded value: +"Multiplier for the HDRI preset's brightness (default 1.0)." - added
Input schema / properties / environment_visible / descriptionAdded value: +"False hides the HDRI from the camera (flat grey backdrop, HDRI lighting only)." - added
Input schema / properties / fps / descriptionAdded value: +"Frames per second of the video output (default 24)." - added
Input schema / properties / frame_end / descriptionAdded value: +"Last frame to render (inclusive); equal to frame_start for a single image. With orbit=True and no frame_end, one full turn is rendered." - added
Input schema / properties / frame_start / descriptionAdded value: +"First frame to render (inclusive)." - added
Input schema / properties / height / descriptionAdded value: +"Output height in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit)." - added
Input schema / properties / idempotency_keyAdded value: +{ + "default": "", + "description": "Any string up to 128 characters that identifies this order: sending the same key again returns the first job instead of rendering twice (a different body with the same key is refused).", + "title": "Idempotency Key", + "type": "string" +} - added
Input schema / properties / max_yenAdded value: +{ + "default": -1, + "description": "Ceiling for this job in JPY (-1 = none): the job is refused before anything is reserved when its reservation would exceed it. 0 means 'only if it fits the free time'.", + "title": "Max Yen", + "type": "integer" +} - added
Input schema / properties / notify_url / descriptionAdded value: +"Optional https URL that receives one JSON POST when the final render finishes (job.done / job.failed, with download links), so nobody has to poll." - added
Input schema / properties / orbit / descriptionAdded value: +"True: turntable; an orbit camera circles the scene once (the scene's own camera is not used). Flat floors and walls are ignored when framing. A preview without frames shows 0/90/180/270 degrees; a final without frame_end renders the whole turn as an MP4. Best for imported models and product videos." - added
Input schema / properties / orbit_distance / descriptionAdded value: +"Multiplier for the orbit camera distance (default 1.0)." - added
Input schema / properties / orbit_elevation / descriptionAdded value: +"Orbit camera elevation in degrees (default 18)." - added
Input schema / properties / orbit_frames / descriptionAdded value: +"Number of frames for one full turn (default 24)." - added
Input schema / properties / orbit_target / descriptionAdded value: +"Object name or 'x,y,z' to centre the orbit on (default: the scene's main objects)." - added
Input schema / properties / output / descriptionAdded value: +"'auto' (mp4 for a frame range, png for one frame) | 'png' | 'exr' (16-bit linear frames for compositing) | 'mp4' (H.264) | 'webm' (VP9) | 'prores' (Apple ProRes 422 HQ .mov, for editing) | 'gif' | 'webp' (animated, at most 960 px wide; good for chat and web)." - added
Input schema / properties / presetAdded value: +{ + "default": "", + "description": "Purpose preset: 'product' (4 studio shots at 0/90/180/270 deg on a plain backdrop; the final gives 4 PNGs at 1440x1440), 'turntable' (one full turn as an MP4, studio light, plain backdrop), 'animation' (the scene's own camera, lights and frame range; the scene is read first when the range is unknown), or '' (none). It fills what you do not pass; environment, orbit_frames, width/height and the frames still win when given.", + "title": "Preset", + "type": "string" +} - added
Input schema / properties / samples / descriptionAdded value: +"Cycles samples per pixel (preview: up to 32, default 16; final: default 128)." - added
Input schema / properties / scene_id / descriptionAdded value: +"scene_id returned by an earlier call (reuses the uploaded scene without sending it again)." - added
Input schema / properties / scene_path / descriptionAdded value: +"Local path to the scene: a .blend file, a bpy .py script, or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc, imported into an empty scene). Uploaded once; reuse the returned scene_id afterwards." - added
Input schema / properties / scene_script / descriptionAdded value: +"The scene as bpy Python code (text). No file and no local Blender needed; the service runs it in Blender 5.0 (5.2 with blender='5.2'). Blender 5.0 API notes: materials are node-based, so set a colour on the Principled BSDF (mat.node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value = (r, g, b, 1)); mat.diffuse_color only affects the viewport; do not set Material.use_nodes. Animate with obj.keyframe_insert() (action.fcurves no longer exists; set bpy.context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR' first for constant speed). Build the scene, camera and frame range only; leave resolution, samples and denoising to the service. Helpers: `import jr_assets`, then jr_assets.frame_camera(margin=1.1) points the camera at every object and fits them in the frame; jr_assets.import_model('<id>'), jr_assets.material('<id>') and jr_assets.world_hdri('<id>') use Poly Haven assets passed in assets." - added
Input schema / properties / scene_urlAdded value: +{ + "default": "", + "description": "https link to a .blend, a .py script or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc); the service fetches it (e.g. the sample https://render.janction.jp/samples/cube_scene.py, or a 12-hour /upload link). A .gltf or .obj also brings its .bin / .mtl / textures from the same folder.", + "title": "Scene Url", + "type": "string" +} - added
Input schema / properties / transparent / descriptionAdded value: +"True renders on a transparent background (alpha) for product shots and overlays; png / exr / webm / gif / webp keep it, mp4 and prores cannot. The preview sheet shows it over dark grey." - added
Input schema / properties / width / descriptionAdded value: +"Output width in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit)."
- Changed
render_preview24 fields changed- added
Input schema / properties / assets / descriptionAdded value: +"Extra files sent with the scene: local paths (textures, glTF .bin), https links, or 'polyhaven:<id>' CC0 assets from asset_search. A .blend finds them through relative paths (same folder); a script through os.environ['JR_ASSETS_DIR'] or the jr_assets helpers." - added
Input schema / properties / blender / descriptionAdded value: +"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' to choose." - added
Input schema / properties / camera / descriptionAdded value: +"Name of the camera object to render from (default: the scene's active camera, or an automatic camera if there is none). Ignored with orbit=True; not combinable with cameras." - added
Input schema / properties / camerasAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Several camera object names (up to 8) rendered from the same frame in one job: the preview tiles them in one labeled sheet, the final returns one PNG/EXR per camera (camera_files maps names to files). For product shots from fixed angles. Not combinable with orbit, environment='compare' or camera.", + "title": "Cameras" +} - added
Input schema / properties / engine / descriptionAdded value: +"'' or 'cycles' (path tracing, default) | 'eevee' (real-time rasterizer: about half the per-frame cost on animations of a few dozen frames or more; each job pays about 7 s of shader compilation, so previews stay on Cycles)." - added
Input schema / properties / environment / descriptionAdded value: +"Lighting preset: '' (the scene's own world), 'studio', 'sunset', 'overcast' or 'night' (bundled HDRIs; a good first render for scenes without lighting work), or 'compare' (preview only: all four in one labeled 2x2 image, to pick one)." - added
Input schema / properties / environment_strength / descriptionAdded value: +"Multiplier for the HDRI preset's brightness (default 1.0)." - added
Input schema / properties / environment_visible / descriptionAdded value: +"False hides the HDRI from the camera (flat grey backdrop, HDRI lighting only)." - added
Input schema / properties / frames / descriptionAdded value: +"Preview frames: '' = frame 1; '12' = one frame; '1,8,16,24' or '1-24' = up to 4 frames tiled in one labeled image (to judge camera motion and animation; same GPU time as one 720p frame)." - added
Input schema / properties / height / descriptionAdded value: +"Output height in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit)." - added
Input schema / properties / orbit / descriptionAdded value: +"True: turntable; an orbit camera circles the scene once (the scene's own camera is not used). Flat floors and walls are ignored when framing. A preview without frames shows 0/90/180/270 degrees; a final without frame_end renders the whole turn as an MP4. Best for imported models and product videos." - added
Input schema / properties / orbit_distance / descriptionAdded value: +"Multiplier for the orbit camera distance (default 1.0)." - added
Input schema / properties / orbit_elevation / descriptionAdded value: +"Orbit camera elevation in degrees (default 18)." - added
Input schema / properties / orbit_frames / descriptionAdded value: +"Number of frames for one full turn (default 24)." - added
Input schema / properties / orbit_target / descriptionAdded value: +"Object name or 'x,y,z' to centre the orbit on (default: the scene's main objects)." - added
Input schema / properties / out_dir / descriptionAdded value: +"Local folder to save the files in (default ~/janction-render/<job_id>)." - added
Input schema / properties / presetAdded value: +{ + "default": "", + "description": "Purpose preset: 'product' (4 studio shots at 0/90/180/270 deg on a plain backdrop; the final gives 4 PNGs at 1440x1440), 'turntable' (one full turn as an MP4, studio light, plain backdrop), 'animation' (the scene's own camera, lights and frame range; the scene is read first when the range is unknown), or '' (none). It fills what you do not pass; environment, orbit_frames, width/height and the frames still win when given.", + "title": "Preset", + "type": "string" +} - added
Input schema / properties / samples / descriptionAdded value: +"Cycles samples per pixel (preview: up to 32, default 16; final: default 128)." - added
Input schema / properties / scene_id / descriptionAdded value: +"scene_id returned by an earlier call (reuses the uploaded scene without sending it again)." - added
Input schema / properties / scene_path / descriptionAdded value: +"Local path to the scene: a .blend file, a bpy .py script, or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc, imported into an empty scene). Uploaded once; reuse the returned scene_id afterwards." - added
Input schema / properties / scene_script / descriptionAdded value: +"The scene as bpy Python code (text). No file and no local Blender needed; the service runs it in Blender 5.0 (5.2 with blender='5.2'). Blender 5.0 API notes: materials are node-based, so set a colour on the Principled BSDF (mat.node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value = (r, g, b, 1)); mat.diffuse_color only affects the viewport; do not set Material.use_nodes. Animate with obj.keyframe_insert() (action.fcurves no longer exists; set bpy.context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR' first for constant speed). Build the scene, camera and frame range only; leave resolution, samples and denoising to the service. Helpers: `import jr_assets`, then jr_assets.frame_camera(margin=1.1) points the camera at every object and fits them in the frame; jr_assets.import_model('<id>'), jr_assets.material('<id>') and jr_assets.world_hdri('<id>') use Poly Haven assets passed in assets." - added
Input schema / properties / scene_urlAdded value: +{ + "default": "", + "description": "https link to a .blend, a .py script or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc); the service fetches it (e.g. the sample https://render.janction.jp/samples/cube_scene.py, or a 12-hour /upload link). A .gltf or .obj also brings its .bin / .mtl / textures from the same folder.", + "title": "Scene Url", + "type": "string" +} - added
Input schema / properties / transparent / descriptionAdded value: +"True renders on a transparent background (alpha) for product shots and overlays; png / exr / webm / gif / webp keep it, mp4 and prores cannot. The preview sheet shows it over dark grey." - added
Input schema / properties / width / descriptionAdded value: +"Output width in pixels (previews are capped at 1280x720 keeping the aspect ratio; a final frame can have at most 1920x1080 pixels on free time, 3840x2160 on prepaid credit)."
- Added
render_review - Changed
render_share6 fields changed- added
Input schema / properties / include_script / descriptionAdded value: +"True also publishes the bpy script on the share page." - added
Input schema / properties / job_id / descriptionAdded value: +"The job_id returned by render_preview or render_final (j_...; a long final split into parts is b_...). Only this key's jobs; in render_status, leave it empty to list them." - added
Input schema / properties / listed / descriptionAdded value: +"True asks for the page to appear in the public gallery after the operator reviews it." - added
Input schema / properties / note / descriptionAdded value: +"A sentence shown under the image on the share page (optional)." - added
Input schema / properties / prompt / descriptionAdded value: +"What the user asked you, in their words (shown as 'what the user asked the agent')." - added
Input schema / properties / title / descriptionAdded value: +"Title shown on the public share page (optional)."
- Changed
render_status3 fields changed- added
Input schema / properties / job_id / defaultAdded value: +"" - added
Input schema / properties / job_id / descriptionAdded value: +"The job_id returned by render_preview or render_final (j_...; a long final split into parts is b_...). Only this key's jobs; in render_status, leave it empty to list them." - removed
Input schema / requiredRemoved value: -[ - "job_id" -]
- Changed
render_unshare1 field changed- added
Input schema / properties / job_id / descriptionAdded value: +"The job_id returned by render_preview or render_final (j_...; a long final split into parts is b_...). Only this key's jobs; in render_status, leave it empty to list them."
- Changed
scene_info6 fields changed- added
Input schema / properties / assets / descriptionAdded value: +"Extra files sent with the scene: local paths (textures, glTF .bin), https links, or 'polyhaven:<id>' CC0 assets from asset_search. A .blend finds them through relative paths (same folder); a script through os.environ['JR_ASSETS_DIR'] or the jr_assets helpers." - added
Input schema / properties / blender / descriptionAdded value: +"Blender version: '' (default: a .blend opens with the version it was saved in, other scenes with 5.0) or '5.0' / '5.2' to choose." - added
Input schema / properties / scene_id / descriptionAdded value: +"scene_id returned by an earlier call (reuses the uploaded scene without sending it again)." - added
Input schema / properties / scene_path / descriptionAdded value: +"Local path to the scene: a .blend file, a bpy .py script, or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc, imported into an empty scene). Uploaded once; reuse the returned scene_id afterwards." - added
Input schema / properties / scene_script / descriptionAdded value: +"The scene as bpy Python code (text). No file and no local Blender needed; the service runs it in Blender 5.0 (5.2 with blender='5.2'). Blender 5.0 API notes: materials are node-based, so set a colour on the Principled BSDF (mat.node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value = (r, g, b, 1)); mat.diffuse_color only affects the viewport; do not set Material.use_nodes. Animate with obj.keyframe_insert() (action.fcurves no longer exists; set bpy.context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR' first for constant speed). Build the scene, camera and frame range only; leave resolution, samples and denoising to the service. Helpers: `import jr_assets`, then jr_assets.frame_camera(margin=1.1) points the camera at every object and fits them in the frame; jr_assets.import_model('<id>'), jr_assets.material('<id>') and jr_assets.world_hdri('<id>') use Poly Haven assets passed in assets." - added
Input schema / properties / scene_urlAdded value: +{ + "default": "", + "description": "https link to a .blend, a .py script or a 3D file (.glb/.gltf/.fbx/.usd/.obj/.stl/.ply/.abc); the service fetches it (e.g. the sample https://render.janction.jp/samples/cube_scene.py, or a 12-hour /upload link). A .gltf or .obj also brings its .bin / .mtl / textures from the same folder.", + "title": "Scene Url", + "type": "string" +}
7 tool updates
v0.2.2- Added
asset_search - Changed
render_download1 field changed- added
Input schema / properties / onlyAdded value: +{ + "default": "all", + "title": "Only", + "type": "string" +}
- Changed
render_final13 fields changed- added
Input schema / properties / assetsAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Assets" +} - added
Input schema / properties / blenderAdded value: +{ + "default": "", + "title": "Blender", + "type": "string" +} - added
Input schema / properties / engineAdded value: +{ + "default": "", + "title": "Engine", + "type": "string" +} - added
Input schema / properties / environmentAdded value: +{ + "default": "", + "title": "Environment", + "type": "string" +} - added
Input schema / properties / environment_strengthAdded value: +{ + "default": 1, + "title": "Environment Strength", + "type": "number" +} - added
Input schema / properties / environment_visibleAdded value: +{ + "default": true, + "title": "Environment Visible", + "type": "boolean" +} - added
Input schema / properties / notify_urlAdded value: +{ + "default": "", + "title": "Notify Url", + "type": "string" +} - added
Input schema / properties / orbitAdded value: +{ + "default": false, + "title": "Orbit", + "type": "boolean" +} - added
Input schema / properties / orbit_distanceAdded value: +{ + "default": 1, + "title": "Orbit Distance", + "type": "number" +} - added
Input schema / properties / orbit_elevationAdded value: +{ + "default": 18, + "title": "Orbit Elevation", + "type": "number" +} - added
Input schema / properties / orbit_framesAdded value: +{ + "default": 24, + "title": "Orbit Frames", + "type": "integer" +} - added
Input schema / properties / orbit_targetAdded value: +{ + "default": "", + "title": "Orbit Target", + "type": "string" +} - added
Input schema / properties / transparentAdded value: +{ + "default": false, + "title": "Transparent", + "type": "boolean" +}
- Changed
render_preview12 fields changed- added
Input schema / properties / assetsAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Assets" +} - added
Input schema / properties / blenderAdded value: +{ + "default": "", + "title": "Blender", + "type": "string" +} - added
Input schema / properties / engineAdded value: +{ + "default": "", + "title": "Engine", + "type": "string" +} - added
Input schema / properties / environmentAdded value: +{ + "default": "", + "title": "Environment", + "type": "string" +} - added
Input schema / properties / environment_strengthAdded value: +{ + "default": 1, + "title": "Environment Strength", + "type": "number" +} - added
Input schema / properties / environment_visibleAdded value: +{ + "default": true, + "title": "Environment Visible", + "type": "boolean" +} - added
Input schema / properties / orbitAdded value: +{ + "default": false, + "title": "Orbit", + "type": "boolean" +} - added
Input schema / properties / orbit_distanceAdded value: +{ + "default": 1, + "title": "Orbit Distance", + "type": "number" +} - added
Input schema / properties / orbit_elevationAdded value: +{ + "default": 18, + "title": "Orbit Elevation", + "type": "number" +} - added
Input schema / properties / orbit_framesAdded value: +{ + "default": 24, + "title": "Orbit Frames", + "type": "integer" +} - added
Input schema / properties / orbit_targetAdded value: +{ + "default": "", + "title": "Orbit Target", + "type": "string" +} - added
Input schema / properties / transparentAdded value: +{ + "default": false, + "title": "Transparent", + "type": "boolean" +}
- Added
render_share - Added
render_unshare - Changed
scene_info2 fields changed- added
Input schema / properties / assetsAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Assets" +} - added
Input schema / properties / blenderAdded value: +{ + "default": "", + "title": "Blender", + "type": "string" +}
9 tool updates
v0.2.1- First observed
billing - First observed
render_cancel - First observed
render_download - First observed
render_estimate - First observed
render_final - First observed
render_info - First observed
render_preview - First observed
render_status - First observed
scene_info
TDQS
Scored across 13 tools
Most tools map to clearly distinct stages of the cloud rendering workflow: estimate, preview, review, final render, status, download, cancel, share, info, billing, and asset search. The main ambiguity is between render_preview and render_review, which both return preview images and checks, and between render_status, render_info, and billing, which all report service or account state. Descriptions explicitly differentiate these, so an agent can tell them apart with care.
Almost all tools use consistent snake_case, and most render operations share the predictable render_ prefix. The few deviations are scene_info, asset_search, and billing, which drop the verb prefix or use a different domain noun, but they remain readable and unsurprising. Overall the naming is coherent with minor inconsistencies.
13 tools is well-scoped for a cloud Blender rendering service. The set covers the full workflow from scene inspection and estimation through preview, review, final render, download, cancel, sharing, and billing without obvious bloat. Each tool appears to earn its place.
The tool surface covers the rendering lifecycle comprehensively: scene_info, render_estimate, render_preview, render_review, render_final, render_status, render_download, render_cancel, render_share, render_unshare, render_info, billing, and asset_search. There are no obvious dead ends for the stated cloud rendering domain. Minor gaps like an explicit job-list tool are handled by render_status without job_id.
Maintenance
Related MCP Connectors
Cloud Blender for AI agents: build, inspect, render and animate 3D scenes over remote MCP. Keep editable .blend projects and export GLB or STL. Make your first 3D asset free—30 compute minutes per UTC month, no credit card. First 100 eligible Free account owners to use all 30 minutes in one UTC month by December 31, 2026 UTC receive one calendar month of beta free: 4 compute hours per UTC month, 2 concurrent workers per deployment, 2 deployments, 2048×2048 renders at up to 256 samples, and 10 GiB storage. Existing usage counts toward the 4-hour allowance. Limited to 100 rewards; expires January 1, 2027 00:00 UTC. Free resumes afterward without an automatic charge. Examples, eligibility and terms: https://sceneplane.online/launch?utm_source=glama&utm_medium=directory&utm_campaign=first100
Blender-as-a-service for agents: search 3D assets, run Blender Python, or brief the studio agent.
Remote MCP server exposing your Node Command fleet (files, shell, processes, browser) to AI hosts.
Remote MCP server for SeenThis AI Hub. Supports browsing, searching, and posting to AI boards.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables remote control of Blender through MCP, allowing code execution, scene manipulation, rendering, and file transfer in a containerized environment.1-
- FlicenseNot gradedqualityCmaintenanceEnables asynchronous Blender rendering via MCP, supporting Cycles, Eevee, and Workbench engines, with GPU detection and job management for animations and still frames.-
- AlicenseNot gradedqualityCmaintenanceEnables MCP-compatible clients to control Blender for modeling, materials, animation, physics, rendering, scene inspection, documentation search, and asset/generation service integration. It exposes a stdio endpoint with many Blender operations for creating and editing 3D content.AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to drive a live Blender session over MCP by creating and transforming primitives, building and assigning materials, inspecting scenes and objects, capturing viewport screenshots, and executing arbitrary bpy Python. Commands are relayed over a local TCP bridge to a Blender add-on and executed on Blender's main thread, supporting multiple simultaneous AI clients.MIT


