mcp-3d-printer-server 1.2.2 → 1.2.5
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 +236 -26
- package/dist/fulu-orca.d.ts +20 -0
- package/dist/fulu-orca.js +453 -0
- package/dist/http-server.d.ts +1 -15
- package/dist/http-server.js +6 -266
- package/dist/index.js +572 -72
- package/dist/printers/prusa.d.ts +3 -0
- package/dist/printers/prusa.js +33 -1
- package/dist/sse-server.d.ts +1 -19
- package/dist/sse-server.js +6 -316
- package/dist/stl/stl-manipulator.d.ts +11 -2
- package/dist/stl/stl-manipulator.js +162 -26
- package/package.json +32 -4
- package/src/fulu-orca.ts +602 -0
- package/src/http-server.ts +4 -323
- package/src/index.ts +642 -72
- package/src/printers/prusa.ts +38 -1
- package/src/sse-server.ts +4 -404
- package/src/stl/stl-manipulator.ts +217 -29
- package/src/test/Tape+Card+55mm+Ring.stl +0 -0
package/dist/http-server.js
CHANGED
|
@@ -1,268 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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 });
|
|
2
|
+
var _a;
|
|
3
|
+
(_a = process.env).MCP_TRANSPORT ?? (_a.MCP_TRANSPORT = "streamable-http");
|
|
4
|
+
if (!process.env.MCP_HTTP_PORT && process.env.HTTP_PORT) {
|
|
5
|
+
process.env.MCP_HTTP_PORT = process.env.HTTP_PORT;
|
|
30
6
|
}
|
|
31
|
-
|
|
32
|
-
|
|
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;
|
|
7
|
+
void import("./index.js");
|
|
8
|
+
export {};
|