autodev-cli 1.4.152 → 1.4.154
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.
- package/media/gimp/gimp_mcp_client.py +1 -1
- package/media/gimp/gimp_mcp_server.py +8 -12
- package/out/commands/mcp.js +2 -2
- package/out/core/gimpClientSource.d.ts +2 -2
- package/out/core/gimpClientSource.js +2 -2
- package/out/core/gimpPluginInstall.js +1 -1
- package/out/core/gimpPluginSource.d.ts +2 -2
- package/out/core/gimpPluginSource.js +9 -13
- package/out/core/gimpPluginSource.js.map +1 -1
- package/out/core/mcpCatalog.js +3 -2
- package/out/core/mcpCatalog.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# Derived from libreearth/gimp-mcp (GPL-3.0; see media/gimp/LICENSE) and fixed by
|
|
6
6
|
# the user / AutoDev for GIMP 3.2 (Store / MSIX). Verified working on real GIMP
|
|
7
7
|
# 3.2.44: a menu-triggered PLUGIN (Tools -> MCP / Filters -> Development -> Start
|
|
8
|
-
# MCP Server) that listens on 127.0.0.1:
|
|
8
|
+
# MCP Server) that listens on 127.0.0.1:9877 and drives GIMP. Keep the "GIMP MCP
|
|
9
9
|
# Server" window open — GLib.MainLoop() holds the out-of-process plug-in alive.
|
|
10
10
|
#
|
|
11
11
|
# This is the user's verified file; the body below is kept byte-for-byte. The
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# ---------------------------------------------------------------------------
|
|
14
14
|
|
|
15
15
|
# GIMP MCP Server Plugin (GIMP 3.2)
|
|
16
|
-
# Listens on localhost:
|
|
16
|
+
# Listens on localhost:9877 so it does not collide with Blender MCP (9876).
|
|
17
17
|
|
|
18
18
|
import gi
|
|
19
19
|
gi.require_version('Gimp', '3.0')
|
|
@@ -31,7 +31,7 @@ import threading
|
|
|
31
31
|
import traceback
|
|
32
32
|
|
|
33
33
|
HOST = '127.0.0.1'
|
|
34
|
-
PORT =
|
|
34
|
+
PORT = 9877
|
|
35
35
|
# Store GIMP redirects GLib config dir into the MSIX LocalCache. Also write
|
|
36
36
|
# to the real roaming profile so the log is findable.
|
|
37
37
|
_LOG_PATHS = [
|
|
@@ -190,7 +190,7 @@ class MCPPlugin(Gimp.PlugIn):
|
|
|
190
190
|
procedure.set_menu_label(_('Start MCP Server'))
|
|
191
191
|
procedure.set_documentation(
|
|
192
192
|
_('Starts an MCP server to control GIMP externally'),
|
|
193
|
-
_('Listens on 127.0.0.1:
|
|
193
|
+
_('Listens on 127.0.0.1:9877 for the GIMP MCP client'),
|
|
194
194
|
name,
|
|
195
195
|
)
|
|
196
196
|
procedure.set_attribution('GIMP MCP', 'GIMP MCP', '2026')
|
|
@@ -211,10 +211,6 @@ class MCPPlugin(Gimp.PlugIn):
|
|
|
211
211
|
global _main_loop
|
|
212
212
|
started, message = self.ensure_server()
|
|
213
213
|
_log(message)
|
|
214
|
-
try:
|
|
215
|
-
Gimp.message(message)
|
|
216
|
-
except Exception:
|
|
217
|
-
pass
|
|
218
214
|
if not started:
|
|
219
215
|
return procedure.new_return_values(Gimp.PDBStatusType.EXECUTION_ERROR, GLib.Error(message))
|
|
220
216
|
|
|
@@ -244,11 +240,12 @@ class MCPPlugin(Gimp.PlugIn):
|
|
|
244
240
|
|
|
245
241
|
GimpUi.init('gimp_mcp_server.py')
|
|
246
242
|
window = Gtk.Window(title='GIMP MCP Server')
|
|
247
|
-
window.set_default_size(
|
|
248
|
-
window.set_keep_above(
|
|
243
|
+
window.set_default_size(360, 120)
|
|
244
|
+
window.set_keep_above(False)
|
|
245
|
+
window.set_skip_taskbar_hint(False)
|
|
249
246
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
|
|
250
247
|
box.set_border_width(16)
|
|
251
|
-
label = Gtk.Label(label=message + '\
|
|
248
|
+
label = Gtk.Label(label=message + '\nLeave this window open.')
|
|
252
249
|
label.set_line_wrap(True)
|
|
253
250
|
box.pack_start(label, True, True, 0)
|
|
254
251
|
button = Gtk.Button(label='Stop server')
|
|
@@ -257,7 +254,6 @@ class MCPPlugin(Gimp.PlugIn):
|
|
|
257
254
|
window.add(box)
|
|
258
255
|
window.connect('destroy', self._on_status_closed)
|
|
259
256
|
window.show_all()
|
|
260
|
-
window.present()
|
|
261
257
|
_log('Status window opened')
|
|
262
258
|
|
|
263
259
|
def _on_status_closed(self, *_args):
|
package/out/commands/mcp.js
CHANGED
|
@@ -298,7 +298,7 @@ function printGimpInstall(res) {
|
|
|
298
298
|
logger_1.log.plain(` client: ${p}`);
|
|
299
299
|
}
|
|
300
300
|
logger_1.log.plain(" In GIMP: Tools → MCP → Start MCP Server (or Filters → Development), and KEEP the");
|
|
301
|
-
logger_1.log.plain(" 'GIMP MCP Server' window open — it holds the plug-in alive on localhost:
|
|
301
|
+
logger_1.log.plain(" 'GIMP MCP Server' window open — it holds the plug-in alive on localhost:9877.");
|
|
302
302
|
}
|
|
303
303
|
else {
|
|
304
304
|
logger_1.log.warn('⚠ The GIMP MCP plugin was NOT installed.');
|
|
@@ -445,7 +445,7 @@ function mcpCommand(program) {
|
|
|
445
445
|
// autodev mcp gimp-install-plugin [dir]
|
|
446
446
|
cmd
|
|
447
447
|
.command('gimp-install-plugin [dir]')
|
|
448
|
-
.description('Install the AUTO-START GIMP 3.x MCP plugin (localhost:
|
|
448
|
+
.description('Install the AUTO-START GIMP 3.x MCP plugin (localhost:9877 opens on GIMP launch — no menu click)')
|
|
449
449
|
.action((dir) => {
|
|
450
450
|
const res = (0, gimpPluginInstall_1.installGimpPlugin)(dir ? { targetDir: path.resolve(dir) } : undefined);
|
|
451
451
|
printGimpInstall(res);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** sha256 (first 12 hex) of the canonical media/gimp/gimp_mcp_client.py. */
|
|
2
|
-
export declare const GIMP_CLIENT_SOURCE_HASH = "
|
|
2
|
+
export declare const GIMP_CLIENT_SOURCE_HASH = "a11b5a3693a8";
|
|
3
3
|
/** Full body of the user-verified GIMP MCP client (FastMCP stdio) (media/gimp/gimp_mcp_client.py). */
|
|
4
|
-
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 <this file>. 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=
|
|
4
|
+
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 <this file>. 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";
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.GIMP_CLIENT_SOURCE = exports.GIMP_CLIENT_SOURCE_HASH = void 0;
|
|
13
13
|
/** sha256 (first 12 hex) of the canonical media/gimp/gimp_mcp_client.py. */
|
|
14
|
-
exports.GIMP_CLIENT_SOURCE_HASH = "
|
|
14
|
+
exports.GIMP_CLIENT_SOURCE_HASH = "a11b5a3693a8";
|
|
15
15
|
/** Full body of the user-verified GIMP MCP client (FastMCP stdio) (media/gimp/gimp_mcp_client.py). */
|
|
16
16
|
exports.GIMP_CLIENT_SOURCE = `#!/usr/bin/env python3
|
|
17
17
|
# ---------------------------------------------------------------------------
|
|
@@ -36,7 +36,7 @@ logger = logging.getLogger("GimpMCPServer")
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class GimpConnection:
|
|
39
|
-
def __init__(self, host='127.0.0.1', port=
|
|
39
|
+
def __init__(self, host='127.0.0.1', port=9877):
|
|
40
40
|
self.host = host
|
|
41
41
|
self.port = port
|
|
42
42
|
self.sock = None
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// The plug-in is MENU-TRIGGERED (the user's verified flow): in GIMP the user runs
|
|
9
9
|
// Tools → MCP → Start MCP Server (or Filters → Development → Start MCP Server) and
|
|
10
10
|
// keeps the "GIMP MCP Server" window open — that holds the out-of-process plug-in
|
|
11
|
-
// alive and the :
|
|
11
|
+
// alive and the :9877 socket. We do NOT auto-start it (the PERSISTENT approach we
|
|
12
12
|
// tried did not work on GIMP 3.2); we only install the files.
|
|
13
13
|
//
|
|
14
14
|
// GROUNDED RULES (see AutoDev research):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** sha256 (first 12 hex) of the canonical media/gimp/gimp_mcp_server.py. */
|
|
2
|
-
export declare const GIMP_PLUGIN_SOURCE_HASH = "
|
|
2
|
+
export declare const GIMP_PLUGIN_SOURCE_HASH = "882fdb94b5c9";
|
|
3
3
|
/** Full body of the user-verified GIMP 3.x MCP server plug-in (media/gimp/gimp_mcp_server.py). */
|
|
4
|
-
export declare const GIMP_PLUGIN_SOURCE = "#!/usr/bin/env python3\n# ---------------------------------------------------------------------------\n# gimp_mcp_server.py \u2014 vendored, USER-VERIFIED GIMP 3.2 MCP bridge (plug-in).\n#\n# Derived from libreearth/gimp-mcp (GPL-3.0; see media/gimp/LICENSE) and fixed by\n# the user / AutoDev for GIMP 3.2 (Store / MSIX). Verified working on real GIMP\n# 3.2.44: a menu-triggered PLUGIN (Tools -> MCP / Filters -> Development -> Start\n# MCP Server) that listens on 127.0.0.1:9876 and drives GIMP. Keep the \"GIMP MCP\n# Server\" window open \u2014 GLib.MainLoop() holds the out-of-process plug-in alive.\n#\n# This is the user's verified file; the body below is kept byte-for-byte. The\n# lines in THIS block are a provenance comment only \u2014 do not edit the code below.\n# ---------------------------------------------------------------------------\n\n# GIMP MCP Server Plugin (GIMP 3.2)\n# Listens on localhost:9876 so the libreearth gimp-mcp client can drive GIMP.\n\nimport gi\ngi.require_version('Gimp', '3.0')\nfrom gi.repository import Gimp\nfrom gi.repository import GLib\nfrom gi.repository import Gio\nfrom gi.repository import GObject\n\nimport io\nimport json\nimport os\nimport socket\nimport sys\nimport threading\nimport traceback\n\nHOST = '127.0.0.1'\nPORT = 9876\n# Store GIMP redirects GLib config dir into the MSIX LocalCache. Also write\n# to the real roaming profile so the log is findable.\n_LOG_PATHS = [\n os.path.join(GLib.get_user_config_dir(), 'GIMP', '3.2', 'gimp_mcp_server.log'),\n os.path.join(os.environ.get('APPDATA', ''), 'GIMP', '3.2', 'gimp_mcp_server.log'),\n]\n\n_server_lock = threading.Lock()\n_server_started = False\n_exec_globals = None\n_main_loop = None\n\n\ndef _log(msg):\n line = msg.rstrip() + '\\n'\n for path in _LOG_PATHS:\n if not path:\n continue\n try:\n os.makedirs(os.path.dirname(path), exist_ok=True)\n with open(path, 'a', encoding='utf-8') as fh:\n fh.write(line)\n except Exception:\n pass\n print(msg, flush=True)\n\n\ndef N_(message):\n return message\n\n\ndef _(message):\n return GLib.dgettext(None, message)\n\n\ndef _serialize(obj):\n if obj is None or isinstance(obj, (str, int, float, bool)):\n return obj\n if isinstance(obj, (list, tuple)):\n return [_serialize(item) for item in obj]\n if isinstance(obj, dict):\n return {str(k): _serialize(v) for k, v in obj.items()}\n data = {'type': obj.__class__.__name__}\n if hasattr(obj, 'get_id'):\n try:\n data['id'] = int(obj.get_id())\n except Exception:\n pass\n elif hasattr(obj, 'ID'):\n data['id'] = obj.ID\n if hasattr(obj, 'get_name'):\n try:\n data['name'] = obj.get_name()\n except Exception:\n pass\n if 'id' not in data and len(data) == 1:\n return str(obj)\n return data\n\n\ndef _resolve_api(api_path):\n parts = [p for p in str(api_path).split('.') if p]\n if parts and parts[0] == 'Gimp':\n parts = parts[1:]\n elif len(parts) >= 3 and parts[0] == 'gi' and parts[1] == 'repository' and parts[2] == 'Gimp':\n parts = parts[3:]\n\n aliases = {\n 'list_images': 'get_images',\n 'list_fonts': 'get_fonts',\n 'fonts_get_list': 'get_fonts',\n }\n\n if not parts:\n return Gimp\n\n obj = Gimp\n for part in parts:\n part = aliases.get(part, part)\n obj = getattr(obj, part)\n return obj\n\n\ndef _exec_namespace():\n global _exec_globals\n if _exec_globals is None:\n ns = {\n '__builtins__': __builtins__,\n '__name__': '__main__',\n 'Gimp': Gimp,\n 'GLib': GLib,\n 'Gio': Gio,\n 'GObject': GObject,\n 'sys': sys,\n 'os': os,\n 'json': json,\n }\n try:\n gi.require_version('Gegl', '0.4')\n from gi.repository import Gegl\n ns['Gegl'] = Gegl\n except Exception as exc:\n _log(f'Gegl import failed: {exc}')\n try:\n gi.require_version('Babl', '0.1')\n from gi.repository import Babl\n ns['Babl'] = Babl\n except Exception:\n pass\n _exec_globals = ns\n return _exec_globals\n\n\ndef _extract_code(args, kwargs):\n if kwargs.get('code'):\n return str(kwargs['code'])\n if not args:\n return ''\n if len(args) >= 2 and args[0] in ('exec', 'eval', 'pyGObject-console'):\n payload = args[1]\n if isinstance(payload, (list, tuple)):\n return '\\n'.join(str(item) for item in payload)\n return str(payload)\n if isinstance(args[0], (list, tuple)):\n return '\\n'.join(str(item) for item in args[0])\n return str(args[0])\n\n\ndef _run_python(code):\n ns = _exec_namespace()\n stdout = io.StringIO()\n old_stdout = sys.stdout\n sys.stdout = stdout\n try:\n exec(code, ns)\n finally:\n sys.stdout = old_stdout\n printed = stdout.getvalue()\n result = ns.get('_result')\n if result is not None:\n return {'printed': printed, 'result': _serialize(result)}\n return {'printed': printed, 'result': None}\n\n\nclass MCPPlugin(Gimp.PlugIn):\n def do_set_i18n(self, name):\n return False\n\n def do_query_procedures(self):\n return ['plug-in-mcp-server']\n\n def do_create_procedure(self, name):\n procedure = Gimp.Procedure.new(\n self, name, Gimp.PDBProcType.PLUGIN, self.run, None\n )\n procedure.set_menu_label(_('Start MCP Server'))\n procedure.set_documentation(\n _('Starts an MCP server to control GIMP externally'),\n _('Listens on 127.0.0.1:9876 for the GIMP MCP client'),\n name,\n )\n procedure.set_attribution('GIMP MCP', 'GIMP MCP', '2026')\n procedure.set_sensitivity_mask(Gimp.ProcedureSensitivityMask.ALWAYS)\n procedure.add_enum_argument(\n 'run-mode',\n _('Run mode'),\n _('The run mode'),\n Gimp.RunMode,\n Gimp.RunMode.INTERACTIVE,\n GObject.ParamFlags.READWRITE,\n )\n procedure.add_menu_path('<Image>/Tools/MCP')\n procedure.add_menu_path('<Image>/Filters/Development/')\n return procedure\n\n def run(self, procedure, config, data):\n global _main_loop\n started, message = self.ensure_server()\n _log(message)\n try:\n Gimp.message(message)\n except Exception:\n pass\n if not started:\n return procedure.new_return_values(Gimp.PDBStatusType.EXECUTION_ERROR, GLib.Error(message))\n\n # GIMP 3 launches Python plug-ins out of process. Returning from run()\n # would exit that process and kill the socket thread. Stay alive and\n # dispatch GLib.idle_add work (API calls) on this main loop.\n # A visible window is required: otherwise \"Start MCP Server\" looks like\n # it did not open anything.\n try:\n self._show_status_window(message)\n except Exception as exc:\n _log(f'Status window failed: {exc}\\n{traceback.format_exc()}')\n\n _main_loop = GLib.MainLoop()\n _log('Entering GLib.MainLoop to keep MCP server process alive')\n try:\n _main_loop.run()\n except Exception as exc:\n _log(f'MainLoop exited: {exc}')\n return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())\n\n def _show_status_window(self, message):\n gi.require_version('Gtk', '3.0')\n gi.require_version('GimpUi', '3.0')\n from gi.repository import Gtk\n from gi.repository import GimpUi\n\n GimpUi.init('gimp_mcp_server.py')\n window = Gtk.Window(title='GIMP MCP Server')\n window.set_default_size(420, 160)\n window.set_keep_above(True)\n box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)\n box.set_border_width(16)\n label = Gtk.Label(label=message + '\\n\\nLeave this window open while Grok talks to GIMP.')\n label.set_line_wrap(True)\n box.pack_start(label, True, True, 0)\n button = Gtk.Button(label='Stop server')\n button.connect('clicked', lambda *_: window.destroy())\n box.pack_start(button, False, False, 0)\n window.add(box)\n window.connect('destroy', self._on_status_closed)\n window.show_all()\n window.present()\n _log('Status window opened')\n\n def _on_status_closed(self, *_args):\n global _main_loop\n _log('Status window closed; stopping MCP server')\n if _main_loop is not None:\n _main_loop.quit()\n\n def ensure_server(self):\n global _server_started\n with _server_lock:\n if _server_started:\n return True, f'MCP Server already listening on {HOST}:{PORT}'\n try:\n thread = threading.Thread(target=self.start_server, daemon=True)\n thread.start()\n _server_started = True\n return True, f'MCP Server started on {HOST}:{PORT}'\n except Exception as exc:\n return False, f'Failed to start MCP Server: {exc}'\n\n def start_server(self):\n sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n try:\n sock.bind((HOST, PORT))\n sock.listen(8)\n _log(f'Server listening on {HOST}:{PORT}')\n except Exception as exc:\n _log(f'Bind/listen failed: {exc}')\n return\n\n while True:\n try:\n client, addr = sock.accept()\n _log(f'Connected to {addr}')\n threading.Thread(target=self.handle_client, args=(client,), daemon=True).start()\n except Exception as exc:\n _log(f'Server accept error: {exc}')\n break\n sock.close()\n\n def handle_client(self, client):\n buffer = b''\n try:\n client.settimeout(120)\n while True:\n chunk = client.recv(65536)\n if not chunk:\n break\n buffer += chunk\n while buffer:\n try:\n command = json.loads(buffer.decode('utf-8'))\n buffer = b''\n except json.JSONDecodeError:\n break\n GLib.idle_add(self.execute_command, command, client)\n except Exception as exc:\n _log(f'Client error: {exc}')\n finally:\n try:\n client.close()\n except Exception:\n pass\n\n def execute_command(self, command, client):\n cmd_type = command.get('type')\n params = command.get('params', {}) or {}\n try:\n if cmd_type == 'ping':\n response = {'status': 'success', 'result': 'pong'}\n elif cmd_type == 'exec':\n code = _extract_code(params.get('args', []), params.get('kwargs', {}))\n if not code:\n code = str(params.get('code', ''))\n response = {'status': 'success', 'result': _run_python(code)}\n elif cmd_type == 'call_api':\n api_path = params.get('api_path', '')\n args = params.get('args', []) or []\n kwargs = params.get('kwargs', {}) or {}\n if api_path in ('exec', 'eval', '__exec__'):\n response = {'status': 'success', 'result': _run_python(_extract_code(args, kwargs))}\n else:\n method = _resolve_api(api_path)\n result = method(*args, **kwargs)\n response = {'status': 'success', 'result': _serialize(result)}\n else:\n response = {'status': 'error', 'message': f'Unknown command: {cmd_type}'}\n except Exception as exc:\n response = {\n 'status': 'error',\n 'message': str(exc),\n 'traceback': traceback.format_exc(),\n }\n _log(f'Command error: {response[\"message\"]}\\n{response[\"traceback\"]}')\n\n try:\n payload = json.dumps(response).encode('utf-8')\n client.sendall(payload)\n except Exception as exc:\n _log(f'Response send failed: {exc}')\n return False\n\n\nGimp.main(MCPPlugin.__gtype__, sys.argv)\n";
|
|
4
|
+
export declare const GIMP_PLUGIN_SOURCE = "#!/usr/bin/env python3\n# ---------------------------------------------------------------------------\n# gimp_mcp_server.py \u2014 vendored, USER-VERIFIED GIMP 3.2 MCP bridge (plug-in).\n#\n# Derived from libreearth/gimp-mcp (GPL-3.0; see media/gimp/LICENSE) and fixed by\n# the user / AutoDev for GIMP 3.2 (Store / MSIX). Verified working on real GIMP\n# 3.2.44: a menu-triggered PLUGIN (Tools -> MCP / Filters -> Development -> Start\n# MCP Server) that listens on 127.0.0.1:9877 and drives GIMP. Keep the \"GIMP MCP\n# Server\" window open \u2014 GLib.MainLoop() holds the out-of-process plug-in alive.\n#\n# This is the user's verified file; the body below is kept byte-for-byte. The\n# lines in THIS block are a provenance comment only \u2014 do not edit the code below.\n# ---------------------------------------------------------------------------\n\n# GIMP MCP Server Plugin (GIMP 3.2)\n# Listens on localhost:9877 so it does not collide with Blender MCP (9876).\n\nimport gi\ngi.require_version('Gimp', '3.0')\nfrom gi.repository import Gimp\nfrom gi.repository import GLib\nfrom gi.repository import Gio\nfrom gi.repository import GObject\n\nimport io\nimport json\nimport os\nimport socket\nimport sys\nimport threading\nimport traceback\n\nHOST = '127.0.0.1'\nPORT = 9877\n# Store GIMP redirects GLib config dir into the MSIX LocalCache. Also write\n# to the real roaming profile so the log is findable.\n_LOG_PATHS = [\n os.path.join(GLib.get_user_config_dir(), 'GIMP', '3.2', 'gimp_mcp_server.log'),\n os.path.join(os.environ.get('APPDATA', ''), 'GIMP', '3.2', 'gimp_mcp_server.log'),\n]\n\n_server_lock = threading.Lock()\n_server_started = False\n_exec_globals = None\n_main_loop = None\n\n\ndef _log(msg):\n line = msg.rstrip() + '\\n'\n for path in _LOG_PATHS:\n if not path:\n continue\n try:\n os.makedirs(os.path.dirname(path), exist_ok=True)\n with open(path, 'a', encoding='utf-8') as fh:\n fh.write(line)\n except Exception:\n pass\n print(msg, flush=True)\n\n\ndef N_(message):\n return message\n\n\ndef _(message):\n return GLib.dgettext(None, message)\n\n\ndef _serialize(obj):\n if obj is None or isinstance(obj, (str, int, float, bool)):\n return obj\n if isinstance(obj, (list, tuple)):\n return [_serialize(item) for item in obj]\n if isinstance(obj, dict):\n return {str(k): _serialize(v) for k, v in obj.items()}\n data = {'type': obj.__class__.__name__}\n if hasattr(obj, 'get_id'):\n try:\n data['id'] = int(obj.get_id())\n except Exception:\n pass\n elif hasattr(obj, 'ID'):\n data['id'] = obj.ID\n if hasattr(obj, 'get_name'):\n try:\n data['name'] = obj.get_name()\n except Exception:\n pass\n if 'id' not in data and len(data) == 1:\n return str(obj)\n return data\n\n\ndef _resolve_api(api_path):\n parts = [p for p in str(api_path).split('.') if p]\n if parts and parts[0] == 'Gimp':\n parts = parts[1:]\n elif len(parts) >= 3 and parts[0] == 'gi' and parts[1] == 'repository' and parts[2] == 'Gimp':\n parts = parts[3:]\n\n aliases = {\n 'list_images': 'get_images',\n 'list_fonts': 'get_fonts',\n 'fonts_get_list': 'get_fonts',\n }\n\n if not parts:\n return Gimp\n\n obj = Gimp\n for part in parts:\n part = aliases.get(part, part)\n obj = getattr(obj, part)\n return obj\n\n\ndef _exec_namespace():\n global _exec_globals\n if _exec_globals is None:\n ns = {\n '__builtins__': __builtins__,\n '__name__': '__main__',\n 'Gimp': Gimp,\n 'GLib': GLib,\n 'Gio': Gio,\n 'GObject': GObject,\n 'sys': sys,\n 'os': os,\n 'json': json,\n }\n try:\n gi.require_version('Gegl', '0.4')\n from gi.repository import Gegl\n ns['Gegl'] = Gegl\n except Exception as exc:\n _log(f'Gegl import failed: {exc}')\n try:\n gi.require_version('Babl', '0.1')\n from gi.repository import Babl\n ns['Babl'] = Babl\n except Exception:\n pass\n _exec_globals = ns\n return _exec_globals\n\n\ndef _extract_code(args, kwargs):\n if kwargs.get('code'):\n return str(kwargs['code'])\n if not args:\n return ''\n if len(args) >= 2 and args[0] in ('exec', 'eval', 'pyGObject-console'):\n payload = args[1]\n if isinstance(payload, (list, tuple)):\n return '\\n'.join(str(item) for item in payload)\n return str(payload)\n if isinstance(args[0], (list, tuple)):\n return '\\n'.join(str(item) for item in args[0])\n return str(args[0])\n\n\ndef _run_python(code):\n ns = _exec_namespace()\n stdout = io.StringIO()\n old_stdout = sys.stdout\n sys.stdout = stdout\n try:\n exec(code, ns)\n finally:\n sys.stdout = old_stdout\n printed = stdout.getvalue()\n result = ns.get('_result')\n if result is not None:\n return {'printed': printed, 'result': _serialize(result)}\n return {'printed': printed, 'result': None}\n\n\nclass MCPPlugin(Gimp.PlugIn):\n def do_set_i18n(self, name):\n return False\n\n def do_query_procedures(self):\n return ['plug-in-mcp-server']\n\n def do_create_procedure(self, name):\n procedure = Gimp.Procedure.new(\n self, name, Gimp.PDBProcType.PLUGIN, self.run, None\n )\n procedure.set_menu_label(_('Start MCP Server'))\n procedure.set_documentation(\n _('Starts an MCP server to control GIMP externally'),\n _('Listens on 127.0.0.1:9877 for the GIMP MCP client'),\n name,\n )\n procedure.set_attribution('GIMP MCP', 'GIMP MCP', '2026')\n procedure.set_sensitivity_mask(Gimp.ProcedureSensitivityMask.ALWAYS)\n procedure.add_enum_argument(\n 'run-mode',\n _('Run mode'),\n _('The run mode'),\n Gimp.RunMode,\n Gimp.RunMode.INTERACTIVE,\n GObject.ParamFlags.READWRITE,\n )\n procedure.add_menu_path('<Image>/Tools/MCP')\n procedure.add_menu_path('<Image>/Filters/Development/')\n return procedure\n\n def run(self, procedure, config, data):\n global _main_loop\n started, message = self.ensure_server()\n _log(message)\n if not started:\n return procedure.new_return_values(Gimp.PDBStatusType.EXECUTION_ERROR, GLib.Error(message))\n\n # GIMP 3 launches Python plug-ins out of process. Returning from run()\n # would exit that process and kill the socket thread. Stay alive and\n # dispatch GLib.idle_add work (API calls) on this main loop.\n # A visible window is required: otherwise \"Start MCP Server\" looks like\n # it did not open anything.\n try:\n self._show_status_window(message)\n except Exception as exc:\n _log(f'Status window failed: {exc}\\n{traceback.format_exc()}')\n\n _main_loop = GLib.MainLoop()\n _log('Entering GLib.MainLoop to keep MCP server process alive')\n try:\n _main_loop.run()\n except Exception as exc:\n _log(f'MainLoop exited: {exc}')\n return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())\n\n def _show_status_window(self, message):\n gi.require_version('Gtk', '3.0')\n gi.require_version('GimpUi', '3.0')\n from gi.repository import Gtk\n from gi.repository import GimpUi\n\n GimpUi.init('gimp_mcp_server.py')\n window = Gtk.Window(title='GIMP MCP Server')\n window.set_default_size(360, 120)\n window.set_keep_above(False)\n window.set_skip_taskbar_hint(False)\n box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)\n box.set_border_width(16)\n label = Gtk.Label(label=message + '\\nLeave this window open.')\n label.set_line_wrap(True)\n box.pack_start(label, True, True, 0)\n button = Gtk.Button(label='Stop server')\n button.connect('clicked', lambda *_: window.destroy())\n box.pack_start(button, False, False, 0)\n window.add(box)\n window.connect('destroy', self._on_status_closed)\n window.show_all()\n _log('Status window opened')\n\n def _on_status_closed(self, *_args):\n global _main_loop\n _log('Status window closed; stopping MCP server')\n if _main_loop is not None:\n _main_loop.quit()\n\n def ensure_server(self):\n global _server_started\n with _server_lock:\n if _server_started:\n return True, f'MCP Server already listening on {HOST}:{PORT}'\n try:\n thread = threading.Thread(target=self.start_server, daemon=True)\n thread.start()\n _server_started = True\n return True, f'MCP Server started on {HOST}:{PORT}'\n except Exception as exc:\n return False, f'Failed to start MCP Server: {exc}'\n\n def start_server(self):\n sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n try:\n sock.bind((HOST, PORT))\n sock.listen(8)\n _log(f'Server listening on {HOST}:{PORT}')\n except Exception as exc:\n _log(f'Bind/listen failed: {exc}')\n return\n\n while True:\n try:\n client, addr = sock.accept()\n _log(f'Connected to {addr}')\n threading.Thread(target=self.handle_client, args=(client,), daemon=True).start()\n except Exception as exc:\n _log(f'Server accept error: {exc}')\n break\n sock.close()\n\n def handle_client(self, client):\n buffer = b''\n try:\n client.settimeout(120)\n while True:\n chunk = client.recv(65536)\n if not chunk:\n break\n buffer += chunk\n while buffer:\n try:\n command = json.loads(buffer.decode('utf-8'))\n buffer = b''\n except json.JSONDecodeError:\n break\n GLib.idle_add(self.execute_command, command, client)\n except Exception as exc:\n _log(f'Client error: {exc}')\n finally:\n try:\n client.close()\n except Exception:\n pass\n\n def execute_command(self, command, client):\n cmd_type = command.get('type')\n params = command.get('params', {}) or {}\n try:\n if cmd_type == 'ping':\n response = {'status': 'success', 'result': 'pong'}\n elif cmd_type == 'exec':\n code = _extract_code(params.get('args', []), params.get('kwargs', {}))\n if not code:\n code = str(params.get('code', ''))\n response = {'status': 'success', 'result': _run_python(code)}\n elif cmd_type == 'call_api':\n api_path = params.get('api_path', '')\n args = params.get('args', []) or []\n kwargs = params.get('kwargs', {}) or {}\n if api_path in ('exec', 'eval', '__exec__'):\n response = {'status': 'success', 'result': _run_python(_extract_code(args, kwargs))}\n else:\n method = _resolve_api(api_path)\n result = method(*args, **kwargs)\n response = {'status': 'success', 'result': _serialize(result)}\n else:\n response = {'status': 'error', 'message': f'Unknown command: {cmd_type}'}\n except Exception as exc:\n response = {\n 'status': 'error',\n 'message': str(exc),\n 'traceback': traceback.format_exc(),\n }\n _log(f'Command error: {response[\"message\"]}\\n{response[\"traceback\"]}')\n\n try:\n payload = json.dumps(response).encode('utf-8')\n client.sendall(payload)\n except Exception as exc:\n _log(f'Response send failed: {exc}')\n return False\n\n\nGimp.main(MCPPlugin.__gtype__, sys.argv)\n";
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.GIMP_PLUGIN_SOURCE = exports.GIMP_PLUGIN_SOURCE_HASH = void 0;
|
|
13
13
|
/** sha256 (first 12 hex) of the canonical media/gimp/gimp_mcp_server.py. */
|
|
14
|
-
exports.GIMP_PLUGIN_SOURCE_HASH = "
|
|
14
|
+
exports.GIMP_PLUGIN_SOURCE_HASH = "882fdb94b5c9";
|
|
15
15
|
/** Full body of the user-verified GIMP 3.x MCP server plug-in (media/gimp/gimp_mcp_server.py). */
|
|
16
16
|
exports.GIMP_PLUGIN_SOURCE = `#!/usr/bin/env python3
|
|
17
17
|
# ---------------------------------------------------------------------------
|
|
@@ -20,7 +20,7 @@ exports.GIMP_PLUGIN_SOURCE = `#!/usr/bin/env python3
|
|
|
20
20
|
# Derived from libreearth/gimp-mcp (GPL-3.0; see media/gimp/LICENSE) and fixed by
|
|
21
21
|
# the user / AutoDev for GIMP 3.2 (Store / MSIX). Verified working on real GIMP
|
|
22
22
|
# 3.2.44: a menu-triggered PLUGIN (Tools -> MCP / Filters -> Development -> Start
|
|
23
|
-
# MCP Server) that listens on 127.0.0.1:
|
|
23
|
+
# MCP Server) that listens on 127.0.0.1:9877 and drives GIMP. Keep the "GIMP MCP
|
|
24
24
|
# Server" window open — GLib.MainLoop() holds the out-of-process plug-in alive.
|
|
25
25
|
#
|
|
26
26
|
# This is the user's verified file; the body below is kept byte-for-byte. The
|
|
@@ -28,7 +28,7 @@ exports.GIMP_PLUGIN_SOURCE = `#!/usr/bin/env python3
|
|
|
28
28
|
# ---------------------------------------------------------------------------
|
|
29
29
|
|
|
30
30
|
# GIMP MCP Server Plugin (GIMP 3.2)
|
|
31
|
-
# Listens on localhost:
|
|
31
|
+
# Listens on localhost:9877 so it does not collide with Blender MCP (9876).
|
|
32
32
|
|
|
33
33
|
import gi
|
|
34
34
|
gi.require_version('Gimp', '3.0')
|
|
@@ -46,7 +46,7 @@ import threading
|
|
|
46
46
|
import traceback
|
|
47
47
|
|
|
48
48
|
HOST = '127.0.0.1'
|
|
49
|
-
PORT =
|
|
49
|
+
PORT = 9877
|
|
50
50
|
# Store GIMP redirects GLib config dir into the MSIX LocalCache. Also write
|
|
51
51
|
# to the real roaming profile so the log is findable.
|
|
52
52
|
_LOG_PATHS = [
|
|
@@ -205,7 +205,7 @@ class MCPPlugin(Gimp.PlugIn):
|
|
|
205
205
|
procedure.set_menu_label(_('Start MCP Server'))
|
|
206
206
|
procedure.set_documentation(
|
|
207
207
|
_('Starts an MCP server to control GIMP externally'),
|
|
208
|
-
_('Listens on 127.0.0.1:
|
|
208
|
+
_('Listens on 127.0.0.1:9877 for the GIMP MCP client'),
|
|
209
209
|
name,
|
|
210
210
|
)
|
|
211
211
|
procedure.set_attribution('GIMP MCP', 'GIMP MCP', '2026')
|
|
@@ -226,10 +226,6 @@ class MCPPlugin(Gimp.PlugIn):
|
|
|
226
226
|
global _main_loop
|
|
227
227
|
started, message = self.ensure_server()
|
|
228
228
|
_log(message)
|
|
229
|
-
try:
|
|
230
|
-
Gimp.message(message)
|
|
231
|
-
except Exception:
|
|
232
|
-
pass
|
|
233
229
|
if not started:
|
|
234
230
|
return procedure.new_return_values(Gimp.PDBStatusType.EXECUTION_ERROR, GLib.Error(message))
|
|
235
231
|
|
|
@@ -259,11 +255,12 @@ class MCPPlugin(Gimp.PlugIn):
|
|
|
259
255
|
|
|
260
256
|
GimpUi.init('gimp_mcp_server.py')
|
|
261
257
|
window = Gtk.Window(title='GIMP MCP Server')
|
|
262
|
-
window.set_default_size(
|
|
263
|
-
window.set_keep_above(
|
|
258
|
+
window.set_default_size(360, 120)
|
|
259
|
+
window.set_keep_above(False)
|
|
260
|
+
window.set_skip_taskbar_hint(False)
|
|
264
261
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
|
|
265
262
|
box.set_border_width(16)
|
|
266
|
-
label = Gtk.Label(label=message + '\\
|
|
263
|
+
label = Gtk.Label(label=message + '\\nLeave this window open.')
|
|
267
264
|
label.set_line_wrap(True)
|
|
268
265
|
box.pack_start(label, True, True, 0)
|
|
269
266
|
button = Gtk.Button(label='Stop server')
|
|
@@ -272,7 +269,6 @@ class MCPPlugin(Gimp.PlugIn):
|
|
|
272
269
|
window.add(box)
|
|
273
270
|
window.connect('destroy', self._on_status_closed)
|
|
274
271
|
window.show_all()
|
|
275
|
-
window.present()
|
|
276
272
|
_log('Status window opened')
|
|
277
273
|
|
|
278
274
|
def _on_status_closed(self, *_args):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gimpPluginSource.js","sourceRoot":"","sources":["../../src/core/gimpPluginSource.ts"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,wDAAwD;AACxD,EAAE;AACF,oFAAoF;AACpF,wFAAwF;AACxF,EAAE;AACF,4GAA4G;AAC5G,0DAA0D;AAC1D,8EAA8E;;;AAE9E,4EAA4E;AAC/D,QAAA,uBAAuB,GAAG,cAAc,CAAC;AAEtD,kGAAkG;AACrF,QAAA,kBAAkB,GAAG
|
|
1
|
+
{"version":3,"file":"gimpPluginSource.js","sourceRoot":"","sources":["../../src/core/gimpPluginSource.ts"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,wDAAwD;AACxD,EAAE;AACF,oFAAoF;AACpF,wFAAwF;AACxF,EAAE;AACF,4GAA4G;AAC5G,0DAA0D;AAC1D,8EAA8E;;;AAE9E,4EAA4E;AAC/D,QAAA,uBAAuB,GAAG,cAAc,CAAC;AAEtD,kGAAkG;AACrF,QAAA,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0WjC,CAAC"}
|
package/out/core/mcpCatalog.js
CHANGED
|
@@ -160,13 +160,14 @@ function getCatalog(platform) {
|
|
|
160
160
|
prerequisites: [{ id: 'uv', installHint: uvInstallHint(platform) }],
|
|
161
161
|
postSetup: [
|
|
162
162
|
`Plugin + client are installed automatically into your GIMP 3.x config (${gimpPluginDir(platform)}) — you copy nothing. Re-run any time with: autodev mcp gimp-install-plugin`,
|
|
163
|
-
"In GIMP: Tools → MCP → Start MCP Server (or Filters → Development → Start MCP Server), and KEEP the 'GIMP MCP Server' window open — it holds the plug-in process alive and the :
|
|
163
|
+
"In GIMP: Tools → MCP → Start MCP Server (or Filters → Development → Start MCP Server), and KEEP the 'GIMP MCP Server' window open — it holds the plug-in process alive and the :9877 socket. One click per GIMP session (the window may hide behind other apps; use the taskbar).",
|
|
164
164
|
'Requires GIMP 3.x (the plugin uses the GIMP 3.0 Python API).',
|
|
165
|
-
|
|
165
|
+
'GIMP MCP uses port 9877; Blender uses 9876, so they no longer collide and can run together.',
|
|
166
166
|
],
|
|
167
167
|
notes: [
|
|
168
168
|
`Log: ${gimpLogPath(platform)} (Store/MSIX GIMP also mirrors a copy inside its sandbox LocalCache).`,
|
|
169
169
|
`The client runs via \`uv run --with ${exports.GIMP_MCP_PIN} <installed client>\`; the ${exports.GIMP_MCP_PIN} pin is load-bearing (current mcp releases break the client on a removed description= kwarg).`,
|
|
170
|
+
'GIMP 3.2.4 can crash (access-violation) on TextLayer merge_down/merge_filter — rasterize text via Gimp.Layer.new_from_visible first, then filter/merge the raster copy.',
|
|
170
171
|
],
|
|
171
172
|
},
|
|
172
173
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcpCatalog.js","sourceRoot":"","sources":["../../src/core/mcpCatalog.ts"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,gFAAgF;AAChF,2EAA2E;AAC3E,gFAAgF;AAChF,wEAAwE;AACxE,EAAE;AACF,uEAAuE;AACvE,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;;;AA4C9E,sCAQC;AAGD,8CAIC;AAGD,wCAIC;AA4BD,sCAcC;AAGD,0CAKC;AAOD,0DAMC;AAGD,
|
|
1
|
+
{"version":3,"file":"mcpCatalog.js","sourceRoot":"","sources":["../../src/core/mcpCatalog.ts"],"names":[],"mappings":"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"}
|