Go backend middleware that unifies the world's AI 3D-generation vendor APIs behind a single standard interface — REST OpenAPI, MCP Streamable HTTP, and a first-party Go SDK.
Zero frontend · Zero CGO · Single binary · Built-in SQLite
中文版见 README.zh.md
One gateway, three surfaces — the same capability exposed however your consumers prefer:
| Interface | Protocol / Port | Who consumes it |
|---|---|---|
| REST OpenAPI | HTTP/JSON · :8080 |
Any HTTP client, curl, your backend |
| MCP Streamable HTTP | MCP 2025-11-25 · :8081 |
AI Agents — Cursor / Claude Code / Trae / Cline |
| Go SDK | Go module (sdk/) |
Go developers embedding in code |
Production-grade plumbing built in: SmartScheduler (weighted routing + circuit breaker) · KeyRotator (multi-key pool, 401→ban / 429→cooldown, auto-recover) · TaskService (async lifecycle, batch ≤100, dedup, webhook, retry, poll failover — auto-migrates to another provider when the current one fails mid-generation) · Data lifecycle cleanup — local model files expire after 6h (configurable), finished task records are pruned after 7 days · Ops alerting — circuit-open / task-failed / bulk-timeout pushed to DingTalk / WeCom / Slack / self-built webhook · Per-IP + Per-Key rate limiting · Metrics / Tracing / JSON logs · SQLite (pure Go, zero CGO) · Artifacts — the task-finish handover directory that lets local MCP servers refine generated models without any protocol bridging
Supported providers: Meshy ✅ · Tripo3D ✅ · Hyper3D ✅ · Neural4D ✅ · WorldLabs ✅ · Luma
REST(:8080) │ MCP(:8081) │ SDK
└ Access Key Auth │ Per-IP / Per-Key Rate Limit │ Logging
└ TaskService: async lifecycle · batch · dedup · webhook · poll failover
└ Cleaner: file TTL (6h) · task retention (7d) ── background sweep
└ SmartScheduler: weighted (success×0.5 + speed×0.3 + priority×0.2) · circuit breaker
└ Provider Adapter: Meshy·Tripo3D·Hyper3D·Neural4D·WorldLabs·Luma(stub)·Mock
└ KeyRotator: multi-key pool · 401→ban · 429→5min · 5×5xx→1min · lazy recover
└ SQLite (zero CGO) · local file cache (models / previews)
└ Alerter: circuit-open / task-failed / bulk-timeout → Webhook (DingTalk·WeCom·Slack)
docker run -d --name modelsyncbridge \
-p 8080:8080 -p 8081:8081 \
-e MSB_AUTH_ENABLE_API_KEY=true \
-e MSB_AUTH_API_KEYS=sk-your-access-key \
-e MSB_SERVER_PUBLIC_URL=http://localhost:8080 \
ghcr.io/halor-qu/modelsyncbridge:latestOr with docker compose: export MSB_AUTH_API_KEYS=... && docker compose up -d
git clone https://github.com/HaloR-Qu/ModelSyncBridge.git && cd ModelSyncBridge
go build -o modelsyncbridge.exe ./cmd/server/ && ./modelsyncbridge.exe| Service | URL |
|---|---|
| REST | http://localhost:8080 |
| MCP | http://localhost:8081/mcp (Streamable HTTP) · http://localhost:8081/sse (legacy SSE) |
Smoke test:
curl http://localhost:8080/v1/3d/health
curl -X POST http://localhost:8080/v1/3d/task/generate \
-H "Content-Type: application/json" \
-d '{"prompt": "a cute cat in a spacesuit"}'Enable a real provider (Mock is built-in, no key needed):
MSB_PROVIDER_MESHY_ENABLED=true MSB_PROVIDER_MESHY_API_KEYS="sk-primary,sk-backup" ./modelsyncbridge.exeModelSyncBridge speaks MCP Streamable HTTP — a remote protocol. Once deployed to a public HTTPS endpoint, any MCP client just configures a URL (no clone/build on their side):
{"mcpServers": {"modelsyncbridge": {"url": "https://mcp.example.com/mcp", "headers": {"Authorization": "Bearer sk-your-access-key"}}}}CI auto-builds multi-arch images (amd64+arm64) to GHCR: docker pull ghcr.io/halor-qu/modelsyncbridge:latest. Caddy/Nginx adds HTTPS; or deploy to Railway / Fly.io / Render. Full guide: docs/deployment.md.
ModelSyncBridge is the "factory that finds production"; your local MCP servers (blender-mcp / trident-mcp / ...) are the "workshops that refine". Register both in one client and let the Agent orchestrate — no MCP-to-MCP bridging, the file system is the shared language:
Agent(dispatcher) → ModelSyncBridge(generate) ──► artifacts/ (manifest.json + model.glb)
│
└── local_path ──► blender-mcp / trident-mcp (refine in-scene)
After a task downloads, the bridge writes a handover directory (storage.artifacts_dir, default ./artifacts): t_<task_id>/model.glb + preview.png + manifest.json, plus latest.json pointing to the newest artifact. The download_3d_model_content MCP tool returns local_path (absolute path, no base64 round-trip) so the Agent can hand the file straight to a local MCP:
bpy.ops.import_scene.gltf(filepath=r'<local_path>')5-minute hands-on tutorial & conventions: docs/workflow.md
Implements MCP 2025-11-25 Streamable HTTP (compatible with 2024-11-05 SSE): initialize / tools/list / tools/call / resources/* / prompts/* / ping / server/discover.
- 6 tools:
generate_3d_model·query_3d_task·query_3d_tasks·cancel_3d_task·list_3d_provider·download_3d_model_content(base64 ≤20MB +local_pathfor local-MCP handoff) - 3 resources (read-only context):
providers/list·tools/guide·workflow/overview - 3 prompts (parameterized templates):
generate-3d-from-text·generate-3d-from-image·polling-strategy
Health GET /mcp/health · discovery POST /mcp/discover. Client setup: docs/mcp-clients.md.
| Input | Output |
|---|---|
![]() |
![]() |
The same prompt can produce progressively higher-quality results by tuning options parameters across model versions:
| v1.0 Base parameters (no texture/pbr) |
v2.0{"texture": true, "pbr": true, "texture_quality": "high"} |
v3.0{"texture": true, "pbr": true, "texture_quality": "extreme", "geometry_quality": "detailed"} |
|---|---|---|
![]() |
![]() |
![]() |
Image-to-3D
| Doc | Description |
|---|---|
| docs/workflow.md | Workflow: 5-min tutorial (generate → local_path → Blender refine) + artifacts convention, import templates, checklist |
| docs/deployment.md | Deployment & config: Docker/compose/GHCR, HTTPS proxy, PaaS, remote MCP, ENV/YAML reference, KeyRotator |
| docs/mcp-clients.md | MCP setup for Cursor / Claude Code / Trae / VS Code / Cline |
| docs/api-reference.md | Full API reference: request/response field tables, request formats, per-provider options |
| docs/sdk.md | Go SDK guide: init, all methods, retry/timeout, error handling |
Chinese versions: api-reference.zh.md · workflow.zh.md · deployment.zh.md · mcp-clients.zh.md · sdk.zh.md
All endpoints are prefixed /v1/3d; auth Authorization: Bearer <key> (or X-API-Key: <key>); /v1/3d/health & /metrics public. Envelope {"code","message","data"}. Generation is async: submit → task_id → poll GET /task/query until terminal state.
| Method | Endpoint | Request Body | Description |
|---|---|---|---|
| POST | /v1/3d/task/generate |
{"prompt":string, "negative_prompt?":string, "reference_image_url?":string, "provider?":string, "model?":string, "options?":object, "webhook_url?":string, "extra?":object} |
Submit generation task; image-to-3D when reference_image_url present |
| POST | /v1/3d/task/batch-generate |
{"requests":[...]} |
Batch ≤100, each item same as /task/generate |
| GET | /v1/3d/task/query?task_id= |
— | Query status, progress, model & preview URLs |
| POST | /v1/3d/task/cancel |
{"task_id":string} |
Cancel a running task |
| GET | /v1/3d/health |
— | Liveness probe |
| GET | /v1/3d/provider/list |
— | List providers + supported models |
| GET | /v1/3d/model/download/{task_id}?type=model|preview |
— | Download local file (stream) |
| GET | /v1/3d/stats/summary · /v1/3d/stats/providers · /v1/3d/stats/provider-scores |
— | Stats & circuit-breaker state |
| GET | /metrics |
— | Prometheus metrics |
Text-to-3D example:
curl -X POST http://localhost:8080/v1/3d/task/generate \
-H "Authorization: Bearer sk-xxx" -H "Content-Type: application/json" \
-d '{"prompt": "a cute cat in a spacesuit", "model": "hyper3d-rodin-1.5"}'Passing model routes only to providers that declare it (see /provider/list); empty model = scheduler auto-pick. Image-to-3D: add reference_image_url.
Full field references (request & response) for every endpoint: docs/api-reference.md
Each provider exposes upstream-specific generation knobs. Put them in the top-level options object; the bridge forwards them to the upstream in the format that provider's API expects (nested options object for Tripo3D, form fields for Hyper3D, flattened JSON for Meshy / Neural4D / WorldLabs). options also participates in request dedup, so different options never hit a stale cache.
REST example with options:
curl -X POST http://localhost:8080/v1/3d/task/generate \
-H "Authorization: Bearer sk-xxx" -H "Content-Type: application/json" \
-d '{
"prompt": "an ancient temple, low-poly",
"model": "v3.1-20260211",
"options": {
"texture": true,
"pbr": true,
"quad": false,
"texture_quality": "extreme",
"geometry_quality": "detailed"
}
}'Image-to-3D: same as above but with "reference_image_url": "https://..." (or "mode": "image" + "image" in the WorldLabs-style request).
MCP (generate_3d_model):
{"prompt": "an ancient temple", "model": "v3.1-20260211", "options": {"texture": true, "pbr": true, "quad": false}}Go SDK:
req := &sdk.GenerateRequest{
Prompt: "an ancient temple",
Model: "v3.1-20260211",
Options: map[string]interface{}{
"texture": true,
"pbr": true,
"quad": false,
"texture_quality": "extreme",
"geometry_quality": "detailed",
},
}The complete per-provider options parameter table (all supported fields, types, defaults, mapping rules): docs/api-reference.md. extra remains available as a generic passthrough map (forwarded verbatim); prefer options for structured upstream knobs.
Webhook: on terminal state POST to webhook_url (header X-Webhook-Source: ModelSyncBridge, exponential-backoff retry, default 3). Dedup: enabled by default (task.enable_dedup, TTL 1h) — identical requests hit the cache, saving provider quota.
Error codes: AUTH_FAILED 401 · RATE_LIMITED 429 · INVALID_PARAM 400 · TASK_NOT_FOUND 404 · PROVIDER_NOT_FOUND / PROVIDER_DISABLED 404 · NO_AVAILABLE_PROVIDER 503 · PROVIDER_ERROR 503 · TASK_TIMEOUT 400 · TASK_ALREADY_CANCELED / TASK_ALREADY_FINISHED 409 · INTERNAL_ERROR 500
ModelSyncBridge/
├── cmd/server/main.go # dual-port entry
├── config/config.yaml # runtime config (ENV overrides)
├── sdk/ # first-party Go SDK
├── internal/
│ ├── config/ model/ store/ # config loader · core types · SQLite persistence
│ ├── vendor/ # adapter interface + manager + httpclient + keyrotator
│ │ # + meshy/tripo3d/hyper3d/neural4d/worldlabs/luma/mock
│ ├── service/ # TaskService · Scheduler · RateLimiter · Downloader · Artifacts
│ ├── rest/ # REST API (:8080)
│ └── mcp/ # MCP Streamable HTTP (:8081)
└── go.mod # Go 1.22, zero CGO, zero heavy deps
GNU Affero General Public License v3.0 (AGPL-3.0) — any derivative work or modification distributed or offered as a network service must be released under AGPL-3.0 as well.




