Skip to content

HTTP Request pane — tabs & layout

When you fire http.send or open a blank :http.new, mnml splits a Request pane (Pane::Request) into your layout. This page is the deep tour of what’s inside it: the three stacked panels, the colored Method chip, the six Edit tabs, and the chords + clicks that drive each.

The pane has one job — let you compose a request, fire it, read the response, and ask Claude about it — without leaving the editor. It’s mnml’s Postman.

The pane is vertically stacked, not tabbed between Edit and Response. From top to bottom:

  1. Request — the editable form (Method chip + URL + tab strip + active-tab content). This grows from the top.
  2. Response — status line + headers + body. Scrolls within the middle region.
  3. AI — a 2-row strip pinned to the bottom of the pane. Always visible. Click it to ask Claude a custom question about this request/response pair.
┌─ Request ────────────────────────────────────────────────────┐
│ [GET] https://api.example.com/users/42 │
│ Body [Headers] Params Auth Vars Source │
│ │
│ Authorization: Bearer eyJhbG... │
│ Accept: application/json │
├──────────────────────────────────────────────────────────────┤
│ response │
│ 200 OK 142 ms 18 lines · 3.2 KB │
│ content-type: application/json │
│ x-trace-id: abc-1234 │
│ { │
│ "id": 42, │
│ "name": "Alice" │
├──────────────────────────────────────────────────────────────┤
│ ai (click here to ask a custom question · `a` quick debug)│
└──────────────────────────────────────────────────────────────┘

Tab toggles which view holds the input focus for the legacy r / y / Y chords; Edit-view mouse + keyboard editing fields are always live. The AI strip is genuinely pinned — scroll the body up or down and the strip stays at the bottom of the pane (poor man’s independent panel scroll).

The footer hint on the AI row reminds you: clicking the header itself opens a custom-question prompt, while pressing a (or .) from Response view fires the canned “debug this for me” prompt without asking.

The first editable row is the Method-chip + URL line:

[GET] https://api.example.com/users/42
[POST] https://api.example.com/users
[PUT] https://api.example.com/users/42
[PATCH] https://api.example.com/users/42
[DELETE]https://api.example.com/users/42
[HEAD] https://api.example.com/users/42
[OPTIONS]https://api.example.com/users

The Method renders as a colored chip — the chip’s background changes per verb so the request’s shape reads at a glance from across the room:

MethodColorPurpose
GETgreenSafe + idempotent — fetching
POSTorangeCreating / mutating — non-idempotent
PUTblueReplacing — idempotent
PATCHcyanUpdating — non-idempotent
DELETEredRemoving — explicit destructive
HEADyellowHeaders only
OPTIONSpurpleCapability probe

Three ways to change the method:

  • Click the chip opens the method dropdown — a context menu listing all 7 verbs. Click one to set it directly (no cycling through verbs you don’t want). Backed by 7 :http.set_method.<verb> palette commands.
  • :http.cycle_method (or Space when the Method field is focused) walks GET → POST → PUT → PATCH → DELETE → HEAD → OPTIONS → GET.
  • :http.set_method.<verb> (one of 7 — .get, .post, .put, .patch, .delete, .head, .options) sets a specific verb. Useful as a chord binding (<leader>hG → set GET, etc).

The URL fills the rest of the row in normal fg color. Click it to focus the field; Enter on the URL row (or the Method chip row) fires the request.

Between the URL row and the tab content sits a 6-tab strip:

[Body] Headers Params Auth Vars Source

The active tab carries bracket markers ([Body]), bold, and underline. Inactive tabs are dim. Color isn’t load-bearing — the brackets + underline + bold survive monochrome themes and stay legible for colorblind users.

ChordAction
Ctrl+]Next tab (Body → Headers → Params → Auth → Vars → Source → Body)
Ctrl+[Previous tab
Ctrl+1Jump to Body
Ctrl+2Jump to Headers
Ctrl+3Jump to Params
Ctrl+4Jump to Auth
Ctrl+5Jump to Vars
Ctrl+6Jump to Source
Mouse clickClick any chip directly

Ctrl+] / Ctrl+[ / Ctrl+1..6 are intercepted before the global chord chain so they work in both input modes — standard mode’s keymap binds those chords to indent / outdent globally, but the dispatcher checks for a focused Request pane in Edit view first and steals them.

Tab is reserved for cycling fields (URL → Method → Headers → Body → URL). It does not switch tabs. The tab strip and the field focus are independent.

Switching to the Source tab also focuses the Source field so you can immediately type or paste. Switching away from Source restores URL focus.

The multi-line Body field. Editable. JSON / XML / form-encoded / plain — anything you can type in.

Body (JSON) — Ctrl+Shift+F formats JSON
{
"name": "Alice",
"email": "[email protected]"
}

The label hint surfaces a detected content type so you see what mnml thinks the body is: JSON (starts with { or [), XML (starts with <), form (looks like key=val&key=val), or no label for plain text. The detection is a cheap leading-bytes sniff on the first ~256 chars, run every frame.

Tab inside Body inserts a literal \t rather than cycling fields — indented bodies are common. Newlines stay newlines; the body is sent verbatim.

SurfaceCall
PaletteHTTP: pretty-print JSON Body field of the active Request pane
Ex-command:http.format_body
Chord (Edit view)Ctrl+Shift+F

Parses the Body field as JSON and rewrites it with 2-space indent. No-op on non-JSON bodies (the toast explains the parse error). Useful after pasting a minified payload from a browser DevTools panel.

The chord matches what most IDEs use for “format code” — and unlike them, mnml’s version is body-scoped: it doesn’t try to format your headers or your URL.

The Key: Value list. Editable as a flat textarea — type Authorization: Bearer xyz\n to add a line. Header keys render in cyan + bold, values in foreground; lines without a : render dim (a visible hint they’re not yet a valid header).

Don’t know whether it’s Content-Type or ContentType? Use the picker:

SurfaceCall
PaletteHTTP: insert a common header (Accept, Content-Type, Authorization, …)
Ex-command:http.insert_header

Opens a picker over 20 IANA-common header namesAccept, Accept-Encoding, Accept-Language, Authorization, Cache-Control, Content-Type, Content-Length, Content-Encoding, Cookie, Host, If-Match, If-None-Match, If-Modified-Since, Origin, Referer, User-Agent, X-Api-Key, X-Forwarded-For, X-Requested-With, X-Trace-Id. Enter inserts Name: at the Headers cursor and switches you into the Headers tab so you can immediately type the value.

A row-per-query-parameter view of whatever’s after the ? in the URL.

+ Add new parameter…
foo = bar
limit = 10

Click rows to interact:

  • + Add new parameter… (green) — opens a prompt for KEY=VALUE; on Enter, appends to the URL with the correct ? / & separator. Same as :http.params_add.
  • Any existing row — clicks the param. v1 deletes the param (:http.params_delete <key>); a v2 edit-prompt is queued.

Two palette commands also drive the surface:

SurfaceCall
:http.params_addPrompt for KEY=VALUE, append to URL
:http.params_clearStrip the entire ?… portion from the URL

The Params tab reflects the URL — edit the URL field directly and the Params tab re-renders on the next frame. Empty query string shows (no query parameters — add ?key=value to URL).

The Auth tab is Postman parity for authentication setup. The current state of the Authorization header summarizes at the top:

Current: Bearer · eyJhbGciOiJIUzI1NiJ9
+ Set Bearer token…
+ Set Basic auth (user:pass)…
+ Set X-Api-Key…
↻ Apply saved preset…
💾 Save current as preset…
✗ Clear Authorization

The summary shows what kind of auth the active header carries — Bearer · <token preview> for bearer tokens, Basic · (base64 user:pass) for basic auth, or (no Authorization header — request will be unauthenticated) when there isn’t one.

Click any action row to dispatch:

ActionPrompt / dispatchResult
+ Set Bearer token…Prompts for the raw token (no Bearer prefix)Replaces or inserts Authorization: Bearer <token>
+ Set Basic auth…Prompts for user:passwordReplaces or inserts Authorization: Basic <base64>
+ Set X-Api-Key…Prompts for the key valueReplaces or inserts X-Api-Key: <value>
↻ Apply saved preset…Picker over .mnml/auth/*.txtReplaces Authorization with the preset’s content
💾 Save current as preset…Prompts for the preset nameWrites the current Authorization value to .mnml/auth/<name>.txt
✗ Clear Authorization (red)No promptRemoves the Authorization header entirely

Auth presets save a configured Authorization header for reuse across requests in the workspace. Two palette commands also drive them outside the Auth tab:

SurfaceCall
PaletteAuth: save current Authorization header as a named preset
Ex-command:auth.save_preset

Prompts for a name. Writes the current Authorization header content (everything after the Authorization: colon) to .mnml/auth/<name>.txt. Workspace-local, per-name file.

SurfaceCall
PaletteAuth: apply a saved preset → active Request Authorization header
Ex-command:auth.apply_preset

Picker over the workspace’s .mnml/auth/*.txt files. Enter writes (or replaces) the Authorization header on the active Request pane.

Add .mnml/auth/ to your .gitignore if the presets are personal tokens; commit a *.txt.example template if they’re shared values.

A live read-out of the active env file’s variables — what {{VAR}} substitution sees when you fire this request.

Active env vars — edit with :http.edit_env
env: dev.env
+ Add new variable…
TOKEN = eyJhbGciOiJIUzI1NiJ9...
BASE_URL = https://staging-api.example.com
LOGIN_EMAIL = [email protected]

Reads both .rqst/env/<active>.env and .mnml/env/<active>.env, with .mnml/ overriding on the same key (the same precedence the runtime substituter uses).

Click rows:

  • + Add new variable… (green) — opens the env-editor add prompt (:http.edit_env+add).
  • Any existing row — opens the env-editor edit prompt for that key. The picker is the same one HTTP envs & templating describes.

The active env name is detected at render time via the same resolution chain as a http.send: --env (when launched headlessly) → $MNML_ENV.rqst/config’s default_envdev.

A paste-target hint surface:

Paste a curl command or .http block here.
Then run :http.paste_curl (or Ctrl+Shift+V) to populate fields.
(clipboard paste-curl reads your system clipboard directly)

The “source” of truth is the clipboard — the tab is documentation for the paste flow, not an editable scratch buffer. Right-clicking anywhere in the Source tab fires the URL-titled field menu with Paste curl from clipboard ready.

An editable in-pane source field is a v2 feature; today’s Source tab is honest about being a hint surface.

These chords work in any Edit-view tab:

ChordAction
TabCycle field forward (URL → Method → Headers → Body → URL)
Shift+TabCycle field backward
Ctrl+] / Ctrl+[Next / previous tab
Ctrl+1..6Jump directly to Body / Headers / Params / Auth / Vars / Source
Ctrl+Shift+VPaste curl from clipboard (populate all fields)
Ctrl+VPaste plain text into focused field
Ctrl+Shift+FFormat Body as JSON
Ctrl+EnterParse Source-tab buffer into Method/URL/Headers/Body
Space (Method focused)Cycle HTTP verb
Enter (URL / Method)Fire the request
Enter (Headers / Body)Insert newline
EscFlip to Response view

The middle panel renders once the request lands. The status line up top:

200 OK 142 ms 18 lines · 3.2 KB
  • Status chip2xx green, 3xx cyan, 4xx yellow, 5xx red. Bold, on the response’s bg_dark.
  • Elapsed — request-to-response wall-clock in ms.
  • Stats — body line count + human-readable byte count (B / KB / MB). Useful when scanning whether the response was empty (0 lines · 0 B) or huge (8431 lines · 1.4 MB).

Then headers, in arrival order, dimmed. Then the body — pretty-printed when the Content-Type says JSON (or the body starts with { / [); raw otherwise.

Below the body, when present: / rows per @assert directive, and name = value rows per @capture directive.

When a sibling <request>.schema.json exists, the Response view paints a one-line footer below the body summarising JSON Schema validation:

✓ Schema valid (users.schema.json)
✗ Schema: 3 errors (users.schema.json) — :http.show_schema_errors

Green-bold on pass; red-bold on fail with the :http.show_schema_errors ex-command literally in the footer text so you can copy it. Yellow warnings render for the edge cases:

  • ⚠ Body isn't JSON — schema (file) skipped — the response wasn’t parseable JSON, so the schema couldn’t run against it.
  • ⚠ Schema read error (file): <err> — the sidecar exists but couldn’t be read.
  • ⚠ Schema parse error (file): <err> — the sidecar exists but isn’t a valid JSON Schema document.

No sidecar = no footer. The full mechanism (resolution order, the two :http.show_schema_errors / :http.revalidate_schema commands, edge cases) lives on its own page — HTTP response schema validation.

ChordAction
rRe-fire the request using the pane’s current field values
yCopy the request as a curl
YCopy the response body
eToggle view (legacy — Tab also works)
a or .Ask Claude about this request/response (canned debug prompt)
j / k / / Scroll the body
g / GTop / bottom of response
Page Up / Page DownPage the body
EscFocus the file tree
SurfaceCall
PaletteHTTP: save active Response body to a file (prompt for path)
Ex-command:http.save_response

Prompts for a destination path; on Enter, writes the active Done response body to that file. Relative paths resolve under the workspace root; absolute paths land where you’d expect. Parent directories are mkdir -p’d. Toasts the byte count + full path on success.

SurfaceCall
PaletteHTTP: diff the active Request pane's last two responses
Ex-command:http.diff_last_two

After re-firing a request (r), the previous Done response is stashed. This command opens a scratch buffer with a unified-diff of status + headers + body between the old and new responses. Useful for “did re-firing actually change anything?” debugging.

The bottom strip is a 2-row pinned region:

─────────────────────────────────────────────────────────────
ai (click here to ask a custom question · `a` quick debug)

Two click affordances + two chords:

  • Click the ai header row — opens a prompt: “Ask Claude about this request/response:”. Type your question, press Enter, and the request + response (status / headers / body, capped at 4000 chars) ship to the AI backend along with your question. Custom-question debugging.
  • a (or .) from Response view — fires the canned http.ai_debug prompt with no custom question (just “why isn’t this working / how do I fix it”). Useful when the failure is obvious to a model but not to you.

Both routes dispatch through the same AI backend you’ve configured (claude CLI, Codex, or direct Anthropic API). See AI panes for backend setup.

Requirements: the pane must hold a Request pane with a Done response (or a transport error). Asking about a still-in-flight Sending state toasts wait for the response first.

Right-click any form row or any tab content row to get a context menu titled with the field’s name:

┌─ Request · Method ──────────┐
│ Cycle method │ ← only on the Method row
│ Send │
│ Paste curl from clipboard │
│ Copy as curl │
│ Switch to Response │
└─────────────────────────────┘

Common items appear on every field’s menu:

ItemWhat it does
SendFires the request
Paste curl from clipboard:http.paste_curl — overwrites fields from clipboard
Copy as curl:http.copy_curl — copies the current request as a curl one-liner
Switch to ResponseFlips the pane to Response view

The Method row’s menu prepends Cycle method so you can change verbs without keyboard focus. The Params / Vars / Source tabs use the URL-titled menu.

Commands that open a scratch pane (http.bench, http.sync, http.diff_last_two, …) toast with a bracketed pane name:

bench summary — 10 samples in 1842 ms → [bench-trace]

The [bench-trace] portion renders in yellow + bold + underlined. Clicking it switches focus to the scratch pane. See Cmdline popup for the full mechanism.

VerbColorPalette id
GETgreenhttp.set_method.get
POSTorangehttp.set_method.post
PUTbluehttp.set_method.put
PATCHcyanhttp.set_method.patch
DELETEredhttp.set_method.delete
HEADyellowhttp.set_method.head
OPTIONSpurplehttp.set_method.options

Plus http.cycle_method (cycles to next verb) and the Method chip click (opens the dropdown).

TabCtrl+NEditablePrimary palette helpers
BodyCtrl+1yes (multi-line)http.format_body (Ctrl+Shift+F)
HeadersCtrl+2yes (multi-line)http.insert_header
ParamsCtrl+3click rowshttp.params_add, http.params_clear
AuthCtrl+4click rowsauth.save_preset, auth.apply_preset
VarsCtrl+5click rowshttp.edit_env
SourceCtrl+6paste targethttp.paste_curl (Ctrl+Shift+V), http.paste_source (Ctrl+Enter)
ActionChordPalette id
Re-firerhttp.send
Copy curlyhttp.copy_curl
Copy bodyYhttp.copy_response_body
Save bodyhttp.save_response
Diff last twohttp.diff_last_two
Ask Claude (canned)a / .http.ai_debug
Ask Claude (custom)click ai strip
Toggle viewTab / ehttp.toggle_view