Skip to main content
Glama

BumpMesh MCP

A local MCP server that applies real image-driven displacement to STL/OBJ meshes using CNC Kitchen's BumpMesh/stlTexturizer. No browser, WebGL, LLM calls, or runtime network downloads are involved.

The TypeScript adapter runs the upstream JavaScript computational core directly. Python or Rust would require a JavaScript subprocess or an algorithm port without a demonstrated benefit for this workload. Node keeps a single runtime and the original mesh algorithms. A worker thread keeps mesh processing off the MCP event loop; this is isolation and cancellation support, not a claim of faster geometry processing.

Install and run

Requires Node.js 22+ and npm. Development was tested on macOS Apple Silicon. sharp uses a native image decoder with platform-specific prebuilt packages; a supported Node/platform combination is required.

git clone https://github.com/kolengri/bumpmesh-mcp.git
cd bumpmesh-mcp
npm ci
npm run build
mkdir -p files
node dist/index.js --root "$PWD/files"

The default transport is stdio. Waiting silently for MCP messages is expected. Diagnostics go to stderr; stdout is reserved for MCP.

npm test
npm run verify:vendor
npm run demo

The demo creates a synthetic cube, a grayscale texture, textured STL and 3MF, and a JSON report under a new demo-output/<timestamp> directory. It exercises the real stdio MCP protocol. No personal files are read or uploaded.

Related MCP server: obrobka-mcp

Tools

Tool

Purpose

list_supported_formats

Formats, limits, units, upstream version, and configured file root

inspect_model

Triangle count, bounds, degenerate triangles, open/non-manifold edge counts

apply_texture

Load, subdivide, displace, optionally decimate/repair, export, and return diagnostics

Inputs: ASCII/binary STL and OBJ geometry. OBJ meshes are merged; materials, UVs, lines, and points are ignored. Textures: single-frame PNG, JPEG, WebP. Exports: binary STL and single-mesh 3MF in millimeters. STEP and 3MF import are deliberately outside this initial adapter, even though the browser application supports them.

Example apply_texture arguments (paths are relative to the configured root):

{
  "model_path": "housing.stl",
  "texture_path": "height-map.png",
  "output_path": "housing-textured.stl",
  "projection": "triplanar",
  "amplitude_mm": 0.3,
  "tile_size_mm": 5,
  "refine_length_mm": 0.4,
  "max_triangles": 100000,
  "exclude_bottom_deg": 5,
  "exclude_top_deg": 5
}

Parameter semantics

  • Coordinates are interpreted as millimeters, with Z up. No scaling, recentering, orientation conversion, or automatic repair of the input is performed.

  • projection: planar_xy, planar_xz, planar_yz, cylindrical (Z axis), spherical, triplanar (default), or cubic.

  • amplitude_mm: signed displacement at white, default 0.3. Black is zero. Negative values engrave. symmetric: true instead maps brightness to [-0.5, +0.5] times amplitude. invert: true reverses brightness first.

  • RGB textures are converted to 8-bit grayscale using weighted encoded sRGB channels (0.2126 R + 0.7152 G + 0.0722 B); alpha is ignored. Upstream samples the resulting red channel with tiled bilinear interpolation. This preserves upstream behavior for grayscale maps; color handling is explicitly defined by this adapter.

  • tile_size_mm: nominal square repeat size, default 5. The core preserves image aspect ratio, so a non-square image produces a proportionally shorter tile on its short axis. offset_u/offset_v are texture cycles; rotation_deg rotates projection coordinates.

  • refine_length_mm: target maximum edge length before displacement, default 0.5. Smaller values increase memory and work. max_triangles is a soft output target after subdivision, not a memory cap; masks and repair can exceed it.

  • exclude_bottom_deg and exclude_top_deg: exclude faces whose normals are within the given angle of -Z/+Z. Zero disables each mask. Shared mask-boundary vertices are pinned by the core. boundary_falloff_mm tapers displacement near masked boundaries. This does not protect arbitrary holes, threads, or vertical mating surfaces.

  • Regularization and flat-face harvesting are disabled in this minimal adapter. Decimation and upstream T-junction repair run only when the output exceeds the triangle target.

The schema returned by tools/list is authoritative for defaults and ranges. Results include the normalized parameters, before/after measurements, output path, byte count, SHA-256, and warnings. Files with degenerate triangles can be inspected but must be repaired before texturing.

Codex connection

Build first. Replace both paths below with absolute paths on the machine where Codex runs. Use command -v node to locate Node if a desktop launcher has a different PATH.

codex mcp add bumpmesh -- /absolute/path/to/node /absolute/path/to/bumpmesh-mcp/dist/index.js --root /absolute/path/to/3d-files
codex mcp list

Equivalent scoped entry in ~/.codex/config.toml (preserve all existing entries):

[mcp_servers.bumpmesh]
command = "/absolute/path/to/node"
args = ["/absolute/path/to/bumpmesh-mcp/dist/index.js", "--root", "/absolute/path/to/3d-files"]
startup_timeout_sec = 20
tool_timeout_sec = 150

The CLI command saves the server but does not prove the active task loaded it. Reload MCP connections or start a new task as required by your client. For a connection-only check, discover the three tools and call list_supported_formats; no model mutation is needed. The project tests separately prove successful mesh processing on disposable fixtures.

Official reference: Codex MCP configuration.

ChatGPT / Streamable HTTP

This server also implements stateless Streamable HTTP:

BUMPMESH_ROOT=/absolute/path/to/3d-files node dist/index.js --http --port 3000

Endpoint: http://127.0.0.1:3000/mcp. It binds only to loopback, rejects browser Origin headers and unexpected Host headers, and supports optional BUMPMESH_HTTP_TOKEN bearer authentication. It is not an OAuth authorization server.

ChatGPT's web developer mode connects to remote SSE/Streamable HTTP endpoints, not this machine's stdio or localhost. For a real ChatGPT connection:

  1. Run the HTTP server on the host holding the intended files, with a dedicated root folder.

  2. Put it behind an HTTPS endpoint with an MCP-compatible OAuth gateway. The gateway must validate users, forward requests to loopback, rewrite Host to 127.0.0.1:3000, and avoid forwarding browser Origin. It can inject BUMPMESH_HTTP_TOKEN as an upstream bearer token. Allow requests lasting at least 150 seconds. OAuth/gateway deployment is not included here.

  3. Enable ChatGPT Developer mode in Settings, create an app for the public HTTPS /mcp endpoint, and configure OAuth for that gateway. The UI and account eligibility are described in the official link below. ChatGPT does not document a generic static-bearer connector mode; do not assume the server's token environment variable alone supplies ChatGPT authentication.

  4. Select the app, discover tools, and call list_supported_formats to verify the root and formats. Files must already exist on the server host; ChatGPT attachments are not automatically synchronized. The current API returns paths and metadata, not downloadable attachments.

For a short disposable test, ChatGPT also supports No Authentication, but this would expose the selected folder's MCP operations to anyone who reaches the endpoint. Use only generated fixtures in an isolated environment, then stop the endpoint. No tunnel or public service is started by this project.

Local HTTP protocol behavior is covered by integration tests. A live ChatGPT connection, gateway authentication, and remote file delivery have not been tested or deployed.

Official reference: ChatGPT Developer mode (remote transports and OAuth/No Authentication).

Agent skill

The instruction-only skill is skills/bumpmesh-texturing/SKILL.md. It covers projection/height semantics, fit boundaries, inspection, error recovery, and evidence limits. It needs this MCP server, not another runtime or paid API.

To install for Codex, copy its folder to your skill directory (do not overwrite an existing customized copy):

mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
cp -R skills/bumpmesh-texturing "${CODEX_HOME:-$HOME/.codex}/skills/"

Reload skills/start a new task if required. Invoke $bumpmesh-texturing with the local model and height-map paths. Other MCP clients can use the same workflow as instructions; installing a local Codex skill does not automatically install it in ChatGPT web.

Limits and failure behavior

  • Required explicit file root; inputs and outputs must remain inside it, including resolved symlinks. Parent output folders must already exist. Existing files, including dangling symlinks, are never replaced.

  • One mesh job at a time; concurrent jobs receive BUSY. Jobs run in a worker with a 120-second timeout and a 768 MiB V8 old-generation limit. Native allocations and typed-array buffers are not covered by that heap limit; this is not an OS sandbox or a total RAM guarantee.

  • 32 MiB model files, 16 MiB texture files, 4096² decoded pixels, 100,000 input triangles. Subdivision progress is checked against 500,000 intermediate triangles; a subdivision iteration can allocate beyond the threshold before the callback aborts. Very large or adversarial inputs should use separate process/container memory limits.

  • Output publication uses a same-directory temporary file and an exclusive hard link. Cancellation before publication removes the temporary file; cancellation after the atomic publication point cannot undo the completed output. Abrupt process termination can leave a .bumpmesh-*.tmp file. Local filesystems supporting hard links are required.

  • TIMEOUT/MESH_BUDGET_EXCEEDED: increase refine_length_mm; reducing the final decimation target does not reduce subdivision work. OUTPUT_EXISTS: choose a new filename. DEGENERATE_GEOMETRY: repair the input.

  • Edge diagnostics weld at 0.0001 mm and do not test self-intersection, orientation consistency, slicer toolpaths, printer behavior, or physical fit. Inspect output in a slicer before printing. Input OBJ scene details and source 3MF printer settings are not preserved.

Source, license, and maintenance

Pinned upstream: a6ac179149b8a17c71a9469dd4cb6f866c0c01d1, inspected 2026-09-23 (upstream commit dated 2026-07-23, v1.2.0). UPSTREAM.md records the reused modules and exact adaptation.

This combined work is AGPL-3.0-only, retaining CNC Kitchen / Stefan Hermann and contributors' notices; see LICENSE. The vendored source and adapter source are included. If distributing binaries or offering a modified network service, provide the corresponding source under the applicable AGPL terms, including its network-interaction source requirement. A private development repository is not itself a source offer to external service users.

Three.js, fflate, Zod, and the MCP SDK use MIT licenses. Sharp uses Apache-2.0 and bundles native components with their own notices (including libvips); retain dependency license notices when redistributing installed binaries.

src/engine.ts contains reusable domain logic, src/server.ts tools, src/index.ts transports, and src/runner.ts worker lifecycle. To update upstream, compare the selected modules, keep the pristine sources and license notices, regenerate nodeExporter.js, update the commit/hash manifest deliberately, and rerun geometry and protocol tests. Do not replace the core with an approximate reimplementation.

Available Tools

3 tools
apply_textureA

Subdivide a local STL/OBJ, displace its surface using a PNG/JPEG/WebP height map, optionally decimate, and export a new STL/3MF. Use after inspect_model. Preserves input coordinates. Never overwrites files. Negative amplitude engraves. No network downloads or browser automation.

ParametersJSON Schema
NameRequiredDescriptionDefault
invertNoInvert image brightness before displacement.
offset_uNoOffset in texture cycles.
offset_vNo
symmetricNoCenter brightness at 0.5; displacement range is +/- half amplitude.
model_pathYesAbsolute path or path relative to the configured BUMPMESH_ROOT.
projectionNotriplanar
output_pathYesNew .stl or .3mf file inside BUMPMESH_ROOT. Parent directory must exist; overwriting is forbidden.
amplitude_mmNoSigned displacement at white; black is zero unless symmetric=true. Negative values engrave.
rotation_degNo
texture_pathYes
tile_size_mmNoNominal square tile size; upstream preserves the image aspect ratio.
max_trianglesNoSoft decimation target, not a memory limit; repairs can increase final count.
exclude_top_degNoExclude faces within this angle of +Z. Zero disables.
refine_length_mmNoTarget maximum edge length before displacement. Smaller values cost more memory.
exclude_bottom_degNoExclude faces within this angle of -Z. Zero disables. Assumes Z-up.
boundary_falloff_mmNo

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate non-readonly and non-destructive behavior, so the bar for adding context is met by specific behavioral details: it preserves input coordinates, never overwrites files, negative amplitude engraves, and performs no network or browser actions. These descriptions go beyond the structured hints and clarify side effects and safety boundaries.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description packs the core workflow into one dense sentence and adds only high-value supplementary notes in short, scannable clauses. There is no filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex 16-parameter tool with no output schema, it covers the key invocation context: file handling, coordinate preservation, safety, ordering relative to inspect_model, and engraving semantics. It could be more explicit about return value or on-screen output, but given no output schema exists, the description still provides enough to call and interpret the tool reasonably.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 69%, leaving parameters like offset_v, rotation_deg, and boundary_falloff_mm without schema docs. The description adds thematic context (e.g., 'Negative amplitude engraves' for amplitude_mm; 'optionally decimate' for max_triangles) but does not systematically compensate for the undocumented parameters, so it matches the baseline for partial coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific action sequence (subdivide, displace, decimate, export) and lists concrete input/output formats (STL/OBJ, PNG/JPEG/WebP, STL/3MF). It clearly differentiates itself from sibling tools list_supported_formats and inspect_model by describing a transformation workflow.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description tells agents to use this after inspect_model, which establishes an expected pipeline sequence. It also states it works on local files ('local STL/OBJ', 'no network downloads or browser automation'), distinguishing it from anything remote. However, it does not explicitly state when not to use it or mention alternative tools by name.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

inspect_modelA
Read-only

Inspect a local STL/OBJ before texturing: triangles, millimeter bounds, open and non-manifold edges. Does not modify the model.

ParametersJSON Schema
NameRequiredDescriptionDefault
model_pathYesAbsolute path or path relative to the configured BUMPMESH_ROOT.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare readOnlyHint=true, and the description reinforces this with 'Does not modify the model' while adding what exactly is inspected. It does not cover failure modes or return format, but for a safe read-only inspection tool this is acceptable.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences with no filler. The first sentence front-loads the action and scope, and the second delivers the key non-mutation guarantee. Every phrase earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter read-only tool, the description covers purpose, parameter expectations, and non-modifying behavior. With no output schema, it hints at return content via 'triangles, millimeter bounds, open and non-manifold edges', but it could explicitly state output format or error behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema covers model_path semantics at 100% by describing absolute or BUMPMESH_ROOT-relative paths. The description adds value by restricting the file type to local STL/OBJ, which is not present in the schema and helps disambiguate the parameter's expected input.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Inspect'), identifies the resource ('local STL/OBJ before texturing'), and lists concrete outputs (triangles, millimeter bounds, open and non-manifold edges). It clearly differentiates from the siblings apply_texture and list_supported_formats by focusing on geometry validation rather than modification or format listing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'before texturing' provides a clear workflow context, indicating this tool is the prerequisite to apply_texture. It doesn't explicitly name alternatives or state exclusions, but the intended use case is evident and distinct from list_supported_formats.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_supported_formatsA
Read-only

List supported model/texture/export formats, processing limits, units, and the configured local file root.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the safety profile is covered. The description adds context by enumerating what data will be surfaced (formats, limits, units, file root), but it does not disclose details like whether the list is complete, how results are structured, or whether the file root is resolved at call time. This is adequate, not exceptional.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence that is front-loaded with the action verb and efficiently lists all result categories without redundancy or filler. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a no-parameter, read-only list tool, the description fully specifies what an agent will receive: supported formats, processing limits, units, and local file root. No output schema exists, but the enumerated result categories are sufficient for an agent to decide whether to call the tool and what to expect.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With zero parameters and an empty schema, there is nothing to document. The schema coverage is 100%, so the baseline of 4 applies; the description adds value by clarifying that the tool returns environment/capability information rather than taking input.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb, 'List', and names the exact resource categories: model/texture/export formats, processing limits, units, and the configured local file root. This is distinct from the sibling tools (inspect_model, apply_texture), which clearly inspect or mutate model data rather than enumerate tool capabilities.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The intended usage is implied by the purpose: call this when you need to know supported formats or environment configuration. However, the description gives no explicit when-to-use guidance or comparisons with sibling tools such as inspect_model, so an agent must infer when this is the right choice versus inspecting a specific model.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.1.0
    • First observedapply_texture
    • First observedinspect_model
    • First observedlist_supported_formats

TDQS

A4.4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a clearly distinct role: listing supported formats/configuration, inspecting a model, and applying a texture operation. There is no overlap or ambiguity in their purposes.

Naming Consistency5/5

All tool names follow a consistent snake_case verb-first pattern: list_supported_formats, inspect_model, apply_texture. The naming style is uniform and predictable.

Tool Count5/5

Three tools is a well-scoped set for a focused single-purpose server. Each tool occupies a necessary step in the local mesh texturing workflow with no redundant additions.

Completeness5/5

The tool surface covers the full intended pipeline: discover supported formats, inspect the input model, and apply a heightmap texture with export options. There are no obvious dead ends or missing operations for the stated domain.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to process images locally via file paths—converting, resizing, removing backgrounds, smart cropping, upscaling, reading or stripping metadata, and batch processing—without files ever leaving the device.
    MIT
  • F
    license
    A
    quality
    B
    maintenance
    Enables AI assistants to perform high-performance 3D CAD design, ultra-fast multi-view rendering, immutable versioned project management, and geometry/mesh verification for 3D printing on Windows.
    17
    -