#!/usr/bin/env python3 """ Cauldron Cloud MCP — Setup wizard Installs dependencies and writes Claude Desktop config automatically. """ import json import os import platform import shutil import subprocess import sys import webbrowser from pathlib import Path # ── Colours ────────────────────────────────────────────────────────────────── def _supports_colour() -> bool: return sys.stdout.isatty() and platform.system() != "Windows" if _supports_colour(): RESET = "\033[0m" BOLD = "\033[1m" GREEN = "\033[32m" YELLOW = "\033[33m" RED = "\033[31m" CYAN = "\033[36m" else: RESET = BOLD = GREEN = YELLOW = RED = CYAN = "" def ok(msg: str) -> None: print(f"{GREEN} ✓ {RESET}{msg}") def info(msg: str) -> None: print(f"{CYAN} → {RESET}{msg}") def warn(msg: str) -> None: print(f"{YELLOW} ⚠ {RESET}{msg}") def err(msg: str) -> None: print(f"{RED} ✗ {RESET}{msg}") def bold(msg: str) -> str: return f"{BOLD}{msg}{RESET}" # ── Helpers ─────────────────────────────────────────────────────────────────── def _python_executable() -> str: """Return the Python executable that is running this script.""" return sys.executable def _config_path() -> Path: system = platform.system() if system == "Windows": base = Path(os.environ.get("APPDATA", "~")).expanduser() return base / "Claude" / "claude_desktop_config.json" if system == "Darwin": return Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json" # Linux / other xdg = os.environ.get("XDG_CONFIG_HOME", "") base = Path(xdg) if xdg else Path.home() / ".config" return base / "Claude" / "claude_desktop_config.json" def _read_config(path: Path) -> dict: if path.exists(): try: return json.loads(path.read_text(encoding="utf-8")) except json.JSONDecodeError: warn("Existing config file contains invalid JSON — it will be backed up and rewritten.") backup = path.with_suffix(".json.bak") shutil.copy(path, backup) info(f"Backup saved to {backup}") return {} def _write_config(path: Path, data: dict) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8") def _install_deps() -> bool: packages = ["mcp>=1.0.0", "httpx>=0.27.0"] info("Installing Python dependencies...") result = subprocess.run( [_python_executable(), "-m", "pip", "install", "--quiet", *packages], capture_output=True, text=True, ) if result.returncode != 0: err("pip install failed:") print(result.stderr.strip()) return False return True def _check_deps() -> bool: try: import mcp # noqa: F401 import httpx # noqa: F401 return True except ImportError: return False def _validate_key(key: str) -> bool: key = key.strip() return key.startswith("cldrn_") and len(key) >= 20 # ── Main ────────────────────────────────────────────────────────────────────── def main() -> None: print() print(bold("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")) print(bold(" Cauldron Cloud — MCP Server Setup")) print(bold("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")) print() # ── 1. Python version check ─────────────────────────────────────────────── if sys.version_info < (3, 10): err(f"Python 3.10+ required (you have {platform.python_version()}).") sys.exit(1) ok(f"Python {platform.python_version()}") # ── 2. Dependencies ─────────────────────────────────────────────────────── if _check_deps(): ok("Dependencies already installed (mcp, httpx)") else: if not _install_deps(): err("Could not install dependencies. Try manually: pip install mcp httpx") sys.exit(1) if not _check_deps(): err("Dependencies installed but import still fails. Check your Python environment.") sys.exit(1) ok("Dependencies installed (mcp, httpx)") # ── 3. Locate server.py ─────────────────────────────────────────────────── server_py = Path(__file__).resolve().parent / "server.py" if not server_py.exists(): err(f"server.py not found at {server_py}") sys.exit(1) ok(f"Server script: {server_py}") # ── 4. Get the API key ──────────────────────────────────────────────────── print() print(bold(" Step: Generate your API key")) print() print(" A browser window will open to the Cauldron portal.") print(" Log in, click Generate , copy the key and paste it below.") print() input(" Press ENTER to open the browser...") portal_url = "https://cauldron.cloud/mcpKeys" webbrowser.open(portal_url) info(f"Opened: {portal_url}") print() api_key = "" for attempt in range(3): raw = input(" Paste your API key here: ").strip() if _validate_key(raw): api_key = raw break err("Key must start with cldrn_ and be at least 20 characters. Try again.") else: err("No valid key provided. Run setup.py again when you have a key.") sys.exit(1) ok(f"Key accepted: {api_key[:14]}…") # ── 5. Detect Claude Desktop config ─────────────────────────────────────── config_path = _config_path() print() info(f"Config file: {config_path}") config = _read_config(config_path) # ── 6. Merge mcpServers entry ───────────────────────────────────────────── python_cmd = _python_executable() mcp_entry = { "command": python_cmd, "args": [str(server_py)], "env": {"CAULDRON_API_KEY": api_key}, } if "mcpServers" not in config: config["mcpServers"] = {} existing = config["mcpServers"].get("cauldron") config["mcpServers"]["cauldron"] = mcp_entry _write_config(config_path, config) if existing: ok("Updated existing cauldron entry in Claude Desktop config") else: ok("Added cauldron entry to Claude Desktop config") # ── 7. Done ─────────────────────────────────────────────────────────────── print() print(bold("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")) print(bold(" Setup complete!")) print(bold("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")) print() print(" Next steps:") print(f" 1. {bold('Restart Claude Desktop')}") print(" 2. Open a new Chat") print(" 3. Look for the 🔌 icon — cauldron should appear in the tool list") print() print(" Try asking Claude:") print(f" {CYAN}\"Show me all open Sell Side deals in the Automotive sector\"{RESET}") print(f" {CYAN}\"Are there any incoming Requests for Help we haven't answered?\"{RESET}") print() if __name__ == "__main__": main()