FastMCP server for Midjourney image generation via PiAPI, with daily-USD cost cap + per-call audit + SSRF-safe image URL inputs.
Strong tool definitions with comprehensive descriptions and well-structured schemas. All 11 tools have clear verb-noun naming (imagine, upscale, blend, etc.) and detailed docstrings explaining WHAT, WHEN, and WHY. Input schemas are complete with types and enums. However, output schemas are not formally documented in the code, descriptions mention return structure but no explicit JSON Schema for responses. Error handling is sophisticated (error_class, sanitize_error) but not all tools document recovery paths. Security patterns are excellent (SSRF guards, cap enforcement, audit logging). Composition is clean, each tool has one responsibility. Average tool description ~180 chars, well within the 10-1024 baseline.
Get today's USD cap snapshot. Returns: { "ok": True, "cap": {"date", "usd_cap", "usd_spent", "usd_remaining", "cap_reached", "calls_today"}, "process_mode_default": "relax|fast|turbo", "estimates_per_task_usd": {"imagine", "variation", "upscale", "describe", "blend"} } PiAPI does not expose a credits-remaining endpoint via REST as of 2026-05-25; track balance in the PiAPI dashboard. Local cap is the operator-controlled safety surface.
Merge 2-5 input images into a new 4-up grid. Args: image_urls: List of 2-5 public HTTPS URLs to blend. Each URL is SSRF-guarded (private/link-local IPs rejected via mycelium-security). aspect_ratio: Canvas ratio (default "1:1"). process_mode: "relax", "fast" (default), or "turbo". Returns a new task object. Daily USD cap is checked pre-flight.
Cancel a non-terminal task (Staged, Pending, or Processing). Args: task_id: PiAPI task identifier. Returns the cancelled task object. If the task is already terminal (Completed or Failed), PiAPI returns an error.
Generate 4 text prompts from an input image (image-to-prompt). Args: image_url: Public HTTPS URL of the image to describe. Must be a public IP; private/link-local IPs are rejected (SSRF guard via mycelium-security). Returns a task object with 4 candidate prompts. Daily USD cap is checked pre-flight.
Output schemas not formally documented. Descriptions mention return structure (e.g., 'Returns task object with task_id, status, output URLs') but no explicit JSON Schema definitions for response payloads. LLMs cannot reliably parse unstructured output or plan downstream tool calls without formal schemas.
Error recovery paths not documented in tool descriptions. While error_class categorization exists (rate_cap, auth, internal_error), descriptions do not guide LLMs on what to do next (e.g., 'If rate_cap, wait N minutes and retry' or 'If auth, check PIAPI_API_KEY in admin.env').
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 65 | 2026-07-28+ | v2 |
Get the current state of a Midjourney task by ID. Returns the full task object including status (Staged | Pending | Processing | Completed | Failed), output (image URLs on completion), and meta.usage (PiAPI credit consume). When status == Completed, the audit log backfills the actual USD cost for the day.
Verify the PiAPI key is configured and return today's cap snapshot. Does NOT make a network call — PiAPI does not have a dedicated /ping endpoint. To verify the key is live, follow up with account_info() or a real imagine call. healthcheck is the quick offline status read.
Generate a 4-up grid from a text prompt. Args: prompt: Text description of the image to generate. aspect_ratio: Canvas ratio (default "1:1"). Examples: "16:9", "4:3", "1:2". process_mode: "relax" (slower, cheaper), "fast" (default), or "turbo" (faster, pricier). skip_prompt_check: If True, skip PiAPI's prompt safety filter (default False). Returns a task object with task_id. Poll via get_task() or wait_for_task(). Daily USD cap is checked pre-flight; returns error_class='rate_cap' if exceeded.
Scan the local audit log for recent task IDs (last N calls). Returns a list of task_ids from the audit log, newest first, up to the specified limit. Useful for finding a task you just submitted without storing the ID explicitly.
Isolate and enlarge one of the 4 grid images from a prior imagine or variation task. Args: origin_task_id: task_id of the parent imagine or variation task. index: Grid position ("1", "2", "3", or "4"). process_mode: "relax", "fast" (default), or "turbo". Returns a new task object with the upscaled image. Daily USD cap is checked pre-flight.
Regenerate variations (V1-V4) or adjust variation strength (high_variation / low_variation) on one of the 4 grid images from a prior imagine or variation task. Args: origin_task_id: task_id of the parent imagine or variation task. index: Grid position ("1", "2", "3", or "4"). prompt: Optional new prompt to steer the variations. If omitted, uses the parent prompt. aspect_ratio: Canvas ratio (default "1:1"). process_mode: "relax", "fast" (default), or "turbo". skip_prompt_check: Skip PiAPI prompt safety filter (default False). Returns a new task object. Daily USD cap is checked pre-flight.
Poll a task until it reaches a terminal state or the timeout fires. Args: task_id: PiAPI task identifier (from imagine/variation/upscale/describe/blend). timeout_seconds: Total wait budget. PiAPI fast-mode jobs typically complete in 30-60s; relax mode can take minutes. Default 180s (3 min) covers fast. poll_interval_seconds: Seconds between polls. Default 3s respects the typical "1 request per few seconds" pattern. PiAPI doesn't publish per-poll rate limits, but 3s is conservative for a few-minute job. Returns the final task object on completion / failure, or a timeout error.
Pagination not implemented for list_recent_tasks. Tool accepts 'limit' but no offset/cursor or total_count in response. If audit log grows large, LLM cannot iterate through all tasks, only the most recent N are accessible.
process_mode parameter in imagine/variation/blend lacks default enforcement documentation. Description says 'default fast' but does not clarify: is this a server-side default if omitted, or must the LLM always pass it? Ambiguity invites omission errors.
Tool descriptions do not document idempotency guarantees. wait_for_task and get_task appear idempotent (read-only), but imagine/variation/upscale/describe/blend are not. LLMs need explicit guidance on which tools are safe to retry without side effects.