diff --git a/README.md b/README.md index 7adf1d0..498cc8f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MooseCP Image Server -A Model Context Protocol (MCP) server designed to provide LLMs with efficient, token-optimized visual access to image directories. Instead of dumping full-resolution images (which waste tokens and cause context overflow), MooseCP provides a hierarchical workflow: **List $\rightarrow$ Scan $\rightarrow$ Preview $\rightarrow$ Inspect**. +A Model Context Protocol (MCP) server designed to provide LLMs with efficient, token-optimized visual access to image directories and AI generation capabilities. Instead of dumping full-resolution images (which waste tokens and cause context overflow), MooseCP provides a hierarchical workflow: **List $\rightarrow$ Scan $\rightarrow$ Preview $\rightarrow$ Inspect**. ## âš ī¸ AI SLOP DISCLAIMER This entire project was vibe-coded by an AI. It is 100% slop code. Use it at your own risk. @@ -32,6 +32,17 @@ Extracts AI generation parameters from PNG files. Returns the full-resolution image. * **Best for**: Final confirmation or deep visual analysis where every pixel counts. +### 🎨 `generate_image` +Triggers an image generation on a local Stable Diffusion WebUI Forge instance. +* **Workflow**: Always call `get_model_info` first to determine the correct prompting style (e.g., tag-based vs. natural language). +* **Features**: Supports model-specific presets, resolution presets, and standard parameter overrides. +* **Output**: Returns a Base64 image for AI analysis and a proxy URL for direct embedding in the chat. + +### â„šī¸ `get_model_info` +Provides the "manual" for available generation models. +* **Best for**: Learning the prompting style, recommended settings, and available resolution presets for a specific model. +* **Workflow**: Call without arguments to see the catalog; call with `model_name` for the detailed guide. + ### 🌐 `browse_wikipedia` Allows the model to browse Wikipedia using its API. * **Best for**: Quickly retrieving summaries, structural maps (ToC), or specific section content from Wikipedia without dumping the entire page. @@ -47,9 +58,10 @@ This server requires Python 3.10+ and the following packages: * `uvicorn`: ASGI server for the SSE transport. * `starlette`: Lightweight ASGI framework. * `Pillow`: Image processing and thumbnail generation. +* `requests`: For communicating with the Stable Diffusion API. ```bash -pip install uvicorn starlette Pillow +pip install uvicorn starlette Pillow requests ``` ### Setup diff --git a/config.py b/config.py index fbb731f..8cb5f3f 100644 --- a/config.py +++ b/config.py @@ -1,12 +1,22 @@ import logging +from pathlib import Path + +# --- Project Root --- +# Get the directory where config.py is located +ROOT_DIR = Path(__file__).parent.resolve() # --- Server & Logs --- HOST = "127.0.0.1" PORT = 8000 LOG_LEVEL = "WARNING" # Options: "DEBUG", "INFO", "WARNING", "ERROR" -LOG_FILE = "debug.log" +LOG_FILE = str(ROOT_DIR / "debug.log") USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0" # Stealth User-Agent to bypass Wikipedia's bot detection +# --- Stable Diffusion Config --- +SD_URL = "http://127.0.0.1:7860" +MODEL_PRESETS_PATH = str(ROOT_DIR / "model_presets.toml") +RES_PRESETS_PATH = str(ROOT_DIR / "resolution_presets.toml") + # --- Model Specific Token Tuning (Tuned for Gemma 4) --- # Patch size is typically (clip.vision.patch_size * n_merge) # For Gemma 4, this is 48px. diff --git a/tools/generate_image.py b/tools/generate_image.py index 3809100..1464e9b 100644 --- a/tools/generate_image.py +++ b/tools/generate_image.py @@ -1,23 +1,8 @@ import requests -import tomllib -import os import base64 import config from typing import Any, Dict, List -from tools.utils import ToolError - -# Paths to config files -MODEL_PRESETS_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "model_presets.toml") -RES_PRESETS_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resolution_presets.toml") - -SD_URL = "http://127.0.0.1:7860" - -def load_toml(path: str) -> Dict[str, Any]: - try: - with open(path, "rb") as f: - return tomllib.load(f) - except Exception as e: - raise ToolError(f"Failed to load config file {path}: {str(e)}") +from tools.utils import ToolError, load_toml async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]: """ @@ -34,7 +19,7 @@ async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]: # 2. FETCH CURRENT SERVER DEFAULTS & MAP LABELS try: - info_resp = requests.get(f"{SD_URL}/info", timeout=10) + info_resp = requests.get(f"{config.SD_URL}/info", timeout=10) info_resp.raise_for_status() info_data = info_resp.json() params_info = info_data["named_endpoints"]["/txt2img"]["parameters"] @@ -62,8 +47,8 @@ async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]: label_map[mapped_label] = idx # 3. LOAD CONFIGS - models_cfg = load_toml(MODEL_PRESETS_PATH) - res_cfg = load_toml(RES_PRESETS_PATH) + models_cfg = load_toml(config.MODEL_PRESETS_PATH) + res_cfg = load_toml(config.RES_PRESETS_PATH) if model_name not in models_cfg: raise ToolError(f"Model '{model_name}' not found in presets. Available: {', '.join(models_cfg.keys())}") diff --git a/tools/get_model_info.py b/tools/get_model_info.py index 7190c60..5c5f613 100644 --- a/tools/get_model_info.py +++ b/tools/get_model_info.py @@ -1,18 +1,7 @@ -import tomllib -import os -from typing import Any, Dict, Union, List -from tools.utils import ToolError - -# Paths to config files -MODEL_PRESETS_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "model_presets.toml") -RES_PRESETS_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resolution_presets.toml") - -def load_toml(path: str) -> Dict[str, Any]: - try: - with open(path, "rb") as f: - return tomllib.load(f) - except Exception as e: - raise ToolError(f"Failed to load config file {path}: {str(e)}") +import requests +from typing import Any, Dict, List +import config +from tools.utils import ToolError, load_toml async def handle(args: Dict[str, Any]) -> Dict[str, Any]: """ @@ -20,8 +9,8 @@ async def handle(args: Dict[str, Any]) -> Dict[str, Any]: """ model_name = args.get("model_name") - models = load_toml(MODEL_PRESETS_PATH) - res_presets = load_toml(RES_PRESETS_PATH) + models = load_toml(config.MODEL_PRESETS_PATH) + res_presets = load_toml(config.RES_PRESETS_PATH) if not model_name: # Return a catalog of all models diff --git a/tools/utils.py b/tools/utils.py index f9bc4d7..ad35198 100644 --- a/tools/utils.py +++ b/tools/utils.py @@ -1,5 +1,6 @@ import time import datetime +import tomllib from pathlib import Path from typing import List, Dict, Any, Optional @@ -8,6 +9,14 @@ class ToolError(Exception): """Custom exception for tool-related errors to be caught by the MCP server.""" pass +def load_toml(path: str) -> Dict[str, Any]: + """Loads a TOML file into a dictionary.""" + try: + with open(path, "rb") as f: + return tomllib.load(f) + except Exception as e: + raise ToolError(f"Failed to load config file {path}: {str(e)}") + def format_relative_time(timestamp: float) -> str: """Converts a timestamp to a human-readable relative format.""" now = time.time()