惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
J
Java Code Geeks
U
Unit 42
GbyAI
GbyAI
大猫的无限游戏
大猫的无限游戏
L
LangChain Blog
D
Docker
F
Fortinet All Blogs
N
Netflix TechBlog - Medium
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
I
InfoQ
The Cloudflare Blog
小众软件
小众软件
V
Visual Studio Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Engineering at Meta
Engineering at Meta
S
SegmentFault 最新的问题
爱范儿
爱范儿
Hugging Face - Blog
Hugging Face - Blog
P
Proofpoint News Feed
V
V2EX
月光博客
月光博客
Martin Fowler
Martin Fowler

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
I Wrote an MCP Server for My 3D Printer
Nic Lydon · 2026-05-18 · via DEV Community

I’m writing this on a Sunday afternoon. The 3D printer on my kitchen counter has been printing for 19 hours and 12 minutes. I know this because I just asked it.

Not by walking into the kitchen. By calling a tool in a Claude conversation:

// kiln_progress
{
  "status": "printing",
  "file": "looki_l1_tests.gcode",
  "layer": 257,
  "target_layer": 315,
  "progress": 0.9112598299980164,
  "print_duration_s": 69192
}

Enter fullscreen mode Exit fullscreen mode

The printer is a Flashforge Adventurer 5M. It’s named “kiln” because everything in my home lab gets a fire-themed name and I’ve already used the good ones (Furnace runs the GPUs, Forge is the inference gateway). It sits next to the cutting board. I bought it on a whim a few weeks ago and I have no idea what I’m doing with 3D printing as a hobby.

But I do know how to wrap an API in MCP tools, and the printer has two of them. So now I have 16 MCP tools for a machine I barely understand.

This post is the receipts.

The two APIs

The AD5M ships with firmware that exposes two ways in:

  • An HTTP API on port 8898 that returns JSON for things like /detail (status, fans, temps, current job). This is what the FlashForge mobile app talks to.
  • A legacy TCP port on 8899 that speaks G-code over a length-prefixed wire format. You send M115 and you get the firmware version back. Send M114 and you get the current head position.

The HTTP API is comfortable. The TCP port is from a more savage era. Both are running on the same printer.

kiln-mcp is a small TypeScript MCP server that wraps both. Read-only G-codes go through the TCP port. State-changing operations like kiln_print and kiln_control (pause/resume/cancel) go through the HTTP API. All calls carry a check code so the printer trusts them.

I also do not trust any LLM with a hot nozzle and an open command channel.

That shaped the design more than anything else. Read-only telemetry is permissive. Stateful operations are constrained. The kiln_mcode tool only accepts read-only M-codes because the first version of this server had that gate softer, and I tightened it after the second time I caught a tool call trying to send M104 (set extruder temp) inside what I thought was a status query.

Here’s what they look like in practice.

The read-only side

kiln_info just dumps firmware and build volume. Under the hood it calls M115:

Machine Type: Flashforge Adventurer 5M
Machine Name: kiln
Firmware: v3.2.7
SN: [redacted]
X: 220 Y: 220 Z: 220
Tool Count: 1

Enter fullscreen mode Exit fullscreen mode

kiln_temps is more useful. As of the moment I’m writing this paragraph:

{
  "nozzle": { "temp": 219.67, "target": 220 },
  "bed": { "temp": 59.53, "target": 60 },
  "chamber": { "temp": 0, "target": 0 }
}

Enter fullscreen mode Exit fullscreen mode

219.67 / 220 is the nozzle holding steady on PLA. 59.53 / 60 is the bed. The chamber slot exists for a heated enclosure I don’t have.

Claude Mobile and 3D Printer

kiln_files lists what’s on the printer’s storage:

looki_l1_tests.gcode
looki05.gcode
looki04.gcode
looki03.gcode
looki02.gcode
looki01.gcode
nameplate_batch_3_13-14_PLA_020mm.gcode
nameplate_batch_2_7-12_PLA_020mm.gcode
nameplate_batch_1x6_PLA_020mm.gcode
plate_1.gcode

Enter fullscreen mode Exit fullscreen mode

I’m iterating on a mount for a wearable AI camera I use, hence looki_l1_tests.gcode being on its fifth revision. The nameplate batches were for a friend. The file names are a journal.

The state-changing side

kiln_print takes one of those file names and starts the job:

kiln_print({ file_name: "looki05.gcode", level: true })

Enter fullscreen mode Exit fullscreen mode

kiln_control does pause / resume / stop. The stop is destructive in the obvious way: cancel an 18-hour print at hour 17, you have an 18-hour-old failed extrusion blob on the bed.

I don’t let Claude call kiln_control casually.

The weird one

The tool I actually built this server for is kiln_image2mesh.

I wanted the shortest possible path from “that would make a neat print” to an STL on the printer.

You hand it an image. It hands you back an STL ready to slice. It runs entirely on the iGPU of one of my mini PCs.

Under the hood:

  • A FastAPI service called Modly that auto-spawns on first use (it isn’t running right now, which is fine).
  • rembg strips the background from the image.
  • TripoSG, an image-to-3D diffusion model from VAST AI, generates the mesh.
  • A marching-cubes octree turns the implicit field into triangles.
  • Mesh simplification brings the face count down to a printable target (default 80,000 faces).
  • The result is written as an STL next to the input image.

Kiln MCP

Total time: 5 to 15 minutes depending on diffusion steps. CFG, seed, foreground ratio, face count, and steps are all parameters. Defaults are tuned for “preview-grade,” which is what I want 95% of the time.

The point: there is no cloud STL service in this pipeline. The image goes onto disk on Furnace, Modly runs locally on the iGPU, the STL lands on the same disk, and then kiln_print ships it. The only thing leaving my network is the message I typed at Claude asking it to do all of that. (And sometimes I don't run that through Claude.)

The honest bits

While drafting this, kiln_status timed out:

timeout after 8000ms (path=/detail)

Enter fullscreen mode Exit fullscreen mode

The printer’s HTTP server gets cranky when it’s deep into a long job. The legacy TCP port answered fine the whole time. Both APIs, one machine, very different attitudes about life. The MCP server papers over this poorly. That’s a TODO.

kiln_modly_status reported api_up: false. The auto-spawn handles that on the next kiln_image2mesh call, but if I were writing this server today I’d add a –prewarm flag.

The ugly old TCP side of the printer has actually been more reliable than the modern JSON API. Which feels about right.

Why bother

I write MCP servers for things at a much higher rate than is reasonable. Most of them are useful in the obvious “I can ask Claude about my pipeline state” way. A few are useful in the less obvious way of “I now have a sharp picture of what the underlying system actually exposes.”

Wrapping the printer was the second category.

The AD5M’s two APIs disagree about a lot of things: units, retry behavior, what “ready” means. Wrapping them forced me to pick a model. The MCP surface is the cleanest description of that printer I have.

That’s the part of MCP work I think people miss. Once you expose a system through tools, you stop writing wrappers and start defining semantics. You have to decide what counts as state, what counts as safe, what operations deserve retries, and what an LLM should never be allowed to do.

That, and: I can now ask Claude to print me a thing.

Relaxed 3D Printing

The amount of glue between an LLM and a hot extruder is not zero, but it’s smaller than you’d think.

When I started writing this, the printer was at layer 257 of 315. I could check again.

I’m not going to.