Skip to content

Blender example on the router sandbox, with renders in chat - #737

Merged
Nash0x7E2 merged 20 commits into
acceleratefrom
cursor/blender-mcp-example-9c0c
Oct 3, 2026
Merged

Nash0x7E2 merged 20 commits into
acceleratefrom
cursor/blender-mcp-example-9c0c

Conversation

@tschellenbach

@tschellenbach tschellenbach commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

How it works

flowchart LR
  user["You: render a lighthouse"] --> conv["Conversation model (llm-fast)"]
  conv -->|"ask skill render"| sub["Subagent (llm-thinking)"]
  sub -->|"run_code code + files"| box["Daytona sandbox built from sandbox_options (bpy 5.2)"]
  box -->|"output + render.png"| sub
  sub -->|"publish"| chat["Upload to the conversation's Chat channel"]
  chat -->|"URL"| settled["task_settled files"]
  settled --> reply["Agent reply with an image attachment, kept in Chat"]
Loading

Why

Thierry's decisions on the first version of this PR:

  1. Extend the existing router sandbox (sandbox: daytona) instead of the example running its own Daytona sandbox behind an MCP server. That means long-running jobs, a custom image or setup with Blender, and returning files as well as text. Then switch the example to it.
  2. Leave passing images back to the model alone.
  3. Post renders to chat as image attachments on the agent's reply, persisted so they show on history reload.

Changes

  • Router sandbox. sandbox_options on agent configs (API, sync, patch, store and migration) has image, setup, timeout_ms (at most 30 minutes), cpu, memory_gb and disk_gb. Any of them builds a Daytona image (Base/DebianSlim plus a RUN per setup command), with a 20-minute start timeout for the build. The harness's fixed 60 s code deadline is gone, so a run is bounded by the sandbox timeout and the skill's own deadline.
  • Files from run_code. A new files argument lists paths, at most 4 of up to 20 MiB each. Daytona downloads them, and the harness publishes them and tells the subagent what was attached, missing or not shown. A task's result carries the latest file of each name.
  • Chat attachments. Conversation.Publish uploads to the channel as the agent (UploadChannelImage or UploadChannelFile). On task_settled the files go onto the reply's Chat attachments (image with image_url, or file with asset_url), and history reads them back as files. The task_settled frame gains files.
  • SDKs. Go reads sandbox_options: in agent.yaml (with timeout: 5m) and Event.Files; Python has SandboxSettings and RemoteEvent.files. Only the models this needs were regenerated in the Python client, to stay clear of Sentry and Google Calendar plugins, user logins in chat #736's connector regeneration. The SDK skill notes what the other SDKs still need.
  • Example. blender_artist is now agent.yaml (sandbox_options installs Blender's libraries and bpy==5.2.2, 5 min runs, 2 CPU / 4 GiB), a render skill (20 min deadline, a starting program with camera framing, files=["/tmp/render.png"]), and instructions. Its MCP server and Daytona code are deleted.
  • Kept from before: Agent.ask() waits on pending_work, and MCPServerLocal holds its session on one task.
Change Test
Built image, sizes, timeout, ceiling DaytonaSuite: TestASetupIsBuiltOnTheImageItNames, TestARunMayTakeAsLongAsTheConfigAllows, TestNoRunMayTakeLongerThanTheCeiling
Files downloaded, missing, capped TestFilesTheCodeWroteComeBackWithItsOutput, TestAFileTheCodeDidNotWriteIsReportedMissing, TestOnlySoManyFilesComeBackFromOneRun
Harness publishes and reports files HarnessSuite: TestFilesTheCodeHandsBackAreAttachedToTheAnswer and four more
Attachment on the reply and on reload TestARenderIsAttachedToTheReplyAndComesBackWithItsHistory (fake Chat), TestARenderReachesStreamChatAndSurvivesAReload (real Stream Chat, downloads the CDN URL)
Config round trip and bounds ConfigsSuite and SyncSuite (integration, Postgres)
SDK loaders and events Go TestSyncSendsHowTheSandboxIsBuilt, TestASettledTaskCarriesTheFilesItsCodeMade; Python test_how_the_sandbox_is_built_is_read_from_a_nested_block, test_work_that_made_a_file_says_where_it_was_put

Evidence

  • Router with a fake Daytona key. The sync stored sandbox_options, the agent delegated to render, and the subagent's run_code went to Daytona with the build request and got 401 Invalid credentials. The agent reported it.
  • Router with the sandbox temporarily swapped for local bpy. This is uncommitted debug code, since removed. The subagent wrote the scene, Blender rendered it, and the router uploaded render.png to Stream's CDN, sent it in task_settled and attached it to the reply. GET /v1/agents/conversations/{cid}/messages returned it under files:

The agent

  • Unverified: a real Daytona image build and render (no DAYTONA_API_KEY here).
  • Go: go test ./... passes in acceleration/ and sdks/go/. Integration runs of api, conversation, store, session and harness pass, except TestPhoneSuite/TestSearchingEveryVendorSaysNoneCanBeAsked, which fails because this environment has real Twilio and Telnyx credentials, and one data-move test that passed on two reruns.
  • dev.py check (with UV_FROZEN=1): ruff and mypy are clean, 1,660 passed. The 44 errors are the Docker-only Redis tests, as on accelerate.

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
cursoragent and others added 4 commits October 3, 2026 20:44
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
@cursor cursor Bot changed the title feat(examples): Blender MCP renders on Daytona Blender MCP example, with ask() and MCPServerLocal fixes Oct 3, 2026
cursoragent and others added 7 commits October 3, 2026 21:15
sandbox_options on agent configs (image, setup, timeout_ms, cpu, memory_gb,
disk_gb) builds the Daytona sandbox from an image and lets one run of code take
up to 30 minutes. run_code takes files: paths the program wrote that are
downloaded, uploaded to the conversation's channel and attached to the reply
that settles the work, so they are there when history is reloaded.
task_settled lists them.

Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
agent.yaml builds Blender into the Daytona sandbox through sandbox_options, a
render skill writes the scene, and the PNG comes back as a run_code file that
the router attaches to the reply. The example's own MCP server and Daytona
code are gone.

Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
…reload

Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
…cenes

Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
@cursor cursor Bot changed the title Blender MCP example, with ask() and MCPServerLocal fixes Blender example on the router sandbox, with renders in chat Oct 3, 2026
cursoragent and others added 8 commits October 3, 2026 22:58
…gone

The 99 operations still generated by oapi-codegen from api/legacy.yaml are
registered with Huma in the file that serves each, with their request and
response types as Go structs documented by tags. The ten routes served by
hand, the sockets, the log stream, the data export and import and the
plugin callback, are declared in handwritten.go, so the server-side check
reads their marks like any other operation's and unspecifiedRoutes goes.
legacy.yaml, oapi-codegen.yaml, generated.go and the oapi-codegen tool
are deleted, so there is nothing left to add an operation to but Go.

The rendered spec is what the merged one was: the same operations,
parameters, bodies, statuses, enums, defaults, formats and words. Huma
documents the 400 its validation answers on eight operations that did
not list one, and the guest 403 now carries the usual error body.

Huma checks a request against its schema before the handler runs, where
the generated server only decoded it, so a body breaking the documented
schema is refused with Huma's message rather than the handler's.

Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
…ed in Go

legacy.yaml and the server's generated.go are gone, so SandboxOptions and sandbox_options on
AgentConfig and AgentConfigRequest are Huma schemas now, and the task_settled files are in the
watch socket's description. The spec and clients are regenerated from Go.

Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
…mcp-example-9c0c

# Conflicts:
#	acceleration/internal/api/handwritten.go
#	acceleration/internal/api/schemas.go

Co-authored-by: Thierry Schellenbach <thierryschellenbach@gmail.com>
@tschellenbach
tschellenbach marked this pull request as ready for review October 3, 2026 23:13
@Nash0x7E2
Nash0x7E2 merged commit 48d75b9 into accelerate Oct 3, 2026
17 checks passed
@Nash0x7E2
Nash0x7E2 deleted the cursor/blender-mcp-example-9c0c branch October 3, 2026 23:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants