threenative-asset-mcp 0.4.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.
Files changed (150) hide show
  1. package/README.md +441 -0
  2. package/dist/ambientcg/client.d.ts +61 -0
  3. package/dist/ambientcg/client.d.ts.map +1 -0
  4. package/dist/ambientcg/client.js +234 -0
  5. package/dist/ambientcg/client.js.map +1 -0
  6. package/dist/audio/catalog.d.ts +36 -0
  7. package/dist/audio/catalog.d.ts.map +1 -0
  8. package/dist/audio/catalog.js +169 -0
  9. package/dist/audio/catalog.js.map +1 -0
  10. package/dist/audio/client.d.ts +46 -0
  11. package/dist/audio/client.d.ts.map +1 -0
  12. package/dist/audio/client.js +196 -0
  13. package/dist/audio/client.js.map +1 -0
  14. package/dist/bundle/client.d.ts +89 -0
  15. package/dist/bundle/client.d.ts.map +1 -0
  16. package/dist/bundle/client.js +324 -0
  17. package/dist/bundle/client.js.map +1 -0
  18. package/dist/config.d.ts +20 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +130 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/download/direct-asset-downloader.d.ts +41 -0
  23. package/dist/download/direct-asset-downloader.d.ts.map +1 -0
  24. package/dist/download/direct-asset-downloader.js +232 -0
  25. package/dist/download/direct-asset-downloader.js.map +1 -0
  26. package/dist/fab/api-download.d.ts +30 -0
  27. package/dist/fab/api-download.d.ts.map +1 -0
  28. package/dist/fab/api-download.js +200 -0
  29. package/dist/fab/api-download.js.map +1 -0
  30. package/dist/fab/browser-transport.d.ts +79 -0
  31. package/dist/fab/browser-transport.d.ts.map +1 -0
  32. package/dist/fab/browser-transport.js +492 -0
  33. package/dist/fab/browser-transport.js.map +1 -0
  34. package/dist/fab/cache.d.ts +22 -0
  35. package/dist/fab/cache.d.ts.map +1 -0
  36. package/dist/fab/cache.js +55 -0
  37. package/dist/fab/cache.js.map +1 -0
  38. package/dist/fab/client.d.ts +61 -0
  39. package/dist/fab/client.d.ts.map +1 -0
  40. package/dist/fab/client.js +868 -0
  41. package/dist/fab/client.js.map +1 -0
  42. package/dist/fab/direct-transport.d.ts +68 -0
  43. package/dist/fab/direct-transport.d.ts.map +1 -0
  44. package/dist/fab/direct-transport.js +184 -0
  45. package/dist/fab/direct-transport.js.map +1 -0
  46. package/dist/fab/download-store.d.ts +21 -0
  47. package/dist/fab/download-store.d.ts.map +1 -0
  48. package/dist/fab/download-store.js +93 -0
  49. package/dist/fab/download-store.js.map +1 -0
  50. package/dist/fab/errors.d.ts +20 -0
  51. package/dist/fab/errors.d.ts.map +1 -0
  52. package/dist/fab/errors.js +65 -0
  53. package/dist/fab/errors.js.map +1 -0
  54. package/dist/fab/impersonate-fetch.d.ts +48 -0
  55. package/dist/fab/impersonate-fetch.d.ts.map +1 -0
  56. package/dist/fab/impersonate-fetch.js +228 -0
  57. package/dist/fab/impersonate-fetch.js.map +1 -0
  58. package/dist/fab/normalize.d.ts +3 -0
  59. package/dist/fab/normalize.d.ts.map +1 -0
  60. package/dist/fab/normalize.js +285 -0
  61. package/dist/fab/normalize.js.map +1 -0
  62. package/dist/fab/schemas.d.ts +10 -0
  63. package/dist/fab/schemas.d.ts.map +1 -0
  64. package/dist/fab/schemas.js +105 -0
  65. package/dist/fab/schemas.js.map +1 -0
  66. package/dist/index.d.ts +3 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +58 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/itch/catalog.d.ts +13 -0
  71. package/dist/itch/catalog.d.ts.map +1 -0
  72. package/dist/itch/catalog.js +50 -0
  73. package/dist/itch/catalog.js.map +1 -0
  74. package/dist/itch/client.d.ts +46 -0
  75. package/dist/itch/client.d.ts.map +1 -0
  76. package/dist/itch/client.js +175 -0
  77. package/dist/itch/client.js.map +1 -0
  78. package/dist/polyhaven/client.d.ts +55 -0
  79. package/dist/polyhaven/client.d.ts.map +1 -0
  80. package/dist/polyhaven/client.js +210 -0
  81. package/dist/polyhaven/client.js.map +1 -0
  82. package/dist/server.d.ts +25 -0
  83. package/dist/server.d.ts.map +1 -0
  84. package/dist/server.js +448 -0
  85. package/dist/server.js.map +1 -0
  86. package/dist/sketchfab/client.d.ts +70 -0
  87. package/dist/sketchfab/client.d.ts.map +1 -0
  88. package/dist/sketchfab/client.js +255 -0
  89. package/dist/sketchfab/client.js.map +1 -0
  90. package/dist/smithsonian/client.d.ts +32 -0
  91. package/dist/smithsonian/client.d.ts.map +1 -0
  92. package/dist/smithsonian/client.js +142 -0
  93. package/dist/smithsonian/client.js.map +1 -0
  94. package/dist/tools/ambientcg.d.ts +296 -0
  95. package/dist/tools/ambientcg.d.ts.map +1 -0
  96. package/dist/tools/ambientcg.js +183 -0
  97. package/dist/tools/ambientcg.js.map +1 -0
  98. package/dist/tools/audio.d.ts +211 -0
  99. package/dist/tools/audio.d.ts.map +1 -0
  100. package/dist/tools/audio.js +156 -0
  101. package/dist/tools/audio.js.map +1 -0
  102. package/dist/tools/bundle.d.ts +237 -0
  103. package/dist/tools/bundle.d.ts.map +1 -0
  104. package/dist/tools/bundle.js +206 -0
  105. package/dist/tools/bundle.js.map +1 -0
  106. package/dist/tools/direct-download.d.ts +53 -0
  107. package/dist/tools/direct-download.d.ts.map +1 -0
  108. package/dist/tools/direct-download.js +68 -0
  109. package/dist/tools/direct-download.js.map +1 -0
  110. package/dist/tools/download-free-asset.d.ts +66 -0
  111. package/dist/tools/download-free-asset.d.ts.map +1 -0
  112. package/dist/tools/download-free-asset.js +60 -0
  113. package/dist/tools/download-free-asset.js.map +1 -0
  114. package/dist/tools/get-asset.d.ts +161 -0
  115. package/dist/tools/get-asset.d.ts.map +1 -0
  116. package/dist/tools/get-asset.js +131 -0
  117. package/dist/tools/get-asset.js.map +1 -0
  118. package/dist/tools/itch.d.ts +120 -0
  119. package/dist/tools/itch.d.ts.map +1 -0
  120. package/dist/tools/itch.js +120 -0
  121. package/dist/tools/itch.js.map +1 -0
  122. package/dist/tools/list-filters.d.ts +127 -0
  123. package/dist/tools/list-filters.d.ts.map +1 -0
  124. package/dist/tools/list-filters.js +68 -0
  125. package/dist/tools/list-filters.js.map +1 -0
  126. package/dist/tools/list-limited-time-free.d.ts +57 -0
  127. package/dist/tools/list-limited-time-free.d.ts.map +1 -0
  128. package/dist/tools/list-limited-time-free.js +47 -0
  129. package/dist/tools/list-limited-time-free.js.map +1 -0
  130. package/dist/tools/polyhaven.d.ts +266 -0
  131. package/dist/tools/polyhaven.d.ts.map +1 -0
  132. package/dist/tools/polyhaven.js +233 -0
  133. package/dist/tools/polyhaven.js.map +1 -0
  134. package/dist/tools/search-assets.d.ts +176 -0
  135. package/dist/tools/search-assets.d.ts.map +1 -0
  136. package/dist/tools/search-assets.js +172 -0
  137. package/dist/tools/search-assets.js.map +1 -0
  138. package/dist/tools/sketchfab.d.ts +325 -0
  139. package/dist/tools/sketchfab.d.ts.map +1 -0
  140. package/dist/tools/sketchfab.js +203 -0
  141. package/dist/tools/sketchfab.js.map +1 -0
  142. package/dist/tools/smithsonian.d.ts +189 -0
  143. package/dist/tools/smithsonian.d.ts.map +1 -0
  144. package/dist/tools/smithsonian.js +189 -0
  145. package/dist/tools/smithsonian.js.map +1 -0
  146. package/dist/tools/source-directory.d.ts +226 -0
  147. package/dist/tools/source-directory.d.ts.map +1 -0
  148. package/dist/tools/source-directory.js +583 -0
  149. package/dist/tools/source-directory.js.map +1 -0
  150. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,441 @@
1
+ # threenative-asset-mcp
2
+
3
+ A [Model Context Protocol](https://modelcontextprotocol.io/) server for finding
4
+ 3D assets across [Fab](https://www.fab.com/),
5
+ [Poly Haven](https://polyhaven.com/), [ambientCG](https://ambientcg.com/),
6
+ [Smithsonian 3D](https://3d.si.edu/), [Sketchfab](https://sketchfab.com/), and a
7
+ curated game-audio catalog spanning Sonniss, Kenney, Tallbeard, Scott Buckley,
8
+ itch.io, Mixkit, Pixabay, Freesound, OpenGameArt, and Abstraction. It gives AI clients provider-scoped,
9
+ structured search, asset metadata, category/filter discovery, downloadable file
10
+ data, and guarded Fab downloads for directly available free files.
11
+
12
+ The audio tools separate **source discovery** from **verified direct downloads**.
13
+ All ten sources are described with license caveats and official browse pages;
14
+ only packs with stable official URLs and known license metadata appear in the
15
+ direct-download catalog. The initial downloadable set is Kenney Interface
16
+ Sounds, Kenney Music Jingles, and all five Sonniss GDC 2026 archives.
17
+
18
+ `fab_search_assets` defaults to free assets. This means Fab reported at least
19
+ one free or effectively free license; it does not imply every license tier is
20
+ free. Use `fab_get_asset` before making license or price claims.
21
+
22
+ Poly Haven results are CC0 and explicitly labelled `Powered by Poly Haven`.
23
+ `polyhaven_list_files` exposes official download URLs, hashes, sizes, and
24
+ dependency relationships with pagination and resolution/format filters.
25
+
26
+ > Status: experimental. Fab's `/i/*` JSON routes are undocumented and can
27
+ > change or restrict automated access. Poly Haven provides a documented public
28
+ > API, but clients must send a unique User-Agent and visibly credit Poly Haven.
29
+ > Sketchfab licenses vary per model and download URLs require a user API token.
30
+ > Review each provider's terms and each asset's license.
31
+
32
+ ## Requirements
33
+
34
+ - Node.js 20.19 or newer
35
+ - A local environment capable of running Playwright Chromium when Fab requests
36
+ browser verification
37
+ - No Epic or Fab login is required or automated
38
+
39
+ ## Install
40
+
41
+ An MCP host can launch the published package with:
42
+
43
+ ```bash
44
+ npx -y threenative-asset-mcp
45
+ ```
46
+
47
+ For a local checkout:
48
+
49
+ ```bash
50
+ npm ci
51
+ npm run browser:install
52
+ npm run typecheck
53
+ npm test
54
+ npm run build
55
+ node dist/index.js
56
+ ```
57
+
58
+ Playwright does not download Chromium as part of a normal package install. Run
59
+ `npx -p playwright@1.62.0 playwright install chromium` once on the MCP host
60
+ before relying on the browser fallback.
61
+
62
+ ## MCP host configuration
63
+
64
+ ### Codex
65
+
66
+ Add this to `~/.codex/config.toml`:
67
+
68
+ ```toml
69
+ [mcp_servers.assets]
70
+ command = "npx"
71
+ args = ["-y", "threenative-asset-mcp"]
72
+ ```
73
+
74
+ For a local build:
75
+
76
+ ```toml
77
+ [mcp_servers.assets]
78
+ command = "node"
79
+ args = ["/absolute/path/to/threenative-asset-mcp/dist/index.js"]
80
+ ```
81
+
82
+ ### Claude Desktop
83
+
84
+ Add a server entry to the Claude Desktop configuration:
85
+
86
+ ```json
87
+ {
88
+ "mcpServers": {
89
+ "assets": {
90
+ "command": "npx",
91
+ "args": ["-y", "threenative-asset-mcp"]
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ ### VS Code
98
+
99
+ Create `.vscode/mcp.json`:
100
+
101
+ ```json
102
+ {
103
+ "servers": {
104
+ "assets": {
105
+ "type": "stdio",
106
+ "command": "npx",
107
+ "args": ["-y", "threenative-asset-mcp"]
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ Restart the MCP host after changing its configuration.
114
+
115
+ ## MCP tools
116
+
117
+ Fab:
118
+
119
+ - `fab_search_assets` — searches public listings. `priceMode` defaults to
120
+ `free`; use `any` or `range` explicitly for paid results.
121
+ - `fab_get_asset` — returns normalized public listing details and per-license
122
+ effective prices.
123
+ - `fab_list_filters` — returns known public filter labels and slugs, including
124
+ an explicit warning when the versioned fallback is used.
125
+ - `fab_list_limited_time_free` — reads only a separately verified curated
126
+ promotion surface. In production it extracts canonical listing UUIDs from
127
+ Fab's public `/limited-time-free` page through the dedicated browser, then
128
+ resolves them through the normal detail client; it never substitutes general
129
+ `is_free=1` search.
130
+ - `fab_download_free_asset` — downloads one directly available free file into
131
+ the dedicated download directory after explicit Fab EULA acknowledgement. It
132
+ refuses purchase, acquisition, library-only, ambiguous, and unsafe-path
133
+ flows.
134
+
135
+ Poly Haven:
136
+
137
+ - `polyhaven_search_assets` — searches HDRIs, textures, and models by text,
138
+ type, and category, with relevance/popularity/date/name sorting and cursor
139
+ pagination.
140
+ - `polyhaven_get_asset` — returns normalized metadata, attributes, authors,
141
+ dimensions, resolution, and CC0 licensing for one asset.
142
+ - `polyhaven_list_categories` — returns category labels and counts for one
143
+ asset type.
144
+ - `polyhaven_list_files` — returns the official file URLs, sizes, MD5 hashes,
145
+ and dependency relationships. Use `resolution` and `format` to select usable
146
+ variants; follow `nextCursor` until absent to retrieve every matching file.
147
+
148
+ ambientCG:
149
+
150
+ - `ambientcg_search_assets` — searches CC0 materials, HDRIs, substances,
151
+ decals, atlases, 3D models, images, brushes, terrains, and HDRI elements.
152
+ - `ambientcg_get_asset` — returns metadata, maps, technique, dimensions,
153
+ statistics, and CC0 licensing.
154
+ - `ambientcg_list_categories` — lists typed categories and asset counts.
155
+ - `ambientcg_list_files` — returns official archives with variant attributes,
156
+ extensions, URLs, and byte sizes.
157
+
158
+ Smithsonian 3D:
159
+
160
+ - `smithsonian_search_assets` — searches Open Access models by text, format,
161
+ quality, owning unit, Draco compression, and glTF orientation compliance.
162
+ - `smithsonian_get_asset` — groups the file-centric API response into one model
163
+ summary.
164
+ - `smithsonian_list_files` — returns direct model files with format, quality,
165
+ compression, and orientation metadata.
166
+
167
+ Sketchfab:
168
+
169
+ - `sketchfab_search_models` — anonymously searches public models, defaulting to
170
+ downloadable results, and preserves author, geometry, archive, and license
171
+ metadata.
172
+ - `sketchfab_get_model` — returns public detail and explicit Creative Commons
173
+ requirements.
174
+ - `sketchfab_list_categories` — lists public category names and slugs.
175
+ - `sketchfab_get_downloads` — uses `SKETCHFAB_API_TOKEN` to retrieve temporary
176
+ download URLs. It never stores the token or returns it in tool output.
177
+
178
+ Game audio:
179
+
180
+ - `audio_list_sources` — lists the ten supported audio libraries, best uses,
181
+ official browse pages, license/attribution cautions, and honest download
182
+ capability (`curated-direct` or `provider-page`).
183
+ - `audio_search_assets` — searches only the curated packs with stable official
184
+ direct URLs. Results preserve license, commercial-use, attribution, source,
185
+ size (when known), and redistribution metadata.
186
+ - `audio_download_asset` — downloads a catalog asset by ID after
187
+ `acceptLicense: true`. It uses an HTTPS host allowlist, validates every
188
+ redirect, streams with a byte cap, writes atomically without overwrite, and
189
+ returns the local path, byte size, and SHA-256.
190
+
191
+ Curated itch.io packs:
192
+
193
+ - `itch_list_downloads` — resolves a fresh no-account download page and lists
194
+ upload IDs, filenames, sizes, CC0 terms, and pack-specific cautions without
195
+ exposing the signed page token.
196
+ - `itch_download_asset` — resolves a fresh 60-second signed file URL and streams
197
+ the selected upload into guarded storage after `acceptLicense: true`. Signed
198
+ URLs are not returned. The initial catalog covers Tallbeard Music Loop
199
+ Bundle, Quaternius Universal Animation Libraries 1 and 2, Brackeys VFX
200
+ Bundle, and KayKit Platformer.
201
+ - `asset_list_bundle_entries` — reads the remote ZIP directory with HTTP byte
202
+ ranges and returns individual paths and sizes without downloading the archive.
203
+ - `asset_download_bundle_entry` — range-fetches and extracts one selected file,
204
+ caches it with a SHA-256 sidecar, and never downloads unrelated bundle files.
205
+ - `asset_list_bundle_animations` — range-fetches only an aggregate GLB and lists
206
+ its named animation clips. For Quaternius, it automatically prefers the
207
+ standard non-root-motion GLB.
208
+ - `asset_download_bundle_animation` — exports one named animation as a valid
209
+ animation-only GLB, removing unrelated clips, meshes, materials, and textures.
210
+ The cached aggregate GLB is reused across requests.
211
+
212
+ Unified source and download routing:
213
+
214
+ - `asset_list_sources` — returns only **agent-ready sources by default** across
215
+ 3D, textures, HDRIs, animations, VFX, 2D, UI, icons, fonts, and audio. An
216
+ agent-ready source has an MCP download tool and requires no manual browser,
217
+ login, checkout, donation prompt, or paywall. Pass `agentReadyOnly: false` to
218
+ inspect the broader research directory, including package-manager, Git, and
219
+ provider-page sources that are not yet integrated.
220
+ - `asset_search_sources` — filters that directory by text, category, access
221
+ mode, and license tag (`cc0`, `cc-by`, `mit`, and others), while preserving
222
+ the same agent-ready-only default.
223
+ - `asset_download_file` — streams a direct URL previously returned by
224
+ `polyhaven_list_files`, `ambientcg_list_files`, `smithsonian_list_files`, or
225
+ the Game-icons.net bulk archive or Kenney Particle Pack entry into guarded
226
+ local storage. Provider hosts and URL shapes are allowlisted, redirects are
227
+ revalidated, existing files are never overwritten, and the result includes
228
+ SHA-256.
229
+
230
+ Recommended agent flow:
231
+
232
+ 1. Call `asset_search_sources` with the requested category/query. Its default
233
+ result set is guaranteed to contain only agent-ready sources.
234
+ 2. Call the returned `searchTool` or `detailTool` when present.
235
+ 3. Resolve variants with the returned `filesTool`.
236
+ 4. Call the returned `downloadTool` with `acceptLicense: true`.
237
+ 5. For aggregate Quaternius animation libraries, skip whole-pack download:
238
+ `itch_list_downloads` → `asset_list_bundle_animations` →
239
+ `asset_download_bundle_animation`.
240
+
241
+ This is intentionally a short MCP tool chain rather than a fake universal URL:
242
+ each provider keeps its real search/variant semantics, while source routing and
243
+ the no-manual-flow guarantee stay uniform.
244
+
245
+ All discovery tools are read-only. Download tools write only within their
246
+ dedicated local directories and never purchase, add to cart or library,
247
+ wishlist, sign in, or overwrite an existing download. Audio packs remain
248
+ subject to their source license; raw redistribution is not implied by download.
249
+
250
+ ## Configuration
251
+
252
+ | Variable | Default | Purpose |
253
+ | ------------------------------- | -------------------------------------------------- | -------------------------------------------------------- |
254
+ | `FAB_DIRECT_TIMEOUT_MS` | `20000` | Direct JSON request timeout. |
255
+ | `FAB_BROWSER_TIMEOUT_MS` | `30000` | Dedicated browser request timeout. |
256
+ | `FAB_BROWSER_MANUAL_TIMEOUT_MS` | `10000` | Headed-mode grace period for visible verification. |
257
+ | `FAB_BROWSER_HEADLESS` | `true` | Set to `0` temporarily for manual verification. |
258
+ | `FAB_BROWSER_PROFILE_DIR` | OS state directory under `threenative-asset-mcp/fab-browser-profile` | MCP-owned Fab browser state. |
259
+ | `FAB_DOWNLOAD_DIR` | `~/Downloads/threenative-asset-mcp/fab` | Dedicated directory for Fab free-file downloads. |
260
+ | `FAB_MAX_DOWNLOAD_BYTES` | `2147483648` | Maximum accepted download size in bytes. |
261
+ | `FAB_DOWNLOAD_TIMEOUT_MS` | `600000` | Total timeout for one file download. |
262
+ | `FAB_CURL_IMPERSONATE` | auto-detected on `PATH` | curl-impersonate wrapper override; `0`/`off` disables. |
263
+ | `FAB_MIN_REQUEST_INTERVAL_MS` | `1000` | Minimum spacing between direct upstream requests. |
264
+ | `FAB_LOG_LEVEL` | `warn` | `debug`, `info`, `warn`, or `error`. |
265
+ | `FAB_LOG_QUERIES` | `false` | Set to `1` only if query text may be written to logs. |
266
+ | `SKETCHFAB_API_TOKEN` | unset | User token for temporary Sketchfab download URLs. |
267
+ | `AUDIO_DOWNLOAD_DIR` | `~/Downloads/threenative-asset-mcp/audio` | Dedicated directory for curated audio downloads. |
268
+ | `AUDIO_MAX_DOWNLOAD_BYTES` | `10737418240` | Maximum accepted bytes per audio archive (10 GiB). |
269
+ | `AUDIO_DOWNLOAD_TIMEOUT_MS` | `1800000` | Total timeout for one audio download (30 minutes). |
270
+ | `ASSET_DOWNLOAD_DIR` | `~/Downloads/threenative-asset-mcp/assets` | Dedicated directory for direct provider downloads. |
271
+ | `ASSET_MAX_DOWNLOAD_BYTES` | `10737418240` | Maximum accepted bytes per provider file (10 GiB). |
272
+ | `ASSET_DOWNLOAD_TIMEOUT_MS` | `1800000` | Total timeout for one provider download (30 minutes). |
273
+
274
+ Direct requests are spaced at least `FAB_MIN_REQUEST_INTERVAL_MS` apart. Only
275
+ HTTP 429, 502, 503, and 504 are retried, at most twice, with backoff and
276
+ `Retry-After` support. Challenges, access denial, invalid input, missing
277
+ listings, and schema drift are never retried by the transport; a
278
+ curl-impersonate challenge response is additionally retried inside the
279
+ impersonation wrapper with longer, jittered waits before the browser fallback
280
+ is engaged.
281
+
282
+ ## Browser-fingerprint TLS (curl-impersonate)
283
+
284
+ Fab's `/i/*` JSON routes sit behind Cloudflare bot management that challenges
285
+ Node's default TLS fingerprint — including plain `fetch` from this MCP and
286
+ headless Chromium — while real browser fingerprints pass. When a
287
+ [curl-impersonate](https://github.com/lwthiker/curl-impersonate) wrapper (for
288
+ example `curl_chrome146`) is available on the host `PATH`, the server performs
289
+ all anonymous JSON reads through it and no browser is needed for search,
290
+ detail, or download resolution. Set `FAB_CURL_IMPERSONATE=0` to force the old
291
+ behavior (plain Node fetch plus the Playwright fallback), or point it at a
292
+ specific wrapper binary.
293
+
294
+ Downloads of free files resolve entirely through the anonymous JSON contract
295
+ when possible: listing detail → `asset-formats/{format}` file listing →
296
+ `download-info` signed URL → guarded file write. The signed distribution URL
297
+ is validated against an exact Epic distribution-host allowlist before use.
298
+ Only when the direct path is challenged does the server fall back to the
299
+ guarded browser click flow below.
300
+
301
+ Process-local cache TTLs are five minutes for search, fifteen minutes for
302
+ listing details, six hours for taxonomy data, and ten minutes for promotions.
303
+ The shared LRU is capped at 500 entries and is cleared on process exit.
304
+
305
+ ## Poly Haven API behavior
306
+
307
+ Poly Haven requests go only to `https://api.polyhaven.com`, with the required
308
+ `threenative-asset-mcp` User-Agent. Asset lists are cached for 15 minutes and
309
+ details, categories, and file trees are cached for up to one hour. Returned file
310
+ URLs are accepted only from `https://dl.polyhaven.org`.
311
+
312
+ The live API is free for personal and commercial use, but use of the API
313
+ requires a visible Poly Haven credit. The assets themselves are CC0. This MCP
314
+ includes `provider`, `license`, and `attribution` fields so downstream clients
315
+ can preserve that distinction. See the
316
+ [official API page](https://polyhaven.com/our-api) and
317
+ [API documentation](https://api.polyhaven.com/).
318
+
319
+ ## Other provider behavior
320
+
321
+ ambientCG uses its anonymous, read-only v3 API. Its files are CC0 and its API
322
+ exposes searchable metadata, categories, and downloadable variants.
323
+
324
+ Smithsonian uses the anonymous Smithsonian 3D file-search API. Files exposed by
325
+ that API are part of Smithsonian Open Access; the MCP retains direct source
326
+ URLs and format/quality metadata.
327
+
328
+ Sketchfab public search, categories, and model detail do not require a token.
329
+ The download endpoint requires a token belonging to the user, configured
330
+ through `SKETCHFAB_API_TOKEN`. The token is sent only in the Sketchfab
331
+ `Authorization` header, is never logged or persisted, and is not included in
332
+ MCP responses. Sketchfab models use different Creative Commons licenses; always
333
+ inspect `license.requirements` before use.
334
+
335
+ Audio direct downloads are catalog-ID based; the MCP does not accept arbitrary
336
+ URLs. Official Kenney downloads are restricted to `kenney.nl` and Sonniss GDC
337
+ downloads to `downloads.sonniss.com`, including redirect revalidation. Sources
338
+ without a stable, verified direct contract remain discoverable as
339
+ `provider-page` instead of being falsely presented as one-click downloads.
340
+
341
+ The generic direct downloader is intentionally narrower than an arbitrary URL
342
+ fetcher. It accepts only official Poly Haven, ambientCG, Smithsonian,
343
+ Game-icons.net, and curated Kenney URL contracts. The itch.io downloader uses
344
+ fresh signed mirror URLs internally but never exposes them. Sketchfab signed
345
+ downloads are exposed through `sketchfab_get_downloads` but are not persisted
346
+ by the generic downloader because their temporary CDN hosts vary and require
347
+ the user's token-backed session. Provider-page-only sources stay provider-page-only until a stable,
348
+ license-safe download contract is verified.
349
+
350
+ ## Dedicated browser profile and privacy
351
+
352
+ When a direct anonymous request receives a Cloudflare challenge, the server may
353
+ open Playwright Chromium with a dedicated MCP-owned profile. It never attaches
354
+ to, copies, or reads the user's normal Chrome/Edge/Chromium profile, cookie
355
+ database, local storage, passwords, or Epic session.
356
+
357
+ If the tool returns `FAB_BROWSER_ATTENTION_REQUIRED`, run the same MCP command
358
+ once with `FAB_BROWSER_HEADLESS=0`. The MCP opens its dedicated Fab homepage at
359
+ startup, so complete any visible verification before calling the tool. A tool
360
+ call allows an additional `FAB_BROWSER_MANUAL_TIMEOUT_MS` grace period, then
361
+ returns `FAB_BROWSER_ATTENTION_REQUIRED` rather than exceeding typical MCP
362
+ client timeouts. Close the MCP process after verification and return to headless
363
+ mode. The server does not solve or bypass challenges.
364
+
365
+ Browser process startup is capped at ten seconds. On a host without a working
366
+ graphical session, headed mode returns `FAB_UPSTREAM_UNAVAILABLE` instead of
367
+ hanging an MCP call; run the manual release gate on a graphical host.
368
+
369
+ To clear browser-owned Fab state, stop every `threenative-asset-mcp` process and
370
+ move only the dedicated directory reported by your configuration out of
371
+ service. The default on Linux can be cleared recoverably with:
372
+
373
+ ```bash
374
+ mv -- "${XDG_STATE_HOME:-$HOME/.local/state}/threenative-asset-mcp/fab-browser-profile" \
375
+ "${XDG_STATE_HOME:-$HOME/.local/state}/threenative-asset-mcp/fab-browser-profile.cleared"
376
+ ```
377
+
378
+ Do not point `FAB_BROWSER_PROFILE_DIR` at a normal browser profile. The server
379
+ rejects known normal-profile locations.
380
+
381
+ The MCP has no analytics or remote telemetry. Application logs are structured
382
+ JSON written only to stderr; stdout is reserved for MCP JSON-RPC. Search query
383
+ text is omitted from logs unless `FAB_LOG_QUERIES=1`. Raw upstream bodies,
384
+ headers, cookies, tokens, stack traces, and browser profile paths are not logged
385
+ or returned to the model.
386
+
387
+ ## Troubleshooting
388
+
389
+ `FAB_CHALLENGE` or `FAB_BROWSER_ATTENTION_REQUIRED`
390
+ : Fab asked for browser verification. First check whether a curl-impersonate
391
+ wrapper is installed (`FAB_LOG_LEVEL=info` logs `fab_impersonate_enabled`
392
+ when active). Otherwise use the headed dedicated-profile step above. If
393
+ verification continues to fail, stop; do not copy a signed-in browser
394
+ session.
395
+
396
+ `FAB_RATE_LIMITED`
397
+ : Wait for `retryAfterSeconds` when present. The MCP already applied its bounded
398
+ retries.
399
+
400
+ `FAB_UPSTREAM_CHANGED`
401
+ : Fab's undocumented response changed. Re-run the sanitized contract probe and
402
+ update normalization and fixtures before continuing.
403
+
404
+ `FAB_UPSTREAM_UNAVAILABLE`
405
+ : Fab is unavailable, or a currently unverified discovery contract was
406
+ intentionally disabled.
407
+
408
+ The MCP host shows no tools
409
+ : Build first, confirm the configured path is absolute, and run
410
+ `npm run inspect`. Logs belong on stderr; any non-JSON stdout is a bug.
411
+
412
+ ## Verification
413
+
414
+ ```bash
415
+ npm ci
416
+ npm run browser:install
417
+ npm run typecheck
418
+ npm test
419
+ npm run build
420
+ npm pack --dry-run
421
+ npm run inspect
422
+ npm run test:providers:live
423
+ ```
424
+
425
+ Live checks are opt-in because they contact Fab:
426
+
427
+ ```bash
428
+ npm run test:live
429
+ ```
430
+
431
+ This runs the real anonymous search, cursor, detail, and dedicated-browser
432
+ contract probe. It exits nonzero when Fab challenges the clean browser or the
433
+ required contract cannot be verified. Live verification must remain anonymous,
434
+ concurrency-one, capped and paced. It must never acquire, purchase, wishlist,
435
+ download, or automatically solve a challenge.
436
+
437
+ `npm run test:providers:live` launches the compiled stdio MCP and exercises live
438
+ search, detail, categories, and file discovery for ambientCG, Smithsonian 3D,
439
+ and Sketchfab. If `SKETCHFAB_API_TOKEN` is configured it also verifies the
440
+ authenticated download endpoint; otherwise it verifies the explicit
441
+ authentication-required response.
@@ -0,0 +1,61 @@
1
+ export declare const AMBIENTCG_TYPES: readonly ["material", "hdri", "substance", "decal", "atlas", "3d-model", "plain-image", "brush", "terrain", "hdri-element"];
2
+ export type AmbientCgAssetType = (typeof AMBIENTCG_TYPES)[number];
3
+ export interface AmbientCgDownload {
4
+ attributes: string;
5
+ extension: string;
6
+ url: string;
7
+ sizeBytes: number;
8
+ }
9
+ export interface AmbientCgAsset {
10
+ id: string;
11
+ type: AmbientCgAssetType;
12
+ title: string;
13
+ description?: string;
14
+ url: string;
15
+ tags: string[];
16
+ releaseDate?: string;
17
+ technique?: string;
18
+ dimensions?: {
19
+ width: number;
20
+ height: number;
21
+ depth: number;
22
+ };
23
+ downloadCount?: number;
24
+ thumbnailUrl?: string;
25
+ maps: string[];
26
+ downloads: AmbientCgDownload[];
27
+ }
28
+ export interface AmbientCgSearchResult {
29
+ assets: AmbientCgAsset[];
30
+ total: number;
31
+ nextOffset?: number;
32
+ }
33
+ export interface AmbientCgCategory {
34
+ id: string;
35
+ title: string;
36
+ type: AmbientCgAssetType;
37
+ assetCount: number;
38
+ }
39
+ export declare class AmbientCgClientError extends Error {
40
+ readonly code: "AMBIENTCG_INVALID_INPUT" | "AMBIENTCG_NOT_FOUND" | "AMBIENTCG_RATE_LIMITED" | "AMBIENTCG_UPSTREAM_UNAVAILABLE" | "AMBIENTCG_UPSTREAM_CHANGED";
41
+ readonly retryable: boolean;
42
+ constructor(code: "AMBIENTCG_INVALID_INPUT" | "AMBIENTCG_NOT_FOUND" | "AMBIENTCG_RATE_LIMITED" | "AMBIENTCG_UPSTREAM_UNAVAILABLE" | "AMBIENTCG_UPSTREAM_CHANGED", message: string, retryable?: boolean);
43
+ }
44
+ type FetchLike = typeof fetch;
45
+ export declare class AmbientCgClient {
46
+ private readonly fetchImpl;
47
+ constructor(fetchImpl?: FetchLike);
48
+ private request;
49
+ search(input: {
50
+ query?: string;
51
+ type?: AmbientCgAssetType;
52
+ sort: "popular" | "latest" | "downloads" | "oldest" | "random" | "alphabet";
53
+ limit: number;
54
+ offset: number;
55
+ includeDownloads?: boolean;
56
+ }): Promise<AmbientCgSearchResult>;
57
+ getAsset(id: string): Promise<AmbientCgAsset>;
58
+ listCategories(): Promise<AmbientCgCategory[]>;
59
+ }
60
+ export {};
61
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/ambientcg/client.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,YAC1B,UAAU,EACV,MAAM,EACN,WAAW,EACX,OAAO,EACP,OAAO,EACP,UAAU,EACV,aAAa,EACb,OAAO,EACP,SAAS,EACT,cAAc,CACN,CAAC;AACX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,iBAAiB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,kBAAkB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAE3C,QAAQ,CAAC,IAAI,EACT,yBAAyB,GACzB,qBAAqB,GACrB,wBAAwB,GACxB,gCAAgC,GAChC,4BAA4B;IAEhC,QAAQ,CAAC,SAAS;IARpB,YACW,IAAI,EACT,yBAAyB,GACzB,qBAAqB,GACrB,wBAAwB,GACxB,gCAAgC,GAChC,4BAA4B,EAChC,OAAO,EAAE,MAAM,EACN,SAAS,UAAQ,EAI3B;CACF;AAED,KAAK,SAAS,GAAG,OAAO,KAAK,CAAC;AAuH9B,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,SAAS;IAAtC,YAA6B,SAAS,GAAE,SAAiB,EAAI;YAE/C,OAAO;IAyCf,MAAM,CAAC,KAAK,EAAE;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,kBAAkB,CAAC;QAC1B,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;QAC5E,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAwCjC;IAEK,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAgBlD;IAEK,cAAc,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAkBnD;CACF"}