threenative-asset-mcp 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -875
- package/dist/audio/generate.d.ts.map +1 -1
- package/dist/audio/generate.js +3 -2
- package/dist/audio/generate.js.map +1 -1
- package/dist/audio/inspect.d.ts +1 -1
- package/dist/audio/inspect.d.ts.map +1 -1
- package/dist/audio/inspect.js +2 -1
- package/dist/audio/inspect.js.map +1 -1
- package/dist/audio/semantic.d.ts +7 -1
- package/dist/audio/semantic.d.ts.map +1 -1
- package/dist/audio/semantic.js +63 -3
- package/dist/audio/semantic.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/fab/fabcli.d.ts +20 -1
- package/dist/fab/fabcli.d.ts.map +1 -1
- package/dist/fab/fabcli.js +46 -11
- package/dist/fab/fabcli.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +31 -5
- package/dist/server.js.map +1 -1
- package/dist/tools/audio.d.ts +2 -0
- package/dist/tools/audio.d.ts.map +1 -1
- package/dist/tools/audio.js +10 -5
- package/dist/tools/audio.js.map +1 -1
- package/dist/tools/download-free-asset.d.ts +1 -1
- package/dist/tools/download-free-asset.d.ts.map +1 -1
- package/dist/tools/import-fab-unity.d.ts +118 -0
- package/dist/tools/import-fab-unity.d.ts.map +1 -0
- package/dist/tools/import-fab-unity.js +90 -0
- package/dist/tools/import-fab-unity.js.map +1 -0
- package/dist/tools/import-unity.d.ts +345 -0
- package/dist/tools/import-unity.d.ts.map +1 -0
- package/dist/tools/import-unity.js +215 -0
- package/dist/tools/import-unity.js.map +1 -0
- package/dist/tools/import-unreal.d.ts +449 -1
- package/dist/tools/import-unreal.d.ts.map +1 -1
- package/dist/tools/import-unreal.js +153 -41
- package/dist/tools/import-unreal.js.map +1 -1
- package/dist/tools/unity-packages.d.ts +43 -0
- package/dist/tools/unity-packages.d.ts.map +1 -0
- package/dist/tools/unity-packages.js +43 -0
- package/dist/tools/unity-packages.js.map +1 -0
- package/dist/unity/blender-script.d.ts +10 -0
- package/dist/unity/blender-script.d.ts.map +1 -0
- package/dist/unity/blender-script.js +72 -0
- package/dist/unity/blender-script.js.map +1 -0
- package/dist/unity/blender.d.ts +30 -0
- package/dist/unity/blender.d.ts.map +1 -0
- package/dist/unity/blender.js +166 -0
- package/dist/unity/blender.js.map +1 -0
- package/dist/unity/cache.d.ts +20 -0
- package/dist/unity/cache.d.ts.map +1 -0
- package/dist/unity/cache.js +58 -0
- package/dist/unity/cache.js.map +1 -0
- package/dist/unity/errors.d.ts +8 -0
- package/dist/unity/errors.d.ts.map +1 -0
- package/dist/unity/errors.js +12 -0
- package/dist/unity/errors.js.map +1 -0
- package/dist/unity/fidelity.d.ts +179 -0
- package/dist/unity/fidelity.d.ts.map +1 -0
- package/dist/unity/fidelity.js +394 -0
- package/dist/unity/fidelity.js.map +1 -0
- package/dist/unity/gltf-materials.d.ts +25 -0
- package/dist/unity/gltf-materials.d.ts.map +1 -0
- package/dist/unity/gltf-materials.js +87 -0
- package/dist/unity/gltf-materials.js.map +1 -0
- package/dist/unity/importer.d.ts +146 -0
- package/dist/unity/importer.d.ts.map +1 -0
- package/dist/unity/importer.js +538 -0
- package/dist/unity/importer.js.map +1 -0
- package/dist/unity/inventory.d.ts +10 -0
- package/dist/unity/inventory.d.ts.map +1 -0
- package/dist/unity/inventory.js +30 -0
- package/dist/unity/inventory.js.map +1 -0
- package/dist/unity/materials.d.ts +102 -0
- package/dist/unity/materials.d.ts.map +1 -0
- package/dist/unity/materials.js +385 -0
- package/dist/unity/materials.js.map +1 -0
- package/dist/unity/model-ids.d.ts +38 -0
- package/dist/unity/model-ids.d.ts.map +1 -0
- package/dist/unity/model-ids.js +128 -0
- package/dist/unity/model-ids.js.map +1 -0
- package/dist/unity/package.d.ts +38 -0
- package/dist/unity/package.d.ts.map +1 -0
- package/dist/unity/package.js +249 -0
- package/dist/unity/package.js.map +1 -0
- package/dist/unity/parity.d.ts +54 -0
- package/dist/unity/parity.d.ts.map +1 -0
- package/dist/unity/parity.js +107 -0
- package/dist/unity/parity.js.map +1 -0
- package/dist/unity/prefab-glb.d.ts +59 -0
- package/dist/unity/prefab-glb.d.ts.map +1 -0
- package/dist/unity/prefab-glb.js +277 -0
- package/dist/unity/prefab-glb.js.map +1 -0
- package/dist/unity/prefab.d.ts +89 -0
- package/dist/unity/prefab.d.ts.map +1 -0
- package/dist/unity/prefab.js +512 -0
- package/dist/unity/prefab.js.map +1 -0
- package/dist/unity/settings.d.ts +29 -0
- package/dist/unity/settings.d.ts.map +1 -0
- package/dist/unity/settings.js +107 -0
- package/dist/unity/settings.js.map +1 -0
- package/dist/unity/textures.d.ts +80 -0
- package/dist/unity/textures.d.ts.map +1 -0
- package/dist/unity/textures.js +331 -0
- package/dist/unity/textures.js.map +1 -0
- package/dist/unity/yaml.d.ts +65 -0
- package/dist/unity/yaml.d.ts.map +1 -0
- package/dist/unity/yaml.js +122 -0
- package/dist/unity/yaml.js.map +1 -0
- package/dist/unreal/contact-sheet.d.ts +13 -0
- package/dist/unreal/contact-sheet.d.ts.map +1 -1
- package/dist/unreal/contact-sheet.js +31 -16
- package/dist/unreal/contact-sheet.js.map +1 -1
- package/dist/unreal/fidelity-baseline.d.ts +2 -0
- package/dist/unreal/fidelity-baseline.d.ts.map +1 -1
- package/dist/unreal/fidelity-baseline.js +1 -0
- package/dist/unreal/fidelity-baseline.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,50 +1,67 @@
|
|
|
1
1
|
# threenative-asset-mcp
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/threenative-asset-mcp)
|
|
4
|
+
[](package.json)
|
|
5
|
+
[](https://github.com/ThreeNativeHQ/threenative-asset-mcp/actions/workflows/ci.yml)
|
|
6
|
+
|
|
7
|
+
A [Model Context Protocol](https://modelcontextprotocol.io/) server that lets AI clients find, inspect and
|
|
8
|
+
download game-ready 3D assets, textures, HDRIs, animations and audio. It gives each provider structured
|
|
9
|
+
search, metadata and guarded downloads, and it converts owned Unreal/Fab packs and Unity Asset Store packages to GLB.
|
|
10
|
+
|
|
11
|
+
> **Status: experimental.** Fab's `/i/*` JSON routes are undocumented and can change or restrict automated
|
|
12
|
+
> access. Sketchfab licenses vary per model. Review each provider's terms and each asset's license.
|
|
13
|
+
> Unreal and Unity support cover specific formats and verified cases; they do not certify whole packs.
|
|
14
|
+
> Unity imports are verified on three real packs; real Unity Editor reference renders are not yet available.
|
|
15
|
+
> Blendkit is optional and needs documented provider permission. Live Blendkit downloads remain **UNVERIFIED**.
|
|
16
|
+
|
|
17
|
+
## Contents
|
|
18
|
+
|
|
19
|
+
| I want to… | Read |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| Install it and connect my MCP host | [Install](#install) and [Host configuration](#mcp-host-configuration) below |
|
|
22
|
+
| See every tool and how to chain them | [docs/tools.md](docs/tools.md) |
|
|
23
|
+
| Generate or inspect game audio | [docs/audio.md](docs/audio.md) |
|
|
24
|
+
| Import an Unreal / Fab pack as GLB | [docs/unreal-import.md](docs/unreal-import.md) |
|
|
25
|
+
| Import a Unity Asset Store package as GLB | [docs/unity-import.md](docs/unity-import.md) |
|
|
26
|
+
| Check which Unreal formats work | [docs/unreal-format-support.md](docs/unreal-format-support.md) |
|
|
27
|
+
| Check which Unity formats work | [docs/unity-format-support.md](docs/unity-format-support.md) |
|
|
28
|
+
| Load imported assets in Three.js | [docs/unreal-threejs-usage.md](docs/unreal-threejs-usage.md) |
|
|
29
|
+
| Read what each importer version changed | [docs/unreal-importer-history.md](docs/unreal-importer-history.md) |
|
|
30
|
+
| Set environment variables | [docs/configuration.md](docs/configuration.md) |
|
|
31
|
+
| Understand provider rules, caching and TLS | [docs/providers.md](docs/providers.md) |
|
|
32
|
+
| Fix an error, or learn what the browser profile touches | [docs/troubleshooting.md](docs/troubleshooting.md) |
|
|
33
|
+
| Run the tests and CI checks | [docs/development.md](docs/development.md) |
|
|
34
|
+
| Read the Fab API contract notes | [docs/fab-contract.md](docs/fab-contract.md) |
|
|
35
|
+
| Use Blendkit (optional, permission required) | [docs/blendkit-integration.md](docs/blendkit-integration.md) |
|
|
36
|
+
|
|
37
|
+
## Supported providers
|
|
38
|
+
|
|
39
|
+
| Provider | Content | License | Needs |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| [Fab](https://www.fab.com/) | 3D, free-file downloads, owned Unreal packs | Per listing | FabCLI login for owned packs only |
|
|
42
|
+
| [Poly Haven](https://polyhaven.com/) | HDRIs, textures, models | CC0 (API use needs a visible credit) | Nothing |
|
|
43
|
+
| [ambientCG](https://ambientcg.com/) | Materials, HDRIs, decals, models | CC0 | Nothing |
|
|
44
|
+
| [Smithsonian 3D](https://3d.si.edu/) | Open Access scanned models | Open Access | Nothing |
|
|
45
|
+
| [Sketchfab](https://sketchfab.com/) | Public models | Varies per model | `SKETCHFAB_API_TOKEN` for downloads only |
|
|
46
|
+
| [Kenney](https://kenney.nl/) | 3D packs, audio | CC0 | Nothing |
|
|
47
|
+
| [itch.io](https://itch.io/) (curated) | Quaternius animations, KayKit, Brackeys VFX, Tallbeard music | CC0 | Nothing |
|
|
48
|
+
| [Blendkit](docs/blendkit-integration.md) (optional) | Free models through your own Blendkit-Client | Private use only, never redistributed | Local Client plus `BLENDKIT_PROVIDER_PERMISSION` |
|
|
49
|
+
| Game audio catalog | Sonniss, Kenney, Tallbeard, Scott Buckley, Mixkit, Pixabay, Freesound, OpenGameArt, Abstraction | Per source | `ELEVENLABS_API_KEY` for generation only |
|
|
50
|
+
|
|
51
|
+
Fab search defaults to free assets, meaning at least one license tier is free. Call `fab_get_asset` before
|
|
52
|
+
making license or price claims. More in [docs/providers.md](docs/providers.md).
|
|
31
53
|
|
|
32
54
|
## Requirements
|
|
33
55
|
|
|
34
56
|
- Node.js 20.19 or newer
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- **FFmpeg and FFprobe on `PATH`** for `audio_inspect_asset` and
|
|
44
|
-
`audio_generate_sound`. Without them nothing is decoded, and the tools report
|
|
45
|
-
that they could not check rather than reporting a pass.
|
|
46
|
-
- An **ElevenLabs API key of your own** for `audio_generate_sound` only. Every
|
|
47
|
-
other tool, including local audio inspection, works without one.
|
|
57
|
+
- Playwright Chromium, only when Fab requests browser verification
|
|
58
|
+
- FFmpeg and FFprobe on `PATH` for the audio tools
|
|
59
|
+
- Unreal imports on Linux also need build packages for older packs. Run `npm run doctor` for the list.
|
|
60
|
+
See [docs/unreal-import.md](docs/unreal-import.md).
|
|
61
|
+
- Unity imports need Blender 4.5 LTS to read FBX models. The MCP downloads a pinned copy into its toolchain cache on first use.
|
|
62
|
+
See [docs/unity-import.md](docs/unity-import.md).
|
|
63
|
+
|
|
64
|
+
No Epic or Fab login is needed for search and free downloads. The MCP never signs in for you.
|
|
48
65
|
|
|
49
66
|
## Install
|
|
50
67
|
|
|
@@ -59,21 +76,13 @@ For a local checkout:
|
|
|
59
76
|
```bash
|
|
60
77
|
npm ci
|
|
61
78
|
npm run browser:install
|
|
62
|
-
npm run typecheck
|
|
63
|
-
npm test
|
|
64
79
|
npm run build
|
|
65
80
|
node dist/index.js
|
|
66
81
|
```
|
|
67
82
|
|
|
68
|
-
Playwright does not download Chromium as part of a normal package install. Run
|
|
69
|
-
`npx -p playwright@1.62.0 playwright install chromium` once on the MCP host
|
|
70
|
-
before relying on the browser fallback.
|
|
71
|
-
|
|
72
83
|
## MCP host configuration
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Add this to `~/.codex/config.toml`:
|
|
85
|
+
Codex (`~/.codex/config.toml`):
|
|
77
86
|
|
|
78
87
|
```toml
|
|
79
88
|
[mcp_servers.assets]
|
|
@@ -81,17 +90,7 @@ command = "npx"
|
|
|
81
90
|
args = ["-y", "threenative-asset-mcp"]
|
|
82
91
|
```
|
|
83
92
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
```toml
|
|
87
|
-
[mcp_servers.assets]
|
|
88
|
-
command = "node"
|
|
89
|
-
args = ["/absolute/path/to/threenative-asset-mcp/dist/index.js"]
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Claude Desktop
|
|
93
|
-
|
|
94
|
-
Add a server entry to the Claude Desktop configuration:
|
|
93
|
+
Claude Desktop:
|
|
95
94
|
|
|
96
95
|
```json
|
|
97
96
|
{
|
|
@@ -104,9 +103,7 @@ Add a server entry to the Claude Desktop configuration:
|
|
|
104
103
|
}
|
|
105
104
|
```
|
|
106
105
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Create `.vscode/mcp.json`:
|
|
106
|
+
VS Code (`.vscode/mcp.json`):
|
|
110
107
|
|
|
111
108
|
```json
|
|
112
109
|
{
|
|
@@ -120,834 +117,72 @@ Create `.vscode/mcp.json`:
|
|
|
120
117
|
}
|
|
121
118
|
```
|
|
122
119
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
## MCP tools
|
|
126
|
-
|
|
127
|
-
Fab:
|
|
128
|
-
|
|
129
|
-
- `fab_search_assets` — searches public listings. `priceMode` defaults to
|
|
130
|
-
`free`; use `any` or `range` explicitly for paid results. Search payloads
|
|
131
|
-
carry no file formats, so there is no format filter; `fab_get_asset` reports
|
|
132
|
-
a listing's formats.
|
|
133
|
-
- `fab_get_asset` — returns normalized public listing details and per-license
|
|
134
|
-
effective prices.
|
|
135
|
-
- `fab_list_filters` — returns known public filter labels and slugs, including
|
|
136
|
-
an explicit warning when the versioned fallback is used.
|
|
137
|
-
- `fab_list_limited_time_free` — reads only a separately verified curated
|
|
138
|
-
promotion surface. In production it extracts canonical listing UUIDs from
|
|
139
|
-
Fab's public `/limited-time-free` page through the dedicated browser, then
|
|
140
|
-
resolves them through the normal detail client; it never substitutes general
|
|
141
|
-
`is_free=1` search.
|
|
142
|
-
- `fab_download_free_asset` — downloads one directly available free file into
|
|
143
|
-
the dedicated download directory after explicit Fab EULA acknowledgement. It
|
|
144
|
-
refuses purchase, acquisition, library-only, ambiguous, and unsafe-path
|
|
145
|
-
flows.
|
|
146
|
-
- `asset_import_unreal` — converts an already-downloaded Unreal directory to
|
|
147
|
-
self-contained GLBs plus `import-report.json`; Unreal Engine is not required.
|
|
148
|
-
Cascade `ParticleSystem` and Niagara systems are exported as data-only
|
|
149
|
-
descriptors under `Effects/<system>-<hash>.<cascade|niagara>.json`, one per
|
|
150
|
-
canonical package and object path so two same-basename systems in different
|
|
151
|
-
folders stay distinct. No runtime plays them: each descriptor lists the
|
|
152
|
-
modules and inputs it does not carry, and a placed effect component points at
|
|
153
|
-
its descriptor through `import-report.json`. Effects with no exported
|
|
154
|
-
descriptor stay named in the report; the importer never reconstructs a
|
|
155
|
-
particle simulation.
|
|
156
|
-
- `fab_import_asset` — downloads an owned listing through the user's existing
|
|
157
|
-
FabCLI session, then runs the same Unreal importer. It never signs in, claims,
|
|
158
|
-
or purchases an asset.
|
|
159
|
-
|
|
160
|
-
Blendkit (through the user's own official Blendkit-Client; see
|
|
161
|
-
[docs/blendkit-integration.md](docs/blendkit-integration.md)):
|
|
162
|
-
|
|
163
|
-
- `blendkit_status` — local-only health: whether the Client runs on loopback,
|
|
164
|
-
its version and `export_glb` recipe, whether live access is enabled, and the
|
|
165
|
-
setup steps still missing. It never contacts Blendkit.
|
|
166
|
-
- `blendkit_import_asset` — acquires one free Blendkit model through the Client,
|
|
167
|
-
converts it with the Client's bundled `export_glb` recipe under headless
|
|
168
|
-
Blender, inspects the GLB (meshes, triangles, PBR texture slots, size) and
|
|
169
|
-
writes a provenance file. It needs the item's license acknowledgement and
|
|
170
|
-
explicit project-use consent, and it refuses paid items and unknown licenses.
|
|
171
|
-
The output is private use only: the Blendkit Terms forbid open-format models
|
|
172
|
-
such as GLB in software and apply that to CC0-labelled items too. It never
|
|
173
|
-
goes into game builds, demos or starter kits, and `purpose: "redistribution"`
|
|
174
|
-
is always refused. Existing outputs are never overwritten. Live search and
|
|
175
|
-
import stay off until `BLENDKIT_PROVIDER_PERMISSION` is set. With it,
|
|
176
|
-
`asset_search` also searches Blendkit.
|
|
177
|
-
|
|
178
|
-
Poly Haven:
|
|
179
|
-
|
|
180
|
-
- `polyhaven_search_assets` — searches HDRIs, textures, and models by text,
|
|
181
|
-
type, and category, with relevance/popularity/date/name sorting and cursor
|
|
182
|
-
pagination.
|
|
183
|
-
- `polyhaven_get_asset` — returns normalized metadata, attributes, authors,
|
|
184
|
-
dimensions, resolution, and CC0 licensing for one asset.
|
|
185
|
-
- `polyhaven_list_categories` — returns category labels and counts for one
|
|
186
|
-
asset type.
|
|
187
|
-
- `polyhaven_list_files` — returns the official file URLs, sizes, MD5 hashes,
|
|
188
|
-
and dependency relationships. Use `resolution` and `format` to select usable
|
|
189
|
-
variants; follow `nextCursor` until absent to retrieve every matching file.
|
|
190
|
-
|
|
191
|
-
ambientCG:
|
|
192
|
-
|
|
193
|
-
- `ambientcg_search_assets` — searches CC0 materials, HDRIs, substances,
|
|
194
|
-
decals, atlases, 3D models, images, brushes, terrains, and HDRI elements.
|
|
195
|
-
- `ambientcg_get_asset` — returns metadata, maps, technique, dimensions,
|
|
196
|
-
statistics, and CC0 licensing.
|
|
197
|
-
- `ambientcg_list_categories` — lists typed categories and asset counts.
|
|
198
|
-
- `ambientcg_list_files` — returns official archives with variant attributes,
|
|
199
|
-
extensions, URLs, and byte sizes.
|
|
200
|
-
|
|
201
|
-
Smithsonian 3D:
|
|
202
|
-
|
|
203
|
-
- `smithsonian_search_assets` — searches Open Access models by text, format,
|
|
204
|
-
quality, owning unit, Draco compression, and glTF orientation compliance.
|
|
205
|
-
- `smithsonian_get_asset` — groups the file-centric API response into one model
|
|
206
|
-
summary.
|
|
207
|
-
- `smithsonian_list_files` — returns direct model files with format, quality,
|
|
208
|
-
compression, and orientation metadata.
|
|
209
|
-
|
|
210
|
-
Sketchfab:
|
|
211
|
-
|
|
212
|
-
- `sketchfab_search_models` — anonymously searches public models, defaulting to
|
|
213
|
-
downloadable results, and preserves author, geometry, archive, and license
|
|
214
|
-
metadata.
|
|
215
|
-
- `sketchfab_get_model` — returns public detail and explicit Creative Commons
|
|
216
|
-
requirements.
|
|
217
|
-
- `sketchfab_list_categories` — lists public category names and slugs.
|
|
218
|
-
- `sketchfab_get_downloads` — uses `SKETCHFAB_API_TOKEN` to retrieve temporary
|
|
219
|
-
download URLs. It never stores the token or returns it in tool output.
|
|
220
|
-
|
|
221
|
-
Game audio:
|
|
222
|
-
|
|
223
|
-
- `audio_list_sources` — lists the ten supported audio libraries, best uses,
|
|
224
|
-
official browse pages, license/attribution cautions, and honest download
|
|
225
|
-
capability (`curated-direct` or `provider-page`).
|
|
226
|
-
- `audio_search_assets` — searches only the curated packs with stable official
|
|
227
|
-
direct URLs. Results preserve license, commercial-use, attribution, source,
|
|
228
|
-
size (when known), and redistribution metadata.
|
|
229
|
-
- `audio_download_asset` — downloads a catalog asset by ID after
|
|
230
|
-
`acceptLicense: true`. It uses an HTTPS host allowlist, validates every
|
|
231
|
-
redirect, streams with a byte cap, writes atomically without overwrite, and
|
|
232
|
-
returns the local path, byte size, and SHA-256.
|
|
233
|
-
- `audio_inspect_asset` — measures one local WAV, Ogg, MP3, or FLAC with the
|
|
234
|
-
pinned `@threenative/playtest` inspector: decode integrity, silence floor,
|
|
235
|
-
peak ceiling, DC offset, a five-band spectrum, the loop seam of a **declared**
|
|
236
|
-
loop, and the requested duration, plus a spectrogram PNG. Needs no ElevenLabs
|
|
237
|
-
key and never modifies or repairs the file. Optionally compares prompt and
|
|
238
|
-
emotional tone when local CLAP is provisioned.
|
|
239
|
-
- `audio_generate_sound` — **spends ElevenLabs credits.** Generates one sound
|
|
240
|
-
effect or ambience from a prompt using *your own* `ELEVENLABS_API_KEY`, saves
|
|
241
|
-
the original response plus a PCM16 WAV, and runs the same inspection
|
|
242
|
-
automatically. Reusing a `requestId` returns the saved result instead of
|
|
243
|
-
charging again.
|
|
244
|
-
|
|
245
|
-
### Generating a sound
|
|
246
|
-
|
|
247
|
-
The loop is **generate once → inspect → revise the one property that failed →
|
|
248
|
-
explicitly generate again.** There is no best-of-N retry: every generation is a
|
|
249
|
-
separate billable invocation you ask for on purpose.
|
|
250
|
-
|
|
251
|
-
Describe source, action, material, distance, and timing. A useful brief:
|
|
252
|
-
|
|
253
|
-
> One short metal latch closing, close microphone, dry recording, single event
|
|
254
|
-
> with natural decay, no speech or music.
|
|
255
|
-
|
|
256
|
-
These are prompting conventions absorbed from ElevenLabs' own
|
|
257
|
-
[sound-effects skill](https://github.com/elevenlabs/skills/blob/9edcbd4b80ed57b8e07a3f86ea520333969fbc3c/sound-effects/SKILL.md)
|
|
258
|
-
(MIT, reviewed at commit `9edcbd4b`), which is linked and attributed rather than
|
|
259
|
-
installed. They are conventions, not guaranteed exclusion controls.
|
|
260
|
-
|
|
261
|
-
`audio_generate_sound` requires `ELEVENLABS_API_KEY` in the server environment.
|
|
262
|
-
It is never bundled, defaulted, or logged: without your own key the tool fails
|
|
263
|
-
with `AUDIO_GENERATE_NO_CREDENTIALS` and an instruction to set one, while the
|
|
264
|
-
catalog and inspection tools keep working normally.
|
|
265
|
-
|
|
266
|
-
What the inspection does and does not tell you:
|
|
267
|
-
|
|
268
|
-
| It answers | It does not answer |
|
|
269
|
-
| --- | --- |
|
|
270
|
-
| Is it broken, silent, clipped, DC-offset, or the wrong duration? | Is it a *good* sound? |
|
|
271
|
-
| Does a declared loop click at the wrap? | Does it suit this scene? |
|
|
272
|
-
| Is the frequency content inside bounds *you* supplied? | Is the mix right? |
|
|
273
|
-
|
|
274
|
-
`artisticQuality` is always `"unverified"`. There is no `soundsGood` flag and no
|
|
275
|
-
single quality score. `recommendation: "audition"` means the technical checks
|
|
276
|
-
passed and every requested check completed — it means *go listen to it*, not
|
|
277
|
-
*ship it*. A definite technical failure recommends `"reject"`; a warning, a
|
|
278
|
-
possible mismatch, or an incomplete requested check recommends `"review"`.
|
|
279
|
-
|
|
280
|
-
If asset compilation rewrites the bytes, inspect the compiled output before
|
|
281
|
-
describing that output as checked.
|
|
282
|
-
|
|
283
|
-
### Optional local CLAP setup
|
|
284
|
-
|
|
285
|
-
Prompt fit and emotion fit are **off by default** (`semantic: "off"`) and add no
|
|
286
|
-
dependency to `npm install`. To enable them:
|
|
287
|
-
|
|
288
|
-
```sh
|
|
289
|
-
python3 -m venv ~/.venvs/clap
|
|
290
|
-
~/.venvs/clap/bin/pip install "laion-clap==1.1.6" "torch==2.4.1" \
|
|
291
|
-
"torchaudio==2.4.1" "librosa==0.10.2.post1" "numpy<2"
|
|
292
|
-
# Download 630k-audioset-best.pt from the LAION-AI/CLAP releases and check its hash.
|
|
293
|
-
export AUDIO_CLAP_CHECKPOINT=/path/to/630k-audioset-best.pt
|
|
294
|
-
export AUDIO_CLAP_PYTHON=~/.venvs/clap/bin/python
|
|
295
|
-
```
|
|
120
|
+
For a local build, use `command = "node"` and the absolute path to `dist/index.js`. Restart the host after
|
|
121
|
+
changing its configuration.
|
|
296
122
|
|
|
297
|
-
|
|
298
|
-
and no provisioned model, you get the technical result plus an explicit
|
|
299
|
-
unavailable semantic result and this instruction — never a semantic pass. A
|
|
300
|
-
generation that requests `semantic: "clap"` is refused in preflight, before
|
|
301
|
-
spending, when the model is missing.
|
|
302
|
-
|
|
303
|
-
Scores are cosine similarities used as **supporting evidence**, never
|
|
304
|
-
probabilities or quality grades. The emotion check builds otherwise identical
|
|
305
|
-
hypotheses — `The sound of {sourceDescription}, with a {mood} emotional tone.` —
|
|
306
|
-
so only the mood clause differs, and **this use of CLAP is a proposed heuristic,
|
|
307
|
-
not a demonstrated emotion-recognition capability**: mood status stays
|
|
308
|
-
`unverified` until the held-out emotion calibration qualifies your domain. Check
|
|
309
|
-
the CLAP code and checkpoint licensing separately before redistributing either.
|
|
310
|
-
|
|
311
|
-
Curated itch.io packs:
|
|
312
|
-
|
|
313
|
-
- `itch_list_downloads` — resolves a fresh no-account download page and lists
|
|
314
|
-
upload IDs, filenames, sizes, CC0 terms, and pack-specific cautions without
|
|
315
|
-
exposing the signed page token.
|
|
316
|
-
- `itch_download_asset` — resolves a fresh 60-second signed file URL and streams
|
|
317
|
-
the selected upload into guarded storage after `acceptLicense: true`. Signed
|
|
318
|
-
URLs are not returned. The initial catalog covers Tallbeard Music Loop
|
|
319
|
-
Bundle, Quaternius Universal Animation Libraries 1 and 2, Brackeys VFX
|
|
320
|
-
Bundle, and KayKit Platformer.
|
|
321
|
-
- `asset_list_bundle_entries` — reads the remote ZIP directory with HTTP byte
|
|
322
|
-
ranges and returns individual paths and sizes without downloading the archive.
|
|
323
|
-
- `asset_download_bundle_entry` — range-fetches and extracts one selected file,
|
|
324
|
-
caches it with a SHA-256 sidecar, and never downloads unrelated bundle files.
|
|
325
|
-
- `asset_list_bundle_animations` — range-fetches only an aggregate GLB and lists
|
|
326
|
-
its named animation clips. For Quaternius, it automatically prefers the
|
|
327
|
-
standard non-root-motion GLB.
|
|
328
|
-
- `asset_download_bundle_animation` — exports one named animation as a valid
|
|
329
|
-
animation-only GLB, removing unrelated clips, meshes, materials, and textures.
|
|
330
|
-
The cached aggregate GLB is reused across requests.
|
|
331
|
-
|
|
332
|
-
Unified source and download routing:
|
|
333
|
-
|
|
334
|
-
- `asset_list_sources` — returns only **agent-ready sources by default** across
|
|
335
|
-
3D, textures, HDRIs, animations, VFX, 2D, UI, icons, fonts, and audio. An
|
|
336
|
-
agent-ready source has an MCP download tool and requires no manual browser,
|
|
337
|
-
login, checkout, donation prompt, or paywall. Pass `agentReadyOnly: false` to
|
|
338
|
-
inspect the broader research directory, including package-manager, Git, and
|
|
339
|
-
provider-page sources that are not yet integrated.
|
|
340
|
-
- `asset_search_sources` — filters that directory by text, category, access
|
|
341
|
-
mode, and license tag (`cc0`, `cc-by`, `mit`, and others), while preserving
|
|
342
|
-
the same agent-ready-only default.
|
|
343
|
-
- `asset_download_file` — streams a direct URL previously returned by
|
|
344
|
-
`polyhaven_list_files`, `ambientcg_list_files`, `smithsonian_list_files`, or
|
|
345
|
-
the Game-icons.net bulk archive or Kenney Particle Pack entry into guarded
|
|
346
|
-
local storage. Provider hosts and URL shapes are allowlisted, redirects are
|
|
347
|
-
revalidated, existing files are never overwritten, and the result includes
|
|
348
|
-
SHA-256.
|
|
349
|
-
|
|
350
|
-
The additive `asset_search` tool searches individual `3d-model`, `texture`,
|
|
351
|
-
`hdri` and `animation` candidates across supported clients. It reports each
|
|
352
|
-
provider, bounded results and local continuation actions. Unknown prices fail
|
|
353
|
-
`freeOnly`; unknown rights fail explicit commercial-use/attribution filters.
|
|
354
|
-
Fab remains a specialized action and generic Sketchfab uses anonymous metadata,
|
|
355
|
-
without authenticated acquisition. Existing tools retain their public names.
|
|
356
|
-
|
|
357
|
-
Use `asset_get` with the returned stable `assetId` to inspect current terms and
|
|
358
|
-
available variants. It prefers GLB, then glTF with its supplied dependencies;
|
|
359
|
-
explicit unavailable formats return alternatives. The read-only call returns an
|
|
360
|
-
expiring `planToken`, required relative files and acknowledgement information.
|
|
361
|
-
A plan is not consent. Call `asset_download` with that token and explicit
|
|
362
|
-
`acceptLicense: true` only after reviewing the terms. Optional `maxBytes` can
|
|
363
|
-
only tighten the configured cap. Changed terms/files invalidate the plan.
|
|
364
|
-
|
|
365
|
-
Acquisition stages originals under `ASSET_DOWNLOAD_DIR/acquisitions`, verifies
|
|
366
|
-
checksums and all glTF buffer/texture references, then atomically publishes a
|
|
367
|
-
complete directory with `asset.acquisition.json`. Replays verify cached bytes
|
|
368
|
-
and provenance; a corrupt cache fails rather than being silently accepted.
|
|
369
|
-
Receipts preserve asset attribution separately from Poly Haven API credit.
|
|
370
|
-
No transfer/signed URLs or credentials enter the new public plan or receipt.
|
|
371
|
-
Archives stay archives with `requiresExtraction: true`; runtime readiness is
|
|
372
|
-
`unverified`. Acquiring originals does not change or adopt assets into a project.
|
|
373
|
-
|
|
374
|
-
Kenney 3D packs are enabled by default; `ASSET_ENABLE_KENNEY=0` disables the
|
|
375
|
-
generic adapter. Each query uses one official listing page and at most three
|
|
376
|
-
item detail checks,
|
|
377
|
-
paces requests, verifies item CC0/free/official-ZIP evidence and never crawls
|
|
378
|
-
or extracts archives. One official Mini Dungeon archive was qualified through
|
|
379
|
-
the packed public MCP on 2026-10-06. Broader catalog coverage and runtime import
|
|
380
|
-
remain unverified; ordinary tests use hand-authored local fixtures.
|
|
381
|
-
|
|
382
|
-
Recommended agent flow:
|
|
383
|
-
|
|
384
|
-
1. Call `asset_search_sources` with the requested category/query. Its default
|
|
385
|
-
result set is guaranteed to contain only agent-ready sources.
|
|
386
|
-
2. Call the returned `searchTool` or `detailTool` when present.
|
|
387
|
-
3. Resolve variants with the returned `filesTool`.
|
|
388
|
-
4. Call the returned `downloadTool` with `acceptLicense: true`.
|
|
389
|
-
5. For aggregate Quaternius animation libraries, skip whole-pack download:
|
|
390
|
-
`itch_list_downloads` → `asset_list_bundle_animations` →
|
|
391
|
-
`asset_download_bundle_animation`.
|
|
392
|
-
|
|
393
|
-
This is intentionally a short MCP tool chain rather than a fake universal URL:
|
|
394
|
-
each provider keeps its real search/variant semantics, while source routing and
|
|
395
|
-
the no-manual-flow guarantee stay uniform.
|
|
396
|
-
|
|
397
|
-
All discovery tools are read-only. Download tools write only within their
|
|
398
|
-
dedicated local directories and never purchase, add to cart or library,
|
|
399
|
-
wishlist, sign in, or overwrite an existing download. Audio packs remain
|
|
400
|
-
subject to their source license; raw redistribution is not implied by download.
|
|
401
|
-
|
|
402
|
-
## Unreal import support
|
|
403
|
-
|
|
404
|
-
The importer writes the same GLB/report layout for cooked and uncooked inputs.
|
|
405
|
-
It auto-provisions UE Viewer for package metadata, textures, and cooked render
|
|
406
|
-
data. For uncooked UE4 editor meshes it also provisions the separate GPL-3.0+
|
|
407
|
-
`unreal-assets-to-glb` CLI in an isolated Python virtual environment and invokes
|
|
408
|
-
it out of process. Modern UE5 packages rejected by UE Viewer use a pinned,
|
|
409
|
-
out-of-process CUE4Parse adapter. Set `THREENATIVE_TOOLCHAIN_AUTOINSTALL=0` to
|
|
410
|
-
require manually installed tools instead.
|
|
411
|
-
|
|
412
|
-
**Supported, verified and missing formats:** [`docs/unreal-format-support.md`](docs/unreal-format-support.md)
|
|
413
|
-
is the tracking ledger for every Unreal 4 and 5 class, with its status, evidence and gaps.
|
|
414
|
-
|
|
415
|
-
### Fab login for owned packs
|
|
416
|
-
|
|
417
|
-
`fab_import_asset` and `fab_list_owned` read your Fab library through
|
|
418
|
-
[FabCLI](https://github.com/zirklerite/FabCLI), which the importer installs on first
|
|
419
|
-
use. The MCP never signs in for you. Log in once; the session is kept in the OS
|
|
420
|
-
keyring (Secret Service on Linux) and lasts about 90 days:
|
|
421
|
-
|
|
422
|
-
```bash
|
|
423
|
-
~/.cache/threenative-asset-mcp/toolchain/fabcli/fabcli auth login # opens a login window
|
|
424
|
-
~/.cache/threenative-asset-mcp/toolchain/fabcli/fabcli auth login --manual # no window: paste a code
|
|
425
|
-
~/.cache/threenative-asset-mcp/toolchain/fabcli/fabcli auth status
|
|
426
|
-
```
|
|
123
|
+
## Tools at a glance
|
|
427
124
|
|
|
428
|
-
|
|
429
|
-
`epicgames.com/id/login?redirectUrl=…` link; open it there, copy `authorizationCode` from the
|
|
430
|
-
JSON the page shows, and paste it into the prompt within a few minutes. It needs an
|
|
431
|
-
interactive terminal. In Claude Code, run it with a `!` prefix. Never paste the code into an
|
|
432
|
-
untrusted service: it grants full access to the Epic account.
|
|
433
|
-
|
|
434
|
-
`fab_list_owned` lists each listing's Unreal artifacts separately (`artifacts[]`: `artifactId`,
|
|
435
|
-
`engineVersions`, `oldestEngine`, decoder `route`, `targetPlatforms`). An artifact's format is its
|
|
436
|
-
oldest listed engine, and that picks the decoder. When a listing has several artifacts and
|
|
437
|
-
`fab_import_asset` gets neither `engine` nor `artifactId`, it takes the best route (CUE4Parse, then
|
|
438
|
-
the MeshDescription converter, then UE Viewer; the newest source format breaks a tie) and says so
|
|
439
|
-
in the report `warnings`. Pass `artifactId` or `engine` to override.
|
|
440
|
-
|
|
441
|
-
**Graph-only colour.** Some Unreal materials compute BaseColor in the material graph (layered
|
|
442
|
-
masters that blend colour textures by mask channels and tint them), so no texture binding can
|
|
443
|
-
reproduce them. A section left without a base colour is baked from its graph: the pack's editor
|
|
444
|
-
package is read with CUE4Parse, a closed node set is evaluated per texel, and the result is a
|
|
445
|
-
base-colour PNG with `source: "graph"` in the section's bindings. The bake is `exact` only when
|
|
446
|
-
every node is understood; engine layer functions that are not in the pack, a mesh without vertex
|
|
447
|
-
colours standing in white, and a UV set the importer drops make it `heuristic`, and the section's
|
|
448
|
-
`graph` field names each approximation. View-dependent nodes (`Fresnel`, `DepthFade`, `TwoSidedSign`)
|
|
449
|
-
stand in as a recorded approximation: a bake has no camera, so `Fresnel` is its mean over a sphere's visible
|
|
450
|
-
surface, `DepthFade` is fully faded in and `TwoSidedSign` is the front face. Nodes it cannot evaluate (for
|
|
451
|
-
example `ReflectionVectorWS` feeding a cubemap lookup, or `Time`) leave the neutral fallback and are listed
|
|
452
|
-
under `graph.unsupportedNodes`.
|
|
453
|
-
The first bake installs the CUE4Parse converter and its private .NET SDK (hundreds of MB, once);
|
|
454
|
-
pass `graphBake: false` to the importer, or set `THREENATIVE_TOOLCHAIN_AUTOINSTALL=0`, to skip it.
|
|
455
|
-
`fab_import_asset` also skips it when its environment has `THREENATIVE_GRAPH_BAKE=0` (the parity
|
|
456
|
-
sweep's `--no-graph-bake` uses this to produce a baseline).
|
|
457
|
-
|
|
458
|
-
| Input | Current result |
|
|
125
|
+
| Group | Tools |
|
|
459
126
|
| --- | --- |
|
|
460
|
-
|
|
|
461
|
-
|
|
|
462
|
-
|
|
|
463
|
-
|
|
|
464
|
-
|
|
|
465
|
-
|
|
|
466
|
-
|
|
|
467
|
-
|
|
|
468
|
-
|
|
|
469
|
-
|
|
|
470
|
-
|
|
|
471
|
-
| Standalone `SoundWave` packages | Collision-free WAV/Ogg/MP3/FLAC files under `audio/`; modern UE5 editor source WAVs and cooked formats exposed by CUE4Parse feed the same report path, verified through Three.js `AudioLoader` |
|
|
472
|
-
| Skeletal and static mesh sockets, static-mesh simple collision, `PhysicsAsset`, and `SoundCue` graphs | Socket nodes (`socket:<name>`) under the joint their bone names (or the mesh node for a static socket); simple collision on the mesh node's `extras.unrealCollision` and in `Collision/<package>.json`; physics bodies, constraints (limits, motions and drives) and collision-disabled pairs in metre-scale `Physics/<asset>.json`, referenced from the skinned mesh node; and each SoundCue graph in `Audio/<cue>.cue.json` with every `WavePlayer` leaf rewritten to the exported audio file or reported as a missing wave. A UE4 package that holds one of these classes routes to the CUE4Parse adapter, which UE Viewer cannot decode; an ordinary UE4 mesh whose only gameplay signal is a `BodySetup` or a cross-package PhysicsAsset/Skeleton reference joins the adapter run for its metadata while keeping its verified UE Viewer geometry route |
|
|
473
|
-
| Runtime `Font` and inline `FontFace` packages | Validated TTF/OTF faces under collision-free `fonts/` paths; direct SFNT and legacy multi-block zlib `FontBulkData` are supported without Unreal Engine, with family/style/weight metadata for the browser `FontFace` API |
|
|
474
|
-
| Offline `Font` packages | Pre-baked glyph pages and serialized `FontCharacter` rectangles become collision-safe atlas PNGs plus BMFont-compatible JSON under `bitmap-fonts/`; Unicode remaps, baseline metrics, kerning, page indices, and distance-field metadata are retained for Three.js bitmap/SDF text |
|
|
475
|
-
| Paper2D `PaperSprite` and `PaperFlipbook` packages | Each sprite becomes a self-contained, unlit GLB with exact baked triangles and a cropped atlas region; flipbooks become JSON manifests preserving FPS and per-frame run lengths. Verified in Three.js on a real UE5.2 Paper2D project without Unreal Engine |
|
|
476
|
-
| Paper2D `PaperTileSet` and `PaperTileMap` packages | Populated layers become indexed, unlit GLB quads with atlas UVs, empty cells omitted, layer order retained, and packed horizontal/vertical/diagonal tile flips decoded. Placed tile maps and inherited Blueprint flipbook components are reconstructed in modern UE5 levels |
|
|
477
|
-
| Paper2D grouped sprites and `TextRenderActor` | Grouped sprite instances become `EXT_mesh_gpu_instancing` batches over shared sprite GLBs; text becomes unlit glyph geometry from offline `UFont` atlas pages, preserving alignment, colour, world size, and placement |
|
|
478
|
-
| `DataTable`, `StringTable`, `CurveTable`, and float/vector/colour curves | Collision-free, directly fetchable JSON under `data/`; row names, typed fields, localized strings, asset references, and rich-curve keys/tangents are retained |
|
|
479
|
-
| Modern UE5 loose static meshes, skeletal meshes, `Texture2D`, Material, and `.umap` packages rejected by UE Viewer | CUE4Parse converts directly to the same output/report pipeline; verified with UE5.5 cooked static geometry, UE5 editor skeletal geometry, Oodle-compressed UE5.3 editor texture source art, and a UE5.2 Paper2D map. Editor source art is restored to display colour: `TSF_BGRA8` sources are stored with red and blue swapped, and sRGB-sampled `TSF_RGBA16` sources are linear, so both are corrected (checked against UE Viewer's export of the same textures from the UE4.27 artifact). Cooked packages using unversioned properties require one matching `.usmap` file beside the imported tree |
|
|
480
|
-
| Separate roughness/metalness, solid palette maps, glass, and mirrors | Packed glTF PBR maps or explicit material fallbacks; extra graph inputs remain named sidecars |
|
|
481
|
-
| UE4 `.umap` placement, direct lights, and serialized Blueprint component templates (object versions 517–522) | Directly loadable scene GLB plus a transform/source manifest; repeated actors instance shared meshes; inherited mesh/light defaults are merged into placed instances; directional, point, and spot lights use `KHR_lights_punctual` |
|
|
482
|
-
| UE5 World Partition `.umap` external actors | The selected level export loads `__ExternalActors__` packages with the same provider mount and complete map path into the scene; external actor/object packages belonging to maps outside the import remain reported as skipped. Real-pack verification is tracked by PRD-598 |
|
|
483
|
-
| Standalone modern UE5 Blueprint prefabs | Serialized static/skeletal mesh and light component defaults become directly loadable scene GLBs; referenced meshes, transforms, skins, and bone hierarchies are retained, while bytecode is explicitly reported and never executed |
|
|
484
|
-
| UE4 ISM/HISM and painted static-mesh foliage placement | Bulk-serialized instance matrices become `EXT_mesh_gpu_instancing`, which Three.js `GLTFLoader` loads as GPU-instanced meshes |
|
|
485
|
-
| UE4 editor `LandscapeComponent` heightfields | Package-relative compressed BGRA8 heightmaps become indexed terrain meshes with decoded normals, component transforms, and original material names |
|
|
486
|
-
| UE5 level `Landscape` and `LandscapeStreamingProxy` heightfields | CUE4Parse follows every proxy component, restores trailer-backed BGRA8 source pixels, and emits indexed terrain meshes with decoded heights, normals, proxy-root transforms, and material identity |
|
|
487
|
-
| ActorX per-frame bone scale, dynamic Blueprint bytecode/construction scripts, Paper Terrain/spline deformation, and Nanite-only data without a fallback mesh | Detected or reported honestly; scene-level omissions are listed in `scenes[].omittedActors`, and Blueprint bytecode is never executed by the importer |
|
|
488
|
-
| Arbitrary Unreal shader graphs | Recoverable PBR inputs become standard glTF materials. Reports mark materials as degraded when shader connections are unavailable, even if every section has a texture; section `limitations` explain omitted layering, graph UV transforms, subsurface lighting, normal strength, and deformation |
|
|
489
|
-
| Encrypted Pak/IoStore | Unsupported without user-supplied keys and archive extraction; never reported as a complete conversion |
|
|
490
|
-
|
|
491
|
-
Importer version 49 also respects material-instance override flags and modern
|
|
492
|
-
parameter names. Contradictory texture samples duplicated across distinct mesh
|
|
493
|
-
section families are repaired only when referenced texture families are
|
|
494
|
-
unambiguous in the same export namespace. These bindings remain heuristic.
|
|
495
|
-
Separate same-resolution opacity maps can supply base-colour alpha without
|
|
496
|
-
altering RGB; different source dimensions are currently unsupported. Existing
|
|
497
|
-
cached conversions are invalidated. A textured result does not certify the
|
|
498
|
-
appearance of the original Unreal shader.
|
|
499
|
-
|
|
500
|
-
Importer version 51 uses adaptive PNG row filtering for composed opacity maps,
|
|
501
|
-
preserving the previous encoder's decoded RGBA and PNG metadata. Untouched textures
|
|
502
|
-
retain their original bytes; their profiles, bit depth, and metadata are not
|
|
503
|
-
re-encoded for storage savings.
|
|
504
|
-
|
|
505
|
-
Importer version 56 stops a colour parameter's zero alpha from becoming the base
|
|
506
|
-
colour factor's opacity (a `Tint` of `A=0` clipped every pixel of a masked grass
|
|
507
|
-
card, so the GLB drew nothing), binds a masked or translucent material's separate
|
|
508
|
-
opacity map (`Opacity=` or an `Other` named `*_Opacity*`) as the base colour's alpha
|
|
509
|
-
heuristically, and composes an opacity map of another resolution when the aspect
|
|
510
|
-
ratio matches. A sweep sheet tile that renders nothing although Unreal has a
|
|
511
|
-
thumbnail for it is recorded under the entry's `sheet.warnings`.
|
|
512
|
-
|
|
513
|
-
Importer version 58 stops a texture-parameter default from inventing a glTF slot from a
|
|
514
|
-
texture UE Viewer already named as another output. A parameter can exist without being
|
|
515
|
-
wired to its own output, so when its default texture is already the resolved `.mat`'s
|
|
516
|
-
Diffuse (or Normal, and so on) it is a shared placeholder, not a second live slot (Old
|
|
517
|
-
West - VOL 5 Town Props declared an unused `Emissive` default equal to the Fill albedo,
|
|
518
|
-
which painted every dark-wood model with a flat grey emissive wash). Real parameter
|
|
519
|
-
overrides and `.mat`-named Emissive slots still bind, a default whose texture the `.mat`
|
|
520
|
-
left in `Other[]` still recovers its slot by parameter name, and a `.mat` that names no
|
|
521
|
-
slot (the modern converter's `Other[n]` references only) still falls back to the
|
|
522
|
-
collected defaults.
|
|
523
|
-
|
|
524
|
-
Importer version 60 recovers the cut-out of a masked foliage card whose UE Viewer `.mat`
|
|
525
|
-
resolves `Opacity=` to the albedo itself, which has no alpha. When the material references a
|
|
526
|
-
packed `<stem>_AORO` map (AO / roughness / opacity in R / G / B) beside that albedo, the map's
|
|
527
|
-
blue channel becomes the base colour's alpha. Temperate Vegetation: conifer Bushes & Saplings
|
|
528
|
-
had every needle card render as a solid rectangle (Pine_GroundTwig_01 as a full quad with the
|
|
529
|
-
needle atlas painted inside it) because the mask was never applied. The binding is heuristic
|
|
530
|
-
and named in the section's limitations; an opaque material, an unrelated texture stem, or a
|
|
531
|
-
map the pack did not export is left alone.
|
|
532
|
-
|
|
533
|
-
Importer version 59 applies a material instance's base-colour tint vector even when its
|
|
534
|
-
parameter name carries extra words. Old West - VOL 5 Town Props overrides both
|
|
535
|
-
`Albedo Color Tint (Base)` (a bright global multiplier) and `Base Color Tint (Mask)` (the
|
|
536
|
-
dark cloth colour) on a master that tints its albedo through a mask; the exact names missed
|
|
537
|
-
both, so every curtain kept `baseColorFactor` [1,1,1] and rendered the untinted light-grey
|
|
538
|
-
albedo. When both a global base multiplier and a mask colour are overridden, the mask tint is
|
|
539
|
-
the surface colour that shows through. Only an instance's own override is matched broadly, so
|
|
540
|
-
a master's placeholder default (the master's red `Base Color Tint (Mask)`) never paints an
|
|
541
|
-
un-tinted instance; a lone mask tint with no global multiplier (the 03a curtain, whose editor
|
|
542
|
-
mesh thumbnail is the untinted albedo) is left alone, and a `RockTint`-style name with no
|
|
543
|
-
colour token beside `Tint` is left to the graph baker.
|
|
544
|
-
|
|
545
|
-
Importer version 57 gives a mesh the `.mat` / `.props.txt` that sit beside it when two
|
|
546
|
-
packages share an object name (a pack with `MI_Rock_Inst` in two folders bound one
|
|
547
|
-
folder's normal map to the other's meshes); with none beside the mesh the pick is named
|
|
548
|
-
in the section's limitations. The graph bake now evaluates `ObjectScale` (1),
|
|
549
|
-
`PerInstanceRandom` (0.5) and `ObjectPositionWS` (origin) as named heuristic
|
|
550
|
-
approximations and `SplitComponents` exactly, which lets layered cliff-rock masters bake
|
|
551
|
-
their colour texture instead of falling back to neutral grey. The parity S4 check no
|
|
552
|
-
longer lets a 90% colour share hide sections whose graph is known to carry colour.
|
|
553
|
-
|
|
554
|
-
Legacy instance sidecars without override flags retain inherited settings for
|
|
555
|
-
ambiguous opaque, false, and zero defaults. Their non-default values retain the
|
|
556
|
-
previous behavior; missing flags are reported as a material limitation.
|
|
557
|
-
|
|
558
|
-
Importer version 50 reads a bounded authored-material subset from tagged UE4
|
|
559
|
-
object-version 516 packages (legacy -7, UE3 864, licensee 0). Exact source paths
|
|
560
|
-
and closest flagged static overrides select the active branches before reduction.
|
|
561
|
-
Mesh-only package selection still loads needed material parents from the source
|
|
562
|
-
file inventory without exporting unrelated packages; lookups stay within the same
|
|
563
|
-
Content namespace. Mesh import-table material references resolve duplicate source
|
|
564
|
-
basenames by exact object path. Multiple matching references refuse source routing;
|
|
565
|
-
a unique basename fallback reports its unresolved mesh routing.
|
|
566
|
-
Reroutes, scalar constants/parameters and representable multiplication recover
|
|
567
|
-
scalar roughness; glTF normalization retains the raw authored value in the report.
|
|
568
|
-
An authored scalar replaces any earlier roughness texture contribution while
|
|
569
|
-
retaining its packed metallic channel. Source functions, Specular conversion,
|
|
570
|
-
foliage shading and unsupported sampling remain explicit limitations.
|
|
571
|
-
|
|
572
|
-
AO.R is attached only with explicit `Texture2D.SRGB=false`, a serialized linear
|
|
573
|
-
sampler mode (`LinearColor`, `LinearGrayscale` or `Masks`), and source coordinates
|
|
574
|
-
matching the sampler of the currently bound albedo. Its existing glTF coordinate
|
|
575
|
-
mapping is reused only after the selected albedo attaches; an incoming unrelated
|
|
576
|
-
base texture cannot establish that reference. Omitted Unreal defaults remain
|
|
577
|
-
unresolved. Unknown color
|
|
578
|
-
interpretation, ambiguous source paths or exported PNG basenames, nonidentity AO
|
|
579
|
-
multipliers and unproved UV transforms withhold AO, including earlier filename
|
|
580
|
-
guesses. Texture metadata is
|
|
581
|
-
read lazily for the selected AO candidate; unrelated large images are untouched
|
|
582
|
-
by the material reader. Other package profiles keep prior reconstruction with a
|
|
583
|
-
named source-reader limitation. Cached conversions are invalidated.
|
|
584
|
-
|
|
585
|
-
The Buffer reader is independently implemented from the tagged package, property
|
|
586
|
-
and native-input schemas at [CUE4Parse commit b4e9544](https://github.com/FabianFG/CUE4Parse/tree/b4e95441bcf0c975eb3adb68c0fb44c740c2cf62/CUE4Parse/UE4).
|
|
587
|
-
Native expression inputs require verified Core=2 and Framework=33 custom versions;
|
|
588
|
-
tagged instances without native inputs do not require these versions. Native
|
|
589
|
-
trailers and omitted class defaults are outside its scope. This subset does not
|
|
590
|
-
certify original Unreal appearance.
|
|
591
|
-
|
|
592
|
-
Automatic uncooked conversion needs Python 3 with `venv` and `pip`. A Linux
|
|
593
|
-
source build fallback for UE Viewer additionally needs `git`, `g++`, `perl`,
|
|
594
|
-
zlib, and SDL2 development headers. Unreal import currently runs on Linux and
|
|
595
|
-
Windows; use the path overrides below for preinstalled executables.
|
|
596
|
-
|
|
597
|
-
Load a reconstructed level with the normal Three.js loader — no Unreal runtime
|
|
598
|
-
or custom `.uasset` loader is involved:
|
|
599
|
-
|
|
600
|
-
```js
|
|
601
|
-
new GLTFLoader().load("assets/fab/<listing>/Scenes/DemoMap.glb", ({ scene }) => {
|
|
602
|
-
threeScene.add(scene);
|
|
603
|
-
});
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
Standalone materials use that same loader. Find a swatch by the report's
|
|
607
|
-
`materialAssets[].libraryName`, then assign its standard Three.js material:
|
|
608
|
-
|
|
609
|
-
```js
|
|
610
|
-
new GLTFLoader().load("assets/fab/<listing>/Materials/UnrealMaterialLibrary.glb", ({ scene }) => {
|
|
611
|
-
const swatch = scene.getObjectByName(materialAsset.libraryName);
|
|
612
|
-
targetMesh.material = swatch.material;
|
|
613
|
-
});
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
Sound waves use Three.js directly as well:
|
|
617
|
-
|
|
618
|
-
```js
|
|
619
|
-
const buffer = await new THREE.AudioLoader().loadAsync(audioAsset.file);
|
|
620
|
-
sound.setBuffer(buffer);
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
Structured data is ordinary runtime JSON — no Unreal object loader is needed:
|
|
624
|
-
|
|
625
|
-
```js
|
|
626
|
-
const table = await fetch(dataAsset.json).then((response) => response.json());
|
|
627
|
-
const defaultAmmo = table.Rows.Default;
|
|
628
|
-
```
|
|
127
|
+
| Unified | `asset_search_sources`, `asset_search`, `asset_get`, `asset_download`, `asset_download_file` |
|
|
128
|
+
| Fab | `fab_search_assets`, `fab_get_asset`, `fab_download_free_asset`, `fab_list_owned`, `fab_import_asset` |
|
|
129
|
+
| Poly Haven | `polyhaven_search_assets`, `polyhaven_get_asset`, `polyhaven_list_files` |
|
|
130
|
+
| ambientCG | `ambientcg_search_assets`, `ambientcg_get_asset`, `ambientcg_list_files` |
|
|
131
|
+
| Smithsonian 3D | `smithsonian_search_assets`, `smithsonian_get_asset`, `smithsonian_list_files` |
|
|
132
|
+
| Sketchfab | `sketchfab_search_models`, `sketchfab_get_model`, `sketchfab_get_downloads` |
|
|
133
|
+
| Blendkit | `blendkit_status`, `blendkit_import_asset` |
|
|
134
|
+
| Audio | `audio_search_assets`, `audio_download_asset`, `audio_inspect_asset`, `audio_generate_sound` |
|
|
135
|
+
| itch.io bundles | `itch_list_downloads`, `asset_list_bundle_entries`, `asset_download_bundle_animation` |
|
|
136
|
+
| Unreal | `asset_import_unreal` |
|
|
137
|
+
| Unity | `unity_list_packages`, `asset_import_unity` |
|
|
629
138
|
|
|
630
|
-
|
|
139
|
+
Recommended flow: `asset_search_sources` → the returned search tool → the returned files tool → the returned
|
|
140
|
+
download tool with `acceptLicense: true`. All discovery tools are read-only. Download tools write only inside
|
|
141
|
+
their own directories, and never purchase, sign in or overwrite. Full list: [docs/tools.md](docs/tools.md).
|
|
631
142
|
|
|
632
|
-
|
|
633
|
-
const face = new FontFace(font.family, `url(${font.file})`, {
|
|
634
|
-
style: font.fontStyle,
|
|
635
|
-
weight: String(font.weight),
|
|
636
|
-
});
|
|
637
|
-
await face.load();
|
|
638
|
-
document.fonts.add(face);
|
|
639
|
-
// Draw with this family on a canvas, then pass the canvas to THREE.CanvasTexture.
|
|
640
|
-
```
|
|
641
|
-
|
|
642
|
-
Paper2D flipbooks reference ordinary GLBs. Select a frame using the manifest's
|
|
643
|
-
`frameRun` and `framesPerSecond`, then show its loaded scene:
|
|
644
|
-
|
|
645
|
-
```js
|
|
646
|
-
const flipbook = await fetch(report.flipbooks[0].manifest).then((response) => response.json());
|
|
647
|
-
const frames = await Promise.all(flipbook.frames.map((frame) => loader.loadAsync(frame.glb)));
|
|
648
|
-
const tick = Math.floor(elapsedSeconds * flipbook.framesPerSecond) % flipbook.frameCount;
|
|
649
|
-
let end = 0;
|
|
650
|
-
frames.forEach(({ scene }, index) => {
|
|
651
|
-
end += flipbook.frames[index].frameRun;
|
|
652
|
-
scene.visible = tick < end && tick >= end - flipbook.frames[index].frameRun;
|
|
653
|
-
});
|
|
654
|
-
```
|
|
655
|
-
|
|
656
|
-
Cubemaps use the standard Three.js equirectangular environment path:
|
|
657
|
-
|
|
658
|
-
```js
|
|
659
|
-
const loader = cubemapAsset.dynamicRange === "hdr" ? new RGBELoader() : new THREE.TextureLoader();
|
|
660
|
-
const environment = await loader.loadAsync(cubemapAsset.file);
|
|
661
|
-
environment.mapping = THREE.EquirectangularReflectionMapping;
|
|
662
|
-
threeScene.environment = environment;
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
Multidimensional textures use the report's explicit Three.js type:
|
|
666
|
-
|
|
667
|
-
```js
|
|
668
|
-
const metadata = await fetch(textureStack.manifest).then((response) => response.json());
|
|
669
|
-
const bytes = new Uint8Array(await fetch(textureStack.data).then((response) => response.arrayBuffer()));
|
|
670
|
-
const texture = metadata.threeTexture === "Data3DTexture"
|
|
671
|
-
? new THREE.Data3DTexture(bytes, metadata.width, metadata.height, metadata.depth)
|
|
672
|
-
: new THREE.DataArrayTexture(bytes, metadata.width, metadata.height, metadata.depth);
|
|
673
|
-
texture.format = THREE.RGBAFormat;
|
|
674
|
-
texture.type = THREE.UnsignedByteType;
|
|
675
|
-
texture.needsUpdate = true;
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
Raw `RGBA16F` textures load through the package's `./data-texture` subpath. The loader
|
|
679
|
-
checks the manifest against the binary (exact length, SHA-256, finite samples) and refuses the
|
|
680
|
-
load, rather than skipping a check, when one cannot run. The source package cannot be fetched
|
|
681
|
-
or re-hashed in a browser, so pin the value the report gives for the texture's source package:
|
|
682
|
-
|
|
683
|
-
```js
|
|
684
|
-
import { loadRgba16fDataTexture } from "threenative-asset-mcp/data-texture";
|
|
685
|
-
|
|
686
|
-
const { texture, manifest } = await loadRgba16fDataTexture(
|
|
687
|
-
new URL(dataTexture.manifest, reportBaseUrl),
|
|
688
|
-
{ expectedSourceSha256: dataTexture.sourceSha256 },
|
|
689
|
-
);
|
|
690
|
-
// texture: THREE.DataTexture, RGBAFormat, HalfFloatType, NoColorSpace, no mipmaps.
|
|
691
|
-
// The stored values are unchanged: negative, HDR, subnormal and alpha bits reach the shader as stored.
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
`loadRgba16fDataTexture` needs an absolute URL and `crypto.subtle`, so serve the page over
|
|
695
|
-
https or from localhost. Its limit is the import's: the texture holds linear half-float
|
|
696
|
-
samples with no decoding, so a material must decide what they mean.
|
|
697
|
-
|
|
698
|
-
Installation-only Unreal `BasicShapes/Plane` references are generated locally
|
|
699
|
-
when the asset pack does not contain that mesh. Rectangular area lights are
|
|
700
|
-
preserved in scene metadata and approximated as punctual point lights because
|
|
701
|
-
glTF's standard punctual-light extension has no area-light type.
|
|
702
|
-
|
|
703
|
-
### Parity sweep and material metadata dumps
|
|
704
|
-
|
|
705
|
-
`npm run parity:fab` imports every owned Fab artifact and scores it against its source package
|
|
706
|
-
(`scorecard.json`). `--export-metadata <dir>` also writes one replayable material dump per pack;
|
|
707
|
-
replay them with `FAB_METADATA_DIR=<dir> npx vitest run tests/fab-metadata.test.ts`. Dumps hold
|
|
708
|
-
licensed pack names and are local-only: never commit them.
|
|
709
|
-
|
|
710
|
-
Set `THREENATIVE_UNREAL_PREVIEW_REFERENCE=/path/to/reference.json` to light the comparison sheets (`parity:fab` and
|
|
711
|
-
`scripts/fidelity-sheet.ts`) with a local version-1 reference profile: radiance and Grid binaries beside the JSON, each
|
|
712
|
-
checked by SHA-256. It is read once per command, and an invalid profile stops the run before any download. Unset, the
|
|
713
|
-
sheets use the approximation and say so. Each picture records what lit it (`identity` in `picture-lighting.json`,
|
|
714
|
-
`sheets/<label>.json` and the scorecard entry). `--resume --sheets` refuses a kept sheet drawn otherwise: resume into a
|
|
715
|
-
new `--out`, or add `--no-sheets`. Profiles and binaries derive from licensed packs: keep them local-only, never commit them.
|
|
716
|
-
|
|
717
|
-
## Configuration
|
|
718
|
-
|
|
719
|
-
| Variable | Default | Purpose |
|
|
720
|
-
| ------------------------------- | -------------------------------------------------- | -------------------------------------------------------- |
|
|
721
|
-
| `FAB_DIRECT_TIMEOUT_MS` | `20000` | Direct JSON request timeout. |
|
|
722
|
-
| `FAB_BROWSER_TIMEOUT_MS` | `30000` | Dedicated browser request timeout. |
|
|
723
|
-
| `FAB_BROWSER_MANUAL_TIMEOUT_MS` | `10000` | Headed-mode grace period for visible verification. |
|
|
724
|
-
| `FAB_BROWSER_HEADLESS` | `true` | Set to `0` temporarily for manual verification. |
|
|
725
|
-
| `FAB_BROWSER_PROFILE_DIR` | OS state directory under `threenative-asset-mcp/fab-browser-profile` | MCP-owned Fab browser state. |
|
|
726
|
-
| `FAB_DOWNLOAD_DIR` | `~/Downloads/threenative-asset-mcp/fab` | Dedicated directory for Fab free-file downloads. |
|
|
727
|
-
| `FAB_MAX_DOWNLOAD_BYTES` | `2147483648` | Maximum accepted download size in bytes. |
|
|
728
|
-
| `FAB_DOWNLOAD_TIMEOUT_MS` | `600000` | Total timeout for one file download. |
|
|
729
|
-
| `FAB_CURL_IMPERSONATE` | auto-detected on `PATH` | curl-impersonate wrapper override; `0`/`off` disables. |
|
|
730
|
-
| `FAB_MIN_REQUEST_INTERVAL_MS` | `1000` | Minimum spacing between direct upstream requests. |
|
|
731
|
-
| `FAB_LOG_LEVEL` | `warn` | `debug`, `info`, `warn`, or `error`. |
|
|
732
|
-
| `FAB_LOG_QUERIES` | `false` | Set to `1` only if query text may be written to logs. |
|
|
733
|
-
| `SKETCHFAB_API_TOKEN` | unset | User token for temporary Sketchfab download URLs. |
|
|
734
|
-
| `AUDIO_DOWNLOAD_DIR` | `~/Downloads/threenative-asset-mcp/audio` | Dedicated directory for curated audio downloads. |
|
|
735
|
-
| `AUDIO_MAX_DOWNLOAD_BYTES` | `10737418240` | Maximum accepted bytes per audio archive (10 GiB). |
|
|
736
|
-
| `AUDIO_DOWNLOAD_TIMEOUT_MS` | `1800000` | Total timeout for one audio download (30 minutes). |
|
|
737
|
-
| `ELEVENLABS_API_KEY` | unset | **Your own** key; required only by `audio_generate_sound`. Never bundled or logged. |
|
|
738
|
-
| `AUDIO_INSPECT_ROOTS` | the audio dir plus the launch directory | Extra `:`-separated roots `audio_inspect_asset` may read. |
|
|
739
|
-
| `AUDIO_CLAP_PYTHON` | unset | Interpreter with LAION-CLAP installed; enables `semantic: "clap"`. |
|
|
740
|
-
| `AUDIO_CLAP_CHECKPOINT` | unset | Path to the `630k-audioset-best.pt` checkpoint. |
|
|
741
|
-
| `AUDIO_CLAP_CHECKPOINT_SHA256` | unset | Optional pin; a mismatched checkpoint is refused. |
|
|
742
|
-
| `ASSET_DOWNLOAD_DIR` | `~/Downloads/threenative-asset-mcp/assets` | Dedicated directory for direct provider downloads. |
|
|
743
|
-
| `ASSET_MAX_DOWNLOAD_BYTES` | `10737418240` | Maximum bytes per direct file or complete generic acquisition (10 GiB). |
|
|
744
|
-
| `ASSET_DOWNLOAD_TIMEOUT_MS` | `1800000` | Total timeout for one provider download (30 minutes). |
|
|
745
|
-
| `ASSET_ENABLE_KENNEY` | `1` | Bounded official Kenney 3D-pack discovery; `0` disables it. Other values fail initialization. |
|
|
746
|
-
| `ASSET_SEARCH_CONCURRENCY` | `4` | Concurrent provider operations per search; range 1–4. |
|
|
747
|
-
| `ASSET_PROVIDER_TIMEOUT_MS` | `8000` | Provider deadline; range 1–30000 milliseconds. |
|
|
748
|
-
| `ASSET_SEARCH_TIMEOUT_MS` | `10000` | Aggregate search/metadata deadline including queue time; range 1–60000. |
|
|
749
|
-
| `ASSET_PLAN_TTL_MS` | `300000` | Acquisition-plan TTL; range 1–900000; at most 100 bounded plans. |
|
|
750
|
-
| `BLENDKIT_CLIENT_URL` | `http://127.0.0.1:62485` | Loopback address of the user's official Blendkit-Client; other hosts fail. |
|
|
751
|
-
| `BLENDKIT_PROVIDER_PERMISSION` | unset | Citation of documented Blendkit permission for this use. Unset keeps live Blendkit search and import off. |
|
|
752
|
-
| `BLENDKIT_API_KEY` | unset | **Your own** key from the official Blendkit sign-in; sent only to the loopback Client, never logged or returned. |
|
|
753
|
-
| `BLENDKIT_BLENDER_PATH` | Client-stored Blender | Absolute path to the Blender that runs the `export_glb` recipe. |
|
|
754
|
-
| `THREENATIVE_TOOLCHAIN_AUTOINSTALL` | `1` | Set to `0` to disable first-use external-tool installs. |
|
|
755
|
-
| `THREENATIVE_TOOLCHAIN_DIR` | OS cache under `threenative-asset-mcp/toolchain` | UE Viewer, FabCLI, and uncooked-converter cache. |
|
|
756
|
-
| `THREENATIVE_UMODEL_PATH` | auto-detected/provisioned | Absolute path to a UE Viewer executable override. |
|
|
757
|
-
| `THREENATIVE_UNCOOKED_CONVERTER_PATH` | auto-detected/provisioned | Absolute path to `unreal-assets-to-glb`. |
|
|
758
|
-
| `THREENATIVE_FABCLI_PATH` | auto-detected/provisioned | Absolute path to FabCLI; login remains user-controlled. |
|
|
759
|
-
|
|
760
|
-
Direct requests are spaced at least `FAB_MIN_REQUEST_INTERVAL_MS` apart. Only
|
|
761
|
-
HTTP 429, 502, 503, and 504 are retried, at most twice, with backoff and
|
|
762
|
-
`Retry-After` support. Challenges, access denial, invalid input, missing
|
|
763
|
-
listings, and schema drift are never retried by the transport; a
|
|
764
|
-
curl-impersonate challenge response is additionally retried inside the
|
|
765
|
-
impersonation wrapper with longer, jittered waits before the browser fallback
|
|
766
|
-
is engaged.
|
|
767
|
-
|
|
768
|
-
## Browser-fingerprint TLS (curl-impersonate)
|
|
769
|
-
|
|
770
|
-
Fab's `/i/*` JSON routes sit behind Cloudflare bot management that challenges
|
|
771
|
-
Node's default TLS fingerprint — including plain `fetch` from this MCP and
|
|
772
|
-
headless Chromium — while real browser fingerprints pass. When a
|
|
773
|
-
[curl-impersonate](https://github.com/lwthiker/curl-impersonate) wrapper (for
|
|
774
|
-
example `curl_chrome146`) is available on the host `PATH`, the server performs
|
|
775
|
-
all anonymous JSON reads through it and no browser is needed for search,
|
|
776
|
-
detail, or download resolution. Set `FAB_CURL_IMPERSONATE=0` to force the old
|
|
777
|
-
behavior (plain Node fetch plus the Playwright fallback), or point it at a
|
|
778
|
-
specific wrapper binary.
|
|
779
|
-
|
|
780
|
-
Downloads of free files resolve entirely through the anonymous JSON contract
|
|
781
|
-
when possible: listing detail → `asset-formats/{format}` file listing →
|
|
782
|
-
`download-info` signed URL → guarded file write. The signed distribution URL
|
|
783
|
-
is validated against an exact Epic distribution-host allowlist before use.
|
|
784
|
-
Only when the direct path is challenged does the server fall back to the
|
|
785
|
-
guarded browser click flow below.
|
|
786
|
-
|
|
787
|
-
Process-local cache TTLs are five minutes for search, fifteen minutes for
|
|
788
|
-
listing details, six hours for taxonomy data, and ten minutes for promotions.
|
|
789
|
-
The shared LRU is capped at 500 entries and is cleared on process exit.
|
|
790
|
-
|
|
791
|
-
## Poly Haven API behavior
|
|
792
|
-
|
|
793
|
-
Poly Haven requests go only to `https://api.polyhaven.com`, with the required
|
|
794
|
-
`threenative-asset-mcp` User-Agent. Asset lists are cached for 15 minutes and
|
|
795
|
-
details, categories, and file trees are cached for up to one hour. Returned file
|
|
796
|
-
URLs are accepted only from `https://dl.polyhaven.org`.
|
|
797
|
-
|
|
798
|
-
The live API is free for personal and commercial use, but use of the API
|
|
799
|
-
requires a visible Poly Haven credit. The assets themselves are CC0. This MCP
|
|
800
|
-
includes `provider`, `license`, and `attribution` fields so downstream clients
|
|
801
|
-
can preserve that distinction. See the
|
|
802
|
-
[official API page](https://polyhaven.com/our-api) and
|
|
803
|
-
[API documentation](https://api.polyhaven.com/).
|
|
804
|
-
|
|
805
|
-
## Other provider behavior
|
|
806
|
-
|
|
807
|
-
ambientCG uses its anonymous, read-only v3 API. Its files are CC0 and its API
|
|
808
|
-
exposes searchable metadata, categories, and downloadable variants.
|
|
809
|
-
|
|
810
|
-
Smithsonian uses the anonymous Smithsonian 3D file-search API. Files exposed by
|
|
811
|
-
that API are part of Smithsonian Open Access; the MCP retains direct source
|
|
812
|
-
URLs and format/quality metadata.
|
|
813
|
-
|
|
814
|
-
Sketchfab public search, categories, and model detail do not require a token.
|
|
815
|
-
The download endpoint requires a token belonging to the user, configured
|
|
816
|
-
through `SKETCHFAB_API_TOKEN`. The token is sent only in the Sketchfab
|
|
817
|
-
`Authorization` header, is never logged or persisted, and is not included in
|
|
818
|
-
MCP responses. Sketchfab models use different Creative Commons licenses; always
|
|
819
|
-
inspect `license.requirements` before use.
|
|
820
|
-
|
|
821
|
-
Audio direct downloads are catalog-ID based; the MCP does not accept arbitrary
|
|
822
|
-
URLs. Official Kenney downloads are restricted to `kenney.nl` and Sonniss GDC
|
|
823
|
-
downloads to `downloads.sonniss.com`, including redirect revalidation. Sources
|
|
824
|
-
without a stable, verified direct contract remain discoverable as
|
|
825
|
-
`provider-page` instead of being falsely presented as one-click downloads.
|
|
826
|
-
|
|
827
|
-
The generic direct downloader is intentionally narrower than an arbitrary URL
|
|
828
|
-
fetcher. It accepts only official Poly Haven, ambientCG, Smithsonian,
|
|
829
|
-
Game-icons.net, and curated Kenney URL contracts. The itch.io downloader uses
|
|
830
|
-
fresh signed mirror URLs internally but never exposes them. Sketchfab signed
|
|
831
|
-
downloads are exposed through `sketchfab_get_downloads` but are not persisted
|
|
832
|
-
by the generic downloader because their temporary CDN hosts vary and require
|
|
833
|
-
the user's token-backed session. Provider-page-only sources stay provider-page-only until a stable,
|
|
834
|
-
license-safe download contract is verified.
|
|
835
|
-
|
|
836
|
-
## Dedicated browser profile and privacy
|
|
837
|
-
|
|
838
|
-
When a direct anonymous request receives a Cloudflare challenge, the server may
|
|
839
|
-
open Playwright Chromium with a dedicated MCP-owned profile. It never attaches
|
|
840
|
-
to, copies, or reads the user's normal Chrome/Edge/Chromium profile, cookie
|
|
841
|
-
database, local storage, passwords, or Epic session.
|
|
842
|
-
|
|
843
|
-
If the tool returns `FAB_BROWSER_ATTENTION_REQUIRED`, run the same MCP command
|
|
844
|
-
once with `FAB_BROWSER_HEADLESS=0`. The MCP opens its dedicated Fab homepage at
|
|
845
|
-
startup, so complete any visible verification before calling the tool. A tool
|
|
846
|
-
call allows an additional `FAB_BROWSER_MANUAL_TIMEOUT_MS` grace period, then
|
|
847
|
-
returns `FAB_BROWSER_ATTENTION_REQUIRED` rather than exceeding typical MCP
|
|
848
|
-
client timeouts. Close the MCP process after verification and return to headless
|
|
849
|
-
mode. The server does not solve or bypass challenges.
|
|
850
|
-
|
|
851
|
-
Browser process startup is capped at ten seconds. On a host without a working
|
|
852
|
-
graphical session, headed mode returns `FAB_UPSTREAM_UNAVAILABLE` instead of
|
|
853
|
-
hanging an MCP call; run the manual release gate on a graphical host.
|
|
854
|
-
|
|
855
|
-
To clear browser-owned Fab state, stop every `threenative-asset-mcp` process and
|
|
856
|
-
move only the dedicated directory reported by your configuration out of
|
|
857
|
-
service. The default on Linux can be cleared recoverably with:
|
|
858
|
-
|
|
859
|
-
```bash
|
|
860
|
-
mv -- "${XDG_STATE_HOME:-$HOME/.local/state}/threenative-asset-mcp/fab-browser-profile" \
|
|
861
|
-
"${XDG_STATE_HOME:-$HOME/.local/state}/threenative-asset-mcp/fab-browser-profile.cleared"
|
|
862
|
-
```
|
|
863
|
-
|
|
864
|
-
Do not point `FAB_BROWSER_PROFILE_DIR` at a normal browser profile. The server
|
|
865
|
-
rejects known normal-profile locations.
|
|
866
|
-
|
|
867
|
-
The MCP has no analytics or remote telemetry. Application logs are structured
|
|
868
|
-
JSON written only to stderr; stdout is reserved for MCP JSON-RPC. Search query
|
|
869
|
-
text is omitted from logs unless `FAB_LOG_QUERIES=1`. Raw upstream bodies,
|
|
870
|
-
headers, cookies, tokens, stack traces, and browser profile paths are not logged
|
|
871
|
-
or returned to the model.
|
|
872
|
-
|
|
873
|
-
## Troubleshooting
|
|
143
|
+
`audio_generate_sound` spends **your own** ElevenLabs credits. Nothing else costs money.
|
|
874
144
|
|
|
875
|
-
|
|
876
|
-
: Fab asked for browser verification. First check whether a curl-impersonate
|
|
877
|
-
wrapper is installed (`FAB_LOG_LEVEL=info` logs `fab_impersonate_enabled`
|
|
878
|
-
when active). Otherwise use the headed dedicated-profile step above. If
|
|
879
|
-
verification continues to fail, stop; do not copy a signed-in browser
|
|
880
|
-
session.
|
|
145
|
+
## Unreal / Fab import
|
|
881
146
|
|
|
882
|
-
`
|
|
883
|
-
|
|
884
|
-
|
|
147
|
+
`fab_import_asset` and `asset_import_unreal` turn an Unreal pack (UE4 and UE5, cooked or uncooked) into
|
|
148
|
+
self-contained GLBs plus an `import-report.json`. Unreal Engine is not required. The importer reports what it
|
|
149
|
+
could not convert instead of claiming a full conversion. Setup, login and output types:
|
|
150
|
+
[docs/unreal-import.md](docs/unreal-import.md). Per-class status: [docs/unreal-format-support.md](docs/unreal-format-support.md).
|
|
885
151
|
|
|
886
|
-
|
|
887
|
-
: Fab's undocumented response changed. Re-run the sanitized contract probe and
|
|
888
|
-
update normalization and fixtures before continuing.
|
|
152
|
+
## Unity Asset Store import
|
|
889
153
|
|
|
890
|
-
`
|
|
891
|
-
|
|
892
|
-
|
|
154
|
+
`asset_import_unity` turns a `.unitypackage`, a UPM tarball or a Unity project folder you already have into
|
|
155
|
+
self-contained GLBs (one per model, plus one per prefab with its materials bound) and an `import-report.json`.
|
|
156
|
+
`unity_list_packages` lists the packages your Unity Editor cached. `fab_import_asset` hands a Unity-format Fab
|
|
157
|
+
artifact to the same importer when FabCLI exposes it. The MCP never signs in to Unity, never calls Unity
|
|
158
|
+
services and never decrypts a download. Unity is not required. The importer reports what it could not convert,
|
|
159
|
+
such as custom shaders, which keep their saved colour and base texture. Setup and output:
|
|
160
|
+
[docs/unity-import.md](docs/unity-import.md). Per-class status: [docs/unity-format-support.md](docs/unity-format-support.md).
|
|
893
161
|
|
|
894
|
-
|
|
895
|
-
: Build first, confirm the configured path is absolute, and run
|
|
896
|
-
`npm run inspect`. Logs belong on stderr; any non-JSON stdout is a bug.
|
|
162
|
+
## Privacy
|
|
897
163
|
|
|
898
|
-
|
|
164
|
+
No analytics or remote telemetry. Logs go to stderr only and omit query text unless `FAB_LOG_QUERIES=1`. The
|
|
165
|
+
dedicated browser profile never reads your normal browser profile or Epic session.
|
|
166
|
+
See [docs/troubleshooting.md](docs/troubleshooting.md).
|
|
899
167
|
|
|
900
|
-
|
|
901
|
-
minimum supported Node 20.19 and Node 24. It installs the committed npm lockfile,
|
|
902
|
-
runs typecheck/build, deterministic fixtures and actual built/npm-packed stdio
|
|
903
|
-
consumers. Reports distinguish intentionally conditional proprietary Unreal
|
|
904
|
-
fixtures. No provider live probes, credentials, package publishing or account
|
|
905
|
-
changes run in that workflow. Local CI-equivalent commands are:
|
|
168
|
+
## Development
|
|
906
169
|
|
|
907
170
|
```bash
|
|
908
|
-
npm ci
|
|
909
|
-
npm run build
|
|
910
|
-
npm run typecheck
|
|
911
|
-
npm run test:ci
|
|
912
|
-
npm run test:smoke
|
|
171
|
+
npm ci && npm run typecheck && npm test
|
|
913
172
|
```
|
|
914
173
|
|
|
915
|
-
|
|
916
|
-
ffmpeg and Python with NumPy/Pillow for local render/audio fixtures. Live probes
|
|
917
|
-
remain separate from deterministic acceptance tests.
|
|
174
|
+
`npm run doctor` lists missing local prerequisites. CI details and live probes: [docs/development.md](docs/development.md).
|
|
918
175
|
|
|
919
|
-
|
|
920
|
-
listed with its install command. Locally, a suite whose tool is missing is
|
|
921
|
-
skipped with that command as the reason. With `CI=true` it fails instead.
|
|
922
|
-
Add `--toolchain` to also require what the Unreal importers need on a fresh
|
|
923
|
-
Linux host, or `--toolchain-only` to check only that group.
|
|
176
|
+
## Contributing and help
|
|
924
177
|
|
|
178
|
+
Start with [Troubleshooting](docs/troubleshooting.md) and `npm run doctor`. Report bugs through
|
|
179
|
+
[GitHub issues](https://github.com/ThreeNativeHQ/threenative-asset-mcp/issues) with the package version, Node
|
|
180
|
+
version, OS, affected tool and sanitized reproduction steps. Never include credentials, signed download URLs or
|
|
181
|
+
licensed asset files. Before a pull request, read [AGENTS.md](AGENTS.md) and run the checks in
|
|
182
|
+
[docs/development.md](docs/development.md).
|
|
925
183
|
|
|
926
|
-
|
|
927
|
-
npm ci
|
|
928
|
-
npm run browser:install
|
|
929
|
-
npm run typecheck
|
|
930
|
-
npm test
|
|
931
|
-
npm run build
|
|
932
|
-
npm pack --dry-run
|
|
933
|
-
npm run inspect
|
|
934
|
-
npm run test:providers:live
|
|
935
|
-
```
|
|
936
|
-
|
|
937
|
-
Live checks are opt-in because they contact Fab:
|
|
938
|
-
|
|
939
|
-
```bash
|
|
940
|
-
npm run test:live
|
|
941
|
-
```
|
|
184
|
+
## License
|
|
942
185
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
concurrency-one, capped and paced. It must never acquire, purchase, wishlist,
|
|
947
|
-
download, or automatically solve a challenge.
|
|
948
|
-
|
|
949
|
-
`npm run test:providers:live` launches the compiled stdio MCP and exercises live
|
|
950
|
-
search, detail, categories, and file discovery for ambientCG, Smithsonian 3D,
|
|
951
|
-
and Sketchfab. If `SKETCHFAB_API_TOKEN` is configured it also verifies the
|
|
952
|
-
authenticated download endpoint; otherwise it verifies the explicit
|
|
953
|
-
authentication-required response.
|
|
186
|
+
The package declares **UNLICENSED** in [package.json](package.json). No open-source license is granted.
|
|
187
|
+
Downloaded assets and external tools keep their own licenses and terms. Public availability does not grant
|
|
188
|
+
redistribution rights.
|