QIDI Studio MCP Server
README.md
# QIDI Studio MCP Server
[](https://github.com/QIDITECH)
[](https://github.com/QIDITECH)
[](https://github.com/QIDITECH)
[](https://github.com/QIDITECH)
**MCP server for managing QIDI Studio 3D printing profiles locally.**
[Русская версия → README_RU.md](README_RU.md)
Connects AI assistants (Claude, Cursor, etc.) directly to your local QIDI Studio configuration files. Reads and writes filament, process, and machine presets via the Model Context Protocol.
> **⚠️ ALPHA VERSION — USE AT YOUR OWN RISK**
> The author is **not responsible** for lost or corrupted presets. Always backup before using MCP.
---
## Why this exists
**Real-time printer control** — start, pause, heat — is easier done by hand in QIDI Studio. MCP is not needed there.
**Print settings, however, are a completely different story.**
QIDI Studio contains hundreds of parameters: temperatures, retractions, fan speeds, support patterns, gaps, accelerations. Every material (PETG, PLA, ABS, composites) and every nozzle requires its own balance. Memorizing all of this and not missing a detail is difficult even for an experienced user.
This MCP solves exactly that: **it helps AI help you pick, create, and save the right preset** — safely, controllably, and with minimal token consumption. You still see the final settings in QIDI Studio before sending the job to print.
### The core problem
- Too many settings across different materials and nozzles
- Easy to forget a key parameter (e.g., `fan_max_speed` for PETG or `support_type` for complex geometry)
- Manually browsing JSON profiles in folders is inconvenient and slow
- No centralized way to "try option A, compare with option B, save the best"
### What the MCP does
- **Auto-detects** the current project from QIDI Studio configs — no need to type printer and nozzle manually
- **Filters** profiles by printer + nozzle + material — you see only what is relevant
- **Safely creates** presets — AI proposes, you confirm, a user preset is saved without overwriting system ones
- **Validates** — checks temperatures, enum values, and unknown keys before writing to disk
---
## ⚠️ Backup First
The server writes directly to QIDI Studio's configuration files. Mistakes happen.
**Where your presets are stored:**
| OS | Path |
|----|------|
| macOS | `~/Library/Application Support/QIDIStudio/user/default/` |
| Windows | `%APPDATA%\QIDIStudio\user\default\` |
| Linux | `~/.config/QIDIStudio/user/default/` |
**Backup:**
```bash
# macOS
cp -r ~/Library/Application\ Support/QIDIStudio/user/default ~/Desktop/QIDIStudio-backup
# Windows
xcopy "%APPDATA%\QIDIStudio\user\default" "%USERPROFILE%\Desktop\QIDIStudio-backup" /E /I
# Linux
cp -r ~/.config/QIDIStudio/user/default ~/QIDIStudio-backup
```
**Restore:**
```bash
# macOS
cp -r ~/Desktop/QIDIStudio-backup/* ~/Library/Application\ Support/QIDIStudio/user/default/
# Windows
xcopy "%USERPROFILE%\Desktop\QIDIStudio-backup" "%APPDATA%\QIDIStudio\user\default" /E /Y
# Linux
cp -r ~/QIDIStudio-backup/* ~/.config/QIDIStudio/user/default/
```
---
## Quick Start
### Requirements
- Python 3.10+
- QIDI Studio installed (the server reads its built-in profiles)
### Install
```bash
git clone https://github.com/QIDITECH/QIDIStudio-MCP.git
cd QIDIStudio-MCP
pip install -r requirements.txt
```
### Connect to Claude Desktop
**Option 1: Automatic (uv)**
```bash
uv run mcp install mcp_server.py --name "QIDI Studio"
```
**Option 2: Manual**
Edit Claude Desktop settings:
- macOS: `~/Library/Application Support/Claude/settings.json`
- Windows: `%APPDATA%\Claude\settings.json`
```json
{
"mcpServers": {
"qidi-studio": {
"command": "python",
"args": ["/path/to/QIDIStudio-MCP/mcp_server.py"]
}
}
}
```
### Environment Variables
| Variable | Purpose | Example |
|----------|---------|---------|
| `QIDI_STUDIO_CONFIG_PATH` | Override user presets path | `~/Library/Application Support/QIDIStudio` |
| `QIDI_STUDIO_SYSTEM_PATH` | Override system profiles path | `/Applications/QIDIStudio.app/Contents/Resources/profiles` |
---
## AI Agent Instructions (Required)
### Step 1: Detect current project (auto)
```
get_current_project_info() → {"machine": "Q2 0.4 nozzle", "filament": "Generic PETG...", "process": "0.20mm Standard @Q2"}
```
If a project is detected, **propose using the detected printer** instead of asking. Still confirm with the user.
### Step 2: If no project detected, ask the user
Show **numbered lists** and ask to pick by number. Use **compact=True** and **limit=10** to keep responses tiny:
```
Detected printers:
1. Q2
2. X-Plus 4
3. X-Max 4
Which printer? (enter number):
Nozzles for Q2:
1. 0.2
2. 0.4
3. 0.6
4. 0.8
Which nozzle? (enter number):
```
> **Filter semantics for process profiles:** `0.20mm Standard @Q2` has no nozzle in its name. It matches `printer_model="Q2"` + `nozzle_size="0.4"` **only if** its `compatible_printers` contains `"Q2 0.4 nozzle"`. Without `compatible_printers` or for a non-existent nozzle, the profile is filtered out. Always pass both printer and nozzle for process queries.
>
> **list_profiles response format:** On success, an array of profiles is returned. On empty filtered result, an object `{"profiles": [], "debug": {...}, "hint": "..."}` is returned. Check the response type: array = results, object with `profiles` = empty + reasons.
>
> **Debug on empty results:** The `debug` field contains `total_available`, `sample_names`, `name_matches`, `fallback_matches`, and `excluded_examples` — use this to understand why the filter dropped profiles.
> **Filter semantics for material_type:** Only applies to filament profiles. For process, use `printer_model` instead.
### Step 3: Always confirm before creating/updating
```
I will create preset "Glass PETG @Qidi Q2 0.4" with:
- Printer: Q2 (0.4 nozzle)
- Base: Generic PETG @Qidi Q2 0.4 nozzle
- Settings: nozzle_temperature=["255"], fan_max_speed=["0"]
Confirm? (yes/no)
```
### NEVER do this
```
❌ list_profiles("filament", "all") → returns 1000+ profiles
❌ list_profiles("filament", "all", material="PLA") → still hundreds across all printers
```
### Correct token-efficient flow
```
get_current_project_info() → auto-detect or skip
get_available_printers() → if not detected
get_available_nozzles("Q2") → if not detected
get_available_vendors() → ["Generic", "QIDI", "HATCHBOX"]
get_available_materials("Q2", "0.4") → ["PETG", "PLA"]
list_profiles("filament", "all", "Q2", "0.4", "PETG", 50, 0, True) → 3 compact profiles
read_profile(...) → inspect one
// CONFIRM with user before:
create_preset(...) → create custom
```
---
## MCP Tools
### Discovery (call these first, always filtered)
| Tool | Purpose |
|------|---------|
| `get_current_project_info()` | Auto-detect active printer/filament/process from QIDI Studio config |
| `get_available_printers()` | Printer models detected from local profiles |
| `get_available_nozzles(printer)` | Nozzle sizes for a specific printer |
| `get_available_vendors()` | Filament brands: Generic, QIDI, HATCHBOX, etc. |
| `get_available_materials(printer, nozzle)` | Materials for a printer+nozzle combo |
| `get_available_process_qualities(printer, nozzle)` | Quality presets for a printer+nozzle |
### Profile Operations
| Tool | Purpose |
|------|---------|
| `discover_settings(type, prefix, keys_only)` | Settings with prefix filter and keys-only mode to save tokens |
| `list_profiles(type, source, printer, nozzle, material, limit, offset, compact)` | **FILTERED** list — never omit filters |
| `read_profile(type, name, source)` | Read a profile's JSON |
| `create_preset(type, name, base, settings)` | Create user preset inheriting from base (only overrides passed keys) |
| `update_preset(type, name, settings)` | Update existing user preset (only passed keys) |
| `clone_preset(type, source, new_name)` | Full copy with resolved inheritance |
| `delete_preset(type, name)` | Remove user preset |
| `compare_profiles(type, a, b, max_diffs)` | Show differences (limit output) |
| `validate_preset_settings(type, settings, strict)` | Safety check + enum validation |
| `get_version_info()` | Detected QIDI Studio version |
| `get_config_directory()` | User config path |
---
## Token-Saving Design
This MCP is designed so that the AI agent spends minimum tokens on routine and maximum on decision-making.
- **Settings are read from local JSON profiles directly** — no hardcoded or outdated lists. `discover_settings` returns only what actually exists in the QIDI Studio profiles on your machine.
- **Filtering cuts 90%+ noise** — `list_profiles` with `printer_model`, `nozzle_size`, and `material_type` returns 3–15 profiles instead of 1000+.
- **Compact mode** — `compact=True` returns only name and source, no paths or metadata.
- **Prefix filtering for settings** — `discover_settings("process", "support_", ...)` returns dozens of fields instead of hundreds.
- **Keys-only mode** — `keys_only=True` gives a list of parameter names without values when you just need to know what is available.
- **Pagination and limits** — `limit=50` and `offset` prevent accidental dumps of the entire catalog.
- **Debug on empty results** — if a filter returns `[]`, the response includes `debug` with `excluded_examples` explaining why profiles were dropped. No guessing needed.
---
## Examples
### Create Glass PETG preset
```json
get_available_printers()
get_available_nozzles("Q2")
get_available_vendors()
get_available_materials("Q2", "0.4")
list_profiles("filament", "all", "Q2", "0.4", "PETG", 50, 0, True)
create_preset("filament", "Glass PETG @Qidi Q2 0.4", "Generic PETG @Qidi Q2 0.4 nozzle",
'{"nozzle_temperature": ["255"], "fan_max_speed": ["0"]}')
```
### Vase mode process
```json
get_available_printers()
get_available_nozzles("Q2")
get_available_process_qualities("Q2", "0.4")
list_profiles("process", "all", "Q2", "0.4", "", 10, 0, True)
clone_preset("process", "0.12mm High Quality @Q2", "0.12mm Vase @Q2", "system")
update_preset("process", "0.12mm Vase @Q2", '{"spiral_mode": "1", "wall_loops": "1"}')
```
### Find support-related settings
```json
discover_settings("process", "support_", False)
discover_settings("process", "", True) # just key names
```
---
## Testing
```bash
python test_server.py
```
---
## Project Structure
```
QIDIStudio-MCP/
├── mcp_server.py # MCP server (tools)
├── qidi_config_manager.py # Local profile manager
├── test_server.py # Test suite
├── requirements.txt # Dependencies (mcp)
├── .gitignore
├── README.md # This file (EN)
└── README_RU.md # Russian version
```
---
## Tested Platforms
| Platform | Status |
|----------|--------|
| macOS | ✅ Tested (primary) |
| Windows | ⚠️ Not tested |
| Linux | ⚠️ Not tested |
Please open an issue if you test on Windows or Linux.
---
## License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues