autodev-cli 1.4.151 → 1.4.152
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 +204 -0
- package/media/gimp/gimp_mcp_server.py +310 -211
- package/out/commands/mcp.js +36 -30
- package/out/commands/mcp.js.map +1 -1
- package/out/configManager.js +39 -8
- package/out/configManager.js.map +1 -1
- package/out/core/gimpClientSource.d.ts +4 -0
- package/out/core/gimpClientSource.js +221 -0
- package/out/core/gimpClientSource.js.map +1 -0
- package/out/core/gimpPluginInstall.d.ts +36 -13
- package/out/core/gimpPluginInstall.js +150 -78
- package/out/core/gimpPluginInstall.js.map +1 -1
- package/out/core/gimpPluginSource.d.ts +3 -3
- package/out/core/gimpPluginSource.js +314 -215
- package/out/core/gimpPluginSource.js.map +1 -1
- package/out/core/mcpCatalog.d.ts +25 -17
- package/out/core/mcpCatalog.js +68 -36
- package/out/core/mcpCatalog.js.map +1 -1
- package/out/core/mcpCatalogMigrate.d.ts +9 -1
- package/out/core/mcpCatalogMigrate.js +34 -22
- package/out/core/mcpCatalogMigrate.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# ---------------------------------------------------------------------------
|
|
3
|
+
# gimp_mcp_client.py — vendored, USER-VERIFIED GIMP MCP client (FastMCP stdio).
|
|
4
|
+
#
|
|
5
|
+
# Derived from libreearth/gimp-mcp (GPL-3.0; see media/gimp/LICENSE) and fixed by
|
|
6
|
+
# the user / AutoDev: reconnect via getpeername, reads > 1024 bytes, get_images.
|
|
7
|
+
# Launched by: uv run --with mcp==1.2.0 <this file>. The body below is kept
|
|
8
|
+
# byte-for-byte; the lines in THIS block are a provenance comment only.
|
|
9
|
+
# ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
# GIMP MCP Server Script
|
|
12
|
+
# Provides an MCP interface to control GIMP via a socket connection.
|
|
13
|
+
|
|
14
|
+
from mcp.server.fastmcp import FastMCP, Context
|
|
15
|
+
import socket
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
20
|
+
logger = logging.getLogger("GimpMCPServer")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class GimpConnection:
|
|
24
|
+
def __init__(self, host='127.0.0.1', port=9876):
|
|
25
|
+
self.host = host
|
|
26
|
+
self.port = port
|
|
27
|
+
self.sock = None
|
|
28
|
+
|
|
29
|
+
def close(self):
|
|
30
|
+
if self.sock:
|
|
31
|
+
try:
|
|
32
|
+
self.sock.close()
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
self.sock = None
|
|
36
|
+
|
|
37
|
+
def connect(self):
|
|
38
|
+
if self.sock:
|
|
39
|
+
try:
|
|
40
|
+
self.sock.getpeername()
|
|
41
|
+
return
|
|
42
|
+
except Exception:
|
|
43
|
+
self.close()
|
|
44
|
+
try:
|
|
45
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
46
|
+
sock.settimeout(30)
|
|
47
|
+
sock.connect((self.host, self.port))
|
|
48
|
+
self.sock = sock
|
|
49
|
+
logger.info(f"Connected to GIMP at {self.host}:{self.port}")
|
|
50
|
+
except Exception as e:
|
|
51
|
+
self.close()
|
|
52
|
+
logger.error(f"Failed to connect: {e}")
|
|
53
|
+
raise ConnectionError(
|
|
54
|
+
"Could not connect to GIMP. Open GIMP and run "
|
|
55
|
+
"Filters > Development > Start MCP Server."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def send_command(self, command_type, params=None):
|
|
59
|
+
self.connect()
|
|
60
|
+
command = {"type": command_type, "params": params or {}}
|
|
61
|
+
try:
|
|
62
|
+
self.sock.sendall(json.dumps(command).encode('utf-8'))
|
|
63
|
+
chunks = []
|
|
64
|
+
while True:
|
|
65
|
+
data = self.sock.recv(65536)
|
|
66
|
+
if not data:
|
|
67
|
+
break
|
|
68
|
+
chunks.append(data)
|
|
69
|
+
try:
|
|
70
|
+
return json.loads(b''.join(chunks).decode('utf-8'))
|
|
71
|
+
except json.JSONDecodeError:
|
|
72
|
+
continue
|
|
73
|
+
raise Exception("GIMP closed the connection before sending a complete response")
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error(f"Communication error: {e}")
|
|
76
|
+
self.close()
|
|
77
|
+
raise Exception(f"Error communicating with GIMP: {e}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
_gimp_connection = None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_gimp_connection():
|
|
84
|
+
global _gimp_connection
|
|
85
|
+
if _gimp_connection is None:
|
|
86
|
+
_gimp_connection = GimpConnection()
|
|
87
|
+
_gimp_connection.connect()
|
|
88
|
+
return _gimp_connection
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
mcp = FastMCP("GimpMCP", description="GIMP integration through MCP")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@mcp.tool()
|
|
95
|
+
def call_api(ctx: Context, api_path: str, args: list = [], kwargs: dict = {}) -> str:
|
|
96
|
+
"""Call any GIMP API method dynamically.
|
|
97
|
+
|
|
98
|
+
Parameters:
|
|
99
|
+
- api_path: The path to the API method (e.g., "Gimp.Image.get_by_id")
|
|
100
|
+
Use "exec" to run Python inside GIMP. Pass the code in args[0].
|
|
101
|
+
- args: List of positional arguments
|
|
102
|
+
- kwargs: Dictionary of keyword arguments
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
- JSON string of the result or error message
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
conn = get_gimp_connection()
|
|
109
|
+
result = conn.send_command("call_api", {"api_path": api_path, "args": args, "kwargs": kwargs})
|
|
110
|
+
if result["status"] == "success":
|
|
111
|
+
return json.dumps(result["result"])
|
|
112
|
+
return f"Error: {result['message']}"
|
|
113
|
+
except Exception as e:
|
|
114
|
+
return f"Error: {e}"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@mcp.tool()
|
|
118
|
+
def list_images(ctx: Context) -> str:
|
|
119
|
+
"""List all open images in GIMP.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
- JSON string of image IDs and names
|
|
123
|
+
"""
|
|
124
|
+
return call_api(ctx, "Gimp.get_images")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@mcp.tool()
|
|
128
|
+
def get_image_info(ctx: Context, image_id: int) -> str:
|
|
129
|
+
"""Get information about a specific image.
|
|
130
|
+
|
|
131
|
+
Parameters:
|
|
132
|
+
- image_id: The ID of the image
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
- JSON string of image details
|
|
136
|
+
"""
|
|
137
|
+
return call_api(
|
|
138
|
+
ctx,
|
|
139
|
+
"exec",
|
|
140
|
+
args=[
|
|
141
|
+
f"""
|
|
142
|
+
image = Gimp.Image.get_by_id({int(image_id)})
|
|
143
|
+
if image is None:
|
|
144
|
+
_result = {{"error": "image not found"}}
|
|
145
|
+
else:
|
|
146
|
+
_result = {{
|
|
147
|
+
"id": image.get_id(),
|
|
148
|
+
"name": image.get_name(),
|
|
149
|
+
"width": image.get_width(),
|
|
150
|
+
"height": image.get_height(),
|
|
151
|
+
"layers": [{{"id": layer.get_id(), "name": layer.get_name()}} for layer in image.get_layers()],
|
|
152
|
+
}}
|
|
153
|
+
"""
|
|
154
|
+
],
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@mcp.tool()
|
|
159
|
+
def apply_gaussian_blur(ctx: Context, image_id: int, radius: float = 5.0) -> str:
|
|
160
|
+
"""Apply Gaussian blur to an image.
|
|
161
|
+
|
|
162
|
+
Parameters:
|
|
163
|
+
- image_id: The ID of the image
|
|
164
|
+
- radius: Blur radius
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
- Success message or error
|
|
168
|
+
"""
|
|
169
|
+
return call_api(
|
|
170
|
+
ctx,
|
|
171
|
+
"exec",
|
|
172
|
+
args=[
|
|
173
|
+
f"""
|
|
174
|
+
image = Gimp.Image.get_by_id({int(image_id)})
|
|
175
|
+
if image is None:
|
|
176
|
+
_result = "image not found"
|
|
177
|
+
else:
|
|
178
|
+
layers = image.get_layers()
|
|
179
|
+
if not layers:
|
|
180
|
+
_result = "no layers"
|
|
181
|
+
else:
|
|
182
|
+
drawable = layers[0]
|
|
183
|
+
blur = Gimp.DrawableFilter.new(drawable, "gegl:gaussian-blur", "")
|
|
184
|
+
cfg = blur.get_config()
|
|
185
|
+
try:
|
|
186
|
+
cfg.set_property("std-dev-x", {float(radius)})
|
|
187
|
+
cfg.set_property("std-dev-y", {float(radius)})
|
|
188
|
+
except Exception:
|
|
189
|
+
pass
|
|
190
|
+
drawable.append_filter(blur)
|
|
191
|
+
drawable.merge_filter(blur)
|
|
192
|
+
Gimp.displays_flush()
|
|
193
|
+
_result = "Applied Gaussian blur successfully"
|
|
194
|
+
"""
|
|
195
|
+
],
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def main():
|
|
200
|
+
mcp.run()
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
if __name__ == "__main__":
|
|
204
|
+
main()
|