Compact mode
How responses are trimmed by default, and how to get the full payload.
Assistants pay for every token a tool returns. The Postal MCP server therefore returns compact objects by default and lets you opt into the raw API payload per call.
The detail parameter
Nine read tools accept detail: "compact" | "full":
list_tracks, get_track, search_tracks, list_track_versions,
list_playlists, get_playlist, get_tag_taxonomy, list_composers,
get_composer.
There is no server-wide switch. Pass detail: "full" on the call that needs
it.
Where the compact shape comes from
The compact shape is the API's own. Each of these tools sends detail to the
REST route as its view query parameter (view=compact or view=full, see
the API reference), and the API answers with the trimmed object and an
X-Postal-View header naming the view it applied. The MCP server passes the
answer through untouched, so REST clients, the MCP server and Postal's own
assistant all receive the same objects from one implementation. The tables
below describe what you receive.
What every response looks like
Each tool returns a one-line human summary followed by pretty-printed JSON. The summary is rendered from a template the API publishes with the tool, read against the payload as served, so counts and paging cursors stay accurate when the body is trimmed.
Found 42 tracks (showing 20, next cursor: ck_…)
[
{ "id": "…", "title": "Night Drive", "artist": "…", "bpm": 92, … }
]What compact keeps
| Object | Kept | Dropped |
|---|---|---|
| Track | id, title, artist, durationSec, bpm, key, createdAt, link, cid, tag names per category, autotagModelVersion (+ autotagModelVersions when mixed) | audio file URLs, shareholders, share-link internals, per-tag confidence and modelVersion |
| Playlist | metadata, publicLink, per-track { id, title, artist, durationSec, order } | download links, full audio profiles |
| Composer | composer fields, linked tracks as { id, name } (max 25, tracksTruncated: true beyond that) | full track objects |
| Taxonomy | plain name arrays per category | ids, codes, ordering |
| Search hit | id, title, artist, bpm, key, durationSec, genres, moods, score, first two reasons, plus the interpretation block | remaining reasons, raw feature vectors |
| Dashboard | everything except entityImage on activity items (always trimmed) |
For list_tracks, view=compact also makes the API read fewer columns, so a
compact listing is cheaper to serve as well as to read. The API refuses
view=compact combined with its fields parameter; the MCP server never
sends both.
Mutations
Write tools return the API route's own response, exactly as a REST client
would receive it: a created playlist, an updated version, a { "success": true }
acknowledgement, whatever that route answers. The summary line says what
happened in words, for example:
Added 3 tracks to playlist pl_123.Earlier server versions replaced these responses with a minimal
{ "ok": true, … } confirmation; that is no longer the case.
Errors
An error result takes the same shape as a success: a sentence, a blank line,
then a JSON body. The body carries the API's stable messageCode as error
and the HTTP status, so an assistant can branch on the code rather than the
wording. The sentence is the API's own message, which for a read on an ID that
does not exist (or is not yours) names the ID and the list tool that returns
valid ones:
Error: Track "does-not-exist" not found: it does not exist or is not yours. list_tracks or search_tracks return the track IDs in your library.
{
"error": "not_found",
"status": 404
}Every read tool answers an unknown ID this way — get_track, get_composer,
get_playlist, the version and brief reads, and get_track_analysis — so an
existence check never passes on a compact object whose fields are all null.
get_entity_stats keeps its partial answer with a notFound list and only
errors when none of the IDs resolve.
A 401 or 403 adds a hint to the sentence (— check that the API key is valid, — the API key is missing a required scope for this action).
When the API answers { ok: false, … } inside a 200 the server converts it
into the same error shape, with the payload's error as the code and
(missing: …) appended to the sentence when the payload lists what was
missing.