# MCP Tools Reference

Every tool the [MCP server](/docs/mcp/overview) exposes. Tool names reach the model prefixed by client convention — in Claude Code, `generate_image` appears as `mcp__modelstack__generate_image`.

| Tool | Purpose |
| --- | --- |
| `list_models` | Chat model IDs usable with `llm_chat` |
| `llm_chat` | Non-streaming chat completion against any routed model |
| `list_generation_models` | Media models, variants, accepted parameters, and minimum price |
| `generate_image` | Queue an image job |
| `generate_video` | Queue a video job |
| `generate_audio` | Queue an audio job |
| `get_generation` | Poll a job and retrieve its output URLs |
| `upload_media` | Store 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.

<ParamField body="model" type="string" required>
  Model ID from `list_models`, e.g. `claude-haiku-4-5`.
</ParamField>

<ParamField body="prompt" type="string">
  A single user message. Ignored when `messages` is supplied.
</ParamField>

<ParamField body="messages" type="array">
  Full conversation as `{ role, content }` objects. Takes precedence over `prompt`.
</ParamField>

<ParamField body="system" type="string">
  System instruction prepended to the conversation.
</ParamField>

<ParamField body="max_tokens" type="integer" />

<ParamField body="temperature" type="number" />

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

<Note>
  Streaming is not available through MCP — a tool call returns one complete
  result. Use the [Chat Completions API](/docs/api-reference/chat-completions)
  directly if you need tokens as they arrive.
</Note>

## Media generation

### list_generation_models

<ParamField body="category" type="string">
  Restrict to one modality: `image`, `video`, or `audio`. Omit to list all.
</ParamField>

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
        }
      ]
    }
  ]
}
```

<Tip>
  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.
</Tip>

### generate_image / generate_video / generate_audio

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

<ParamField body="model" type="string" required>
  Base model ID from `list_generation_models`, e.g. `google/nano-banana-2`.
</ParamField>

<ParamField body="prompt" type="string" required>
  Text description of the desired output.
</ParamField>

<ParamField body="generation_type" type="string">
  The variant to run. Defaults to `TEXT_TO_IMAGE`, `TEXT_TO_VIDEO`, or
  `TEXT_TO_AUDIO` for the respective tool.
</ParamField>

<ParamField body="input_media" type="array">
  Reference media as `{ type, key }` objects, where `key` comes from
  `upload_media`. Required by variants like `IMAGE_TO_IMAGE` and
  `IMAGE_TO_VIDEO`.
</ParamField>

<ParamField body="settings" type="object">
  Model-specific parameters passed through verbatim — `aspect_ratio`,
  `resolution`, `duration`, `seed`, `negative_prompt`, and so on. See
  `supported_params` for the chosen variant.
</ParamField>

```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.
</Warning>

### get_generation

<ParamField body="job_id" type="string" required>
  The `job_id` returned by a `generate_*` tool.
</ParamField>

```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.

<ParamField body="source_url" type="string" required>
  Publicly reachable `https://` URL of the media to store.
</ParamField>

<ParamField body="filename" type="string">
  Name for the stored file. Defaults to the last path segment of `source_url`.
</ParamField>

```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" }
```

<Note>
  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.
</Note>

## 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:

| Message | Cause |
| --- | --- |
| `Unknown model "…"` | Not in the catalog — call `list_generation_models` |
| `Model "…" does not support TEXT_TO_VIDEO` | Wrong variant for that model; the message lists the supported ones |
| `Model "…" has no video variants` | Model exists but not for that modality |
| `Generation request rejected (HTTP 402)` | Insufficient balance — see [Balance & Credits](/docs/billing/balance) |
| `"source_url" must be an https:// URL` | Plain HTTP sources are refused |

## Next steps

<CardGroup cols={2}>
  <Card title="MCP Overview" icon="hub" href="/docs/mcp/overview">
    Installation, scopes, billing, and protocol support.
  </Card>
  <Card title="Generation Types" icon="category" href="/docs/api-reference/generation-types">
    Every variant and what inputs it expects.
  </Card>
  <Card title="Generation Parameters" icon="tune" href="/docs/api-reference/generation-parameters">
    The full `settings` vocabulary.
  </Card>
  <Card title="Model Pricing" icon="payments" href="/docs/billing/model-pricing">
    What each generation costs.
  </Card>
</CardGroup>

---
