Skip to main content
There are two ways to build a wrapper, and both produce the exact same thing:
  • The web wrapper studio — a visual editor on the ModelRunner site. Best when you want to see your schema, template, and a live preview as you build.
  • Your AI assistant over MCP — describe what you want in chat and let the assistant draft, preview, and publish it. Best when you’d rather not hand-build the schema and mappings.
Either way, a wrapper is the same four parts — an input schema, a prompt template, one or more base models, and the field mappings between them. This page covers the studio briefly, then walks a full example with an AI assistant.

In the web wrapper studio

Sign in and open Dashboard → Settings → Wrappers to see the wrappers you own and create new ones. The studio is a guided form — you don’t hand-write JSON. It gives you:
  • a visual schema builder to add input fields with their types, enums, and defaults — no JSON Schema by hand;
  • a prompt template editor for the Handlebars recipe;
  • a base model picker with a per-model field-mappings editor to map your inputs onto each model;
  • a live preview that renders your template and the resulting base-model input as you edit;
  • metadata — display name, a URL-safe alias with a live availability check, category, a Markdown description, a thumbnail, and tags;
  • visibility (private / unlisted / public) and status (draft / published) controls.
The flow is: add a base model, define your input fields, write the prompt template, map the fields, watch the preview, then save as a draft and publish when it’s ready. Everything you create stays listed under the same dashboard, where you can edit or delete it later.
The studio UI evolves, so we don’t document it screen by screen — but the concepts are identical to the API-level example below, and the prompt templates and field mappings references apply equally to both paths.

Building with an AI assistant

The rest of this page builds a wrapper end to end from an AI assistant connected to the MCP server — the most reproducible path. The assistant reads the base model, drafts the wrapper, dry-runs the transform, and publishes it, all from chat.

The tools your assistant uses

Once the MCP server is connected, these wrapper tools are available:
ToolWhat it does
wrapper_authoring_guideReturns the canonical authoring rulebook. Your assistant reads this before drafting.
get_wrapper / list_wrappers / recommended_wrappersInspect existing wrappers for reference.
get_model / get_model_raw_schemaRead the base model’s input fields — you need these to write field mappings.
preview_wrapperDry-run the prompt template + mappings against a sample input. No wrapper is created.
create_wrapperCreate a wrapper you own. Defaults to visibility: private, status: draft.
patch_wrapper / delete_wrapperUpdate or remove one of your wrappers.
You never paste ownerName into these tools — ownership is derived from your authenticated identity. The assistant only authors the design of the wrapper.

Worked example: a “cinematic portrait” generator

We’ll build a text-to-image wrapper that takes a subject and a mood, and turns them into a polished cinematic prompt for an image model. Caller-facing, it’s two fields. Behind the scenes, it bakes in lighting language, locks the output format, and applies a fixed quality.
1

Pick a base model and read its inputs

Ask your assistant for a base model, then inspect its schema:
"Find a good text-to-image model and show me its input fields."
→ recommended_models(category="image")  → bytedance/sdxl-lightning-4step
→ get_model(endpoint="bytedance/sdxl-lightning-4step")
You’re looking for the field names the model accepts — e.g. prompt, num_outputs, output_format. Your field mappings have to produce exactly those keys, so this step is not optional.
If you write mappings without knowing the base model’s real input properties, create will reject keys the model doesn’t accept. Always read the base model first.
2

Let the assistant load the authoring guide

"Read the wrapper authoring guide, then design this wrapper."
→ wrapper_authoring_guide()
This gives the assistant the rules for valid schemas, templates, and mappings — so the draft it produces will pass validation.
3

Design the wrapper

The assistant proposes a wrapper definition. For our example it looks like this:
{
  "alias": "cinematic-portrait",
  "name": "Cinematic Portrait",
  "category": "text-to-image",
  "shortDescription": "Studio-grade cinematic portraits from a subject and a mood.",
  "description": "Type a subject and pick a mood. We handle the lighting, composition, and quality.",
  "thumbnailUrl": "https://media.modelrunner.ai/a50ZZtkEMuPgLdAYMrYpF.jpeg",
  "inputModalities": ["text"],
  "outputModalities": ["image"],
  "schema": {
    "components": {
      "schemas": {
        "Input": {
          "type": "object",
          "required": ["subject"],
          "properties": {
            "subject": {
              "type": "string",
              "description": "Who or what to portray, e.g. 'a fox in a trench coat'."
            },
            "mood": {
              "type": "string",
              "enum": ["warm", "moody", "noir"],
              "default": "warm",
              "description": "Lighting and tone of the portrait."
            }
          }
        }
      }
    }
  },
  "promptTemplate": "{{subject}}, {{lookup maps.mood mood}}, cinematic portrait, shallow depth of field, 85mm",
  "templateContext": {
    "maps": {
      "mood": {
        "warm": "golden-hour warmth, soft rim light",
        "moody": "low-key lighting, deep shadows",
        "noir": "high-contrast black and white, dramatic shadows"
      }
    },
    "constants": {}
  },
  "defaultBaseModelId": "<base-model-uuid>",
  "baseModels": [
    {
      "baseModelId": "<base-model-uuid>",
      "fieldMappings": {
        "staticValues": { "num_outputs": 1, "output_format": "png" },
        "omit": ["mood"]
      }
    }
  ],
  "visibility": "private",
  "status": "draft"
}
Three design moves are worth noting:
  • subject and mood are the only fields the caller sees. Everything else — quality, count, format — is decided for them.
  • The template rewrites mood into rich lighting language via a lookup map, then assembles the final prompt.
  • The mappings force num_outputs and output_format with staticValues, and omit the now-consumed mood field so it never reaches the base model.
4

Preview the transformation

Before creating anything, dry-run it:
→ preview_wrapper(
    promptTemplate, templateContext,
    sampleInput = { "subject": "a fox in a trench coat", "mood": "noir" },
    fieldMappings (optional)
  )
The preview shows the rendered prompt and the final base-model input:
{
  "prompt": "a fox in a trench coat, high-contrast black and white, dramatic shadows, cinematic portrait, shallow depth of field, 85mm",
  "num_outputs": 1,
  "output_format": "png"
}
Preview validates rendering — the template, presets, renames, and omits. It does not guarantee every produced key is accepted by the base model; that’s checked when you create. Treat preview as your fast feedback loop, not the final word.
5

Create it

"Looks right — create it."
→ create_wrapper({ ...the design above })
The wrapper is created private and in draft. It’s now callable by you at yourname/cinematic-portrait for testing.
6

Test, iterate, publish

Call your wrapper like any model (see calling a wrapper), refine with patch_wrapper, then flip visibility to public and status to published when it’s ready:
→ patch_wrapper(id, { visibility: "public", status: "published" })

Choosing what to expose

The wrapper schema is your public contract — keep it small and stable. A useful rule of thumb when deciding where each setting goes:
QuestionGoes in
Should the user control it?The schema (exposed input field)
Should it be fixed and hidden?fieldMappings.staticValues
Is it derived from other inputs?The promptTemplate
Does it just need a name change for the model?fieldMappings.renames
Should one friendly choice expand into several model fields?fieldMappings.presets
Is it a helper field that shouldn’t reach the model?fieldMappings.omit
Never put base_model in your schema — it’s a platform-level selector the caller sends separately (see switching base models). And don’t expose raw provider-only fields unless they’re genuinely part of the experience you’re designing.

Where to go next

Prompt templates

The full set of Handlebars helpers, lookup maps, and constraints.

Switching base models

Add a second model and let callers choose at request time.