SketchXFlow bridge
Connects the Blender already installed on your machine to SketchXFlow in the
cloud, so a generated site can use a model you actually built instead of picking
one off a stock shelf.
Your machine Cloud (Cloud Run)
┌──────────────────────────────┐ ┌──────────────────────────┐
│ Blender GUI ── MCP addon :9876│ │ FastAPI │
│ ▲ │ │ /api/bridge/agent (wss)│
│ │ │ wss │ device registry │
│ sketchxflow-bridge run ──────┼────────────►│ job queue │
│ (outbound only) │ ticketed │ page generator │
└──────────────────────────────┘ └──────────────────────────┘
The bridge dials out. Nothing listens on your machine, so there is no port
to forward, no firewall rule, and no inbound surface — which is also why this
works from a laptop on hotel wifi.
Why a bridge and not a plugin
Blender's MCP addon speaks unframed JSON over a loopback TCP socket. A browser
cannot open a raw TCP socket, and the addon is not an HTTP server, so no amount
of CORS makes the SketchXFlow page talk to it directly. Something native has to
sit in between. This is that.
Install and pair
Needs Node 22 or newer (the agent uses Node's own WebSocket, fetch and
FormData and has no runtime dependencies at all).
npm install -g @sketchxflow/bridge-agent
Building from this repo instead (which is the only option until the package is
published):
cd bridge-agent && npm install && npm run build
node dist/index.js pair ABCD-EFGH --url https://your-sketchxflow-api
node dist/index.js run
In SketchXFlow, go to Account → Machines and press Pair a machine. Then,
on the computer with Blender:
sketchxflow-bridge pair ABCD-EFGH --url https://your-sketchxflow-api
sketchxflow-bridge run
pair swaps the code for a long-lived device token and stores it in your
platform's own per-user config directory with 0600 permissions. The cloud
keeps only a salted SHA-256 of it, so a leaked server database cannot be
replayed.
The --url is the API origin, not the website's. The agent holds a
WebSocket open, and the web app's rewrite proxy cannot carry an upgrade — the
account page prints the right one for you.
Check what the bridge can see at any time:
sketchxflow-bridge status
Blender needs the MCP addon
Install blender-mcp and connect it
(sidebar → BlenderMCP → Connect). That addon is what exposes the running
scene on 127.0.0.1:9876.
Without it, builds still run — the agent falls back to
blender --background --factory-startup — but they happen in a scratch process
you cannot see, so a model you tweaked by hand is not the model the site gets.
The GUI is always preferred when it is there.
What runs without asking, and what does not
SketchXFlow's model can write arbitrary Python into your Blender. That is the
point of the bridge, and it is also remote code execution on your workstation,
so the two categories are treated differently:
blender.status, blender.scene, blender.collections, blender.object, blender.screenshot, blender.exportGlb, blender.render | Run unattended. They read your scene; the export writes into the bridge's own work directory. |
blender.build, blender.importGlb | Prompt. They change the scene you have open. |
blender.exec, and any blender.command that is not a read | Prompt, with the payload printed in full. |
────────────────────────────────────────────────────────────────────────
SketchXFlow wants to run Python inside Blender
Why: measuring the bounds of the hero model
────────────────────────────────────────────────────────────────────────
│ import bpy
│ print(bpy.data.objects['Hero'].dimensions)
────────────────────────────────────────────────────────────────────────
[y] run once [a] always allow blender.exec [N] decline :
a is scoped to the method, not the verb, so approving
blender.command:get_scene_info for the session does not silently approve
blender.command:execute_code.
--bypass turns the prompt off:
sketchxflow-bridge run --bypass
Do that only where you trust the whole pipeline into the model. A prompt
injection in an uploaded brand asset or a scraped competitor page reaches the
same tool you do, and with --bypass there is nothing between it and this
machine. The bridge announces the mode at startup and reports it to the cloud,
so the account page shows which machines are unattended.
With no TTY attached (a service, a container) and no --bypass, prompting jobs
are declined rather than silently run.
What it can do
blender.build | Builds a scene from a declarative JSON spec — primitives, materials, modifiers — using scripts/build.py. Everything lands in its own collection, centred and normalised to a known size. |
blender.exportGlb | Exports a collection as a web-ready .glb: Draco compressed, modifiers applied, +Y up, decimated if it blows the byte budget. scripts/export_glb.py. |
blender.render | A framed PNG of one collection, with everything else hidden. Adds its own camera and sun and puts the scene back afterwards. scripts/render.py. |
blender.importGlb | Pulls a .glb from the cloud you are paired with into its own collection. Additive; never resets your file. |
blender.collections | What is in the file, by collection, with mesh counts — filtered to collections actually linked into the view layer, because anything else cannot be exported. This is what the account page's export picker offers. |
blender.scene / blender.object / blender.screenshot | Reads, straight from the addon. |
blender.command | Any addon command, including the optional Polyhaven, Sketchfab, PolyPizza and Hyper3D/Hunyuan handlers when you have them enabled. |
blender.exec | Arbitrary Python. Prompts, always. |
agent.capabilities | What this Blender can do right now — discovered from the addon, so handlers you switched off do not appear and a new addon release does not need a new bridge. |
The three named build verbs run SketchXFlow's own Python, shipped in scripts/
next to dist/. The cloud chooses which script runs and supplies a JSON spec;
it never supplies the source. That is exactly why they can run unattended.
Where files go
<config dir>/bridge-work/out/models/ ← exported .glb
<config dir>/bridge-work/out/renders/ ← rendered .png
<config dir>/bridge-work/out/downloads/ ← anything importGlb fetched
Whatever a job produces is uploaded to SketchXFlow and attached to the job, up
to 64 MB per file. A .glb is additionally registered as one of your models, so
the page generator can rank it against your brief and put it on a page.
Configuration
bridge.json in the config directory, or flags, or environment:
--url, SKETCHXFLOW_URL | API base URL |
SKETCHXFLOW_BRIDGE_TOKEN | Device token, for a container that cannot pair interactively |
--bypass | Run scene changes and Python without prompting |
--workDir | Where builds happen |
--blenderCmd, BLENDER_CMD | Blender executable, if it is somewhere unusual |
--port | Loopback port the MCP addon listens on (default 9876) |
Config directory: %APPDATA%\SketchXFlow · ~/Library/Application Support/SketchXFlow ·
$XDG_CONFIG_HOME/sketchxflow.
Disconnecting a machine
Disconnect it in Account → Machines. The record is tombstoned and any live
socket is closed immediately, so a stolen laptop stops being able to run jobs
the moment the button is pressed, not at the end of some token lifetime.
sketchxflow-bridge unpair only clears the local copy — it does not tell the
cloud to stop trusting it.
Protocol
src/protocol.ts mirrors backend/bridge/protocol.py. The Python is the
authority; both are dependency-free and type-only so neither drags the other's
world in. If they disagree, the Python is right.