UNPKG

@kolbo/mcp

Version:

Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code

123 lines (90 loc) • 12 kB
# Cost Awareness, Validation & Constraints Load this file when you need to: confirm cost before firing a generation, validate input params against a model's caps, or quote real cost after a generation completes. ## Billing Units by Type Creative generations bill against the user's Kolbo credit balance. **Billing units differ by type** — apply the correct formula before generating. | Type | Billing unit | Credit range | Example | |------|-------------|-------------|---------| | **Image** | per image (flat) | 1–30 cr | Flux.1 Fast = 1 cr, Midjourney = 4 cr. If `resolution` is set, check `resolution_multipliers` — some families multiply cost significantly at higher tiers. | | **Image edit** | per image (flat) | 2–20 cr | | | **Video** | **cr/s × duration** | 2–30 cr/s | Kandinsky 5 Fast × 5s = 10 cr; Seedance 2.0 × 10s = 300 cr. Check `resolution_multipliers` + `sound_credit_multiplier`. | | **Video from image** | **cr/s × duration** | 4–30 cr/s | Same per-second rule. | | **Elements (ref-to-video)** | output seconds normally; combined input + output seconds when `video_input_credit` exists and video is attached | 4–40 cr/s | Read both pricing profiles from `list_models type="elements"`. | | **Lipsync** | **cr/s × duration** | 5–20 cr/s | | | **Music** | per generation (flat) | 15–60 cr | Suno v5 = 15 cr; ElevenLabs Music = 60 cr | | **Speech (TTS)** | per 100 characters | 2–5 cr/100 chars | ElevenLabs (5) × 500 chars = 25 cr | | **Sound effects** | per generation (flat) | 4–7 cr | | | **3D model** | per model (flat) | 5–300 cr | Trellis = 5 cr; Meshy v6 = 150 cr; Marble 1.1 = 300 cr | | **Transcription (stt)** | per minute of audio | `model.credit × duration_minutes` | | ## Calculation Formulas Apply when confirming cost before firing: - **Video / Lipsync**: normally `total = model_credit_per_second × output_duration_seconds`. Never assume the credit shown is a flat per-generation cost for these types. - **Video-input tariff**: when the request contains one or more video inputs and the model returns `video_input_credit`, use `ceil(video_input_credit × (sum ceil(each input video duration) + output_seconds) × video_input_resolution_multipliers[resolution])`. Dedicated Seedance Video Edit output seconds follow the selected source clip; Seedance Extend output seconds are only the requested added duration. Do not also apply the ordinary `credit` profile. - **Music**: flat per generation — `total = model_credit` (duration does not change cost). - **TTS**: `total = model_credit × ceil(character_count / 100)`. Count actual characters first. 1000 chars with ElevenLabs = 50 credits. - **Images / 3D / Sound effects**: `total = model_credit × quantity`. - **Resolution / audio multipliers**: if `resolution` is set or model has native audio, read `resolution_multipliers[tier]` and `sound_credit_multiplier`. Formula: `final = base × resolutionMult × (sound ? soundMult : 1) × durationSeconds`. ### Tier label → pixel mapping (rough) - Images: `"1K"` ≈ 1024px, `"2K"` ≈ Full HD (1920×1080), `"3K"` ≈ QHD (2560×1440), `"4K"` ≈ UHD (3840×2160). Picker shows only tiers the model supports (per `supported_resolutions`). - Videos: `"720p"` / `"1080p"` / `"1440p"` / `"2160p"` = vertical pixels. Some models use model-specific labels like `"512P"` / `"1024P"` (Hailuo). ## When to Confirm Cost **Skip cost confirmation when:** - Model + count + aspect + creative direction are already pinned by the user ("make 5 videos, seedance 2 fast, 15s" IS the confirmation). **Required cost confirmation when:** - Anything else — present a one-line summary: "8 videos × 5s × [model] @ X cr/s = **Y credits**. Proceed?" - Suggest a cheaper alternative if one exists. - Wait for the user's confirm before firing. **Batch totalling 100+ credits:** run `check_credits` first and include the available balance in the summary. **After generating, report cost only — never "credits remaining".** Coding/chat usage also deducts credits, so any balance you compute (opening balance − generation costs) is wrong by the time you say it. Quote `credits_used` per generation / batch total; if the user asks what's left, run `check_credits` fresh at that moment and quote that number. ## ⚠️ Quote Real Cost, Never Estimates (CRITICAL) Pre-flight formulas above are for **preview only** — after firing, quote the returned `credits_used`, never `base × count`. Log `credits_used`, resolution, duration and sound state per entry — format in `production-log.md`. ## Validation Pattern — Every Generation Before submitting: 1. Call `list_models type=<tool-type>` (text mode is enough for picking; `format: "json"` for programmatic comparison). 2. For each input array (refs / DNAs / elements) — check `length <= <cap>` from the canonical field reference below. If over, drop the lowest-priority entries OR ask the user. 3. For each enumerated value (`aspect_ratio` / `resolution` / `duration`) — check it's in `supported_*`. If not, **do not silently substitute**; show the user the allowed set and ask. 4. For each duration-bearing file (source/reference video for elements/v2v/extend, audio for lipsync/elements) — pre-check duration against the min/max range. Use ffmpeg if needed. Seedance 2/2.5 video inputs are rejected below `min_video_duration` (currently 4 seconds). 5. For uploads — pre-check size against `max_file_size`. The MCP tool descriptions also embed the cap field name on the relevant parameter (e.g. `reference_images: "...Cap: pass at most max_reference_images..."`) — use those as inline reminders. ## Canonical Field Reference — Which `list_models` Field Controls Which Input The same conceptual slot (e.g. "max reference images") lives under **different field names per model family**. Read the row for your tool, not the model name. | Your input | Tool(s) | Field on the model | What `0` / `null` means | |---|---|---|---| | `reference_images` | `generate_image`, `generate_image_edit` (uses `source_images`), `generate_creative_director`, `generate_video` | `max_reference_images` | `0` = no refs | | `reference_images` | `generate_elements` | `elements_max_images` | `0` = no image refs | | `reference_images` | `generate_video_from_video` | `max_images` | `0` = no secondary image input | | `reference_videos` | `generate_elements` | `elements_max_videos` | `0` = no video refs | | `reference_videos` | `generate_video_from_video` | `max_videos` | `<= 1` = only the source_video | | `elements` | `generate_video_from_video` | `max_elements` | `0` = no elements | | `audio_url` | `generate_elements` | `elements_max_audio` (+ `max_audio_duration` for the file) | `0` = no audio ref | | `visual_dna_ids` | every DNA-aware tool | `max_visual_dna` (+ `supports_visual_dna` boolean) | `null` / `0` / `false` = model rejects DNA | | `aspect_ratio` | any | `supported_aspect_ratios` (or `_by_type[<type>]` when multimodal) | empty → `default_aspect_ratio` if set | | `resolution` | any | `supported_resolutions` (+ `resolution_multipliers` for cost) | empty → no resolution tiering | | `duration` (video output) | video tools | `supported_durations`, else `min_output_duration`–`max_output_duration` | both null → omit and let server default | | **input** video duration | `lipsync-video`, `generate_video_from_video`, `generate_elements` with video refs | `min_video_duration` – `max_video_duration` | outside range → reject | | input audio duration | `generate_lipsync`, `generate_elements` audio | `min_audio_duration` – `max_audio_duration` (+ `audio_max_follows_video_duration` for lipsync) | outside range → reject | | audio file format | any audio input | `supported_audio_formats` (e.g. `["mp3","wav","m4a"]`; empty = all) | pre-validate before upload | | recording duration | `text_to_speech` recording UX | `min_recording_duration` – `max_recording_duration` | usually null for plain TTS | | upload file size | every file upload | `max_file_size` (bytes) | null → use platform default | | `num_images` | image tools | `images_per_request` overrides for fixed-output models (Midjourney returns 4) | null → `num_images` honored as-is | | `prompt` | every tool | `requires_prompt`, `min_prompt_length`, `max_prompt_length` | null → unconstrained | | sound on/off | video tools | `sound_generation_type` (`"native"` vs `"none"`), `sound_enabled_by_default`, `sound_credit_multiplier` | not `"native"` → can't emit synced audio | | capability gate | route decision | `supports_visual_dna`, `supports_first_last_frame`, `supports_audio_input` | `false` → the controller silently drops that param | Normal cost formula: `final_cost = credit × output_seconds × resolution_multipliers[resolution] × (sound_enabled ? sound_credit_multiplier : 1)`, multiplied by `num_images` / `scene_count` as applicable. When `video_input_credit` applies, replace it with the combined input/output formula above and use `video_input_resolution_multipliers`. ## Decision Rule for Resolution 1. **User specified resolution explicitly** ("4K", "1080p", "480p") → ALWAYS verify in `supported_resolutions` BEFORE firing. If not supported: - ❌ Do **NOT** silently substitute. The user asked for 480p; sending 720p without consent burns 1.5–2× the credits they expected. - ✅ Show them the supported set in one line and ask: > "Seedance 2 elements supports `[720p, 1080p, 1440p, 2160p]` — 480p isn't available. Closest cheap option is 720p (~+0 credits over your intent). Want 720p, or pick another?" - Only fire after they reply. 2. **No explicit video output resolution**: choose the cheapest supported tier using current catalog pricing and pass it explicitly. This applies to drafts, normal work and final delivery alike. Do not default to 720p/1080p when a cheaper supported tier exists. Fixed-resolution models use their native output. 3. **Creative intent is not spending authorization**: "finish fully", "cinematic", "professional", "final", "production", "hero" and "don't ask me" do not authorize higher resolution, upscaling or a second high-resolution generation. A budget is a ceiling, not a target. Reference-video resolution and export resolution do not authorize matching generation resolution. 4. Preserve explicit user-selected settings. Otherwise proceed economically without a resolution approval loop. Inspect missing pricing/capabilities before dispatch. Upgrade only when the user explicitly selects a higher output tier or authorizes the resolution increase; never treat silence as approval. Image quality follows the selected model's live catalog summary and default_quality, not a hardcoded family preference or generic final-work maximum. 5. **Sound on a video model with `sound_credit_multiplier > 1`** → if user didn't ask for sound, leave it off. If user said "with sound" / "with music", enable it. ## Defaults When Nothing Is Specified - **Image**: `1K` (or the cheapest in `supported_resolutions`). - **Video**: cheapest supported output resolution from current catalog pricing, passed explicitly. Use the duration required by the user/task; do not lengthen clips to spend the available budget. - **Sound**: respect `sound_enabled_by_default`; if false, leave off. ## Log Approved Resolution / Duration / Sound Choices After the user approves the actual result, log its `credits_used`, resolution, duration, and sound state. Pending and rejected outputs stay out; use the format in `production-log.md`. ## Reference evidence in generation status For a reference audit, inspect the persisted status result's `visual_dna` and reference image fields. The widget's `visual_dnas` is display metadata, not the original submitted request. Missing widget metadata means unknown, not that no reference was used. Server-side @mention resolution can attach references beyond the caller’s explicit fields. Keep submitted inputs, persisted references, and observed visual fidelity distinct; attaching a DNA does not prove identity fidelity.