mcp-3d-printer-server 1.0.2 → 1.2.1
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 +105 -29
- 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 -12
- package/dist/printers/bambu.js +319 -232
- package/dist/printers/printer-factory.d.ts +0 -1
- package/dist/printers/printer-factory.js +6 -4
- 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/dist/types.d.ts +0 -6
- package/dist/types.js +0 -20
- package/package.json +10 -4
- package/src/http-server.ts +328 -0
- package/src/index.ts +582 -50
- package/src/printers/bambu.ts +452 -299
- package/src/printers/printer-factory.ts +7 -4
- 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/src/types.ts +0 -19
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,17 +11,19 @@
|
|
|
9
11
|
[](https://www.npmjs.com/package/mcp-3d-printer-server)
|
|
10
12
|
[](https://github.com/yourusername/mcp-3d-printer-server)
|
|
11
13
|
|
|
14
|
+
|
|
15
|
+
<a href="https://glama.ai/mcp/servers/7f6v2enbgk">
|
|
16
|
+
<img width="380" height="200" src="https://glama.ai/mcp/servers/7f6v2enbgk/badge" alt="3D Printer Server MCP server" />
|
|
17
|
+
</a>
|
|
18
|
+
|
|
12
19
|
<details>
|
|
13
20
|
<summary><strong>✨ What's New / Significant Updates (as of last session)</strong></summary>
|
|
14
21
|
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
-
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **New STL Manipulation Tools:** Added `merge_vertices`, `center_model`, and `lay_flat` tools for basic model preparation using `three.js`.
|
|
21
|
-
- **Configuration Update:** Added `BAMBU_STUDIO_CONFIG_PATH` environment variable for preset loading.
|
|
22
|
-
- **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.
|
|
23
27
|
|
|
24
28
|
</details>
|
|
25
29
|
|
|
@@ -81,6 +85,7 @@
|
|
|
81
85
|
- [Visualization Limitations](#visualization-limitations)
|
|
82
86
|
- [Performance Considerations](#performance-considerations)
|
|
83
87
|
- [Testing Recommendations](#testing-recommendations)
|
|
88
|
+
- [Appendix: MCP in Practice (Code Execution, Scale, and Safety)](#appendix-mcp-in-practice-code-execution-scale-and-safety)
|
|
84
89
|
- [Badges](#badges)
|
|
85
90
|
- [License](#license)
|
|
86
91
|
|
|
@@ -206,9 +211,12 @@ TEMP_DIR=/path/to/temp/dir
|
|
|
206
211
|
# Bambu Labs specific configuration
|
|
207
212
|
BAMBU_SERIAL=your_printer_serial # REQUIRED for Bambu
|
|
208
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)
|
|
209
217
|
|
|
210
218
|
# Slicer configuration (for slice_stl tool)
|
|
211
|
-
SLICER_TYPE=prusaslicer # Options: prusaslicer, cura, slic3r, orcaslicer
|
|
219
|
+
SLICER_TYPE=prusaslicer # Options: prusaslicer, cura, slic3r, orcaslicer, bambustudio
|
|
212
220
|
SLICER_PATH=/path/to/slicer/executable
|
|
213
221
|
SLICER_PROFILE=/path/to/slicer/profile
|
|
214
222
|
|
|
@@ -217,6 +225,18 @@ SLICER_PROFILE=/path/to/slicer/profile
|
|
|
217
225
|
# Example Windows: C:\Users\your_user\AppData\Roaming\BambuStudio\user\YOUR_USER_ID
|
|
218
226
|
# Example Linux: /home/your_user/.config/BambuStudio/user/YOUR_USER_ID
|
|
219
227
|
BAMBU_STUDIO_CONFIG_PATH=
|
|
228
|
+
|
|
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=
|
|
220
240
|
```
|
|
221
241
|
|
|
222
242
|
## Usage with Claude Desktop
|
|
@@ -249,7 +269,8 @@ BAMBU_STUDIO_CONFIG_PATH=
|
|
|
249
269
|
"PRINTER_HOST": "your_printer_ip",
|
|
250
270
|
"PRINTER_TYPE": "bambu",
|
|
251
271
|
"BAMBU_SERIAL": "your_printer_serial",
|
|
252
|
-
"BAMBU_TOKEN": "your_access_token"
|
|
272
|
+
"BAMBU_TOKEN": "your_access_token",
|
|
273
|
+
"BAMBU_MODEL": "p1s"
|
|
253
274
|
}
|
|
254
275
|
}
|
|
255
276
|
}
|
|
@@ -294,8 +315,9 @@ Repetier-Server is a host software for 3D printers.
|
|
|
294
315
|
Bambu Lab printers use MQTT for status and control and FTP for file operations.
|
|
295
316
|
|
|
296
317
|
- Authentication: Serial number and access token required (set `BAMBU_SERIAL` and `BAMBU_TOKEN`)
|
|
297
|
-
-
|
|
298
|
-
-
|
|
318
|
+
- 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.
|
|
319
|
+
- Requirements: Printer must be on the same network with Developer Mode and LAN Only Mode enabled
|
|
320
|
+
- Compatible with: X1C, X1E, P1S, P1P, A1, A1 Mini, H2D
|
|
299
321
|
|
|
300
322
|
#### Finding Your Bambu Printer's Serial Number and Access Token
|
|
301
323
|
|
|
@@ -654,19 +676,22 @@ Set the temperature of a printer component.
|
|
|
654
676
|
|
|
655
677
|
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.
|
|
656
678
|
|
|
679
|
+
**`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.
|
|
680
|
+
|
|
657
681
|
```json
|
|
658
682
|
{
|
|
659
683
|
"three_mf_path": "/path/to/your_model.3mf",
|
|
660
|
-
"
|
|
661
|
-
"
|
|
662
|
-
"
|
|
663
|
-
|
|
664
|
-
"
|
|
665
|
-
"
|
|
666
|
-
"
|
|
667
|
-
"
|
|
668
|
-
"
|
|
669
|
-
"
|
|
684
|
+
"bambu_model": "p1s",
|
|
685
|
+
"bed_type": "textured_plate",
|
|
686
|
+
"host": "your_bambu_ip",
|
|
687
|
+
"bambu_serial": "YOUR_SERIAL",
|
|
688
|
+
"bambu_token": "YOUR_TOKEN",
|
|
689
|
+
"use_ams": true,
|
|
690
|
+
"ams_mapping": [0, 1, 2, 3],
|
|
691
|
+
"bed_leveling": true,
|
|
692
|
+
"flow_calibration": false,
|
|
693
|
+
"vibration_calibration": false,
|
|
694
|
+
"timelapse": false
|
|
670
695
|
}
|
|
671
696
|
```
|
|
672
697
|
|
|
@@ -742,17 +767,17 @@ Here are some example commands you can give to Claude after connecting the MCP s
|
|
|
742
767
|
|
|
743
768
|
Due to the nature of the Bambu Lab printer API, there are some limitations:
|
|
744
769
|
|
|
745
|
-
1. **
|
|
770
|
+
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.
|
|
746
771
|
|
|
747
|
-
2. **
|
|
772
|
+
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.
|
|
748
773
|
|
|
749
|
-
3. **
|
|
774
|
+
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.
|
|
750
775
|
|
|
751
|
-
4. **
|
|
776
|
+
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.
|
|
752
777
|
|
|
753
|
-
5. **
|
|
778
|
+
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.
|
|
754
779
|
|
|
755
|
-
6. **Status
|
|
780
|
+
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.
|
|
756
781
|
|
|
757
782
|
## Limitations and Considerations
|
|
758
783
|
|
|
@@ -780,6 +805,57 @@ Due to the nature of the Bambu Lab printer API, there are some limitations:
|
|
|
780
805
|
- Test modifications on simple geometries before attempting complex ones
|
|
781
806
|
- Consider running on a system with at least 4GB of available RAM for larger operations
|
|
782
807
|
|
|
808
|
+
## Appendix: MCP in Practice (Code Execution, Scale, and Safety)
|
|
809
|
+
|
|
810
|
+
Last updated: 2026-02-24
|
|
811
|
+
|
|
812
|
+
### State of MCP Right Now
|
|
813
|
+
MCP remains a high-value protocol for interoperability, but large tool surfaces have a real downside: tool schemas, retries, and tool-call traces can flood the context window. More tools often means more token overhead unless the execution model is disciplined.
|
|
814
|
+
|
|
815
|
+
### Why Code Mode / Code Execution Has Become Important
|
|
816
|
+
Recent production workflows increasingly move orchestration out of conversational turns and into executable code loops. This keeps context leaner, makes behavior easier to audit, and improves reproducibility.
|
|
817
|
+
|
|
818
|
+
Core reading:
|
|
819
|
+
- Cloudflare: https://blog.cloudflare.com/code-mode/
|
|
820
|
+
- Cloudflare: https://blog.cloudflare.com/code-execution-with-mcp/
|
|
821
|
+
- Anthropic: https://www.anthropic.com/engineering/code-execution-with-mcp
|
|
822
|
+
|
|
823
|
+
### Setup Guidance for Users
|
|
824
|
+
For practical day-to-day use, start with a codemode-oriented gateway and keep direct tool exposure narrow:
|
|
825
|
+
- `codemode-mcp`: https://github.com/jx-codes/codemode-mcp
|
|
826
|
+
- UTCP routing guidance: https://www.utcp.io
|
|
827
|
+
|
|
828
|
+
Even with strong setup, model/tool usage can still be inconsistent across client versions and model releases, so retries and deterministic fallbacks are still required.
|
|
829
|
+
|
|
830
|
+
### Peter Steinberger Workflow Pattern
|
|
831
|
+
A high-signal pattern is converting broad MCP toolsets into targeted CLIs so the model calls fewer, better-defined commands:
|
|
832
|
+
- MCPorter: https://github.com/steipete/mcporter
|
|
833
|
+
- OpenClaw: https://github.com/steipete/openclaw
|
|
834
|
+
|
|
835
|
+
### What Works Best With Which Clients
|
|
836
|
+
- Claude Code, Codex CLI, and Cursor agent modes: strong fit for direct MCP and code-execution workflows.
|
|
837
|
+
- Thin hosted chat clients: usually better when MCP servers are wrapped behind narrower CLIs/gateways.
|
|
838
|
+
- Large multi-tool servers: best when grouped into task-specific wrappers instead of exposing every tool directly.
|
|
839
|
+
|
|
840
|
+
This ecosystem changes quickly; if you are reading this later, parts of this section may already be stale.
|
|
841
|
+
|
|
842
|
+
### Prompt Injection: Risks, Consequences, and Mitigations
|
|
843
|
+
Prompt injection is still an open problem for tool-using agents. It is manageable, but not solved.
|
|
844
|
+
|
|
845
|
+
Primary risks:
|
|
846
|
+
- Hidden malicious instructions in remote content or tool output.
|
|
847
|
+
- Secret exfiltration via unintended network/tool calls.
|
|
848
|
+
- Unauthorized state changes (filesystem, infrastructure, or printer actions).
|
|
849
|
+
|
|
850
|
+
Practical mitigations:
|
|
851
|
+
- Least-privilege credentials and short-lived tokens.
|
|
852
|
+
- Strict schema validation and explicit allowlists for actions/hosts.
|
|
853
|
+
- Human confirmation gates for destructive operations.
|
|
854
|
+
- Execution sandboxing with resource/time limits.
|
|
855
|
+
- Structured logs and replayable runs for incident response.
|
|
856
|
+
|
|
857
|
+
Treat tool output as untrusted input by default.
|
|
858
|
+
|
|
783
859
|
## Badges
|
|
784
860
|
|
|
785
861
|
| Badge | Description |
|
|
@@ -795,4 +871,4 @@ Due to the nature of the Bambu Lab printer API, there are some limitations:
|
|
|
795
871
|
|
|
796
872
|
## License
|
|
797
873
|
|
|
798
|
-
GPL-2.0
|
|
874
|
+
GPL-2.0
|
|
@@ -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;
|