first commit

This commit is contained in:
DigiJ
2026-03-13 12:56:43 -07:00
commit 159cf9fcfe
309 changed files with 64584 additions and 0 deletions

389
mcp_config.py Normal file
View File

@@ -0,0 +1,389 @@
#!/usr/bin/env python3
"""
MCP Server Configuration and Management
This module provides utilities to configure and manage the MCP server
for LLM_Train models.
"""
import json
import os
import subprocess
import sys
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from typing import Dict, Any, Optional
from pathlib import Path
class MCPServerConfig:
"""Configuration management for the MCP server."""
def __init__(self, config_file: str = "mcp_config.json"):
self.config_file = config_file
self.default_config = {
"server": {
"name": "llm-train-models",
"version": "1.0.0",
"description": "Multi-model MCP server for LLM_Train",
"host": "localhost",
"port": 8000,
"auto_start": False
},
"models": {
"cache_size": 3,
"default_context": 4096,
"default_gpu_layers": 0
},
"logging": {
"level": "INFO",
"file": "mcp_server.log"
}
}
self.config = self.load_config()
def load_config(self) -> Dict[str, Any]:
"""Load configuration from file."""
try:
if os.path.exists(self.config_file):
with open(self.config_file, 'r') as f:
loaded = json.load(f)
# Merge with defaults
return self._merge_config(self.default_config, loaded)
return self.default_config.copy()
except Exception as e:
print(f"Error loading MCP config: {e}")
return self.default_config.copy()
def _merge_config(self, defaults: Dict, loaded: Dict) -> Dict:
"""Merge loaded config with defaults."""
result = defaults.copy()
for key, value in loaded.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = self._merge_config(result[key], value)
else:
result[key] = value
return result
def save_config(self) -> bool:
"""Save configuration to file."""
try:
with open(self.config_file, 'w') as f:
json.dump(self.config, f, indent=2)
return True
except Exception as e:
print(f"Error saving MCP config: {e}")
return False
def get(self, path: str, default: Any = None) -> Any:
"""Get config value using dot notation."""
keys = path.split('.')
value = self.config
for key in keys:
if isinstance(value, dict) and key in value:
value = value[key]
else:
return default
return value
def set(self, path: str, value: Any):
"""Set config value using dot notation."""
keys = path.split('.')
target = self.config
for key in keys[:-1]:
if key not in target:
target[key] = {}
target = target[key]
target[keys[-1]] = value
def generate_claude_config(self) -> Dict[str, Any]:
"""Generate Claude Desktop MCP configuration."""
script_path = os.path.abspath("mcp_server.py")
python_path = sys.executable
return {
"mcpServers": {
"llm-train-models": {
"command": python_path,
"args": [script_path],
"env": {
"PYTHONPATH": os.getcwd()
}
}
}
}
class MCPConfigGUI:
"""GUI for configuring the MCP server."""
def __init__(self, parent: tk.Tk):
self.parent = parent
self.config = MCPServerConfig()
self.dialog = tk.Toplevel(parent)
self.dialog.title("MCP Server Configuration")
self.dialog.geometry("600x500")
self.dialog.transient(parent)
self.dialog.grab_set()
self._build_ui()
self._load_values()
def _build_ui(self):
"""Build the configuration UI."""
notebook = ttk.Notebook(self.dialog)
notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Server Settings Tab
server_frame = ttk.Frame(notebook)
notebook.add(server_frame, text="Server")
self._build_server_tab(server_frame)
# Model Settings Tab
model_frame = ttk.Frame(notebook)
notebook.add(model_frame, text="Models")
self._build_models_tab(model_frame)
# Claude Integration Tab
claude_frame = ttk.Frame(notebook)
notebook.add(claude_frame, text="Claude Integration")
self._build_claude_tab(claude_frame)
# Buttons
button_frame = ttk.Frame(self.dialog)
button_frame.pack(fill=tk.X, padx=10, pady=(0, 10))
ttk.Button(button_frame, text="Save", command=self._save_config).pack(side=tk.RIGHT, padx=5)
ttk.Button(button_frame, text="Cancel", command=self.dialog.destroy).pack(side=tk.RIGHT)
ttk.Button(button_frame, text="Test Server", command=self._test_server).pack(side=tk.LEFT)
def _build_server_tab(self, parent: ttk.Frame):
"""Build server settings tab."""
frame = ttk.LabelFrame(parent, text="Server Configuration", padding=10)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Server name
ttk.Label(frame, text="Server Name:").grid(row=0, column=0, sticky=tk.W, pady=5)
self.server_name_var = tk.StringVar()
ttk.Entry(frame, textvariable=self.server_name_var, width=30).grid(row=0, column=1, sticky=tk.W, pady=5)
# Auto start
self.auto_start_var = tk.BooleanVar()
ttk.Checkbutton(frame, text="Auto-start server with application",
variable=self.auto_start_var).grid(row=1, column=0, columnspan=2, sticky=tk.W, pady=10)
# Logging level
ttk.Label(frame, text="Logging Level:").grid(row=2, column=0, sticky=tk.W, pady=5)
self.log_level_var = tk.StringVar()
ttk.Combobox(frame, textvariable=self.log_level_var,
values=["DEBUG", "INFO", "WARNING", "ERROR"],
state="readonly", width=15).grid(row=2, column=1, sticky=tk.W, pady=5)
# Log file
ttk.Label(frame, text="Log File:").grid(row=3, column=0, sticky=tk.W, pady=5)
log_frame = ttk.Frame(frame)
log_frame.grid(row=3, column=1, sticky=tk.W, pady=5)
self.log_file_var = tk.StringVar()
ttk.Entry(log_frame, textvariable=self.log_file_var, width=25).pack(side=tk.LEFT)
ttk.Button(log_frame, text="Browse",
command=self._browse_log_file).pack(side=tk.LEFT, padx=5)
def _build_models_tab(self, parent: ttk.Frame):
"""Build model settings tab."""
frame = ttk.LabelFrame(parent, text="Model Configuration", padding=10)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Cache size
ttk.Label(frame, text="Model Cache Size:").grid(row=0, column=0, sticky=tk.W, pady=5)
self.cache_size_var = tk.IntVar()
cache_spin = tk.Spinbox(frame, from_=1, to=10, textvariable=self.cache_size_var, width=10)
cache_spin.grid(row=0, column=1, sticky=tk.W, pady=5)
ttk.Label(frame, text="models").grid(row=0, column=2, sticky=tk.W, padx=5)
# Default context
ttk.Label(frame, text="Default Context Size:").grid(row=1, column=0, sticky=tk.W, pady=5)
self.default_ctx_var = tk.IntVar()
ctx_spin = tk.Spinbox(frame, from_=512, to=32768, increment=512,
textvariable=self.default_ctx_var, width=10)
ctx_spin.grid(row=1, column=1, sticky=tk.W, pady=5)
ttk.Label(frame, text="tokens").grid(row=1, column=2, sticky=tk.W, padx=5)
# Default GPU layers
ttk.Label(frame, text="Default GPU Layers:").grid(row=2, column=0, sticky=tk.W, pady=5)
self.default_gpu_var = tk.IntVar()
gpu_spin = tk.Spinbox(frame, from_=0, to=100, textvariable=self.default_gpu_var, width=10)
gpu_spin.grid(row=2, column=1, sticky=tk.W, pady=5)
ttk.Label(frame, text="layers").grid(row=2, column=2, sticky=tk.W, padx=5)
# Info
info_text = ("Cache size determines how many models can be loaded simultaneously.\\n"
"Default settings are used when loading models via MCP.")
ttk.Label(frame, text=info_text, foreground="gray").grid(row=3, column=0, columnspan=3, pady=10)
def _build_claude_tab(self, parent: ttk.Frame):
"""Build Claude integration tab."""
frame = ttk.LabelFrame(parent, text="Claude Desktop Integration", padding=10)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Instructions - wrapped properly
instructions_frame = ttk.Frame(frame)
instructions_frame.pack(fill=tk.X, pady=(0, 10))
# Create a text widget for instructions with proper wrapping
instructions_text = tk.Text(instructions_frame, height=8, wrap=tk.WORD,
background="#f0f0f0", relief=tk.FLAT,
borderwidth=0, state=tk.DISABLED)
instructions_text.pack(fill=tk.X)
# Add properly formatted instructions
instructions_content = """To use this MCP server with Claude Desktop, follow these steps:
1. Locate your Claude Desktop configuration file:
• Windows: %APPDATA%\\Claude\\claude_desktop_config.json
• macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
• Linux: ~/.config/Claude/claude_desktop_config.json
2. Open the configuration file in a text editor (create it if it doesn't exist)
3. Add the JSON configuration shown below to the file
4. Save the file and restart Claude Desktop
5. The LLM_Train models server will be available in Claude Desktop"""
instructions_text.config(state=tk.NORMAL)
instructions_text.insert(1.0, instructions_content)
instructions_text.config(state=tk.DISABLED)
# Config display
ttk.Label(frame, text="Configuration JSON:", font=("TkDefaultFont", 10, "bold")).pack(anchor=tk.W, pady=(10,5))
# Create text frame with proper layout
text_frame = ttk.Frame(frame)
text_frame.pack(fill=tk.BOTH, expand=True)
# Make the config text read-only and styled
self.config_text = tk.Text(text_frame, height=12, width=70, wrap=tk.WORD,
state=tk.DISABLED, background="#f8f8f8",
font=("Courier", 9))
config_scroll = ttk.Scrollbar(text_frame, orient="vertical", command=self.config_text.yview)
self.config_text.configure(yscrollcommand=config_scroll.set)
self.config_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
config_scroll.pack(side=tk.RIGHT, fill=tk.Y)
# Buttons
button_frame = ttk.Frame(frame)
button_frame.pack(fill=tk.X, pady=10)
ttk.Button(button_frame, text="Copy to Clipboard",
command=self._copy_config).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Save to File",
command=self._save_claude_config).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Refresh",
command=self._update_claude_config).pack(side=tk.LEFT, padx=5)
self._update_claude_config()
def _load_values(self):
"""Load configuration values into UI."""
self.server_name_var.set(self.config.get('server.name', 'llm-train-models'))
self.auto_start_var.set(self.config.get('server.auto_start', False))
self.log_level_var.set(self.config.get('logging.level', 'INFO'))
self.log_file_var.set(self.config.get('logging.file', 'mcp_server.log'))
self.cache_size_var.set(self.config.get('models.cache_size', 3))
self.default_ctx_var.set(self.config.get('models.default_context', 4096))
self.default_gpu_var.set(self.config.get('models.default_gpu_layers', 0))
def _save_config(self):
"""Save configuration."""
self.config.set('server.name', self.server_name_var.get())
self.config.set('server.auto_start', self.auto_start_var.get())
self.config.set('logging.level', self.log_level_var.get())
self.config.set('logging.file', self.log_file_var.get())
self.config.set('models.cache_size', self.cache_size_var.get())
self.config.set('models.default_context', self.default_ctx_var.get())
self.config.set('models.default_gpu_layers', self.default_gpu_var.get())
if self.config.save_config():
messagebox.showinfo("Success", "Configuration saved successfully!")
self.dialog.destroy()
else:
messagebox.showerror("Error", "Failed to save configuration")
def _browse_log_file(self):
"""Browse for log file location."""
filename = filedialog.asksaveasfilename(
title="Select Log File",
defaultextension=".log",
filetypes=[("Log files", "*.log"), ("All files", "*.*")]
)
if filename:
self.log_file_var.set(filename)
def _update_claude_config(self):
"""Update the Claude configuration display."""
config = self.config.generate_claude_config()
config_json = json.dumps(config, indent=2)
self.config_text.delete(1.0, tk.END)
self.config_text.insert(1.0, config_json)
def _copy_config(self):
"""Copy configuration to clipboard."""
config_text = self.config_text.get(1.0, tk.END)
self.dialog.clipboard_clear()
self.dialog.clipboard_append(config_text)
messagebox.showinfo("Copied", "Configuration copied to clipboard!")
def _save_claude_config(self):
"""Save Claude configuration to file."""
filename = filedialog.asksaveasfilename(
title="Save Claude Config",
defaultextension=".json",
filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
initialvalue="claude_desktop_config.json"
)
if filename:
try:
config = self.config.generate_claude_config()
with open(filename, 'w') as f:
json.dump(config, f, indent=2)
messagebox.showinfo("Saved", f"Configuration saved to {filename}")
except Exception as e:
messagebox.showerror("Error", f"Failed to save file: {e}")
def _test_server(self):
"""Test the MCP server."""
try:
# Try to run the server with --help to test if it's working
result = subprocess.run([
sys.executable, "mcp_server.py", "--help"
], capture_output=True, text=True, timeout=10)
if result.returncode == 0:
messagebox.showinfo("Test Successful",
"MCP server appears to be working correctly!")
else:
messagebox.showerror("Test Failed",
f"Server test failed:\\n{result.stderr}")
except Exception as e:
messagebox.showerror("Test Error", f"Failed to test server: {e}")
def open_mcp_config(parent: tk.Tk):
"""Open the MCP configuration dialog."""
MCPConfigGUI(parent)
if __name__ == "__main__":
# Test the configuration
root = tk.Tk()
root.withdraw() # Hide main window
app = MCPConfigGUI(root)
root.mainloop()