@glbforge/mcp
Advanced tools
+48
-4
@@ -22,3 +22,3 @@ #!/usr/bin/env node | ||
| import { analyze, createNodeIO, extrudeImage, getProfile, optimize, PROFILES, stripMaterials, toStl, } from '@glbforge/core'; | ||
| import { MeshyClient } from '@glbforge/meshy'; | ||
| import { FAL_MODELS, FalClient, MeshyClient } from '@glbforge/meshy'; | ||
| // --- .env (never overrides real env vars, never logs values) --- | ||
@@ -60,3 +60,3 @@ try { | ||
| }); | ||
| const server = new McpServer({ name: 'glbforge', version: '0.3.0' }); | ||
| const server = new McpServer({ name: 'glbforge', version: '0.4.0' }); | ||
| server.registerTool('list_profiles', { | ||
@@ -161,2 +161,4 @@ description: 'List the available web performance budget profiles (triangle/texture/file-size caps).', | ||
| .describe('Puffy-sticker dome height in meters (e.g. 0.04); supersedes bevel'), | ||
| emboss: z.number().optional() | ||
| .describe('Luminance micro-relief in meters — bright artwork rises (try depth*0.15)'), | ||
| preset: z.enum(['enamel', 'chrome', 'neon', 'acrylic', 'rubber']).optional() | ||
@@ -170,3 +172,3 @@ .describe('Material preset for the forged piece'), | ||
| }, | ||
| }, async ({ path, out, mode, threshold, width, depth, bevel, bevelSegments, layers, pillow, preset, simplify, texture, color, metallic, roughness }) => { | ||
| }, async ({ path, out, mode, threshold, width, depth, bevel, bevelSegments, layers, pillow, emboss, preset, simplify, texture, color, metallic, roughness }) => { | ||
| const bytes = await readFile(path); | ||
@@ -178,3 +180,3 @@ const rgba = color | ||
| const { doc, stats } = await extrudeImage(new Uint8Array(bytes), { | ||
| mode, threshold, width, depth, bevel, bevelSegments, layers, pillow, preset, simplify, | ||
| mode, threshold, width, depth, bevel, bevelSegments, layers, pillow, emboss, preset, simplify, | ||
| texture, color: rgba, metallic, roughness, | ||
@@ -211,2 +213,44 @@ }); | ||
| }); | ||
| server.registerTool('generate_image_to_3d', { | ||
| description: 'True volumetric 3D from a single image via open models on fal.ai GPU inference ' + | ||
| '(needs FAL_KEY): hunyuan (Hunyuan3D-2, highest quality), trellis (balanced), ' + | ||
| 'triposr (fastest). Cheaper than Meshy for most subjects; use Meshy for the richest ' + | ||
| 'PBR texturing. Returns a request id — poll generation_status, then download.', | ||
| inputSchema: { | ||
| image: z.string().describe('Local path or http(s) URL of the source image'), | ||
| model: z.enum(['hunyuan', 'trellis', 'triposr']).default('hunyuan'), | ||
| }, | ||
| }, async ({ image, model }) => { | ||
| const client = new FalClient(); | ||
| let imageUrl = image; | ||
| if (!/^https?:\/\//.test(image)) { | ||
| const ext = image.toLowerCase().split('.').pop(); | ||
| const mime = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', webp: 'image/webp' }[ext ?? '']; | ||
| if (!mime) | ||
| throw new Error(`Unsupported image extension ".${ext}"`); | ||
| imageUrl = `data:${mime};base64,${(await readFile(image)).toString('base64')}`; | ||
| } | ||
| const requestId = await client.submit(FAL_MODELS[model], imageUrl); | ||
| return json({ requestId, model, hint: 'Poll generation_status; typical time 1-3 minutes.' }); | ||
| }); | ||
| server.registerTool('generation_status', { | ||
| description: 'Check an open-model generation (from generate_image_to_3d); when COMPLETED, pass download=true with an out path to save the GLB.', | ||
| inputSchema: { | ||
| requestId: z.string(), | ||
| model: z.enum(['hunyuan', 'trellis', 'triposr']), | ||
| download: z.boolean().default(false), | ||
| out: z.string().optional().describe('Absolute output path for the .glb when downloading'), | ||
| }, | ||
| }, async ({ requestId, model, download, out }) => { | ||
| const client = new FalClient(); | ||
| const st = await client.status(FAL_MODELS[model], requestId); | ||
| if (!download || st.status !== 'COMPLETED') { | ||
| return json({ status: st.status, queuePosition: st.queuePosition }); | ||
| } | ||
| if (!out) | ||
| throw new Error('pass "out" to download the finished model'); | ||
| const bytes = await client.downloadGlb(await client.resultGlbUrl(FAL_MODELS[model], requestId)); | ||
| await writeFile(out, bytes); | ||
| return json({ status: 'COMPLETED', out, bytes: bytes.byteLength }); | ||
| }); | ||
| server.registerTool('meshy_create_task', { | ||
@@ -213,0 +257,0 @@ description: 'Start a Meshy generation task (needs MESHY_API_KEY). kind "image-to-3d" takes a ' + |
+3
-3
| { | ||
| "name": "@glbforge/mcp", | ||
| "version": "0.3.1", | ||
| "version": "0.4.0", | ||
| "type": "module", | ||
@@ -12,4 +12,4 @@ "bin": { | ||
| "zod": "^3.23.0", | ||
| "@glbforge/core": "^0.3.0", | ||
| "@glbforge/meshy": "^0.3.0" | ||
| "@glbforge/core": "^0.4.0", | ||
| "@glbforge/meshy": "^0.4.0" | ||
| }, | ||
@@ -16,0 +16,0 @@ "devDependencies": { |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
20691
13.64%342
14.77%5
25%+ Added
+ Added
- Removed
- Removed
Updated
Updated