3dprint-oracle 0.1.0

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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/data/3dprint.sqlite +0 -0
  4. package/dist/data/db.d.ts +88 -0
  5. package/dist/data/db.d.ts.map +1 -0
  6. package/dist/data/db.js +175 -0
  7. package/dist/data/db.js.map +1 -0
  8. package/dist/data/schema.sql +86 -0
  9. package/dist/server.d.ts +8 -0
  10. package/dist/server.d.ts.map +1 -0
  11. package/dist/server.js +58 -0
  12. package/dist/server.js.map +1 -0
  13. package/dist/tools/compare-materials.d.ts +4 -0
  14. package/dist/tools/compare-materials.d.ts.map +1 -0
  15. package/dist/tools/compare-materials.js +79 -0
  16. package/dist/tools/compare-materials.js.map +1 -0
  17. package/dist/tools/diagnose-print-issue.d.ts +4 -0
  18. package/dist/tools/diagnose-print-issue.d.ts.map +1 -0
  19. package/dist/tools/diagnose-print-issue.js +58 -0
  20. package/dist/tools/diagnose-print-issue.js.map +1 -0
  21. package/dist/tools/get-filament.d.ts +4 -0
  22. package/dist/tools/get-filament.d.ts.map +1 -0
  23. package/dist/tools/get-filament.js +95 -0
  24. package/dist/tools/get-filament.js.map +1 -0
  25. package/dist/tools/get-material-profile.d.ts +4 -0
  26. package/dist/tools/get-material-profile.d.ts.map +1 -0
  27. package/dist/tools/get-material-profile.js +57 -0
  28. package/dist/tools/get-material-profile.js.map +1 -0
  29. package/dist/tools/list-manufacturers.d.ts +4 -0
  30. package/dist/tools/list-manufacturers.d.ts.map +1 -0
  31. package/dist/tools/list-manufacturers.js +32 -0
  32. package/dist/tools/list-manufacturers.js.map +1 -0
  33. package/dist/tools/list-materials.d.ts +4 -0
  34. package/dist/tools/list-materials.d.ts.map +1 -0
  35. package/dist/tools/list-materials.js +25 -0
  36. package/dist/tools/list-materials.js.map +1 -0
  37. package/dist/tools/recommend-material.d.ts +4 -0
  38. package/dist/tools/recommend-material.d.ts.map +1 -0
  39. package/dist/tools/recommend-material.js +195 -0
  40. package/dist/tools/recommend-material.js.map +1 -0
  41. package/dist/tools/search-filaments.d.ts +4 -0
  42. package/dist/tools/search-filaments.d.ts.map +1 -0
  43. package/dist/tools/search-filaments.js +86 -0
  44. package/dist/tools/search-filaments.js.map +1 -0
  45. package/package.json +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 gregario
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ <p align="center">
2
+ <a href="https://www.npmjs.com/package/3dprint-oracle"><img src="https://img.shields.io/npm/v/3dprint-oracle.svg" alt="npm version"></a>
3
+ <a href="https://www.npmjs.com/package/3dprint-oracle"><img src="https://img.shields.io/npm/dm/3dprint-oracle.svg" alt="npm downloads"></a>
4
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg" alt="Node.js 18+"></a>
5
+ <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-compatible-purple.svg" alt="MCP Compatible"></a>
6
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License"></a>
7
+ <a href="https://github.com/sponsors/gregario"><img src="https://img.shields.io/badge/sponsor-♥-ea4aaa.svg" alt="Sponsor"></a>
8
+ </p>
9
+
10
+ # 3dprint-oracle
11
+
12
+ 3D printing filament and materials knowledge MCP server. Gives LLMs authoritative access to 7,000+ filaments and curated material science knowledge.
13
+
14
+ ## Features
15
+
16
+ - **7,000+ filaments** from SpoolmanDB (53 manufacturers, 33 material types)
17
+ - **Material science knowledge** — properties, troubleshooting, recommendations
18
+ - **8 MCP tools** — search, lookup, compare, recommend, diagnose
19
+ - **Embedded data** — no API keys, no network calls at runtime
20
+
21
+ ## Installation
22
+
23
+ ### Claude Desktop
24
+
25
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "3dprint-oracle": {
31
+ "command": "npx",
32
+ "args": ["-y", "3dprint-oracle"]
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Tools
39
+
40
+ | Tool | Description |
41
+ |------|-------------|
42
+ | `search_filaments` | Search filaments by name, material, manufacturer, color |
43
+ | `get_filament` | Get full specs for a specific filament |
44
+ | `list_manufacturers` | Browse manufacturers with filament counts |
45
+ | `list_materials` | Browse material types with counts |
46
+ | `get_material_profile` | Authoritative properties for a material type |
47
+ | `compare_materials` | Side-by-side comparison of 2-3 materials |
48
+ | `recommend_material` | Get ranked recommendations for your requirements |
49
+ | `diagnose_print_issue` | Troubleshoot print problems with material-specific fixes |
50
+
51
+ ## Data Sources
52
+
53
+ - Filament data: [SpoolmanDB](https://github.com/Donkie/SpoolmanDB) (MIT license)
54
+ - Material knowledge: Hand-curated from authoritative 3D printing references
55
+
56
+ ## License
57
+
58
+ MIT
Binary file
@@ -0,0 +1,88 @@
1
+ import Database from 'better-sqlite3';
2
+ export declare function getDatabase(dataDir?: string): Database.Database;
3
+ export declare function getTableNames(db: Database.Database): string[];
4
+ export interface FilamentRow {
5
+ id: number;
6
+ name: string;
7
+ manufacturer_id: number;
8
+ manufacturer_name: string;
9
+ material_id: number;
10
+ material_name: string;
11
+ density: number | null;
12
+ diameter: number;
13
+ weight: number | null;
14
+ spool_weight: number | null;
15
+ extruder_temp: number | null;
16
+ extruder_temp_min: number | null;
17
+ extruder_temp_max: number | null;
18
+ bed_temp: number | null;
19
+ bed_temp_min: number | null;
20
+ bed_temp_max: number | null;
21
+ color_name: string | null;
22
+ color_hex: string | null;
23
+ finish: string | null;
24
+ translucent: number;
25
+ glow: number;
26
+ }
27
+ export interface SearchFilters {
28
+ material?: string;
29
+ manufacturer?: string;
30
+ diameter?: number;
31
+ }
32
+ export interface SearchResult {
33
+ rows: FilamentRow[];
34
+ total: number;
35
+ }
36
+ export declare function searchFilaments(db: Database.Database, query: string, filters?: SearchFilters, limit?: number, offset?: number): SearchResult;
37
+ export declare function getFilamentById(db: Database.Database, id: number): FilamentRow | null;
38
+ export declare function getFilamentByName(db: Database.Database, name: string): FilamentRow | null;
39
+ export interface ManufacturerRow {
40
+ id: number;
41
+ name: string;
42
+ website: string | null;
43
+ country: string | null;
44
+ filament_count: number;
45
+ }
46
+ export declare function listManufacturers(db: Database.Database, materialFilter?: string): ManufacturerRow[];
47
+ export interface MaterialRow {
48
+ id: number;
49
+ name: string;
50
+ density: number | null;
51
+ extruder_temp: number | null;
52
+ bed_temp: number | null;
53
+ filament_count: number;
54
+ }
55
+ export declare function listMaterials(db: Database.Database): MaterialRow[];
56
+ export interface MaterialProfileRow {
57
+ id: number;
58
+ material_name: string;
59
+ print_temp_min: number | null;
60
+ print_temp_max: number | null;
61
+ bed_temp_min: number | null;
62
+ bed_temp_max: number | null;
63
+ strength: string;
64
+ flexibility: string;
65
+ uv_resistance: string;
66
+ food_safe: string;
67
+ moisture_sensitivity: string;
68
+ difficulty: string;
69
+ typical_uses: string;
70
+ pros: string;
71
+ cons: string;
72
+ nozzle_notes: string | null;
73
+ enclosure_needed: number;
74
+ }
75
+ export declare function getMaterialProfile(db: Database.Database, name: string): MaterialProfileRow | null;
76
+ export declare function getAllMaterialProfiles(db: Database.Database): MaterialProfileRow[];
77
+ export interface TroubleshootingRow {
78
+ id: number;
79
+ symptom: string;
80
+ material_name: string | null;
81
+ cause: string;
82
+ fix: string;
83
+ probability: string;
84
+ }
85
+ export declare function getTroubleshooting(db: Database.Database, symptom: string, material?: string): TroubleshootingRow[];
86
+ export declare function getAvailableSymptoms(db: Database.Database): string[];
87
+ export declare function getAvailableMaterialNames(db: Database.Database): string[];
88
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/data/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,175 @@
1
+ import Database from 'better-sqlite3';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ const DB_FILENAME = '3dprint.sqlite';
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const SCHEMA_PATH = path.join(__dirname, 'schema.sql');
8
+ export function getDatabase(dataDir) {
9
+ let db;
10
+ if (dataDir === ':memory:') {
11
+ db = new Database(':memory:');
12
+ }
13
+ else {
14
+ const dir = dataDir ?? __dirname;
15
+ const dbPath = dir.endsWith('.sqlite') ? dir : path.join(dir, DB_FILENAME);
16
+ const parentDir = path.dirname(dbPath);
17
+ if (!fs.existsSync(parentDir)) {
18
+ fs.mkdirSync(parentDir, { recursive: true });
19
+ }
20
+ db = new Database(dbPath);
21
+ }
22
+ db.pragma('journal_mode = WAL');
23
+ db.pragma('foreign_keys = ON');
24
+ initializeSchema(db);
25
+ return db;
26
+ }
27
+ function initializeSchema(db) {
28
+ const schema = fs.readFileSync(SCHEMA_PATH, 'utf-8');
29
+ db.exec(schema);
30
+ }
31
+ export function getTableNames(db) {
32
+ const rows = db
33
+ .prepare("SELECT name FROM sqlite_master WHERE type IN ('table', 'trigger') AND name NOT LIKE 'sqlite_%' ORDER BY name")
34
+ .all();
35
+ return rows.map((r) => r.name);
36
+ }
37
+ /**
38
+ * Sanitize a query string for FTS5 MATCH — escape special characters.
39
+ */
40
+ function sanitizeFtsQuery(query) {
41
+ // Remove FTS5 special chars: " * ^ ( ) { } [ ] + - ~ : OR AND NOT
42
+ return query.replace(/["\*\^\(\)\{\}\[\]\+\-~:]/g, ' ').trim();
43
+ }
44
+ const FILAMENT_BASE_SELECT = `
45
+ SELECT f.*, m.name AS manufacturer_name
46
+ FROM filaments f
47
+ JOIN manufacturers m ON f.manufacturer_id = m.id
48
+ `;
49
+ export function searchFilaments(db, query, filters = {}, limit = 20, offset = 0) {
50
+ const conditions = [];
51
+ const params = [];
52
+ // FTS match
53
+ const sanitized = sanitizeFtsQuery(query);
54
+ if (sanitized.length > 0) {
55
+ // Add wildcard suffix for prefix matching
56
+ const ftsTerms = sanitized
57
+ .split(/\s+/)
58
+ .filter(Boolean)
59
+ .map((t) => `"${t}"*`)
60
+ .join(' ');
61
+ conditions.push('f.id IN (SELECT rowid FROM filaments_fts WHERE filaments_fts MATCH ?)');
62
+ params.push(ftsTerms);
63
+ }
64
+ // Filters
65
+ if (filters.material) {
66
+ conditions.push('f.material_name = ?');
67
+ params.push(filters.material);
68
+ }
69
+ if (filters.manufacturer) {
70
+ conditions.push('m.name = ?');
71
+ params.push(filters.manufacturer);
72
+ }
73
+ if (filters.diameter) {
74
+ conditions.push('f.diameter = ?');
75
+ params.push(filters.diameter);
76
+ }
77
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
78
+ // Total count
79
+ const countSql = `
80
+ SELECT COUNT(*) AS cnt
81
+ FROM filaments f
82
+ JOIN manufacturers m ON f.manufacturer_id = m.id
83
+ ${where}
84
+ `;
85
+ const { cnt } = db.prepare(countSql).get(...params);
86
+ // Page of results
87
+ const dataSql = `
88
+ ${FILAMENT_BASE_SELECT}
89
+ ${where}
90
+ ORDER BY f.name
91
+ LIMIT ? OFFSET ?
92
+ `;
93
+ const rows = db
94
+ .prepare(dataSql)
95
+ .all(...params, limit, offset);
96
+ return { rows, total: cnt };
97
+ }
98
+ export function getFilamentById(db, id) {
99
+ const row = db
100
+ .prepare(`${FILAMENT_BASE_SELECT} WHERE f.id = ?`)
101
+ .get(id);
102
+ return row ?? null;
103
+ }
104
+ export function getFilamentByName(db, name) {
105
+ const row = db
106
+ .prepare(`${FILAMENT_BASE_SELECT} WHERE f.name = ?`)
107
+ .get(name);
108
+ return row ?? null;
109
+ }
110
+ export function listManufacturers(db, materialFilter) {
111
+ if (materialFilter) {
112
+ return db
113
+ .prepare(`SELECT m.*, COUNT(f.id) AS filament_count
114
+ FROM manufacturers m
115
+ JOIN filaments f ON f.manufacturer_id = m.id
116
+ WHERE f.material_name = ?
117
+ GROUP BY m.id
118
+ ORDER BY m.name`)
119
+ .all(materialFilter);
120
+ }
121
+ return db
122
+ .prepare(`SELECT m.*, COUNT(f.id) AS filament_count
123
+ FROM manufacturers m
124
+ JOIN filaments f ON f.manufacturer_id = m.id
125
+ GROUP BY m.id
126
+ ORDER BY m.name`)
127
+ .all();
128
+ }
129
+ export function listMaterials(db) {
130
+ return db
131
+ .prepare(`SELECT mat.*, COUNT(f.id) AS filament_count
132
+ FROM materials mat
133
+ JOIN filaments f ON f.material_id = mat.id
134
+ GROUP BY mat.id
135
+ ORDER BY mat.name`)
136
+ .all();
137
+ }
138
+ export function getMaterialProfile(db, name) {
139
+ const row = db
140
+ .prepare('SELECT * FROM material_profiles WHERE material_name = ?')
141
+ .get(name);
142
+ return row ?? null;
143
+ }
144
+ export function getAllMaterialProfiles(db) {
145
+ return db
146
+ .prepare('SELECT * FROM material_profiles ORDER BY material_name')
147
+ .all();
148
+ }
149
+ export function getTroubleshooting(db, symptom, material) {
150
+ if (material) {
151
+ return db
152
+ .prepare(`SELECT * FROM troubleshooting
153
+ WHERE symptom = ? AND (material_name = ? OR material_name IS NULL)
154
+ ORDER BY probability DESC`)
155
+ .all(symptom, material);
156
+ }
157
+ return db
158
+ .prepare(`SELECT * FROM troubleshooting
159
+ WHERE symptom = ?
160
+ ORDER BY probability DESC`)
161
+ .all(symptom);
162
+ }
163
+ export function getAvailableSymptoms(db) {
164
+ const rows = db
165
+ .prepare('SELECT DISTINCT symptom FROM troubleshooting ORDER BY symptom')
166
+ .all();
167
+ return rows.map((r) => r.symptom);
168
+ }
169
+ export function getAvailableMaterialNames(db) {
170
+ const rows = db
171
+ .prepare('SELECT DISTINCT material_name FROM material_profiles ORDER BY material_name')
172
+ .all();
173
+ return rows.map((r) => r.material_name);
174
+ }
175
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/data/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ -- Manufacturers
2
+ CREATE TABLE IF NOT EXISTS manufacturers (
3
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
4
+ name TEXT NOT NULL UNIQUE,
5
+ website TEXT,
6
+ country TEXT
7
+ );
8
+
9
+ -- Materials (from SpoolmanDB materials.json)
10
+ CREATE TABLE IF NOT EXISTS materials (
11
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
12
+ name TEXT NOT NULL UNIQUE,
13
+ density REAL,
14
+ extruder_temp INTEGER,
15
+ bed_temp INTEGER
16
+ );
17
+
18
+ -- Filaments (expanded: one row per filament x color x diameter x weight combo)
19
+ CREATE TABLE IF NOT EXISTS filaments (
20
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
21
+ name TEXT NOT NULL,
22
+ manufacturer_id INTEGER NOT NULL REFERENCES manufacturers(id),
23
+ material_id INTEGER NOT NULL REFERENCES materials(id),
24
+ material_name TEXT NOT NULL,
25
+ density REAL,
26
+ diameter REAL NOT NULL,
27
+ weight REAL,
28
+ spool_weight REAL,
29
+ extruder_temp INTEGER,
30
+ extruder_temp_min INTEGER,
31
+ extruder_temp_max INTEGER,
32
+ bed_temp INTEGER,
33
+ bed_temp_min INTEGER,
34
+ bed_temp_max INTEGER,
35
+ color_name TEXT,
36
+ color_hex TEXT,
37
+ finish TEXT,
38
+ translucent INTEGER DEFAULT 0,
39
+ glow INTEGER DEFAULT 0
40
+ );
41
+
42
+ -- FTS5 for filament search
43
+ CREATE VIRTUAL TABLE IF NOT EXISTS filaments_fts USING fts5(
44
+ name,
45
+ color_name,
46
+ content='filaments',
47
+ content_rowid='id',
48
+ tokenize='porter unicode61'
49
+ );
50
+
51
+ -- Triggers to keep FTS in sync
52
+ CREATE TRIGGER IF NOT EXISTS filaments_ai AFTER INSERT ON filaments BEGIN
53
+ INSERT INTO filaments_fts(rowid, name, color_name)
54
+ VALUES (new.id, new.name, new.color_name);
55
+ END;
56
+
57
+ -- Material profiles (curated knowledge layer)
58
+ CREATE TABLE IF NOT EXISTS material_profiles (
59
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
60
+ material_name TEXT NOT NULL UNIQUE,
61
+ print_temp_min INTEGER,
62
+ print_temp_max INTEGER,
63
+ bed_temp_min INTEGER,
64
+ bed_temp_max INTEGER,
65
+ strength TEXT NOT NULL,
66
+ flexibility TEXT NOT NULL,
67
+ uv_resistance TEXT NOT NULL,
68
+ food_safe TEXT NOT NULL,
69
+ moisture_sensitivity TEXT NOT NULL,
70
+ difficulty TEXT NOT NULL,
71
+ typical_uses TEXT NOT NULL,
72
+ pros TEXT NOT NULL,
73
+ cons TEXT NOT NULL,
74
+ nozzle_notes TEXT,
75
+ enclosure_needed INTEGER DEFAULT 0
76
+ );
77
+
78
+ -- Troubleshooting (curated knowledge layer)
79
+ CREATE TABLE IF NOT EXISTS troubleshooting (
80
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
81
+ symptom TEXT NOT NULL,
82
+ material_name TEXT,
83
+ cause TEXT NOT NULL,
84
+ fix TEXT NOT NULL,
85
+ probability TEXT NOT NULL DEFAULT 'medium'
86
+ );
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ export interface ServerOptions {
4
+ dbPath?: string;
5
+ db?: import('better-sqlite3').Database;
6
+ }
7
+ export declare function createServer(options?: ServerOptions): McpServer;
8
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAgBpE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,OAAO,gBAAgB,EAAE,QAAQ,CAAC;CACxC;AAED,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,CA6B/D"}
package/dist/server.js ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { getDatabase } from './data/db.js';
5
+ import { readFileSync } from 'node:fs';
6
+ import { fileURLToPath } from 'node:url';
7
+ import path from 'node:path';
8
+ import { registerSearchFilaments } from './tools/search-filaments.js';
9
+ import { registerGetFilament } from './tools/get-filament.js';
10
+ import { registerListManufacturers } from './tools/list-manufacturers.js';
11
+ import { registerListMaterials } from './tools/list-materials.js';
12
+ import { registerGetMaterialProfile } from './tools/get-material-profile.js';
13
+ import { registerCompareMaterials } from './tools/compare-materials.js';
14
+ import { registerRecommendMaterial } from './tools/recommend-material.js';
15
+ import { registerDiagnosePrintIssue } from './tools/diagnose-print-issue.js';
16
+ export function createServer(options) {
17
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
+ let version = '0.0.0';
19
+ try {
20
+ const pkg = JSON.parse(readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
21
+ version = pkg.version;
22
+ }
23
+ catch {
24
+ // Fallback version if package.json not found (e.g., in tests)
25
+ }
26
+ const server = new McpServer({
27
+ name: '3dprint-oracle',
28
+ version,
29
+ });
30
+ const db = options?.db ?? getDatabase(options?.dbPath);
31
+ registerSearchFilaments(server, db);
32
+ registerGetFilament(server, db);
33
+ registerListManufacturers(server, db);
34
+ registerListMaterials(server, db);
35
+ registerGetMaterialProfile(server, db);
36
+ registerCompareMaterials(server, db);
37
+ registerRecommendMaterial(server, db);
38
+ registerDiagnosePrintIssue(server, db);
39
+ return server;
40
+ }
41
+ // Only start stdio when run directly
42
+ const isMain = process.argv[1] &&
43
+ fileURLToPath(import.meta.url).includes(process.argv[1]);
44
+ if (isMain) {
45
+ console.error('3dprint-oracle MCP server starting...');
46
+ const server = createServer();
47
+ const transport = new StdioServerTransport();
48
+ await server.connect(transport);
49
+ process.on('SIGINT', async () => {
50
+ await server.close();
51
+ process.exit(0);
52
+ });
53
+ process.on('SIGTERM', async () => {
54
+ await server.close();
55
+ process.exit(0);
56
+ });
57
+ }
58
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type Database from 'better-sqlite3';
3
+ export declare function registerCompareMaterials(server: McpServer, db: Database.Database): void;
4
+ //# sourceMappingURL=compare-materials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare-materials.d.ts","sourceRoot":"","sources":["../../src/tools/compare-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAQ3C,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,EAAE,EAAE,QAAQ,CAAC,QAAQ,GACpB,IAAI,CA4FN"}
@@ -0,0 +1,79 @@
1
+ import { z } from 'zod';
2
+ import { getMaterialProfile, getAvailableMaterialNames, } from '../data/db.js';
3
+ export function registerCompareMaterials(server, db) {
4
+ server.registerTool('compare_materials', {
5
+ title: 'Compare Materials',
6
+ description: 'Compare 2-3 material types side by side across all properties: strength, flexibility, heat resistance, food safety, print difficulty, and more. Useful for deciding which material to use for a project.',
7
+ inputSchema: {
8
+ materials: z
9
+ .array(z.string())
10
+ .min(2)
11
+ .max(3)
12
+ .describe('Array of 2-3 material type names to compare (e.g., ["PLA", "PETG", "ABS"])'),
13
+ },
14
+ }, async ({ materials }) => {
15
+ const profiles = [];
16
+ for (const name of materials) {
17
+ let profile = getMaterialProfile(db, name);
18
+ if (!profile) {
19
+ profile = getMaterialProfile(db, name.toUpperCase());
20
+ }
21
+ if (!profile) {
22
+ return {
23
+ isError: true,
24
+ content: [
25
+ {
26
+ type: 'text',
27
+ text: `Material "${name}" not found. Cannot compare. Available materials: ${getAvailableMaterialNames(db).join(', ')}`,
28
+ },
29
+ ],
30
+ };
31
+ }
32
+ profiles.push(profile);
33
+ }
34
+ const names = profiles.map((p) => p.material_name);
35
+ const header = `# Material Comparison: ${names.join(' vs ')}\n`;
36
+ const properties = [
37
+ { label: 'Print Temp', getter: (p) => `${p.print_temp_min}-${p.print_temp_max}°C` },
38
+ { label: 'Bed Temp', getter: (p) => `${p.bed_temp_min}-${p.bed_temp_max}°C` },
39
+ { label: 'Strength', getter: (p) => p.strength },
40
+ { label: 'Flexibility', getter: (p) => p.flexibility },
41
+ { label: 'UV Resistance', getter: (p) => p.uv_resistance },
42
+ { label: 'Food Safe', getter: (p) => p.food_safe },
43
+ { label: 'Moisture Sensitivity', getter: (p) => p.moisture_sensitivity },
44
+ { label: 'Difficulty', getter: (p) => p.difficulty },
45
+ { label: 'Enclosure Needed', getter: (p) => p.enclosure_needed ? 'Yes' : 'No' },
46
+ { label: 'Nozzle', getter: (p) => p.nozzle_notes ?? 'Standard' },
47
+ ];
48
+ // Build table
49
+ const colWidth = 25;
50
+ const labelWidth = 22;
51
+ const separator = '-'.repeat(labelWidth + colWidth * names.length + names.length + 1);
52
+ let table = `| ${'Property'.padEnd(labelWidth)}|`;
53
+ for (const name of names) {
54
+ table += ` ${name.padEnd(colWidth - 1)}|`;
55
+ }
56
+ table += '\n' + separator + '\n';
57
+ for (const prop of properties) {
58
+ let row = `| ${prop.label.padEnd(labelWidth)}|`;
59
+ for (const profile of profiles) {
60
+ const val = prop.getter(profile);
61
+ row += ` ${val.substring(0, colWidth - 2).padEnd(colWidth - 1)}|`;
62
+ }
63
+ table += row + '\n';
64
+ }
65
+ // When to use summary
66
+ const whenToUse = profiles
67
+ .map((p) => `- **${p.material_name}**: ${p.typical_uses} (${p.pros})`)
68
+ .join('\n');
69
+ const text = [
70
+ header,
71
+ table,
72
+ '',
73
+ '## When to use each',
74
+ whenToUse,
75
+ ].join('\n');
76
+ return { content: [{ type: 'text', text }] };
77
+ });
78
+ }
79
+ //# sourceMappingURL=compare-materials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare-materials.js","sourceRoot":"","sources":["../../src/tools/compare-materials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type Database from 'better-sqlite3';
3
+ export declare function registerDiagnosePrintIssue(server: McpServer, db: Database.Database): void;
4
+ //# sourceMappingURL=diagnose-print-issue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose-print-issue.d.ts","sourceRoot":"","sources":["../../src/tools/diagnose-print-issue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAO3C,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,EAAE,EAAE,QAAQ,CAAC,QAAQ,GACpB,IAAI,CAmEN"}