This commit is contained in:
moosecrap 2026-07-25 21:00:51 -07:00
parent f808f4d599
commit ca33869007
5 changed files with 44 additions and 39 deletions

View File

@ -1,6 +1,6 @@
# MooseCP Image Server # 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 ## ⚠️ AI SLOP DISCLAIMER
This entire project was vibe-coded by an AI. It is 100% slop code. Use it at your own risk. 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. Returns the full-resolution image.
* **Best for**: Final confirmation or deep visual analysis where every pixel counts. * **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` ### 🌐 `browse_wikipedia`
Allows the model to browse Wikipedia using its API. 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. * **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. * `uvicorn`: ASGI server for the SSE transport.
* `starlette`: Lightweight ASGI framework. * `starlette`: Lightweight ASGI framework.
* `Pillow`: Image processing and thumbnail generation. * `Pillow`: Image processing and thumbnail generation.
* `requests`: For communicating with the Stable Diffusion API.
```bash ```bash
pip install uvicorn starlette Pillow pip install uvicorn starlette Pillow requests
``` ```
### Setup ### Setup

View File

@ -1,12 +1,22 @@
import logging import logging
from pathlib import Path
# --- Project Root ---
# Get the directory where config.py is located
ROOT_DIR = Path(__file__).parent.resolve()
# --- Server & Logs --- # --- Server & Logs ---
HOST = "127.0.0.1" HOST = "127.0.0.1"
PORT = 8000 PORT = 8000
LOG_LEVEL = "WARNING" # Options: "DEBUG", "INFO", "WARNING", "ERROR" 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 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) --- # --- Model Specific Token Tuning (Tuned for Gemma 4) ---
# Patch size is typically (clip.vision.patch_size * n_merge) # Patch size is typically (clip.vision.patch_size * n_merge)
# For Gemma 4, this is 48px. # For Gemma 4, this is 48px.

View File

@ -1,23 +1,8 @@
import requests import requests
import tomllib
import os
import base64 import base64
import config import config
from typing import Any, Dict, List from typing import Any, Dict, List
from tools.utils import ToolError from tools.utils import ToolError, load_toml
# 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)}")
async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]: 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 # 2. FETCH CURRENT SERVER DEFAULTS & MAP LABELS
try: 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_resp.raise_for_status()
info_data = info_resp.json() info_data = info_resp.json()
params_info = info_data["named_endpoints"]["/txt2img"]["parameters"] 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 label_map[mapped_label] = idx
# 3. LOAD CONFIGS # 3. LOAD CONFIGS
models_cfg = load_toml(MODEL_PRESETS_PATH) models_cfg = load_toml(config.MODEL_PRESETS_PATH)
res_cfg = load_toml(RES_PRESETS_PATH) res_cfg = load_toml(config.RES_PRESETS_PATH)
if model_name not in models_cfg: if model_name not in models_cfg:
raise ToolError(f"Model '{model_name}' not found in presets. Available: {', '.join(models_cfg.keys())}") raise ToolError(f"Model '{model_name}' not found in presets. Available: {', '.join(models_cfg.keys())}")

View File

@ -1,18 +1,7 @@
import tomllib import requests
import os from typing import Any, Dict, List
from typing import Any, Dict, Union, List import config
from tools.utils import ToolError from tools.utils import ToolError, load_toml
# 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)}")
async def handle(args: Dict[str, Any]) -> Dict[str, Any]: 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") model_name = args.get("model_name")
models = load_toml(MODEL_PRESETS_PATH) models = load_toml(config.MODEL_PRESETS_PATH)
res_presets = load_toml(RES_PRESETS_PATH) res_presets = load_toml(config.RES_PRESETS_PATH)
if not model_name: if not model_name:
# Return a catalog of all models # Return a catalog of all models

View File

@ -1,5 +1,6 @@
import time import time
import datetime import datetime
import tomllib
from pathlib import Path from pathlib import Path
from typing import List, Dict, Any, Optional 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.""" """Custom exception for tool-related errors to be caught by the MCP server."""
pass 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: def format_relative_time(timestamp: float) -> str:
"""Converts a timestamp to a human-readable relative format.""" """Converts a timestamp to a human-readable relative format."""
now = time.time() now = time.time()