Skip to main content
Glama
jonit-dev
by jonit-dev

threenative-asset-mcp

A Model Context Protocol server for finding 3D assets across Fab, Poly Haven, ambientCG, Smithsonian 3D, Sketchfab, and a curated game-audio catalog spanning Sonniss, Kenney, Tallbeard, Scott Buckley, itch.io, Mixkit, Pixabay, Freesound, OpenGameArt, and Abstraction. It gives AI clients provider-scoped, structured search, asset metadata, category/filter discovery, downloadable file data, and guarded Fab downloads for directly available free files.

The audio tools separate source discovery from verified direct downloads. All ten sources are described with license caveats and official browse pages; only packs with stable official URLs and known license metadata appear in the direct-download catalog. The initial downloadable set is Kenney Interface Sounds, Kenney Music Jingles, and all five Sonniss GDC 2026 archives.

fab_search_assets defaults to free assets. This means Fab reported at least one free or effectively free license; it does not imply every license tier is free. Use fab_get_asset before making license or price claims.

Poly Haven results are CC0 and explicitly labelled Powered by Poly Haven. polyhaven_list_files exposes official download URLs, hashes, sizes, and dependency relationships with pagination and resolution/format filters.

Status: experimental. Fab's /i/* JSON routes are undocumented and can change or restrict automated access. Poly Haven provides a documented public API, but clients must send a unique User-Agent and visibly credit Poly Haven. Sketchfab licenses vary per model and download URLs require a user API token. Review each provider's terms and each asset's license.

Requirements

  • Node.js 20.19 or newer

  • A local environment capable of running Playwright Chromium when Fab requests browser verification

  • No Epic or Fab login is required or automated

Install

An MCP host can launch the published package with:

npx -y threenative-asset-mcp

For a local checkout:

npm ci
npm run browser:install
npm run typecheck
npm test
npm run build
node dist/index.js

Playwright does not download Chromium as part of a normal package install. Run npx -p playwright@1.62.0 playwright install chromium once on the MCP host before relying on the browser fallback.

MCP host configuration

Codex

Add this to ~/.codex/config.toml:

[mcp_servers.assets]
command = "npx"
args = ["-y", "threenative-asset-mcp"]

For a local build:

[mcp_servers.assets]
command = "node"
args = ["/absolute/path/to/threenative-asset-mcp/dist/index.js"]

Claude Desktop

Add a server entry to the Claude Desktop configuration:

{
  "mcpServers": {
    "assets": {
      "command": "npx",
      "args": ["-y", "threenative-asset-mcp"]
    }
  }
}

VS Code

Create .vscode/mcp.json:

{
  "servers": {
    "assets": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "threenative-asset-mcp"]
    }
  }
}

Restart the MCP host after changing its configuration.

MCP tools

Fab:

  • fab_search_assets — searches public listings. priceMode defaults to free; use any or range explicitly for paid results.

  • fab_get_asset — returns normalized public listing details and per-license effective prices.

  • fab_list_filters — returns known public filter labels and slugs, including an explicit warning when the versioned fallback is used.

  • fab_list_limited_time_free — reads only a separately verified curated promotion surface. In production it extracts canonical listing UUIDs from Fab's public /limited-time-free page through the dedicated browser, then resolves them through the normal detail client; it never substitutes general is_free=1 search.

  • fab_download_free_asset — downloads one directly available free file into the dedicated download directory after explicit Fab EULA acknowledgement. It refuses purchase, acquisition, library-only, ambiguous, and unsafe-path flows.

  • asset_import_unreal — converts an already-downloaded Unreal directory to self-contained GLBs plus import-report.json; Unreal Engine is not required.

  • fab_import_asset — downloads an owned listing through the user's existing FabCLI session, then runs the same Unreal importer. It never signs in, claims, or purchases an asset.

Poly Haven:

  • polyhaven_search_assets — searches HDRIs, textures, and models by text, type, and category, with relevance/popularity/date/name sorting and cursor pagination.

  • polyhaven_get_asset — returns normalized metadata, attributes, authors, dimensions, resolution, and CC0 licensing for one asset.

  • polyhaven_list_categories — returns category labels and counts for one asset type.

  • polyhaven_list_files — returns the official file URLs, sizes, MD5 hashes, and dependency relationships. Use resolution and format to select usable variants; follow nextCursor until absent to retrieve every matching file.

ambientCG:

  • ambientcg_search_assets — searches CC0 materials, HDRIs, substances, decals, atlases, 3D models, images, brushes, terrains, and HDRI elements.

  • ambientcg_get_asset — returns metadata, maps, technique, dimensions, statistics, and CC0 licensing.

  • ambientcg_list_categories — lists typed categories and asset counts.

  • ambientcg_list_files — returns official archives with variant attributes, extensions, URLs, and byte sizes.

Smithsonian 3D:

  • smithsonian_search_assets — searches Open Access models by text, format, quality, owning unit, Draco compression, and glTF orientation compliance.

  • smithsonian_get_asset — groups the file-centric API response into one model summary.

  • smithsonian_list_files — returns direct model files with format, quality, compression, and orientation metadata.

Sketchfab:

  • sketchfab_search_models — anonymously searches public models, defaulting to downloadable results, and preserves author, geometry, archive, and license metadata.

  • sketchfab_get_model — returns public detail and explicit Creative Commons requirements.

  • sketchfab_list_categories — lists public category names and slugs.

  • sketchfab_get_downloads — uses SKETCHFAB_API_TOKEN to retrieve temporary download URLs. It never stores the token or returns it in tool output.

Game audio:

  • audio_list_sources — lists the ten supported audio libraries, best uses, official browse pages, license/attribution cautions, and honest download capability (curated-direct or provider-page).

  • audio_search_assets — searches only the curated packs with stable official direct URLs. Results preserve license, commercial-use, attribution, source, size (when known), and redistribution metadata.

  • audio_download_asset — downloads a catalog asset by ID after acceptLicense: true. It uses an HTTPS host allowlist, validates every redirect, streams with a byte cap, writes atomically without overwrite, and returns the local path, byte size, and SHA-256.

Curated itch.io packs:

  • itch_list_downloads — resolves a fresh no-account download page and lists upload IDs, filenames, sizes, CC0 terms, and pack-specific cautions without exposing the signed page token.

  • itch_download_asset — resolves a fresh 60-second signed file URL and streams the selected upload into guarded storage after acceptLicense: true. Signed URLs are not returned. The initial catalog covers Tallbeard Music Loop Bundle, Quaternius Universal Animation Libraries 1 and 2, Brackeys VFX Bundle, and KayKit Platformer.

  • asset_list_bundle_entries — reads the remote ZIP directory with HTTP byte ranges and returns individual paths and sizes without downloading the archive.

  • asset_download_bundle_entry — range-fetches and extracts one selected file, caches it with a SHA-256 sidecar, and never downloads unrelated bundle files.

  • asset_list_bundle_animations — range-fetches only an aggregate GLB and lists its named animation clips. For Quaternius, it automatically prefers the standard non-root-motion GLB.

  • asset_download_bundle_animation — exports one named animation as a valid animation-only GLB, removing unrelated clips, meshes, materials, and textures. The cached aggregate GLB is reused across requests.

Unified source and download routing:

  • asset_list_sources — returns only agent-ready sources by default across 3D, textures, HDRIs, animations, VFX, 2D, UI, icons, fonts, and audio. An agent-ready source has an MCP download tool and requires no manual browser, login, checkout, donation prompt, or paywall. Pass agentReadyOnly: false to inspect the broader research directory, including package-manager, Git, and provider-page sources that are not yet integrated.

  • asset_search_sources — filters that directory by text, category, access mode, and license tag (cc0, cc-by, mit, and others), while preserving the same agent-ready-only default.

  • asset_download_file — streams a direct URL previously returned by polyhaven_list_files, ambientcg_list_files, smithsonian_list_files, or the Game-icons.net bulk archive or Kenney Particle Pack entry into guarded local storage. Provider hosts and URL shapes are allowlisted, redirects are revalidated, existing files are never overwritten, and the result includes SHA-256.

Recommended agent flow:

  1. Call asset_search_sources with the requested category/query. Its default result set is guaranteed to contain only agent-ready sources.

  2. Call the returned searchTool or detailTool when present.

  3. Resolve variants with the returned filesTool.

  4. Call the returned downloadTool with acceptLicense: true.

  5. For aggregate Quaternius animation libraries, skip whole-pack download: itch_list_downloadsasset_list_bundle_animationsasset_download_bundle_animation.

This is intentionally a short MCP tool chain rather than a fake universal URL: each provider keeps its real search/variant semantics, while source routing and the no-manual-flow guarantee stay uniform.

All discovery tools are read-only. Download tools write only within their dedicated local directories and never purchase, add to cart or library, wishlist, sign in, or overwrite an existing download. Audio packs remain subject to their source license; raw redistribution is not implied by download.

Unreal import support

The importer writes the same GLB/report layout for cooked and uncooked inputs. It auto-provisions UE Viewer for package metadata, textures, and cooked render data. For uncooked UE4 editor meshes it also provisions the separate GPL-3.0+ unreal-assets-to-glb CLI in an isolated Python virtual environment and invokes it out of process. Modern UE5 packages rejected by UE Viewer use a pinned, out-of-process CUE4Parse adapter. Set THREENATIVE_TOOLCHAIN_AUTOINSTALL=0 to require manually installed tools instead.

Input

Current result

Cooked loose UE4 static meshes and textures

GLB geometry, LOD sections, embedded textures, and common PBR reconstruction

Cooked loose UE4 skeletal meshes and standalone animation packages

Standard skinned GLB; joints, weights, inverse-bind matrices, and existing clips are preserved; ActorX PSA clips are attached by case-insensitive bone name when at least 80% of tracks match, without duplicating an existing clip name

Uncooked UE4 FMeshDescription static meshes (object versions 517–522)

GLB geometry with centimetres converted to metres; verified on Fab Office Pack Vol.1 (47/47 meshes)

Standalone Texture2D packages

Collision-free source-relative PNGs under textures/; duplicate Unreal basenames are exported independently instead of overwriting one another

TextureCube environment maps

Collision-free 2:1 equirectangular PNG or lossless Radiance HDR files under cubemaps/; CUE4Parse decodes cooked faces, while editor TSF_BGRE8 source art retains its lighting range without Unreal Engine

Texture2DArray, TextureCubeArray, and VolumeTexture

Collision-free RGBA8 slice data plus JSON dimensions under textures3d/; use DataArrayTexture for arrays and Data3DTexture for volumes. Cube-array faces remain ordered in groups of six for custom shaders. Floating-point/HDR stacks are rejected rather than reduced to RGBA8

Standalone Material and MaterialInstanceConstant packages

One directly loadable Materials/UnrealMaterialLibrary.glb; each source-relative swatch name is collision-free, follows parent instances, embeds resolved texture inputs, and applies common base-color/emissive vectors plus roughness/metallic/opacity scalars

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

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

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

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

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

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

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

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. Cooked packages using unversioned properties require one matching .usmap file beside the imported tree

Separate roughness/metalness, solid palette maps, glass, and mirrors

Packed glTF PBR maps or explicit material fallbacks; extra graph inputs remain named sidecars

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

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

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

UE4 editor LandscapeComponent heightfields

Package-relative compressed BGRA8 heightmaps become indexed terrain meshes with decoded normals, component transforms, and original material names

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

Arbitrary Unreal shader graphs

Common PBR inputs become standard glTF materials; graph inputs with no glTF counterpart remain named in the report instead of being silently discarded

Encrypted Pak/IoStore

Unsupported without user-supplied keys and archive extraction; never reported as a complete conversion

Automatic uncooked conversion needs Python 3 with venv and pip. A Linux source build fallback for UE Viewer additionally needs git, g++, perl, zlib, and SDL2 development headers. Unreal import currently runs on Linux and Windows; use the path overrides below for preinstalled executables.

Load a reconstructed level with the normal Three.js loader — no Unreal runtime or custom .uasset loader is involved:

new GLTFLoader().load("assets/fab/<listing>/Scenes/DemoMap.glb", ({ scene }) => {
  threeScene.add(scene);
});

Standalone materials use that same loader. Find a swatch by the report's materialAssets[].libraryName, then assign its standard Three.js material:

new GLTFLoader().load("assets/fab/<listing>/Materials/UnrealMaterialLibrary.glb", ({ scene }) => {
  const swatch = scene.getObjectByName(materialAsset.libraryName);
  targetMesh.material = swatch.material;
});

Sound waves use Three.js directly as well:

const buffer = await new THREE.AudioLoader().loadAsync(audioAsset.file);
sound.setBuffer(buffer);

Structured data is ordinary runtime JSON — no Unreal object loader is needed:

const table = await fetch(dataAsset.json).then((response) => response.json());
const defaultAmmo = table.Rows.Default;

Fonts can back Three.js canvas textures or text libraries that accept web fonts:

const face = new FontFace(font.family, `url(${font.file})`, {
  style: font.fontStyle,
  weight: String(font.weight),
});
await face.load();
document.fonts.add(face);
// Draw with this family on a canvas, then pass the canvas to THREE.CanvasTexture.

Paper2D flipbooks reference ordinary GLBs. Select a frame using the manifest's frameRun and framesPerSecond, then show its loaded scene:

const flipbook = await fetch(report.flipbooks[0].manifest).then((response) => response.json());
const frames = await Promise.all(flipbook.frames.map((frame) => loader.loadAsync(frame.glb)));
const tick = Math.floor(elapsedSeconds * flipbook.framesPerSecond) % flipbook.frameCount;
let end = 0;
frames.forEach(({ scene }, index) => {
  end += flipbook.frames[index].frameRun;
  scene.visible = tick < end && tick >= end - flipbook.frames[index].frameRun;
});

Cubemaps use the standard Three.js equirectangular environment path:

const loader = cubemapAsset.dynamicRange === "hdr" ? new RGBELoader() : new THREE.TextureLoader();
const environment = await loader.loadAsync(cubemapAsset.file);
environment.mapping = THREE.EquirectangularReflectionMapping;
threeScene.environment = environment;

Multidimensional textures use the report's explicit Three.js type:

const metadata = await fetch(textureStack.manifest).then((response) => response.json());
const bytes = new Uint8Array(await fetch(textureStack.data).then((response) => response.arrayBuffer()));
const texture = metadata.threeTexture === "Data3DTexture"
  ? new THREE.Data3DTexture(bytes, metadata.width, metadata.height, metadata.depth)
  : new THREE.DataArrayTexture(bytes, metadata.width, metadata.height, metadata.depth);
texture.format = THREE.RGBAFormat;
texture.type = THREE.UnsignedByteType;
texture.needsUpdate = true;

Installation-only Unreal BasicShapes/Plane references are generated locally when the asset pack does not contain that mesh. Rectangular area lights are preserved in scene metadata and approximated as punctual point lights because glTF's standard punctual-light extension has no area-light type.

Configuration

Variable

Default

Purpose

FAB_DIRECT_TIMEOUT_MS

20000

Direct JSON request timeout.

FAB_BROWSER_TIMEOUT_MS

30000

Dedicated browser request timeout.

FAB_BROWSER_MANUAL_TIMEOUT_MS

10000

Headed-mode grace period for visible verification.

FAB_BROWSER_HEADLESS

true

Set to 0 temporarily for manual verification.

FAB_BROWSER_PROFILE_DIR

OS state directory under threenative-asset-mcp/fab-browser-profile

MCP-owned Fab browser state.

FAB_DOWNLOAD_DIR

~/Downloads/threenative-asset-mcp/fab

Dedicated directory for Fab free-file downloads.

FAB_MAX_DOWNLOAD_BYTES

2147483648

Maximum accepted download size in bytes.

FAB_DOWNLOAD_TIMEOUT_MS

600000

Total timeout for one file download.

FAB_CURL_IMPERSONATE

auto-detected on PATH

curl-impersonate wrapper override; 0/off disables.

FAB_MIN_REQUEST_INTERVAL_MS

1000

Minimum spacing between direct upstream requests.

FAB_LOG_LEVEL

warn

debug, info, warn, or error.

FAB_LOG_QUERIES

false

Set to 1 only if query text may be written to logs.

SKETCHFAB_API_TOKEN

unset

User token for temporary Sketchfab download URLs.

AUDIO_DOWNLOAD_DIR

~/Downloads/threenative-asset-mcp/audio

Dedicated directory for curated audio downloads.

AUDIO_MAX_DOWNLOAD_BYTES

10737418240

Maximum accepted bytes per audio archive (10 GiB).

AUDIO_DOWNLOAD_TIMEOUT_MS

1800000

Total timeout for one audio download (30 minutes).

ASSET_DOWNLOAD_DIR

~/Downloads/threenative-asset-mcp/assets

Dedicated directory for direct provider downloads.

ASSET_MAX_DOWNLOAD_BYTES

10737418240

Maximum accepted bytes per provider file (10 GiB).

ASSET_DOWNLOAD_TIMEOUT_MS

1800000

Total timeout for one provider download (30 minutes).

THREENATIVE_TOOLCHAIN_AUTOINSTALL

1

Set to 0 to disable first-use external-tool installs.

THREENATIVE_TOOLCHAIN_DIR

OS cache under threenative-asset-mcp/toolchain

UE Viewer, FabCLI, and uncooked-converter cache.

THREENATIVE_UMODEL_PATH

auto-detected/provisioned

Absolute path to a UE Viewer executable override.

THREENATIVE_UNCOOKED_CONVERTER_PATH

auto-detected/provisioned

Absolute path to unreal-assets-to-glb.

THREENATIVE_FABCLI_PATH

auto-detected/provisioned

Absolute path to FabCLI; login remains user-controlled.

Direct requests are spaced at least FAB_MIN_REQUEST_INTERVAL_MS apart. Only HTTP 429, 502, 503, and 504 are retried, at most twice, with backoff and Retry-After support. Challenges, access denial, invalid input, missing listings, and schema drift are never retried by the transport; a curl-impersonate challenge response is additionally retried inside the impersonation wrapper with longer, jittered waits before the browser fallback is engaged.

Browser-fingerprint TLS (curl-impersonate)

Fab's /i/* JSON routes sit behind Cloudflare bot management that challenges Node's default TLS fingerprint — including plain fetch from this MCP and headless Chromium — while real browser fingerprints pass. When a curl-impersonate wrapper (for example curl_chrome146) is available on the host PATH, the server performs all anonymous JSON reads through it and no browser is needed for search, detail, or download resolution. Set FAB_CURL_IMPERSONATE=0 to force the old behavior (plain Node fetch plus the Playwright fallback), or point it at a specific wrapper binary.

Downloads of free files resolve entirely through the anonymous JSON contract when possible: listing detail → asset-formats/{format} file listing → download-info signed URL → guarded file write. The signed distribution URL is validated against an exact Epic distribution-host allowlist before use. Only when the direct path is challenged does the server fall back to the guarded browser click flow below.

Process-local cache TTLs are five minutes for search, fifteen minutes for listing details, six hours for taxonomy data, and ten minutes for promotions. The shared LRU is capped at 500 entries and is cleared on process exit.

Poly Haven API behavior

Poly Haven requests go only to https://api.polyhaven.com, with the required threenative-asset-mcp User-Agent. Asset lists are cached for 15 minutes and details, categories, and file trees are cached for up to one hour. Returned file URLs are accepted only from https://dl.polyhaven.org.

The live API is free for personal and commercial use, but use of the API requires a visible Poly Haven credit. The assets themselves are CC0. This MCP includes provider, license, and attribution fields so downstream clients can preserve that distinction. See the official API page and API documentation.

Other provider behavior

ambientCG uses its anonymous, read-only v3 API. Its files are CC0 and its API exposes searchable metadata, categories, and downloadable variants.

Smithsonian uses the anonymous Smithsonian 3D file-search API. Files exposed by that API are part of Smithsonian Open Access; the MCP retains direct source URLs and format/quality metadata.

Sketchfab public search, categories, and model detail do not require a token. The download endpoint requires a token belonging to the user, configured through SKETCHFAB_API_TOKEN. The token is sent only in the Sketchfab Authorization header, is never logged or persisted, and is not included in MCP responses. Sketchfab models use different Creative Commons licenses; always inspect license.requirements before use.

Audio direct downloads are catalog-ID based; the MCP does not accept arbitrary URLs. Official Kenney downloads are restricted to kenney.nl and Sonniss GDC downloads to downloads.sonniss.com, including redirect revalidation. Sources without a stable, verified direct contract remain discoverable as provider-page instead of being falsely presented as one-click downloads.

The generic direct downloader is intentionally narrower than an arbitrary URL fetcher. It accepts only official Poly Haven, ambientCG, Smithsonian, Game-icons.net, and curated Kenney URL contracts. The itch.io downloader uses fresh signed mirror URLs internally but never exposes them. Sketchfab signed downloads are exposed through sketchfab_get_downloads but are not persisted by the generic downloader because their temporary CDN hosts vary and require the user's token-backed session. Provider-page-only sources stay provider-page-only until a stable, license-safe download contract is verified.

Dedicated browser profile and privacy

When a direct anonymous request receives a Cloudflare challenge, the server may open Playwright Chromium with a dedicated MCP-owned profile. It never attaches to, copies, or reads the user's normal Chrome/Edge/Chromium profile, cookie database, local storage, passwords, or Epic session.

If the tool returns FAB_BROWSER_ATTENTION_REQUIRED, run the same MCP command once with FAB_BROWSER_HEADLESS=0. The MCP opens its dedicated Fab homepage at startup, so complete any visible verification before calling the tool. A tool call allows an additional FAB_BROWSER_MANUAL_TIMEOUT_MS grace period, then returns FAB_BROWSER_ATTENTION_REQUIRED rather than exceeding typical MCP client timeouts. Close the MCP process after verification and return to headless mode. The server does not solve or bypass challenges.

Browser process startup is capped at ten seconds. On a host without a working graphical session, headed mode returns FAB_UPSTREAM_UNAVAILABLE instead of hanging an MCP call; run the manual release gate on a graphical host.

To clear browser-owned Fab state, stop every threenative-asset-mcp process and move only the dedicated directory reported by your configuration out of service. The default on Linux can be cleared recoverably with:

mv -- "${XDG_STATE_HOME:-$HOME/.local/state}/threenative-asset-mcp/fab-browser-profile" \
  "${XDG_STATE_HOME:-$HOME/.local/state}/threenative-asset-mcp/fab-browser-profile.cleared"

Do not point FAB_BROWSER_PROFILE_DIR at a normal browser profile. The server rejects known normal-profile locations.

The MCP has no analytics or remote telemetry. Application logs are structured JSON written only to stderr; stdout is reserved for MCP JSON-RPC. Search query text is omitted from logs unless FAB_LOG_QUERIES=1. Raw upstream bodies, headers, cookies, tokens, stack traces, and browser profile paths are not logged or returned to the model.

Troubleshooting

FAB_CHALLENGE or FAB_BROWSER_ATTENTION_REQUIRED : Fab asked for browser verification. First check whether a curl-impersonate wrapper is installed (FAB_LOG_LEVEL=info logs fab_impersonate_enabled when active). Otherwise use the headed dedicated-profile step above. If verification continues to fail, stop; do not copy a signed-in browser session.

FAB_RATE_LIMITED : Wait for retryAfterSeconds when present. The MCP already applied its bounded retries.

FAB_UPSTREAM_CHANGED : Fab's undocumented response changed. Re-run the sanitized contract probe and update normalization and fixtures before continuing.

FAB_UPSTREAM_UNAVAILABLE : Fab is unavailable, or a currently unverified discovery contract was intentionally disabled.

The MCP host shows no tools : Build first, confirm the configured path is absolute, and run npm run inspect. Logs belong on stderr; any non-JSON stdout is a bug.

Verification

npm ci
npm run browser:install
npm run typecheck
npm test
npm run build
npm pack --dry-run
npm run inspect
npm run test:providers:live

Live checks are opt-in because they contact Fab:

npm run test:live

This runs the real anonymous search, cursor, detail, and dedicated-browser contract probe. It exits nonzero when Fab challenges the clean browser or the required contract cannot be verified. Live verification must remain anonymous, concurrency-one, capped and paced. It must never acquire, purchase, wishlist, download, or automatically solve a challenge.

npm run test:providers:live launches the compiled stdio MCP and exercises live search, detail, categories, and file discovery for ambientCG, Smithsonian 3D, and Sketchfab. If SKETCHFAB_API_TOKEN is configured it also verifies the authenticated download endpoint; otherwise it verifies the explicit authentication-required response.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jonit-dev/threenative-asset-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server