Tools Reference

MCP Tools Reference

Every tool the MCP server exposes. Tool names reach the model prefixed by client convention — in Claude Code, generate_image appears as mcp__modelstack__generate_image.

ToolPurpose
list_modelsChat model IDs usable with llm_chat
llm_chatNon-streaming chat completion against any routed model
list_generation_modelsMedia models, variants, accepted parameters, and minimum price
generate_imageQueue an image job
generate_videoQueue a video job
generate_audioQueue an audio job
get_generationPoll a job and retrieve its output URLs
upload_mediaStore reference media and return its storage key

Chat

list_models

No parameters. Returns every chat model ID available to your key.

json
{ "models": ["claude-opus-5", "claude-haiku-4-5", "gpt-5.4", "…"] }

llm_chat

Sends a chat completion and returns the reply as plain text. Useful for handing bulk or low-stakes work to a cheaper model than the calling agent runs on.

stringrequired

Model ID from list_models, e.g. claude-haiku-4-5.

string

A single user message. Ignored when messages is supplied.

array

Full conversation as { role, content } objects. Takes precedence over prompt.

string

System instruction prepended to the conversation.

integer
number
json
{
  "model": "claude-haiku-4-5",
  "text": "…",
  "finish_reason": "stop",
  "usage": { "prompt_tokens": 26, "completion_tokens": 3, "total_tokens": 29 }
}
info

Streaming is not available through MCP — a tool call returns one complete result. Use the Chat Completions API directly if you need tokens as they arrive.

Media generation

list_generation_models

string

Restrict to one modality: image, video, or audio. Omit to list all.

Returns each model with its variants, the parameters that variant accepts, and its floor price:

json
{
  "models": [
    {
      "model": "google/nano-banana-2",
      "display_name": "Nano Banana 2",
      "family": "google",
      "variants": [
        {
          "generation_type": "TEXT_TO_IMAGE",
          "supported_params": ["prompt", "aspect_ratio", "resolution", "output_format"],
          "min_price_usd": 0.08
        }
      ]
    }
  ]
}
lightbulb

Have your agent call this before generating. It is the authoritative list of which settings keys a model accepts, so the agent stops guessing parameter names.

generate_image / generate_video / generate_audio

All three take the same shape and return a job_id immediately.

stringrequired

Base model ID from list_generation_models, e.g. google/nano-banana-2.

stringrequired

Text description of the desired output.

string

The variant to run. Defaults to TEXT_TO_IMAGE, TEXT_TO_VIDEO, or TEXT_TO_AUDIO for the respective tool.

array

Reference media as { type, key } objects, where key comes from upload_media. Required by variants like IMAGE_TO_IMAGE and IMAGE_TO_VIDEO.

object

Model-specific parameters passed through verbatim — aspect_ratio, resolution, duration, seed, negative_prompt, and so on. See supported_params for the chosen variant.

json
{
  "job_id": "58eae738-17e8-4d3a-9f57-a2e735f83925",
  "status": "pending",
  "model": "google/nano-banana-2-lite",
  "generation_type": "IMAGE_TO_IMAGE"
}
warning

Storage keys belong in input_media, never in settings. Values in settings are forwarded to the provider unchanged, so a provider handed a raw generations/inputs/… string has nothing it can fetch. Keys in input_media are resolved to signed URLs first. Publicly reachable URLs are fine in settings for models that declare a URL parameter.

get_generation

stringrequired

The job_id returned by a generate_* tool.

json
{
  "job_id": "58eae738…",
  "status": "completed",
  "output_urls": ["https://…"],
  "output_keys": ["media/{userId}/{generationId}/0.jpg"],
  "error": null,
  "cost_usd": 0.04,
  "created_at": "2026-08-11T03:26:08.900Z",
  "completed_at": "2026-08-11T03:27:42.841Z"
}

status is one of pending, processing, completed, or failed. On failed, error explains why and the cost is refunded.

output_urls are signed and valid for roughly an hour; output_keys are the permanent storage keys. If signing fails, output_urls comes back empty while output_keys stays populated, so a completed job is never lost.

upload_media

Stores reference media and returns its key. Fetches the source server-side, so file bytes never pass through the conversation.

stringrequired

Publicly reachable https:// URL of the media to store.

string

Name for the stored file. Defaults to the last path segment of source_url.

json
{
  "key": "generations/inputs/{userId}/{uuid}-ref.png",
  "media_type": "image",
  "size_bytes": 1106817
}

Only image/*, video/*, and audio/* content types are accepted, up to 50 MB.

Uploading a local file

upload_media needs a URL, so a file on disk goes to the upload endpoint directly:

bash
curl -X POST https://api.modelstack.cc/v1/media/upload \
  -H "Authorization: Bearer YOUR_MODELSTACK_API_KEY" \
  -F file=@reference.png
json
{ "key": "generations/inputs/{userId}/{uuid}-reference.png", "mediaType": "image" }
info

An agent cannot see the Authorization header — it lives in the MCP client config, not the conversation. Agents that stop here quietly fall back to text-to-image and lose your reference image. The header is readable on the same machine the agent's shell runs on, so it can be piped into the request without ever being printed. The upload_media tool description carries the exact recipe, and instructs the agent to ask you for the key rather than silently downgrading if the lookup fails.

Workflows

Text to image

list_generation_models(category: "image")
generate_image(model: "google/nano-banana-2", prompt: "…",
               settings: { aspect_ratio: "16:9", resolution: "2K" })
get_generation(job_id)   ← repeat until completed

Image to image with a reference

upload_media(source_url: "https://cdn.example/seed.png")
  → { key: "generations/inputs/…" }

generate_image(model: "google/nano-banana-2-lite",
               prompt: "make the background deep navy",
               generation_type: "IMAGE_TO_IMAGE",
               input_media: [{ type: "image", key: "generations/inputs/…" }])
get_generation(job_id)

Delegating to a cheaper model

llm_chat(model: "claude-haiku-4-5",
         system: "Reply with one sentence.",
         prompt: "Summarize this changelog: …")

Errors

Tool-level failures come back as a normal result with isError: true and a plain-text explanation, rather than a protocol error — this lets the calling model read the message and correct itself. Common cases:

MessageCause
Unknown model "…"Not in the catalog — call list_generation_models
Model "…" does not support TEXT_TO_VIDEOWrong variant for that model; the message lists the supported ones
Model "…" has no video variantsModel exists but not for that modality
Generation request rejected (HTTP 402)Insufficient balance — see Balance & Credits
"source_url" must be an https:// URLPlain HTTP sources are refused

Next steps