DEV Community

JANCTION Render
JANCTION Render

Posted on

Blender rendering from an OpenAI Agents SDK or LangChain agent, and the 5-second MCP timeout that breaks it

Official JANCTION Render: https://render.janction.jp

JANCTION Render is a cloud GPU render farm for Blender that AI agents call as an MCP server or HTTP API. It is free up to 500 JPY of GPU time per new key, then 0.1 JPY per GPU-second. This post is for people building their own agent: how to connect it from the OpenAI Agents SDK, LangChain or plain Python, and the one setting that makes the first call fail.

The error

McpError: Timed out while waiting for response to ClientRequest. Waited 5.0 seconds.
Enter fullscreen mode Exit fullscreen mode

MCPServerStreamableHttp in the OpenAI Agents SDK (0.23.1) waits 5 seconds for a tool call by default (client_session_timeout_seconds=5). A render tool answers when the image exists, not when the job is queued. We called render_preview on the sample scene with four lighting presets, once with the default and once with 120 seconds:

  • default: failed after 5.1 s with the error above
  • 120 s: the image came back after 7.3 s (4.03 GPU-seconds of rendering)

A lighter preview of the same scene (one preset) came back in 4.7 to 5.0 seconds, right at the edge, so the default can look fine in a quick test and then fail on the first real scene. Set the timeout explicitly.

OpenAI Agents SDK

The service is a remote MCP server at https://render.janction.jp/mcp. The key goes in as a bearer token; curl -s -X POST https://render.janction.jp/v1/keys gives one, with no sign-up.

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())
Enter fullscreen mode Exit fullscreen mode

The agent sees 12 tools. The ones it uses in order are scene_info, render_preview, render_estimate, render_final, render_status and render_download. Long renders do not need a longer timeout: a short final returns its links in one call, a longer one returns a job id, and render_download(job_id, wait_seconds=45) waits up to 45 seconds per call. 120 seconds covers every tool.

LangChain

With langchain-mcp-adapters (0.3.2), the same server is one entry in MultiServerMCPClient. The transport name is streamable_http.

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())
Enter fullscreen mode Exit fullscreen mode

Without an agent framework

If your code decides what to render and only needs the frames, the Python client is shorter. 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"))
Enter fullscreen mode Exit fullscreen mode

For a service rather than a script: pass notify_url (https) to POST /v1/jobs and get one JSON POST when the job finishes instead of polling, and send an Idempotency-Key header so a retry does not start a second job.

What it costs an agent

Each new key gets 500 JPY of GPU time (5,000 GPU-seconds) for 14 days, with no card. After that, previews stay free up to 2 GPU-minutes a day and other GPU time is 0.1 JPY per GPU-second. A 24-frame 720p MP4 at 64 samples took about 58 GPU-seconds, about 6 JPY. When a key is out of credit, the job is refused before it runs: the HTTP API answers 402 with a top-up link, and over MCP the tool result carries the same link for a person to open. Each key also has a spending cap per job and per day. More on that: https://dev.to/_903a3934b362f0afe2a8/letting-an-ai-agent-pay-for-renders-without-surprises-free-credit-first-a-top-up-link-spending-3i1m

The same examples, kept current: https://github.com/JasmyLab-JANCTION/janction-render#from-your-own-agent-or-app. A plain-text summary for models: https://render.janction.jp/llms.txt

Official JANCTION Render: https://render.janction.jp (pricing: https://render.janction.jp/pricing, facts: https://render.janction.jp/facts). Code and client: https://github.com/JasmyLab-JANCTION/janction-render

Top comments (0)