@spatialpack/mcp
A Model Context Protocol server that exposes the SpatialPack
glTF / GLB / USDZ analysis + optimization toolkit to any MCP-aware
client (Claude Code, Claude Desktop, Cline, custom agents).
Install + run
npx @spatialpack/mcp --repo-root /path/to/spatialpack-checkout
pnpm --filter @spatialpack/mcp build
node packages/mcp/dist/bin/stdio.js --repo-root .
Add to Claude Code
claude mcp add --transport stdio spatialpack \
-- node /absolute/path/to/packages/mcp/dist/bin/stdio.js \
--repo-root /absolute/path/to/spatialpack
After adding, every Claude Code session in that project can call:
- Tools — 29 total. Core analysis + optimization:
analyze, optimize, recipe_search, lod_bundle,
usdz_export, usdz_validate, compare_images, phash,
phash_distance, canonical_cid, batch_optimize,
compatibility_check, list_presets, explain_budget,
diagnose_asset, cohort_recipes_build, cohort_recipes_lookup.
Cross-asset packaging + verification (2026-05-23):
dedup_textures (SPEC-0091), bundle_glb_zip (SPEC-0091),
unbundle_glb_zip (SPEC-0091), usdc_to_usda (SPEC-0076 Phase H),
safety_gate (SPEC-0090).
- Resources —
spec://NNNN (per-pass design docs),
learnings://current (cross-cutting failure modes),
preset://{web-mobile|web-desktop|quality-max},
target://compatibility-matrix, run-history://recent.
- Prompts —
optimize_for_shopify, find_best_recipe,
diagnose_asset, compare_cohorts.
New tools (2026-05-23)
dedup_textures | Scan N GLBs for byte-identical embedded images, extract any image shared by ≥min_occurrences GLBs into a shared sidecar dir, rewrite GLBs to reference the sidecar via image.uri. Supports dry_run and in_place. Returns per-image and per-GLB savings. | SPEC-0091 |
bundle_glb_zip | Pack a primary GLB plus sidecar files into a single .glb.zip archive with a versioned manifest.json. Auto-DEFLATE per entry. | SPEC-0091 |
unbundle_glb_zip | Inverse: read a .glb.zip and extract the primary GLB + every sidecar (CRC-verified, sandbox-clamped). | SPEC-0091 |
usdc_to_usda | Read a binary USDC file and emit the equivalent USDA text (same conversion as Pixar usdcat). Optional output_path to also write the text. | SPEC-0076 Phase H |
safety_gate | Pure-compute cohort-aware safety gate over per-asset visual-diff metrics (ssim / ΔE94 / edgeDelta). Returns per-asset verdicts + aggregate pass/fail; optional JSON+markdown report write. | SPEC-0090 |
HTTP transport
For hosted / cloud deployment:
node packages/mcp/dist/bin/http.js --port 3037 --token "$BEARER"
Pass the same token as Authorization: Bearer <token> from the
client. Without --token (or $SPATIALPACK_MCP_TOKEN) the server
runs unauthenticated — development only.
Configuration
SPATIALPACK_MCP_SANDBOX | Sandbox root for every file path argument. | os.tmpdir()/spatialpack-mcp-<pid> |
SPATIALPACK_MCP_LOG | `silent | error |
SPATIALPACK_MCP_TOKEN | HTTP transport bearer token. | (unset = unauth) |
PORT | HTTP transport listen port. | 3037 |
Path sandboxing
Every tool that reads / writes the filesystem resolves the user-
provided path through a sandbox check (SandboxViolationError if it
escapes). Set --sandbox (CLI) or SPATIALPACK_MCP_SANDBOX (env)
to point at the directory where the model is allowed to operate
(typically a per-conversation scratch dir or a shared "drop-zone"
under your project tree).
Result cache
Stateless tools (analyze, canonical_cid, optimize with the
same input + options) cache results in-process keyed on a hash of
the tool name + sorted-arg JSON. Cache is per-server-process, LRU,
default 64 entries.
Error envelopes
Every tool wraps its handler in safeRun(). Failures return
isError: true with a structured { error: { code, message, detail? } }
in structuredContent, plus a one-line text summary. Codes:
SANDBOX_VIOLATION, NOT_FOUND, PERMISSION_DENIED, TIMEOUT,
CANCELLED, INTERNAL.
Testing
pnpm --filter @spatialpack/mcp test
The suite spins up the server and an in-process MCP client via
InMemoryTransport.createLinkedPair() and exercises tool listing,
resource reads, sandbox enforcement, error envelopes, and a real
analyze + optimize round-trip on a corpus asset (skipped when the
corpus isn't present locally).