Skip to content

Repository files navigation

ModelSyncBridge

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

Go MCP Zero CGO License CI Docker

中文版见 README.zh.md


Highlights

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 ⚠️ stub · Mock ✅ (built-in, no key needed)


Architecture

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)

Quick Start

Option A — Docker

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:latest

Or with docker compose: export MSB_AUTH_API_KEYS=... && docker compose up -d

Option B — Build from source

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.exe

Remote Deployment (remote Agents, no clone needed)

ModelSyncBridge 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.


Local MCP Collaboration Workflow

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


MCP Server (:8081)

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_path for 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.


Showcase

Image-to-3D

Input Output
Image-to-3D input Image-to-3D output

Version Iteration (Options Tuning)

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"}
v1.0 v2.0 v3.0

Image-to-3D


Documentation

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


API Reference

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

options — upstream generation parameters

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


Project Layout

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

License

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.

About

Unified AI 3D generation gateway for multi-vendors. Support REST SDK and MCP-SSE, natively for 3D software plugins and AI Agents.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages