/** sha256 (first 12 hex) of the canonical media/gimp/gimp_mcp_client.py. */ export declare const GIMP_CLIENT_SOURCE_HASH = "a11b5a3693a8"; /** Full body of the user-verified GIMP MCP client (FastMCP stdio) (media/gimp/gimp_mcp_client.py). */ export declare const GIMP_CLIENT_SOURCE = "#!/usr/bin/env python3\n# ---------------------------------------------------------------------------\n# gimp_mcp_client.py \u2014 vendored, USER-VERIFIED GIMP MCP client (FastMCP stdio).\n#\n# Derived from libreearth/gimp-mcp (GPL-3.0; see media/gimp/LICENSE) and fixed by\n# the user / AutoDev: reconnect via getpeername, reads > 1024 bytes, get_images.\n# Launched by: uv run --with mcp==1.2.0 . The body below is kept\n# byte-for-byte; the lines in THIS block are a provenance comment only.\n# ---------------------------------------------------------------------------\n\n# GIMP MCP Server Script\n# Provides an MCP interface to control GIMP via a socket connection.\n\nfrom mcp.server.fastmcp import FastMCP, Context\nimport socket\nimport json\nimport logging\n\nlogging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')\nlogger = logging.getLogger(\"GimpMCPServer\")\n\n\nclass GimpConnection:\n def __init__(self, host='127.0.0.1', port=9877):\n self.host = host\n self.port = port\n self.sock = None\n\n def close(self):\n if self.sock:\n try:\n self.sock.close()\n except Exception:\n pass\n self.sock = None\n\n def connect(self):\n if self.sock:\n try:\n self.sock.getpeername()\n return\n except Exception:\n self.close()\n try:\n sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n sock.settimeout(30)\n sock.connect((self.host, self.port))\n self.sock = sock\n logger.info(f\"Connected to GIMP at {self.host}:{self.port}\")\n except Exception as e:\n self.close()\n logger.error(f\"Failed to connect: {e}\")\n raise ConnectionError(\n \"Could not connect to GIMP. Open GIMP and run \"\n \"Filters > Development > Start MCP Server.\"\n )\n\n def send_command(self, command_type, params=None):\n self.connect()\n command = {\"type\": command_type, \"params\": params or {}}\n try:\n self.sock.sendall(json.dumps(command).encode('utf-8'))\n chunks = []\n while True:\n data = self.sock.recv(65536)\n if not data:\n break\n chunks.append(data)\n try:\n return json.loads(b''.join(chunks).decode('utf-8'))\n except json.JSONDecodeError:\n continue\n raise Exception(\"GIMP closed the connection before sending a complete response\")\n except Exception as e:\n logger.error(f\"Communication error: {e}\")\n self.close()\n raise Exception(f\"Error communicating with GIMP: {e}\")\n\n\n_gimp_connection = None\n\n\ndef get_gimp_connection():\n global _gimp_connection\n if _gimp_connection is None:\n _gimp_connection = GimpConnection()\n _gimp_connection.connect()\n return _gimp_connection\n\n\nmcp = FastMCP(\"GimpMCP\", description=\"GIMP integration through MCP\")\n\n\n@mcp.tool()\ndef call_api(ctx: Context, api_path: str, args: list = [], kwargs: dict = {}) -> str:\n \"\"\"Call any GIMP API method dynamically.\n\n Parameters:\n - api_path: The path to the API method (e.g., \"Gimp.Image.get_by_id\")\n Use \"exec\" to run Python inside GIMP. Pass the code in args[0].\n - args: List of positional arguments\n - kwargs: Dictionary of keyword arguments\n\n Returns:\n - JSON string of the result or error message\n \"\"\"\n try:\n conn = get_gimp_connection()\n result = conn.send_command(\"call_api\", {\"api_path\": api_path, \"args\": args, \"kwargs\": kwargs})\n if result[\"status\"] == \"success\":\n return json.dumps(result[\"result\"])\n return f\"Error: {result['message']}\"\n except Exception as e:\n return f\"Error: {e}\"\n\n\n@mcp.tool()\ndef list_images(ctx: Context) -> str:\n \"\"\"List all open images in GIMP.\n\n Returns:\n - JSON string of image IDs and names\n \"\"\"\n return call_api(ctx, \"Gimp.get_images\")\n\n\n@mcp.tool()\ndef get_image_info(ctx: Context, image_id: int) -> str:\n \"\"\"Get information about a specific image.\n\n Parameters:\n - image_id: The ID of the image\n\n Returns:\n - JSON string of image details\n \"\"\"\n return call_api(\n ctx,\n \"exec\",\n args=[\n f\"\"\"\nimage = Gimp.Image.get_by_id({int(image_id)})\nif image is None:\n _result = {{\"error\": \"image not found\"}}\nelse:\n _result = {{\n \"id\": image.get_id(),\n \"name\": image.get_name(),\n \"width\": image.get_width(),\n \"height\": image.get_height(),\n \"layers\": [{{\"id\": layer.get_id(), \"name\": layer.get_name()}} for layer in image.get_layers()],\n }}\n\"\"\"\n ],\n )\n\n\n@mcp.tool()\ndef apply_gaussian_blur(ctx: Context, image_id: int, radius: float = 5.0) -> str:\n \"\"\"Apply Gaussian blur to an image.\n\n Parameters:\n - image_id: The ID of the image\n - radius: Blur radius\n\n Returns:\n - Success message or error\n \"\"\"\n return call_api(\n ctx,\n \"exec\",\n args=[\n f\"\"\"\nimage = Gimp.Image.get_by_id({int(image_id)})\nif image is None:\n _result = \"image not found\"\nelse:\n layers = image.get_layers()\n if not layers:\n _result = \"no layers\"\n else:\n drawable = layers[0]\n blur = Gimp.DrawableFilter.new(drawable, \"gegl:gaussian-blur\", \"\")\n cfg = blur.get_config()\n try:\n cfg.set_property(\"std-dev-x\", {float(radius)})\n cfg.set_property(\"std-dev-y\", {float(radius)})\n except Exception:\n pass\n drawable.append_filter(blur)\n drawable.merge_filter(blur)\n Gimp.displays_flush()\n _result = \"Applied Gaussian blur successfully\"\n\"\"\"\n ],\n )\n\n\ndef main():\n mcp.run()\n\n\nif __name__ == \"__main__\":\n main()\n";