sd-api-mcp 0.2.0__py3-none-any.whl

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.
sd_api_mcp/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """MCP server for Stable Diffusion WebUI API."""
2
+
3
+ __version__ = "0.1.0"
sd_api_mcp/client.py ADDED
@@ -0,0 +1,118 @@
1
+ """SD WebUI API クライアント。"""
2
+
3
+ from contextlib import asynccontextmanager
4
+
5
+ import httpx
6
+
7
+
8
+ class SDAPIError(Exception):
9
+ """SD WebUI API エラーの基底クラス。"""
10
+
11
+
12
+ class SDConnectionError(SDAPIError):
13
+ """SD WebUI への接続失敗。"""
14
+
15
+
16
+ class SDTimeoutError(SDAPIError):
17
+ """SD WebUI へのリクエストタイムアウト。"""
18
+
19
+
20
+ @asynccontextmanager
21
+ async def _convert_httpx_errors():
22
+ """httpx例外をドメイン例外に変換するコンテキストマネージャ。"""
23
+ try:
24
+ yield
25
+ except httpx.ConnectError as e:
26
+ raise SDConnectionError(str(e)) from e
27
+ except httpx.TimeoutException as e:
28
+ raise SDTimeoutError(str(e)) from e
29
+ except httpx.HTTPStatusError as e:
30
+ raise SDAPIError(str(e)) from e
31
+
32
+
33
+ def _with_controlnet(payload: dict, units: list[dict] | None) -> dict:
34
+ """ControlNetユニットをペイロードに追加する(DRY)。"""
35
+ if not units:
36
+ return payload
37
+ return {**payload, "alwayson_scripts": {"controlnet": {"args": units}}}
38
+
39
+
40
+ def _extract_images(data: dict, error_msg: str) -> list[str]:
41
+ """レスポンスから画像リストを取り出す(DRY)。"""
42
+ images = data.get("images", [])
43
+ if not images:
44
+ raise SDAPIError(error_msg)
45
+ return images
46
+
47
+
48
+ class SDWebUIClient:
49
+ """SD WebUI API への非同期HTTPクライアント。"""
50
+
51
+ def __init__(
52
+ self,
53
+ base_url: str,
54
+ timeout: float,
55
+ auth: tuple[str, str] | None,
56
+ ) -> None:
57
+ auth_param = httpx.BasicAuth(*auth) if auth else None
58
+ self._client = httpx.AsyncClient(
59
+ base_url=base_url,
60
+ timeout=timeout,
61
+ auth=auth_param,
62
+ )
63
+
64
+ async def _get(self, path: str) -> list | dict:
65
+ async with _convert_httpx_errors():
66
+ resp = await self._client.get(path)
67
+ resp.raise_for_status()
68
+ return resp.json()
69
+
70
+ async def _post(self, path: str, payload: dict) -> list | dict:
71
+ async with _convert_httpx_errors():
72
+ resp = await self._client.post(path, json=payload)
73
+ resp.raise_for_status()
74
+ return resp.json()
75
+
76
+ async def get_sd_models(self) -> list[str]:
77
+ data = await self._get("/sdapi/v1/sd-models")
78
+ return [m["title"] for m in data] # type: ignore[index]
79
+
80
+ async def set_sd_model(self, model_name: str) -> None:
81
+ await self._post("/sdapi/v1/options", {"sd_model_checkpoint": model_name})
82
+
83
+ async def get_upscalers(self) -> list[str]:
84
+ data = await self._get("/sdapi/v1/upscalers")
85
+ return [u["name"] for u in data] # type: ignore[index]
86
+
87
+ async def get_controlnet_models(self) -> list[str]:
88
+ data = await self._get("/controlnet/model_list")
89
+ assert isinstance(data, dict)
90
+ return data["model_list"]
91
+
92
+ async def get_controlnet_modules(self) -> list[str]:
93
+ data = await self._get("/controlnet/module_list")
94
+ assert isinstance(data, dict)
95
+ return data["module_list"]
96
+
97
+ async def txt2img(
98
+ self,
99
+ payload: dict,
100
+ controlnet_units: list[dict] | None = None,
101
+ ) -> list[str]:
102
+ data = await self._post("/sdapi/v1/txt2img", _with_controlnet(payload, controlnet_units))
103
+ assert isinstance(data, dict)
104
+ return _extract_images(data, "画像が生成されませんでした")
105
+
106
+ async def img2img(
107
+ self,
108
+ payload: dict,
109
+ controlnet_units: list[dict] | None = None,
110
+ ) -> list[str]:
111
+ data = await self._post("/sdapi/v1/img2img", _with_controlnet(payload, controlnet_units))
112
+ assert isinstance(data, dict)
113
+ return _extract_images(data, "画像が生成されませんでした")
114
+
115
+ async def upscale_images(self, payload: dict) -> list[str]:
116
+ data = await self._post("/sdapi/v1/extra-batch-images", payload)
117
+ assert isinstance(data, dict)
118
+ return _extract_images(data, "アップスケール結果がありませんでした")
sd_api_mcp/server.py ADDED
@@ -0,0 +1,287 @@
1
+ """MCP server for Stable Diffusion WebUI API."""
2
+
3
+ import base64
4
+ import os
5
+ import uuid
6
+ from contextlib import asynccontextmanager
7
+ from pathlib import Path
8
+ from typing import Annotated
9
+
10
+ from fastmcp import FastMCP
11
+ from fastmcp.exceptions import ToolError
12
+ from pydantic import BaseModel, Field
13
+
14
+ from sd_api_mcp.client import SDAPIError, SDConnectionError, SDTimeoutError, SDWebUIClient
15
+
16
+ _client: SDWebUIClient | None = None
17
+
18
+
19
+ @asynccontextmanager
20
+ async def lifespan(server: FastMCP):
21
+ global _client
22
+ base_url = os.environ.get("SD_WEBUI_URL", "http://host.docker.internal:7860")
23
+ timeout = float(os.environ.get("REQUEST_TIMEOUT", "300"))
24
+ user = os.environ.get("SD_AUTH_USER")
25
+ password = os.environ.get("SD_AUTH_PASS")
26
+ auth = (user, password) if user and password else None
27
+ _client = SDWebUIClient(base_url=base_url, timeout=timeout, auth=auth)
28
+ yield
29
+ _client = None
30
+
31
+
32
+ mcp = FastMCP(name="sd-api-mcp", lifespan=lifespan)
33
+
34
+
35
+ class ControlNetUnit(BaseModel):
36
+ image: str
37
+ model: str
38
+ module: str = "none"
39
+ weight: float = 1.0
40
+ guidance_start: float = 0.0
41
+ guidance_end: float = 1.0
42
+ control_mode: int = 0
43
+ resize_mode: int = 1
44
+
45
+
46
+ def _save_image(b64: str, output_dir: str) -> str:
47
+ """base64画像をPNGとして保存し、絶対パスを返す(DRY)。"""
48
+ out = Path(output_dir)
49
+ out.mkdir(parents=True, exist_ok=True)
50
+ path = out / f"sd_{uuid.uuid4().hex}.png"
51
+ path.write_bytes(base64.b64decode(b64))
52
+ return str(path.resolve())
53
+
54
+
55
+ def _to_tool_error(e: SDAPIError) -> ToolError:
56
+ if isinstance(e, SDConnectionError):
57
+ return ToolError(f"SD WebUI に接続できません: {e}")
58
+ if isinstance(e, SDTimeoutError):
59
+ return ToolError(f"リクエストがタイムアウトしました: {e}")
60
+ return ToolError(f"API エラー: {e}")
61
+
62
+
63
+ def _read_image_b64(path: str) -> str:
64
+ """画像ファイルをbase64文字列に変換する(DRY)。"""
65
+ src = Path(path)
66
+ if not src.exists():
67
+ raise ToolError(f"画像ファイルが見つかりません: {path}")
68
+ return base64.b64encode(src.read_bytes()).decode()
69
+
70
+
71
+ def _build_controlnet_units(units: list[ControlNetUnit] | None) -> list[dict] | None:
72
+ """ControlNetUnitリストをdictリストに変換する(DRY)。"""
73
+ if not units:
74
+ return None
75
+ return [{**u.model_dump(exclude={"image"}), "image": _read_image_b64(u.image)} for u in units]
76
+
77
+
78
+ @mcp.tool()
79
+ async def txt2img(
80
+ prompt: Annotated[str, Field(description="生成プロンプト")],
81
+ negative_prompt: Annotated[str, Field(description="ネガティブプロンプト")] = "",
82
+ steps: Annotated[int, Field(description="サンプリングステップ数", ge=1, le=150)] = 20,
83
+ width: Annotated[int, Field(description="画像の幅", ge=64, le=2048)] = 512,
84
+ height: Annotated[int, Field(description="画像の高さ", ge=64, le=2048)] = 512,
85
+ cfg_scale: Annotated[float, Field(description="CFGスケール", ge=1.0, le=30.0)] = 7.0,
86
+ sampler_name: Annotated[str, Field(description="サンプラー名")] = "Euler a",
87
+ scheduler_name: Annotated[str, Field(description="スケジューラー名")] = "Automatic",
88
+ seed: Annotated[int, Field(description="シード値(-1でランダム)", ge=-1)] = -1,
89
+ batch_size: Annotated[int, Field(description="生成枚数", ge=1, le=4)] = 1,
90
+ restore_faces: Annotated[bool, Field(description="顔修復を有効にする")] = False,
91
+ tiling: Annotated[bool, Field(description="タイリング画像を生成する")] = False,
92
+ distilled_cfg_scale: Annotated[
93
+ float, Field(description="Distilled CFGスケール", ge=1.0, le=30.0)
94
+ ] = 3.5,
95
+ controlnet_units: Annotated[
96
+ list[ControlNetUnit] | None, Field(description="ControlNetユニットのリスト")
97
+ ] = None,
98
+ output_dir: Annotated[str, Field(description="出力ディレクトリ")] = "./output",
99
+ ) -> list[str]:
100
+ """テキストプロンプトから画像を生成してファイルに保存する。
101
+
102
+ Returns:
103
+ 保存された画像ファイルの絶対パスリスト
104
+ """
105
+ assert _client is not None
106
+ payload = {
107
+ "prompt": prompt,
108
+ "negative_prompt": negative_prompt,
109
+ "steps": steps,
110
+ "width": width,
111
+ "height": height,
112
+ "cfg_scale": cfg_scale,
113
+ "sampler_name": sampler_name,
114
+ "scheduler": scheduler_name,
115
+ "seed": seed,
116
+ "n_iter": batch_size,
117
+ "restore_faces": restore_faces,
118
+ "tiling": tiling,
119
+ "distilled_cfg_scale": distilled_cfg_scale,
120
+ }
121
+ try:
122
+ cn_units = _build_controlnet_units(controlnet_units)
123
+ images = await _client.txt2img(payload, controlnet_units=cn_units)
124
+ except SDAPIError as e:
125
+ raise _to_tool_error(e) from e
126
+ return [_save_image(b64, output_dir) for b64 in images]
127
+
128
+
129
+ @mcp.tool()
130
+ async def img2img(
131
+ init_image: Annotated[str, Field(description="入力画像のファイルパス")],
132
+ prompt: Annotated[str, Field(description="生成プロンプト")],
133
+ negative_prompt: Annotated[str, Field(description="ネガティブプロンプト")] = "",
134
+ denoising_strength: Annotated[
135
+ float, Field(description="デノイズ強度(0.0〜1.0)", ge=0.0, le=1.0)
136
+ ] = 0.75,
137
+ steps: Annotated[int, Field(description="サンプリングステップ数", ge=1, le=150)] = 20,
138
+ width: Annotated[int, Field(description="画像の幅", ge=64, le=2048)] = 512,
139
+ height: Annotated[int, Field(description="画像の高さ", ge=64, le=2048)] = 512,
140
+ cfg_scale: Annotated[float, Field(description="CFGスケール", ge=1.0, le=30.0)] = 7.0,
141
+ sampler_name: Annotated[str, Field(description="サンプラー名")] = "Euler a",
142
+ scheduler_name: Annotated[str, Field(description="スケジューラー名")] = "Automatic",
143
+ seed: Annotated[int, Field(description="シード値(-1でランダム)", ge=-1)] = -1,
144
+ batch_size: Annotated[int, Field(description="生成枚数", ge=1, le=4)] = 1,
145
+ restore_faces: Annotated[bool, Field(description="顔修復を有効にする")] = False,
146
+ tiling: Annotated[bool, Field(description="タイリング画像を生成する")] = False,
147
+ controlnet_units: Annotated[
148
+ list[ControlNetUnit] | None, Field(description="ControlNetユニットのリスト")
149
+ ] = None,
150
+ output_dir: Annotated[str, Field(description="出力ディレクトリ")] = "./output",
151
+ ) -> list[str]:
152
+ """入力画像とプロンプトから画像を生成してファイルに保存する。
153
+
154
+ Returns:
155
+ 保存された画像ファイルの絶対パスリスト
156
+ """
157
+ assert _client is not None
158
+ init_b64 = _read_image_b64(init_image)
159
+ payload = {
160
+ "init_images": [init_b64],
161
+ "prompt": prompt,
162
+ "negative_prompt": negative_prompt,
163
+ "denoising_strength": denoising_strength,
164
+ "steps": steps,
165
+ "width": width,
166
+ "height": height,
167
+ "cfg_scale": cfg_scale,
168
+ "sampler_name": sampler_name,
169
+ "scheduler": scheduler_name,
170
+ "seed": seed,
171
+ "n_iter": batch_size,
172
+ "restore_faces": restore_faces,
173
+ "tiling": tiling,
174
+ }
175
+ try:
176
+ cn_units = _build_controlnet_units(controlnet_units)
177
+ images = await _client.img2img(payload, controlnet_units=cn_units)
178
+ except SDAPIError as e:
179
+ raise _to_tool_error(e) from e
180
+ return [_save_image(b64, output_dir) for b64 in images]
181
+
182
+
183
+ @mcp.tool()
184
+ async def get_sd_models() -> list[str]:
185
+ """利用可能なStable Diffusionモデルの一覧を返す。"""
186
+ assert _client is not None
187
+ try:
188
+ return await _client.get_sd_models()
189
+ except SDAPIError as e:
190
+ raise _to_tool_error(e) from e
191
+
192
+
193
+ @mcp.tool()
194
+ async def set_sd_model(
195
+ model_name: Annotated[str, Field(description="設定するモデル名")],
196
+ ) -> str:
197
+ """アクティブなStable Diffusionモデルを変更する。"""
198
+ assert _client is not None
199
+ try:
200
+ await _client.set_sd_model(model_name)
201
+ except SDAPIError as e:
202
+ raise _to_tool_error(e) from e
203
+ return f"モデルを設定しました: {model_name}"
204
+
205
+
206
+ @mcp.tool()
207
+ async def get_sd_upscalers() -> list[str]:
208
+ """利用可能なアップスケーラーの一覧を返す。"""
209
+ assert _client is not None
210
+ try:
211
+ return await _client.get_upscalers()
212
+ except SDAPIError as e:
213
+ raise _to_tool_error(e) from e
214
+
215
+
216
+ @mcp.tool()
217
+ async def upscale_images(
218
+ images: Annotated[list[str], Field(description="アップスケールする画像ファイルパスのリスト")],
219
+ resize_mode: Annotated[int, Field(description="0=倍率指定, 1=サイズ指定", ge=0, le=1)] = 0,
220
+ upscaling_resize: Annotated[
221
+ float, Field(description="アップスケール倍率(resize_mode=0時)", ge=1.0)
222
+ ] = 4.0,
223
+ upscaling_resize_w: Annotated[int, Field(description="目標幅px(resize_mode=1時)", ge=64)] = 512,
224
+ upscaling_resize_h: Annotated[
225
+ int, Field(description="目標高さpx(resize_mode=1時)", ge=64)
226
+ ] = 512,
227
+ upscaler_1: Annotated[str, Field(description="プライマリアップスケーラー")] = "R-ESRGAN 4x+",
228
+ upscaler_2: Annotated[str, Field(description="セカンダリアップスケーラー")] = "None",
229
+ output_dir: Annotated[str, Field(description="出力ディレクトリ")] = "./output",
230
+ ) -> list[str]:
231
+ """画像をアップスケールしてファイルに保存する。
232
+
233
+ Returns:
234
+ 保存された画像ファイルの絶対パスリスト
235
+ """
236
+ assert _client is not None
237
+ image_list = [{"data": _read_image_b64(p), "name": Path(p).name} for p in images]
238
+ payload = {
239
+ "resize_mode": resize_mode,
240
+ "show_extras_results": True,
241
+ "gfpgan_visibility": 0,
242
+ "codeformer_visibility": 0,
243
+ "codeformer_weight": 0,
244
+ "upscaling_resize": upscaling_resize,
245
+ "upscaling_resize_w": upscaling_resize_w,
246
+ "upscaling_resize_h": upscaling_resize_h,
247
+ "upscaling_crop": True,
248
+ "upscaler_1": upscaler_1,
249
+ "upscaler_2": upscaler_2,
250
+ "extras_upscaler_2_visibility": 0,
251
+ "upscale_first": False,
252
+ "imageList": image_list,
253
+ }
254
+ try:
255
+ result_images = await _client.upscale_images(payload)
256
+ except SDAPIError as e:
257
+ raise _to_tool_error(e) from e
258
+ return [_save_image(b64, output_dir) for b64 in result_images]
259
+
260
+
261
+ @mcp.tool()
262
+ async def get_controlnet_models() -> list[str]:
263
+ """利用可能なControlNetモデルの一覧を返す。"""
264
+ assert _client is not None
265
+ try:
266
+ return await _client.get_controlnet_models()
267
+ except SDAPIError as e:
268
+ raise _to_tool_error(e) from e
269
+
270
+
271
+ @mcp.tool()
272
+ async def get_controlnet_modules() -> list[str]:
273
+ """利用可能なControlNetプリプロセッサの一覧を返す。"""
274
+ assert _client is not None
275
+ try:
276
+ return await _client.get_controlnet_modules()
277
+ except SDAPIError as e:
278
+ raise _to_tool_error(e) from e
279
+
280
+
281
+ def main() -> None:
282
+ """Start the MCP server."""
283
+ mcp.run()
284
+
285
+
286
+ if __name__ == "__main__":
287
+ main()
@@ -0,0 +1,258 @@
1
+ Metadata-Version: 2.4
2
+ Name: sd-api-mcp
3
+ Version: 0.2.0
4
+ Summary: MCP server wrapping the Stable Diffusion WebUI API
5
+ Project-URL: Homepage, https://github.com/HizZaniya/sd-api-mcp
6
+ Project-URL: Repository, https://github.com/HizZaniya/sd-api-mcp
7
+ Project-URL: Bug Tracker, https://github.com/HizZaniya/sd-api-mcp/issues
8
+ Author: HizZaniya
9
+ License: MIT
10
+ Keywords: image-generation,mcp,stable-diffusion,webui
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Multimedia :: Graphics
17
+ Requires-Python: >=3.13
18
+ Requires-Dist: fastmcp>=3.3.1
19
+ Requires-Dist: httpx>=0.28.0
20
+ Requires-Dist: pillow>=11.0.0
21
+ Description-Content-Type: text/markdown
22
+
23
+ # SD API MCP
24
+
25
+ > Stable Diffusion WebUI API (AUTOMATIC1111/ForgeUI) を操作する MCP サーバ
26
+
27
+ ## 前提条件
28
+
29
+ - [Stable Diffusion WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui)(または ForgeUI)が起動していること
30
+ - [uv](https://docs.astral.sh/uv/) がインストール済みであること
31
+
32
+ ## MCPクライアント設定
33
+
34
+ ### Claude Code
35
+
36
+ ```bash
37
+ claude mcp add sd-api-mcp uvx sd-api-mcp
38
+ ```
39
+
40
+ または `.claude/settings.json` に追加:
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "sd-api-mcp": {
46
+ "command": "uvx",
47
+ "args": ["sd-api-mcp"],
48
+ "env": {
49
+ "SD_WEBUI_URL": "http://localhost:7860"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ### Cursor
57
+
58
+ `.cursor/mcp.json` に追加:
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "sd-api-mcp": {
64
+ "command": "uvx",
65
+ "args": ["sd-api-mcp"],
66
+ "env": {
67
+ "SD_WEBUI_URL": "http://localhost:7860"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ ### VS Code (GitHub Copilot)
75
+
76
+ `.vscode/mcp.json` に追加:
77
+
78
+ ```json
79
+ {
80
+ "servers": {
81
+ "sd-api-mcp": {
82
+ "command": "uvx",
83
+ "args": ["sd-api-mcp"],
84
+ "env": {
85
+ "SD_WEBUI_URL": "http://localhost:7860"
86
+ }
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## 環境変数
93
+
94
+ | 変数名 | デフォルト | 説明 |
95
+ |---|---|---|
96
+ | `SD_WEBUI_URL` | `http://host.docker.internal:7860` | SD WebUI のベース URL |
97
+ | `SD_AUTH_USER` | (なし) | Basic 認証ユーザー名(任意) |
98
+ | `SD_AUTH_PASS` | (なし) | Basic 認証パスワード(任意) |
99
+ | `SD_OUTPUT_DIR` | `./output` | 生成画像の保存先ディレクトリ |
100
+ | `REQUEST_TIMEOUT` | `300` | HTTP リクエストのタイムアウト秒数 |
101
+
102
+ ## ツールリファレンス
103
+
104
+ ### `txt2img`
105
+
106
+ テキストプロンプトから画像を生成し、ファイルに保存します。
107
+
108
+ | パラメータ | 型 | デフォルト | 説明 |
109
+ |---|---|---|---|
110
+ | `prompt` | str | 必須 | 生成プロンプト |
111
+ | `negative_prompt` | str | `""` | ネガティブプロンプト |
112
+ | `steps` | int | `20` | サンプリングステップ数(1〜150) |
113
+ | `width` | int | `512` | 画像の幅 px(64〜2048) |
114
+ | `height` | int | `512` | 画像の高さ px(64〜2048) |
115
+ | `cfg_scale` | float | `7.0` | CFG スケール(1.0〜30.0) |
116
+ | `sampler_name` | str | `"Euler a"` | サンプラー名 |
117
+ | `scheduler_name` | str | `"Automatic"` | スケジューラー名 |
118
+ | `seed` | int | `-1` | シード値(-1 でランダム) |
119
+ | `batch_size` | int | `1` | 生成枚数(1〜4) |
120
+ | `restore_faces` | bool | `false` | 顔修復を有効にする |
121
+ | `tiling` | bool | `false` | タイリング画像を生成する |
122
+ | `distilled_cfg_scale` | float | `3.5` | Distilled CFG スケール(1.0〜30.0) |
123
+ | `controlnet_units` | list | `null` | ControlNet ユニットのリスト |
124
+ | `output_dir` | str | `"./output"` | 出力ディレクトリ |
125
+
126
+ **戻り値**: 生成された画像ファイルの絶対パスリスト
127
+
128
+ ### `img2img`
129
+
130
+ 入力画像とプロンプトから画像を生成し、ファイルに保存します。
131
+
132
+ | パラメータ | 型 | デフォルト | 説明 |
133
+ |---|---|---|---|
134
+ | `init_image` | str | 必須 | 入力画像のファイルパス |
135
+ | `prompt` | str | 必須 | 生成プロンプト |
136
+ | `negative_prompt` | str | `""` | ネガティブプロンプト |
137
+ | `denoising_strength` | float | `0.75` | デノイズ強度(0.0〜1.0) |
138
+ | `steps` | int | `20` | サンプリングステップ数(1〜150) |
139
+ | `width` | int | `512` | 画像の幅 px(64〜2048) |
140
+ | `height` | int | `512` | 画像の高さ px(64〜2048) |
141
+ | `cfg_scale` | float | `7.0` | CFG スケール(1.0〜30.0) |
142
+ | `sampler_name` | str | `"Euler a"` | サンプラー名 |
143
+ | `scheduler_name` | str | `"Automatic"` | スケジューラー名 |
144
+ | `seed` | int | `-1` | シード値(-1 でランダム) |
145
+ | `batch_size` | int | `1` | 生成枚数(1〜4) |
146
+ | `restore_faces` | bool | `false` | 顔修復を有効にする |
147
+ | `tiling` | bool | `false` | タイリング画像を生成する |
148
+ | `controlnet_units` | list | `null` | ControlNet ユニットのリスト |
149
+ | `output_dir` | str | `"./output"` | 出力ディレクトリ |
150
+
151
+ **戻り値**: 生成された画像ファイルの絶対パスリスト
152
+
153
+ ### `get_sd_models`
154
+
155
+ 利用可能な Stable Diffusion モデルの一覧を返します。
156
+
157
+ パラメータなし。**戻り値**: モデル名の文字列リスト
158
+
159
+ ### `set_sd_model`
160
+
161
+ アクティブな Stable Diffusion モデルを変更します。
162
+
163
+ | パラメータ | 型 | デフォルト | 説明 |
164
+ |---|---|---|---|
165
+ | `model_name` | str | 必須 | 設定するモデル名 |
166
+
167
+ **戻り値**: 設定完了メッセージ(文字列)
168
+
169
+ ### `get_sd_upscalers`
170
+
171
+ 利用可能なアップスケーラーの一覧を返します。
172
+
173
+ パラメータなし。**戻り値**: アップスケーラー名の文字列リスト
174
+
175
+ ### `upscale_images`
176
+
177
+ 画像をアップスケールして保存します。
178
+
179
+ | パラメータ | 型 | デフォルト | 説明 |
180
+ |---|---|---|---|
181
+ | `images` | list[str] | 必須 | アップスケールする画像ファイルパスのリスト |
182
+ | `resize_mode` | int | `0` | 0=倍率指定, 1=サイズ指定 |
183
+ | `upscaling_resize` | float | `4.0` | アップスケール倍率(`resize_mode=0` 時) |
184
+ | `upscaling_resize_w` | int | `512` | 目標幅 px(`resize_mode=1` 時) |
185
+ | `upscaling_resize_h` | int | `512` | 目標高さ px(`resize_mode=1` 時) |
186
+ | `upscaler_1` | str | `"R-ESRGAN 4x+"` | プライマリアップスケーラー |
187
+ | `upscaler_2` | str | `"None"` | セカンダリアップスケーラー |
188
+ | `output_dir` | str | `"./output"` | 出力ディレクトリ |
189
+
190
+ **戻り値**: 保存された画像ファイルの絶対パスリスト
191
+
192
+ ### `get_controlnet_models`
193
+
194
+ 利用可能な ControlNet モデルの一覧を返します。
195
+
196
+ パラメータなし。**戻り値**: ControlNet モデル名の文字列リスト
197
+
198
+ ### `get_controlnet_modules`
199
+
200
+ 利用可能な ControlNet プリプロセッサの一覧を返します。
201
+
202
+ パラメータなし。**戻り値**: ControlNet プリプロセッサ名の文字列リスト
203
+
204
+ ## 開発者向け
205
+
206
+ ### 環境構築
207
+
208
+ ```bash
209
+ git clone https://github.com/HizZaniya/sd-api-mcp.git
210
+ cd sd-api-mcp
211
+ uv sync --group dev
212
+ pre-commit install
213
+ ```
214
+
215
+ ### テスト実行
216
+
217
+ ```bash
218
+ uv run pytest -v
219
+ ```
220
+
221
+ 型チェック・Lint:
222
+
223
+ ```bash
224
+ uv run ty check
225
+ uv run ruff check .
226
+ uv run ruff format --check .
227
+ ```
228
+
229
+ ### ファイル構成
230
+
231
+ ```
232
+ src/sd_api_mcp/
233
+ ├── server.py # MCP プロトコル層 — ツール定義・リクエスト受付
234
+ └── client.py # SD WebUI API 通信層 — httpx による HTTP クライアント
235
+ ```
236
+
237
+ - `server.py`: FastMCP のツールデコレータでツールを定義し、MCP プロトコルを処理する。SD WebUI への直接アクセスは行わない。
238
+ - `client.py`: httpx を使って SD WebUI の REST API と通信する。MCP プロトコルを知らない。
239
+
240
+ ### リリース手順
241
+
242
+ 1. Conventional Commits 形式でコミットを積む(例: `feat: add new tool`, `fix: handle timeout`)
243
+ 2. GitHub Actions の **Release** ワークフローを手動トリガーする
244
+ 3. `cz bump` がコミット履歴からバージョンを自動決定し、`CHANGELOG.md` を更新してタグを作成
245
+ 4. **Publish** ワークフローが自動実行され、TestPyPI で検証後 PyPI に公開される
246
+
247
+ ### コントリビューション
248
+
249
+ 1. `main` ブランチから feature ブランチを作成する
250
+ 2. テストを先に書いてから実装する(TDD)
251
+ 3. PR を作成する(テンプレートに従うこと)
252
+ 4. CI(lint / format / typecheck / test)がすべてパスしたらレビューを依頼する
253
+
254
+ コミットメッセージは [Conventional Commits](https://www.conventionalcommits.org/) 形式を推奨します。
255
+
256
+ ## ライセンス
257
+
258
+ MIT
@@ -0,0 +1,7 @@
1
+ sd_api_mcp/__init__.py,sha256=aQ6MNW0I99c_o3rwCvBYIW1Ad4TzTeTzESEdehAbEug,72
2
+ sd_api_mcp/client.py,sha256=9ie7eW90Wmi0fe0OIk0qW_qDhh37Nfec1NtMZRm4caU,3990
3
+ sd_api_mcp/server.py,sha256=O2iB_pespIElwL0DpiJ8kqf-rkOTW2ZBA3LXV3x8TvI,11071
4
+ sd_api_mcp-0.2.0.dist-info/METADATA,sha256=mKrCZ8s_c-TJc8abPKomhQ80S6FVV-SZxkZSs-LJEf8,8640
5
+ sd_api_mcp-0.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
6
+ sd_api_mcp-0.2.0.dist-info/entry_points.txt,sha256=tNjlh_UWzrz_UB_weHy3kcKIWZeqaFOpjA48JUp-9BI,54
7
+ sd_api_mcp-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sd-api-mcp = sd_api_mcp.server:main