mcp-3d-printer-server 1.0.3 → 1.2.2
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.
- package/README.md +95 -51
- package/dist/http-server.d.ts +16 -0
- package/dist/http-server.js +268 -0
- package/dist/index.js +462 -37
- package/dist/printers/bambu.d.ts +23 -9
- package/dist/printers/bambu.js +260 -124
- package/dist/printers/prusa.d.ts +5 -0
- package/dist/printers/prusa.js +160 -59
- package/dist/sse-server.d.ts +20 -0
- package/dist/sse-server.js +318 -0
- package/dist/stl/stl-manipulator.d.ts +5 -5
- package/dist/stl/stl-manipulator.js +32 -7
- package/package.json +8 -4
- package/src/http-server.ts +328 -0
- package/src/index.ts +582 -50
- package/src/printers/bambu.ts +456 -223
- package/src/printers/prusa.ts +218 -73
- package/src/sse-server.ts +409 -0
- package/src/stl/stl-manipulator.ts +38 -8
- package/src/test/Tape+Card+55mm+Ring.stl +0 -0
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
1
3
|
# MCP 3D Printer Server
|
|
2
4
|
|
|
3
5
|
[](https://www.npmjs.com/package/mcp-3d-printer-server)
|
|
@@ -9,6 +11,7 @@
|
|
|
9
11
|
[](https://www.npmjs.com/package/mcp-3d-printer-server)
|
|
10
12
|
[](https://github.com/yourusername/mcp-3d-printer-server)
|
|
11
13
|
|
|
14
|
+
|
|
12
15
|
<a href="https://glama.ai/mcp/servers/7f6v2enbgk">
|
|
13
16
|
<img width="380" height="200" src="https://glama.ai/mcp/servers/7f6v2enbgk/badge" alt="3D Printer Server MCP server" />
|
|
14
17
|
</a>
|
|
@@ -16,14 +19,11 @@
|
|
|
16
19
|
<details>
|
|
17
20
|
<summary><strong>✨ What's New / Significant Updates (as of last session)</strong></summary>
|
|
18
21
|
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
-
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
24
|
-
- **New STL Manipulation Tools:** Added `merge_vertices`, `center_model`, and `lay_flat` tools for basic model preparation using `three.js`.
|
|
25
|
-
- **Configuration Update:** Added `BAMBU_STUDIO_CONFIG_PATH` environment variable for preset loading.
|
|
26
|
-
- **FTP Usage Note:** Acknowledged in documentation that file operations for Bambu currently use potentially unsecured FTP via `bambu-js`.
|
|
22
|
+
- **Dual Local Transports:** Added explicit `stdio` and `streamable-http` runtime modes with environment-based transport selection.
|
|
23
|
+
- **Bambu Reliability Pass:** Fixed Bambu argument wiring bugs, added FTP-backed file operations, improved status refresh behavior, and implemented practical command paths for `startJob`, `setTemperature`, and `print_3mf`.
|
|
24
|
+
- **Blender Bridge Tooling:** Added `blender_mcp_edit_model` with optional execution mode for model-edit collaboration workflows.
|
|
25
|
+
- **Transport Behavior Tests:** Added real behavior tests for both transports (`initialize`, `tools/list`, success + failing `tools/call`, origin rejection).
|
|
26
|
+
- **Docker Modernization:** Updated Docker build flow to work without BuildKit-specific features and verified streamable HTTP initialization in container smoke testing.
|
|
27
27
|
|
|
28
28
|
</details>
|
|
29
29
|
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
- [Running with Docker](#running-with-docker)
|
|
58
58
|
- [Using Slicers with Docker](#using-slicers-with-docker)
|
|
59
59
|
- [Configuration](#configuration)
|
|
60
|
-
- [Usage
|
|
60
|
+
- [Usage](#usage)
|
|
61
61
|
- [Supported Printer Management Systems](#supported-printer-management-systems)
|
|
62
62
|
- [OctoPrint](#octoprint)
|
|
63
63
|
- [Klipper (via Moonraker)](#klipper-via-moonraker)
|
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
- [Visualization Limitations](#visualization-limitations)
|
|
86
86
|
- [Performance Considerations](#performance-considerations)
|
|
87
87
|
- [Testing Recommendations](#testing-recommendations)
|
|
88
|
+
- [Appendix: MCP Safety Notes](#appendix-mcp-safety-notes)
|
|
88
89
|
- [Badges](#badges)
|
|
89
90
|
- [License](#license)
|
|
90
91
|
|
|
@@ -210,9 +211,12 @@ TEMP_DIR=/path/to/temp/dir
|
|
|
210
211
|
# Bambu Labs specific configuration
|
|
211
212
|
BAMBU_SERIAL=your_printer_serial # REQUIRED for Bambu
|
|
212
213
|
BAMBU_TOKEN=your_access_token # REQUIRED for Bambu
|
|
214
|
+
BAMBU_MODEL=p1s # REQUIRED for Bambu: p1s, p1p, x1c, x1e, a1, a1mini, h2d
|
|
215
|
+
BED_TYPE=textured_plate # Bed plate: textured_plate, cool_plate, engineering_plate, hot_plate
|
|
216
|
+
NOZZLE_DIAMETER=0.4 # Nozzle diameter in mm (default: 0.4)
|
|
213
217
|
|
|
214
218
|
# Slicer configuration (for slice_stl tool)
|
|
215
|
-
SLICER_TYPE=prusaslicer # Options: prusaslicer, cura, slic3r, orcaslicer
|
|
219
|
+
SLICER_TYPE=prusaslicer # Options: prusaslicer, cura, slic3r, orcaslicer, bambustudio
|
|
216
220
|
SLICER_PATH=/path/to/slicer/executable
|
|
217
221
|
SLICER_PROFILE=/path/to/slicer/profile
|
|
218
222
|
|
|
@@ -221,47 +225,69 @@ SLICER_PROFILE=/path/to/slicer/profile
|
|
|
221
225
|
# Example Windows: C:\Users\your_user\AppData\Roaming\BambuStudio\user\YOUR_USER_ID
|
|
222
226
|
# Example Linux: /home/your_user/.config/BambuStudio/user/YOUR_USER_ID
|
|
223
227
|
BAMBU_STUDIO_CONFIG_PATH=
|
|
224
|
-
```
|
|
225
228
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
1
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
"PRINTER_HOST": "your_printer_ip",
|
|
238
|
-
"PRINTER_TYPE": "octoprint"
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
229
|
+
# MCP transport configuration
|
|
230
|
+
MCP_TRANSPORT=stdio # Options: stdio, streamable-http
|
|
231
|
+
MCP_HTTP_HOST=127.0.0.1
|
|
232
|
+
MCP_HTTP_PORT=3000
|
|
233
|
+
MCP_HTTP_PATH=/mcp
|
|
234
|
+
MCP_HTTP_STATEFUL=true
|
|
235
|
+
MCP_HTTP_JSON_RESPONSE=true
|
|
236
|
+
MCP_HTTP_ALLOWED_ORIGINS=http://localhost
|
|
237
|
+
|
|
238
|
+
# Optional bridge command for blender_mcp_edit_model execute=true mode
|
|
239
|
+
BLENDER_MCP_BRIDGE_COMMAND=
|
|
243
240
|
```
|
|
244
241
|
|
|
245
|
-
|
|
242
|
+
## Usage
|
|
243
|
+
|
|
244
|
+
Add this server to your MCP client's config (Claude Desktop, Claude Code, Cursor, Codex CLI, or any MCP-compatible client). The config format is the same everywhere -- an `mcpServers` entry with the command and env vars:
|
|
246
245
|
|
|
247
246
|
```json
|
|
248
247
|
{
|
|
249
248
|
"mcpServers": {
|
|
250
249
|
"3dprint": {
|
|
251
|
-
"command": "
|
|
250
|
+
"command": "npx",
|
|
251
|
+
"args": ["-y", "mcp-3d-printer-server"],
|
|
252
252
|
"env": {
|
|
253
253
|
"PRINTER_HOST": "your_printer_ip",
|
|
254
254
|
"PRINTER_TYPE": "bambu",
|
|
255
255
|
"BAMBU_SERIAL": "your_printer_serial",
|
|
256
|
-
"BAMBU_TOKEN": "your_access_token"
|
|
256
|
+
"BAMBU_TOKEN": "your_access_token",
|
|
257
|
+
"BAMBU_MODEL": "p1s",
|
|
258
|
+
"SLICER_TYPE": "bambustudio",
|
|
259
|
+
"SLICER_PATH": "/Applications/BambuStudio.app/Contents/MacOS/BambuStudio"
|
|
257
260
|
}
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
263
|
}
|
|
261
264
|
```
|
|
262
265
|
|
|
263
|
-
|
|
264
|
-
|
|
266
|
+
For non-Bambu printers, replace the Bambu-specific env vars with `API_KEY` and the appropriate `PRINTER_TYPE` (see [Supported Printer Management Systems](#supported-printer-management-systems)).
|
|
267
|
+
|
|
268
|
+
Where this config lives depends on your client:
|
|
269
|
+
|
|
270
|
+
| Client | Config location |
|
|
271
|
+
|--------|----------------|
|
|
272
|
+
| Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
273
|
+
| Claude Desktop (Windows) | `%APPDATA%\Claude\claude_desktop_config.json` |
|
|
274
|
+
| Claude Code (project) | `.mcp.json` in project root |
|
|
275
|
+
| Claude Code (global) | `~/.claude/settings.json` |
|
|
276
|
+
| Cursor | MCP settings in Cursor preferences |
|
|
277
|
+
| Codex CLI | MCP config per Codex docs |
|
|
278
|
+
|
|
279
|
+
Restart your client after editing the config.
|
|
280
|
+
|
|
281
|
+
### Recommended: use with codemode-mcp
|
|
282
|
+
|
|
283
|
+
For any MCP server with a large tool surface, wrapping it behind [codemode-mcp](https://github.com/jx-codes/codemode-mcp) dramatically reduces token usage. Instead of exposing every tool definition to the model, codemode lets the agent write code against a two-tool interface (`search()` and `execute()`), loading only the tools it needs on demand.
|
|
284
|
+
|
|
285
|
+
Anthropic and Cloudflare independently demonstrated this pattern reduces MCP token costs by up to 98%:
|
|
286
|
+
|
|
287
|
+
- [Code execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp) (Anthropic)
|
|
288
|
+
- [Code Mode: give agents an entire API in 1,000 tokens](https://blog.cloudflare.com/code-mode-mcp/) (Cloudflare)
|
|
289
|
+
|
|
290
|
+
This applies to all MCP servers, not just this one.
|
|
265
291
|
|
|
266
292
|
## Supported Printer Management Systems
|
|
267
293
|
|
|
@@ -298,8 +324,9 @@ Repetier-Server is a host software for 3D printers.
|
|
|
298
324
|
Bambu Lab printers use MQTT for status and control and FTP for file operations.
|
|
299
325
|
|
|
300
326
|
- Authentication: Serial number and access token required (set `BAMBU_SERIAL` and `BAMBU_TOKEN`)
|
|
301
|
-
-
|
|
302
|
-
-
|
|
327
|
+
- Printer model: **Required** (set `BAMBU_MODEL`). Valid values: `p1s`, `p1p`, `x1c`, `x1e`, `a1`, `a1mini`, `h2d`. This ensures the slicer generates correct G-code for your specific printer.
|
|
328
|
+
- Requirements: Printer must be on the same network with Developer Mode and LAN Only Mode enabled
|
|
329
|
+
- Compatible with: X1C, X1E, P1S, P1P, A1, A1 Mini, H2D
|
|
303
330
|
|
|
304
331
|
#### Finding Your Bambu Printer's Serial Number and Access Token
|
|
305
332
|
|
|
@@ -658,19 +685,22 @@ Set the temperature of a printer component.
|
|
|
658
685
|
|
|
659
686
|
Uploads a `.3mf` file to a Bambu printer via FTP and initiates the print job via an MQTT command. Allows overriding some print parameters like AMS mapping.
|
|
660
687
|
|
|
688
|
+
**`bambu_model` is required** -- it ensures the slicer generates G-code for the correct printer. Using the wrong model can cause the bed to crash into the nozzle and damage the printer. If not provided in the tool call and `BAMBU_MODEL` is not set in the environment, the server will ask interactively via MCP elicitation (if supported by your client) or return a clear error.
|
|
689
|
+
|
|
661
690
|
```json
|
|
662
691
|
{
|
|
663
692
|
"three_mf_path": "/path/to/your_model.3mf",
|
|
664
|
-
"
|
|
665
|
-
"
|
|
666
|
-
"
|
|
667
|
-
|
|
668
|
-
"
|
|
669
|
-
"
|
|
670
|
-
"
|
|
671
|
-
"
|
|
672
|
-
"
|
|
673
|
-
"
|
|
693
|
+
"bambu_model": "p1s",
|
|
694
|
+
"bed_type": "textured_plate",
|
|
695
|
+
"host": "your_bambu_ip",
|
|
696
|
+
"bambu_serial": "YOUR_SERIAL",
|
|
697
|
+
"bambu_token": "YOUR_TOKEN",
|
|
698
|
+
"use_ams": true,
|
|
699
|
+
"ams_mapping": [0, 1, 2, 3],
|
|
700
|
+
"bed_leveling": true,
|
|
701
|
+
"flow_calibration": false,
|
|
702
|
+
"vibration_calibration": false,
|
|
703
|
+
"timelapse": false
|
|
674
704
|
}
|
|
675
705
|
```
|
|
676
706
|
|
|
@@ -746,17 +776,17 @@ Here are some example commands you can give to Claude after connecting the MCP s
|
|
|
746
776
|
|
|
747
777
|
Due to the nature of the Bambu Lab printer API, there are some limitations:
|
|
748
778
|
|
|
749
|
-
1. **
|
|
779
|
+
1. **Printable 3MF requirement:** `print_3mf` requires a sliced 3MF that includes at least one `Metadata/plate_<n>.gcode` entry so the server can compute MD5 and start the job correctly.
|
|
750
780
|
|
|
751
|
-
2. **
|
|
781
|
+
2. **AMS behavior caveat:** The current `bambu-js` project-file command path always sends `use_ams=true`; passing `use_ams=false` is treated as best-effort and surfaced with a warning.
|
|
752
782
|
|
|
753
|
-
3. **
|
|
783
|
+
3. **Temperature control path:** Temperature updates are implemented through G-code command dispatch (`M104`/`M140`) over MQTT, so effective behavior still depends on printer firmware acceptance and current printer state.
|
|
754
784
|
|
|
755
|
-
4. **
|
|
785
|
+
4. **File transfer channel:** File operations use Bambu's FTPS path (port 990) via `bambu-js`. This is more secure than plain FTP, but still assumes a trusted local network environment and library-managed TLS behavior.
|
|
756
786
|
|
|
757
|
-
5. **
|
|
787
|
+
5. **Direct start path scope:** `startJob` currently targets `.gcode` files on printer storage; `.3mf` jobs should be initiated through `print_3mf` so metadata and plate selection are handled.
|
|
758
788
|
|
|
759
|
-
6. **Status
|
|
789
|
+
6. **Status consistency:** Status reads force a `pushall` refresh when possible, but complete real-time, event-stream status semantics across all Bambu models still need deeper hardening.
|
|
760
790
|
|
|
761
791
|
## Limitations and Considerations
|
|
762
792
|
|
|
@@ -784,6 +814,20 @@ Due to the nature of the Bambu Lab printer API, there are some limitations:
|
|
|
784
814
|
- Test modifications on simple geometries before attempting complex ones
|
|
785
815
|
- Consider running on a system with at least 4GB of available RAM for larger operations
|
|
786
816
|
|
|
817
|
+
## Appendix: MCP Safety Notes
|
|
818
|
+
|
|
819
|
+
See [Recommended: use with codemode-mcp](#recommended-use-with-codemode-mcp) for reducing token overhead with large tool surfaces.
|
|
820
|
+
|
|
821
|
+
### Prompt Injection: Risks and Mitigations
|
|
822
|
+
|
|
823
|
+
Prompt injection is an open problem for tool-using agents. Practical mitigations:
|
|
824
|
+
|
|
825
|
+
- Least-privilege credentials and short-lived tokens.
|
|
826
|
+
- Strict schema validation and explicit allowlists for actions/hosts.
|
|
827
|
+
- Human confirmation gates for destructive operations.
|
|
828
|
+
- Execution sandboxing with resource/time limits.
|
|
829
|
+
- Treat tool output as untrusted input by default.
|
|
830
|
+
|
|
787
831
|
## Badges
|
|
788
832
|
|
|
789
833
|
| Badge | Description |
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
declare class ThreeDPrinterMCPServerHTTP {
|
|
3
|
+
private server;
|
|
4
|
+
private printerFactory;
|
|
5
|
+
private stlManipulator;
|
|
6
|
+
private app;
|
|
7
|
+
constructor();
|
|
8
|
+
private setupExpressApp;
|
|
9
|
+
private handleJSONRPCRequest;
|
|
10
|
+
setupErrorHandling(): void;
|
|
11
|
+
setupHandlers(): void;
|
|
12
|
+
setupResourceHandlers(): void;
|
|
13
|
+
setupToolHandlers(): void;
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export default ThreeDPrinterMCPServerHTTP;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import express from 'express';
|
|
4
|
+
import cors from 'cors';
|
|
5
|
+
import dotenv from "dotenv";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { PrinterFactory } from "./printers/printer-factory.js";
|
|
9
|
+
import { STLManipulator } from "./stl/stl-manipulator.js";
|
|
10
|
+
// Load environment variables from .env file
|
|
11
|
+
dotenv.config();
|
|
12
|
+
// Default values
|
|
13
|
+
const DEFAULT_HOST = process.env.PRINTER_HOST || "localhost";
|
|
14
|
+
const DEFAULT_PORT = process.env.PRINTER_PORT || "80";
|
|
15
|
+
const DEFAULT_API_KEY = process.env.API_KEY || "";
|
|
16
|
+
const DEFAULT_TYPE = process.env.PRINTER_TYPE || "octoprint";
|
|
17
|
+
const TEMP_DIR = process.env.TEMP_DIR || path.join(process.cwd(), "temp");
|
|
18
|
+
// HTTP Server configuration
|
|
19
|
+
const HTTP_PORT = parseInt(process.env.HTTP_PORT || "3001");
|
|
20
|
+
// Slicer configuration
|
|
21
|
+
const DEFAULT_SLICER_TYPE = process.env.SLICER_TYPE || "prusaslicer";
|
|
22
|
+
const DEFAULT_SLICER_PATH = process.env.SLICER_PATH || "";
|
|
23
|
+
const DEFAULT_SLICER_PROFILE = process.env.SLICER_PROFILE || "";
|
|
24
|
+
// Bambu-specific default values
|
|
25
|
+
const DEFAULT_BAMBU_SERIAL = process.env.BAMBU_SERIAL || "";
|
|
26
|
+
const DEFAULT_BAMBU_TOKEN = process.env.BAMBU_TOKEN || "";
|
|
27
|
+
// Ensure temp directory exists
|
|
28
|
+
if (!fs.existsSync(TEMP_DIR)) {
|
|
29
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
class ThreeDPrinterMCPServerHTTP {
|
|
32
|
+
constructor() {
|
|
33
|
+
this.server = new Server({
|
|
34
|
+
name: "mcp-3d-printer-server",
|
|
35
|
+
version: "1.0.0"
|
|
36
|
+
}, {
|
|
37
|
+
capabilities: {
|
|
38
|
+
resources: {},
|
|
39
|
+
tools: {},
|
|
40
|
+
prompts: {}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
this.printerFactory = new PrinterFactory();
|
|
44
|
+
this.stlManipulator = new STLManipulator(TEMP_DIR);
|
|
45
|
+
this.app = express();
|
|
46
|
+
this.setupExpressApp();
|
|
47
|
+
this.setupHandlers();
|
|
48
|
+
this.setupErrorHandling();
|
|
49
|
+
}
|
|
50
|
+
setupExpressApp() {
|
|
51
|
+
// Enable CORS for all origins
|
|
52
|
+
this.app.use(cors({
|
|
53
|
+
origin: '*',
|
|
54
|
+
methods: ['GET', 'POST', 'OPTIONS'],
|
|
55
|
+
allowedHeaders: ['Content-Type', 'Accept'],
|
|
56
|
+
credentials: true
|
|
57
|
+
}));
|
|
58
|
+
// Parse JSON bodies
|
|
59
|
+
this.app.use(express.json({ limit: '50mb' }));
|
|
60
|
+
// Main MCP endpoint - supports both standard and streaming responses
|
|
61
|
+
this.app.post('/mcp', async (req, res) => {
|
|
62
|
+
const acceptHeader = req.get('Accept') || '';
|
|
63
|
+
const wantsStream = acceptHeader.includes('text/event-stream');
|
|
64
|
+
try {
|
|
65
|
+
const requests = Array.isArray(req.body) ? req.body : [req.body];
|
|
66
|
+
const responses = [];
|
|
67
|
+
for (const request of requests) {
|
|
68
|
+
const response = await this.handleJSONRPCRequest(request);
|
|
69
|
+
if (response) {
|
|
70
|
+
responses.push(response);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// If client wants streaming and we have responses, use SSE format
|
|
74
|
+
if (wantsStream && responses.length > 0) {
|
|
75
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
76
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
77
|
+
res.setHeader('Connection', 'keep-alive');
|
|
78
|
+
for (const response of responses) {
|
|
79
|
+
res.write(`data: ${JSON.stringify(response)}\n\n`);
|
|
80
|
+
}
|
|
81
|
+
res.end();
|
|
82
|
+
}
|
|
83
|
+
else if (responses.length > 0) {
|
|
84
|
+
// Standard JSON response
|
|
85
|
+
res.json(responses.length === 1 ? responses[0] : responses);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// No responses (notifications)
|
|
89
|
+
res.status(202).send();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error('Error handling request:', error);
|
|
94
|
+
res.status(500).json({
|
|
95
|
+
jsonrpc: "2.0",
|
|
96
|
+
error: {
|
|
97
|
+
code: -32603,
|
|
98
|
+
message: "Internal error",
|
|
99
|
+
data: error instanceof Error ? error.message : String(error)
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// GET endpoint for establishing SSE stream (Streamable HTTP)
|
|
105
|
+
this.app.get('/mcp', async (req, res) => {
|
|
106
|
+
const acceptHeader = req.get('Accept') || '';
|
|
107
|
+
if (!acceptHeader.includes('text/event-stream')) {
|
|
108
|
+
res.status(405).json({
|
|
109
|
+
error: 'Method not allowed. Use POST for non-streaming requests.'
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Set SSE headers
|
|
114
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
115
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
116
|
+
res.setHeader('Connection', 'keep-alive');
|
|
117
|
+
res.setHeader('X-Accel-Buffering', 'no');
|
|
118
|
+
// Send initial connection event
|
|
119
|
+
res.write(`event: open\ndata: {"type": "connection", "status": "connected"}\n\n`);
|
|
120
|
+
// Keep connection alive with periodic pings
|
|
121
|
+
const pingInterval = setInterval(() => {
|
|
122
|
+
if (!res.writableEnded) {
|
|
123
|
+
res.write(`event: ping\ndata: ${new Date().toISOString()}\n\n`);
|
|
124
|
+
}
|
|
125
|
+
}, 30000);
|
|
126
|
+
// Handle client disconnect
|
|
127
|
+
req.on('close', () => {
|
|
128
|
+
clearInterval(pingInterval);
|
|
129
|
+
res.end();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
// Health check endpoint
|
|
133
|
+
this.app.get('/health', (req, res) => {
|
|
134
|
+
res.json({
|
|
135
|
+
status: 'ok',
|
|
136
|
+
server: 'mcp-3d-printer-server',
|
|
137
|
+
transport: 'streamable-http',
|
|
138
|
+
version: '1.0.0'
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
// Root endpoint
|
|
142
|
+
this.app.get('/', (req, res) => {
|
|
143
|
+
res.json({
|
|
144
|
+
name: 'MCP 3D Printer Server (HTTP)',
|
|
145
|
+
version: '1.0.0',
|
|
146
|
+
endpoints: {
|
|
147
|
+
mcp: '/mcp',
|
|
148
|
+
health: '/health'
|
|
149
|
+
},
|
|
150
|
+
transport: 'streamable-http',
|
|
151
|
+
features: ['stdio', 'sse', 'http'],
|
|
152
|
+
documentation: 'https://github.com/DMontgomery40/mcp-3D-printer-server'
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
async handleJSONRPCRequest(request) {
|
|
157
|
+
try {
|
|
158
|
+
// Route based on method
|
|
159
|
+
switch (request.method) {
|
|
160
|
+
case 'initialize':
|
|
161
|
+
return {
|
|
162
|
+
jsonrpc: "2.0",
|
|
163
|
+
id: request.id,
|
|
164
|
+
result: {
|
|
165
|
+
protocolVersion: "2025-03-26",
|
|
166
|
+
capabilities: this.server.capabilities,
|
|
167
|
+
serverInfo: {
|
|
168
|
+
name: "mcp-3d-printer-server",
|
|
169
|
+
version: "1.0.0"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
case 'resources/list':
|
|
174
|
+
const resourcesResult = await this.server.handleRequest(request);
|
|
175
|
+
return {
|
|
176
|
+
jsonrpc: "2.0",
|
|
177
|
+
id: request.id,
|
|
178
|
+
result: resourcesResult
|
|
179
|
+
};
|
|
180
|
+
case 'resources/read':
|
|
181
|
+
const readResult = await this.server.handleRequest(request);
|
|
182
|
+
return {
|
|
183
|
+
jsonrpc: "2.0",
|
|
184
|
+
id: request.id,
|
|
185
|
+
result: readResult
|
|
186
|
+
};
|
|
187
|
+
case 'tools/list':
|
|
188
|
+
const toolsResult = await this.server.handleRequest(request);
|
|
189
|
+
return {
|
|
190
|
+
jsonrpc: "2.0",
|
|
191
|
+
id: request.id,
|
|
192
|
+
result: toolsResult
|
|
193
|
+
};
|
|
194
|
+
case 'tools/call':
|
|
195
|
+
const callResult = await this.server.handleRequest(request);
|
|
196
|
+
return {
|
|
197
|
+
jsonrpc: "2.0",
|
|
198
|
+
id: request.id,
|
|
199
|
+
result: callResult
|
|
200
|
+
};
|
|
201
|
+
default:
|
|
202
|
+
if (request.id) {
|
|
203
|
+
return {
|
|
204
|
+
jsonrpc: "2.0",
|
|
205
|
+
id: request.id,
|
|
206
|
+
error: {
|
|
207
|
+
code: -32601,
|
|
208
|
+
message: `Method not found: ${request.method}`
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return null; // Notification
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
if (request.id) {
|
|
217
|
+
return {
|
|
218
|
+
jsonrpc: "2.0",
|
|
219
|
+
id: request.id,
|
|
220
|
+
error: {
|
|
221
|
+
code: -32603,
|
|
222
|
+
message: "Internal error",
|
|
223
|
+
data: error instanceof Error ? error.message : String(error)
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
setupErrorHandling() {
|
|
231
|
+
this.server.onerror = (error) => {
|
|
232
|
+
console.error("[MCP Error]", error);
|
|
233
|
+
};
|
|
234
|
+
process.on("SIGINT", async () => {
|
|
235
|
+
console.log("\nShutting down HTTP server...");
|
|
236
|
+
await this.printerFactory.disconnectAll();
|
|
237
|
+
await this.server.close();
|
|
238
|
+
process.exit(0);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
setupHandlers() {
|
|
242
|
+
this.setupResourceHandlers();
|
|
243
|
+
this.setupToolHandlers();
|
|
244
|
+
}
|
|
245
|
+
// Copy all handlers from original index.ts
|
|
246
|
+
setupResourceHandlers() {
|
|
247
|
+
// ... (copy from original)
|
|
248
|
+
}
|
|
249
|
+
setupToolHandlers() {
|
|
250
|
+
// ... (copy from original)
|
|
251
|
+
}
|
|
252
|
+
// Copy all delegating methods from original index.ts
|
|
253
|
+
// ... (copy all printer methods)
|
|
254
|
+
async run() {
|
|
255
|
+
// Start Express server
|
|
256
|
+
this.app.listen(HTTP_PORT, () => {
|
|
257
|
+
console.log(`🚀 MCP 3D Printer Server (HTTP) running on http://localhost:${HTTP_PORT}`);
|
|
258
|
+
console.log(`📡 MCP endpoint: http://localhost:${HTTP_PORT}/mcp`);
|
|
259
|
+
console.log(`✨ Supports both standard HTTP and Server-Sent Events`);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Check if running as main module
|
|
264
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
265
|
+
const server = new ThreeDPrinterMCPServerHTTP();
|
|
266
|
+
server.run().catch(console.error);
|
|
267
|
+
}
|
|
268
|
+
export default ThreeDPrinterMCPServerHTTP;
|