[object Object]

← back to Cli Printing Press

feat(cli): add ElevenLabs generation support (#1307)

5f43de505252859e2ebbe846a4ecffb598e7655d · 2026-05-15 21:44:53 -0500 · Cathryn Lavery

* feat(cli): add ElevenLabs generation support

* fix(cli): share catalog metadata helpers

* fix(cli): scope headerOverrides to branches that use it in promoted commands

The promoted-command template declared `headerOverrides` at the top of
the handler when UsesBinaryResponse was set, but the paginated branch
(both HasStore and non-HasStore) passes nil to resolvePaginatedRead /
paginatedGet and never references the variable. The same was true for
the HasStore + GET branch that calls resolveRead. An endpoint with both
UsesBinaryResponse=true and pagination (or HasStore-GET) generated Go
that failed to compile with "headerOverrides declared and not used".

Move the declaration into each branch that actually consumes it (live
GET, DELETE, multipart, form, and JSON-body POST/PUT/PATCH). Regression
test pins a paginated binary GET and runs `go build` on the output.

Reported by Greptile on #1307.

* fix(cli): thread binary-response header through promoted pagination and store paths

The promoted-command template called paginatedGet, resolvePaginatedRead,
and resolveRead with nil headers regardless of UsesBinaryResponse. When
the live API call dispatched (cache miss for store-backed reads, or any
paginated binary GET), the BinaryResponseHeader sentinel never reached
the client, so the runtime ran sanitizeJSONResponse on raw binary bytes
and sent Accept: application/json — corrupting audio responses and
making strict APIs return JSON variants of binary endpoints.

Mirror the pattern already used in command_endpoint.go.tmpl: hoist
headerOverrides to the top of the handler when UsesBinaryResponse is
set, then thread headerOverrides (else nil) through every helper call
that accepts headers. Drops the per-branch headerOverrides declarations
the previous compile-fix introduced — all consumers now share one.

Two regression tests pin the wiring: the paginated path asserts
headerOverrides is passed to paginatedGet (forcing no-store via an
explicit VisionTemplateSet so IsZero() skips re-profiling), and the
store-backed path asserts resolveRead receives headerOverrides instead
of nil.

Reported by Greptile on #1307.

* chore(cli): refresh GraphQL shared-endpoint goldens after main merge

Mergify's update_method=merge brought main into this branch, which
includes the new PostWithParams helper. The GraphQL shared-endpoint
generator now threads a params map and calls PostWithParams instead
of Post for both list and get operations. Regenerated goldens to
reflect the merged behavior; no template change in this commit.

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Trevin Chow <trevin@trevinchow.com>

Files touched

Diff

commit 5f43de505252859e2ebbe846a4ecffb598e7655d
Author: Cathryn Lavery <50469282+cathrynlavery@users.noreply.github.com>
Date:   Fri May 15 21:44:53 2026 -0500

    feat(cli): add ElevenLabs generation support (#1307)
    
    * feat(cli): add ElevenLabs generation support
    
    * fix(cli): share catalog metadata helpers
    
    * fix(cli): scope headerOverrides to branches that use it in promoted commands
    
    The promoted-command template declared `headerOverrides` at the top of
    the handler when UsesBinaryResponse was set, but the paginated branch
    (both HasStore and non-HasStore) passes nil to resolvePaginatedRead /
    paginatedGet and never references the variable. The same was true for
    the HasStore + GET branch that calls resolveRead. An endpoint with both
    UsesBinaryResponse=true and pagination (or HasStore-GET) generated Go
    that failed to compile with "headerOverrides declared and not used".
    
    Move the declaration into each branch that actually consumes it (live
    GET, DELETE, multipart, form, and JSON-body POST/PUT/PATCH). Regression
    test pins a paginated binary GET and runs `go build` on the output.
    
    Reported by Greptile on #1307.
    
    * fix(cli): thread binary-response header through promoted pagination and store paths
    
    The promoted-command template called paginatedGet, resolvePaginatedRead,
    and resolveRead with nil headers regardless of UsesBinaryResponse. When
    the live API call dispatched (cache miss for store-backed reads, or any
    paginated binary GET), the BinaryResponseHeader sentinel never reached
    the client, so the runtime ran sanitizeJSONResponse on raw binary bytes
    and sent Accept: application/json — corrupting audio responses and
    making strict APIs return JSON variants of binary endpoints.
    
    Mirror the pattern already used in command_endpoint.go.tmpl: hoist
    headerOverrides to the top of the handler when UsesBinaryResponse is
    set, then thread headerOverrides (else nil) through every helper call
    that accepts headers. Drops the per-branch headerOverrides declarations
    the previous compile-fix introduced — all consumers now share one.
    
    Two regression tests pin the wiring: the paginated path asserts
    headerOverrides is passed to paginatedGet (forcing no-store via an
    explicit VisionTemplateSet so IsZero() skips re-profiling), and the
    store-backed path asserts resolveRead receives headerOverrides instead
    of nil.
    
    Reported by Greptile on #1307.
    
    * chore(cli): refresh GraphQL shared-endpoint goldens after main merge
    
    Mergify's update_method=merge brought main into this branch, which
    includes the new PostWithParams helper. The GraphQL shared-endpoint
    generator now threads a params map and calls PostWithParams instead
    of Post for both list and get operations. Regenerated goldens to
    reflect the merged behavior; no template change in this commit.
    
    ---------
    
    Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
    Co-authored-by: Trevin Chow <trevin@trevinchow.com>
---
 .github/workflows/validate-catalog.yml             |   2 +
 AGENTS.md                                          |   1 +
 catalog/elevenlabs.yaml                            |  16 ++
 docs/CATALOG.md                                    |   2 +
 internal/catalog/catalog.go                        |   4 +
 internal/catalog/catalog_test.go                   |   9 +
 internal/catalogmeta/catalogmeta.go                |  68 +++++++
 internal/cli/catalog.go                            |   3 +
 internal/cli/generate_test.go                      |  22 +-
 internal/cli/root.go                               |   7 +
 .../generator/binary_paginated_promoted_test.go    | 101 ++++++++++
 internal/generator/endpoint_is_write_test.go       |  10 +-
 internal/generator/form_test.go                    |   4 +-
 internal/generator/generator_test.go               |  12 +-
 internal/generator/multipart_test.go               |  35 +++-
 internal/generator/mutating_query_params_test.go   |  56 ++++++
 internal/generator/promoted_poststore_test.go      |   8 +-
 internal/generator/templates/client.go.tmpl        |  96 +++++----
 .../generator/templates/command_endpoint.go.tmpl   | 222 +++++----------------
 .../generator/templates/command_promoted.go.tmpl   |  65 ++++--
 internal/generator/templates/mcp_tools.go.tmpl     |  81 +++++++-
 internal/generator/tier_routing_test.go            |   4 +-
 internal/openapi/parser.go                         | 138 ++++++++++++-
 internal/openapi/parser_test.go                    |  59 ++++++
 internal/pipeline/mcpsync/sync.go                  |  16 ++
 internal/pipeline/mcpsync/sync_test.go             |  40 ++++
 internal/pipeline/publish.go                       |  19 ++
 internal/pipeline/publish_test.go                  |  41 ++++
 internal/spec/spec.go                              |  13 +-
 testdata/golden/expected/catalog-list/stdout.txt   |   3 +
 .../internal/client/client.go                      |  48 +++--
 .../printing-press-rich-auth/internal/mcp/tools.go |  37 +++-
 .../internal/cli/projects_avatar_upload-project.go |   8 +-
 .../internal/cli/projects_create.go                |   3 +-
 .../internal/cli/projects_tasks_update-project.go  |   8 +-
 .../internal/client/client.go                      |  72 ++++---
 .../printing-press-golden/internal/mcp/tools.go    |  65 +++++-
 .../internal/cli/things_get.go                     |   4 +-
 .../internal/cli/things_list.go                    |   3 +-
 .../mcp-cloudflare/internal/mcp/tools.go           |  35 +++-
 .../internal/cli/stores_create.go                  |   3 +-
 .../public-param-golden/internal/mcp/tools.go      |  39 +++-
 .../tier-routing-golden/internal/client/client.go  |  48 +++--
 .../tier-routing-golden/internal/mcp/tools.go      |  41 +++-
 44 files changed, 1204 insertions(+), 367 deletions(-)

diff --git a/.github/workflows/validate-catalog.yml b/.github/workflows/validate-catalog.yml
index ae9ab8db..8373f39d 100644
--- a/.github/workflows/validate-catalog.yml
+++ b/.github/workflows/validate-catalog.yml
@@ -100,6 +100,8 @@ jobs:
 
             ./printing-press generate \
               --spec "$SPEC_FILE" \
+              --spec-url "$SPEC_URL" \
+              --name "$NAME" \
               --output "$OUT_DIR" \
               --validate
 
diff --git a/AGENTS.md b/AGENTS.md
index e444a469..d013aaf3 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -178,6 +178,7 @@ When adding or editing `catalog/*.yaml`, first decide whether the entry belongs
 - `bearer_refresh`, when present, must include `bundle_url` and `pattern`; `bundle_url` must use HTTPS, and `pattern` must compile as a Go regexp.
 - `auth_key_url`, when present, must use HTTPS. It overrides any URL inferred from the spec and surfaces in the printed CLI as `Get a key at: <URL>`.
 - `auth_instructions`, when present, is a one-line string rendered under the URL. It overrides any `x-auth-instructions` value from the spec.
+- `base_url`, when present, must use HTTPS. Use it only when the upstream spec omits `servers:` and the correct API origin is known.
 - Rebuild the binary after editing; `catalog.FS` is a Go embed.
 See [`docs/CATALOG.md`](docs/CATALOG.md) for the inclusion rubric, evidence checklist, validation rationale, wrapper-only entry shape, and bearer-refresh metadata.
 
diff --git a/catalog/elevenlabs.yaml b/catalog/elevenlabs.yaml
new file mode 100644
index 00000000..99366304
--- /dev/null
+++ b/catalog/elevenlabs.yaml
@@ -0,0 +1,16 @@
+name: elevenlabs
+display_name: ElevenLabs
+description: Generate, transform, transcribe, dub, and manage AI audio, voices, music, and conversational agents with ElevenLabs.
+category: ai
+spec_url: https://api.elevenlabs.io/openapi.json
+spec_format: json
+openapi_version: "3.1"
+base_url: https://api.elevenlabs.io
+tier: official
+homepage: https://elevenlabs.io/docs
+spec_source: official
+auth_required: true
+auth_key_url: https://elevenlabs.io/app/settings/api-keys
+auth_instructions: Set ELEVENLABS_API_KEY to an ElevenLabs API key; requests send it as the xi-api-key header.
+client_pattern: rest
+http_transport: standard
diff --git a/docs/CATALOG.md b/docs/CATALOG.md
index 6e167285..304a5da3 100644
--- a/docs/CATALOG.md
+++ b/docs/CATALOG.md
@@ -96,3 +96,5 @@ Set `auth_key_url:` when the inference would land on a generic homepage and you
 Catalog entries may also declare `auth_instructions:` — a one-line string of free-form guidance ("Settings → Personal access tokens → Generate new") that the printed CLI prints under the `Get a key at:` line. Use this when the URL lands on a docs page rather than the keys UI: the URL says where to start, the instruction says what to do once there.
 
 Catalog `auth_instructions` overrides any value from the spec's [`x-auth-instructions`](SPEC-EXTENSIONS.md#x-auth-instructions) extension. The printed CLI surfaces it in auth prompts, `doctor`, and the new `auth setup` command (which also takes `--launch` to open the URL in a browser).
+
+Catalog entries may declare `base_url:` when the upstream spec intentionally omits `servers:` and the correct API origin is known. The value must be HTTPS and is used only when the parsed spec has no usable base URL.
diff --git a/internal/catalog/catalog.go b/internal/catalog/catalog.go
index 2fef309b..e48a8b27 100644
--- a/internal/catalog/catalog.go
+++ b/internal/catalog/catalog.go
@@ -115,6 +115,7 @@ type Entry struct {
 	SpecURL           string     `yaml:"spec_url"`
 	SpecFormat        string     `yaml:"spec_format"`
 	OpenAPIVersion    string     `yaml:"openapi_version"`
+	BaseURL           string     `yaml:"base_url,omitempty"`
 	Tier              string     `yaml:"tier"`
 	VerifiedDate      string     `yaml:"verified_date"`
 	Homepage          string     `yaml:"homepage"`
@@ -311,6 +312,9 @@ func (e *Entry) Validate() error {
 			return fmt.Errorf("http_transport must be one of: standard, browser-http, browser-chrome, browser-chrome-h3")
 		}
 	}
+	if e.BaseURL != "" && !strings.HasPrefix(e.BaseURL, "https://") {
+		return fmt.Errorf(`base_url must start with "https://"`)
+	}
 	if err := validateBearerRefresh(e.BearerRefresh); err != nil {
 		return err
 	}
diff --git a/internal/catalog/catalog_test.go b/internal/catalog/catalog_test.go
index bad82ef9..942e8a3d 100644
--- a/internal/catalog/catalog_test.go
+++ b/internal/catalog/catalog_test.go
@@ -18,6 +18,7 @@ category: developer-tools
 spec_url: https://example.com/openapi.yaml
 spec_format: yaml
 openapi_version: "3.0"
+base_url: https://api.example.com
 tier: community
 verified_date: "2026-03-23"
 homepage: https://example.com
@@ -40,6 +41,7 @@ notes: Example fixture.
 	assert.Equal(t, "https://example.com/openapi.yaml", entry.SpecURL)
 	assert.Equal(t, "yaml", entry.SpecFormat)
 	assert.Equal(t, "3.0", entry.OpenAPIVersion)
+	assert.Equal(t, "https://api.example.com", entry.BaseURL)
 	assert.Equal(t, "community", entry.Tier)
 	assert.Equal(t, "2026-03-23", entry.VerifiedDate)
 	assert.Equal(t, "https://example.com", entry.Homepage)
@@ -144,6 +146,13 @@ func TestValidateEntry(t *testing.T) {
 			},
 			wantErr: "http_transport must be one of",
 		},
+		{
+			name: "non https base url",
+			mutate: func(e *Entry) {
+				e.BaseURL = "http://api.example.com"
+			},
+			wantErr: `base_url must start with "https://"`,
+		},
 		{
 			name: "bearer refresh missing bundle URL",
 			mutate: func(e *Entry) {
diff --git a/internal/catalogmeta/catalogmeta.go b/internal/catalogmeta/catalogmeta.go
new file mode 100644
index 00000000..845f6cb2
--- /dev/null
+++ b/internal/catalogmeta/catalogmeta.go
@@ -0,0 +1,68 @@
+package catalogmeta
+
+import (
+	"strings"
+
+	"github.com/mvanhorn/cli-printing-press/v4/internal/catalog"
+	"github.com/mvanhorn/cli-printing-press/v4/internal/naming"
+	"github.com/mvanhorn/cli-printing-press/v4/internal/spec"
+)
+
+func RebaseAuthEnvPrefix(auth *spec.AuthConfig, oldName, newName string) {
+	if auth == nil || oldName == "" || newName == "" || oldName == newName {
+		return
+	}
+	oldPrefix := naming.EnvPrefix(oldName) + "_"
+	newPrefix := naming.EnvPrefix(newName) + "_"
+	for i, envVar := range auth.EnvVars {
+		if suffix, ok := strings.CutPrefix(envVar, oldPrefix); ok {
+			auth.EnvVars[i] = newPrefix + suffix
+		}
+	}
+	for i := range auth.EnvVarSpecs {
+		if suffix, ok := strings.CutPrefix(auth.EnvVarSpecs[i].Name, oldPrefix); ok {
+			auth.EnvVarSpecs[i].Name = newPrefix + suffix
+		}
+	}
+}
+
+func IsReplaceableBaseURL(baseURL string, placeholder bool) bool {
+	switch strings.TrimRight(strings.TrimSpace(baseURL), "/") {
+	case "", strings.TrimRight(spec.PlaceholderBaseURL, "/"), "https://api.example.com":
+		return true
+	default:
+		return placeholder
+	}
+}
+
+func ApplyRuntimeMetadata(apiSpec *spec.APISpec, entry *catalog.Entry) {
+	if apiSpec == nil || entry == nil {
+		return
+	}
+	if entry.BaseURL != "" && IsReplaceableBaseURL(apiSpec.BaseURL, apiSpec.BaseURLIsPlaceholder) {
+		apiSpec.BaseURL = strings.TrimRight(entry.BaseURL, "/")
+		apiSpec.BaseURLIsPlaceholder = false
+	}
+	if entry.DisplayName != "" {
+		apiSpec.DisplayName = entry.DisplayName
+		apiSpec.DisplayNameDerivedFromTitle = false
+	}
+	if entry.Description != "" {
+		apiSpec.CLIDescription = entry.Description
+	}
+	if entry.AuthKeyURL != "" {
+		apiSpec.Auth.KeyURL = entry.AuthKeyURL
+	}
+	if entry.AuthInstructions != "" {
+		apiSpec.Auth.Instructions = entry.AuthInstructions
+	}
+	if entry.ClientPattern != "" {
+		apiSpec.ClientPattern = entry.ClientPattern
+	}
+	if entry.HTTPTransport != "" {
+		apiSpec.HTTPTransport = entry.HTTPTransport
+	}
+	if entry.SpecSource != "" {
+		apiSpec.SpecSource = entry.SpecSource
+	}
+}
diff --git a/internal/cli/catalog.go b/internal/cli/catalog.go
index 89728df8..0175efff 100644
--- a/internal/cli/catalog.go
+++ b/internal/cli/catalog.go
@@ -117,6 +117,9 @@ func newCatalogShowCmd() *cobra.Command {
 			if entry.OpenAPIVersion != "" {
 				fmt.Printf("OpenAPI:        %s\n", entry.OpenAPIVersion)
 			}
+			if entry.BaseURL != "" {
+				fmt.Printf("Base URL:       %s\n", entry.BaseURL)
+			}
 			if entry.Homepage != "" {
 				fmt.Printf("Homepage:       %s\n", entry.Homepage)
 			}
diff --git a/internal/cli/generate_test.go b/internal/cli/generate_test.go
index a9a819ef..2bc057a4 100644
--- a/internal/cli/generate_test.go
+++ b/internal/cli/generate_test.go
@@ -11,6 +11,7 @@ import (
 	"testing"
 
 	"github.com/mvanhorn/cli-printing-press/v4/internal/catalog"
+	"github.com/mvanhorn/cli-printing-press/v4/internal/catalogmeta"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/pipeline"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/spec"
 	"github.com/stretchr/testify/assert"
@@ -1815,11 +1816,12 @@ resources:
 }
 
 func TestEnrichSpecFromCatalogCopiesGenerationMetadata(t *testing.T) {
-	apiSpec := &spec.APISpec{Name: "test-api"}
+	apiSpec := &spec.APISpec{Name: "test-api", BaseURL: spec.PlaceholderBaseURL, BaseURLIsPlaceholder: true}
 
 	enrichSpecFromCatalogEntry(apiSpec, &catalog.Entry{
 		DisplayName: "Test.API",
 		OwnerName:   "Trevin Chow",
+		BaseURL:     "https://api.example.com/",
 		MCP: spec.MCPConfig{
 			Transport:     []string{"stdio", "http"},
 			Orchestration: "code",
@@ -1829,11 +1831,29 @@ func TestEnrichSpecFromCatalogCopiesGenerationMetadata(t *testing.T) {
 
 	assert.Equal(t, "Test.API", apiSpec.DisplayName)
 	assert.Equal(t, "Trevin Chow", apiSpec.OwnerName)
+	assert.Equal(t, "https://api.example.com", apiSpec.BaseURL)
+	assert.False(t, apiSpec.BaseURLIsPlaceholder)
 	assert.Equal(t, []string{"stdio", "http"}, apiSpec.MCP.Transport)
 	assert.Equal(t, "code", apiSpec.MCP.Orchestration)
 	assert.Equal(t, "hidden", apiSpec.MCP.EndpointTools)
 }
 
+func TestRebaseAuthEnvPrefix(t *testing.T) {
+	auth := spec.AuthConfig{
+		EnvVars: []string{"ELEVENLABS_DOCUMENTATION_API_KEY", "UNCHANGED_TOKEN"},
+		EnvVarSpecs: []spec.AuthEnvVar{
+			{Name: "ELEVENLABS_DOCUMENTATION_CLIENT_ID"},
+			{Name: "CUSTOM_SECRET"},
+		},
+	}
+
+	catalogmeta.RebaseAuthEnvPrefix(&auth, "elevenlabs-documentation", "elevenlabs")
+
+	assert.Equal(t, []string{"ELEVENLABS_API_KEY", "UNCHANGED_TOKEN"}, auth.EnvVars)
+	assert.Equal(t, "ELEVENLABS_CLIENT_ID", auth.EnvVarSpecs[0].Name)
+	assert.Equal(t, "CUSTOM_SECRET", auth.EnvVarSpecs[1].Name)
+}
+
 func TestEnrichSpecFromCatalogMatchesSpecURLWhenSlugDiffers(t *testing.T) {
 	apiSpec := &spec.APISpec{
 		Name:                        "cloud-run-admin",
diff --git a/internal/cli/root.go b/internal/cli/root.go
index 9debd614..b53c52a2 100644
--- a/internal/cli/root.go
+++ b/internal/cli/root.go
@@ -19,6 +19,7 @@ import (
 	"github.com/mvanhorn/cli-printing-press/v4/internal/artifacts"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/browsersniff"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/catalog"
+	"github.com/mvanhorn/cli-printing-press/v4/internal/catalogmeta"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/docspec"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/generator"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/graphql"
@@ -294,6 +295,7 @@ func newGenerateCmd() *cobra.Command {
 					return &ExitError{Code: ExitSpecError, Err: fmt.Errorf("parsing spec %s: %w", specFile, err)}
 				}
 
+				enrichSpecFromCatalog(apiSpec, catalogSpecLookupRefs(specFiles, specURL)...)
 				if apiSpec.BaseURLIsPlaceholder {
 					return &ExitError{Code: ExitSpecError, Err: fmt.Errorf("spec %s declares no `servers:` block and no per-operation servers; the generator cannot resolve a real base URL and refuses to ship a CLI whose `doctor` would DNS-fail on every call. Add a `servers:` block with the real API host, or run via crowd-sniff with `--base-url` to supply one", specFile)}
 				}
@@ -312,6 +314,7 @@ func newGenerateCmd() *cobra.Command {
 				// cmd/<slug>-pp-cli matches what manifest/publish-validate look
 				// for. Explicit --name still wins.
 				if cliName != "" {
+					catalogmeta.RebaseAuthEnvPrefix(&apiSpec.Auth, apiSpec.Name, cliName)
 					apiSpec.Name = cliName
 				} else if researchName := pipeline.LoadAPINameFromResearchDir(researchDir); researchName != "" {
 					apiSpec.Name = researchName
@@ -1564,6 +1567,10 @@ func enrichSpecFromCatalogEntry(apiSpec *spec.APISpec, entry *catalog.Entry) {
 	if entry.Homepage != "" && apiSpec.WebsiteURL == "" {
 		apiSpec.WebsiteURL = entry.Homepage
 	}
+	if entry.BaseURL != "" && catalogmeta.IsReplaceableBaseURL(apiSpec.BaseURL, apiSpec.BaseURLIsPlaceholder) {
+		apiSpec.BaseURL = strings.TrimRight(entry.BaseURL, "/")
+		apiSpec.BaseURLIsPlaceholder = false
+	}
 	if entry.Category != "" && apiSpec.Category == "" {
 		apiSpec.Category = entry.Category
 	}
diff --git a/internal/generator/binary_paginated_promoted_test.go b/internal/generator/binary_paginated_promoted_test.go
new file mode 100644
index 00000000..1028e4d1
--- /dev/null
+++ b/internal/generator/binary_paginated_promoted_test.go
@@ -0,0 +1,101 @@
+package generator
+
+import (
+	"path/filepath"
+	"testing"
+
+	"github.com/mvanhorn/cli-printing-press/v4/internal/naming"
+	"github.com/mvanhorn/cli-printing-press/v4/internal/spec"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+// Regression: promoted endpoints that combine UsesBinaryResponse with
+// either pagination or store-backed read previously called paginatedGet /
+// resolvePaginatedRead / resolveRead with nil headers. The live API call
+// then went out without the BinaryResponseHeader sentinel, so the client
+// ran sanitizeJSONResponse on raw binary bytes (corrupting audio) and
+// advertised Accept: application/json. The fix hoists headerOverrides
+// when UsesBinaryResponse is set and threads it into every helper call.
+func TestGenerateBinaryPaginatedPromotedThreadsHeader(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("audioapi")
+	apiSpec.Resources = map[string]spec.Resource{
+		"voices": {
+			Description: "Voices",
+			Endpoints: map[string]spec.Endpoint{
+				"list": {
+					Method:         "GET",
+					Path:           "/voices",
+					Description:    "List voices",
+					ResponseFormat: spec.ResponseFormatBinary,
+					Pagination: &spec.Pagination{
+						Type:           "cursor",
+						LimitParam:     "limit",
+						CursorParam:    "after",
+						NextCursorPath: "next_cursor",
+						HasMoreField:   "has_more",
+					},
+					Params: []spec.Param{
+						{Name: "limit", Type: "integer", Description: "Page size"},
+						{Name: "after", Type: "string", Description: "Cursor"},
+					},
+				},
+			},
+		},
+	}
+
+	outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+	gen := New(apiSpec, outputDir)
+	// Force no-store so the paginatedGet branch is exercised; resolvePaginatedRead
+	// would be wired the same way (both accept the headers map). Export keeps
+	// VisionSet.IsZero() false so the generator doesn't recompute from profile.
+	gen.VisionSet = VisionTemplateSet{Export: true}
+	require.NoError(t, gen.Generate())
+
+	endpointSrc := readGeneratedFile(t, outputDir, "internal", "cli", "promoted_voices.go")
+	assert.Contains(t, endpointSrc, `headerOverrides := map[string]string{"X-Printing-Press-Binary-Response": "true"}`,
+		"binary paginated promoted must declare headerOverrides")
+	assert.Contains(t, endpointSrc, `paginatedGet(c, path, map[string]string{`,
+		"non-HasStore pagination must use paginatedGet")
+	assert.NotContains(t, endpointSrc, `}, nil, flagAll,`,
+		"paginated binary endpoint must pass headerOverrides, not nil")
+	assert.Contains(t, endpointSrc, `}, headerOverrides, flagAll,`,
+		"paginated binary endpoint must thread headerOverrides into paginatedGet")
+}
+
+// Regression: store-backed binary GET previously passed nil to resolveRead,
+// so the live API call dispatched without the binary sentinel header.
+func TestGenerateBinaryStoreBackedPromotedThreadsHeader(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("audioapi")
+	apiSpec.Resources = map[string]spec.Resource{
+		"voices": {
+			Description: "Voices",
+			Endpoints: map[string]spec.Endpoint{
+				"get": {
+					Method:         "GET",
+					Path:           "/voices/{voice_id}",
+					Description:    "Get voice sample",
+					ResponseFormat: spec.ResponseFormatBinary,
+					Params: []spec.Param{
+						{Name: "voice_id", Type: "string", Required: true, Positional: true, PathParam: true, Description: "Voice ID"},
+					},
+				},
+			},
+		},
+	}
+
+	outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+	gen := New(apiSpec, outputDir)
+	gen.VisionSet = VisionTemplateSet{Store: true}
+	require.NoError(t, gen.Generate())
+
+	endpointSrc := readGeneratedFile(t, outputDir, "internal", "cli", "promoted_voices.go")
+	assert.Contains(t, endpointSrc, `headerOverrides := map[string]string{"X-Printing-Press-Binary-Response": "true"}`,
+		"store-backed binary GET must declare headerOverrides")
+	assert.Contains(t, endpointSrc, `resolveRead(cmd.Context(), c, flags, "voices", false, path, params, headerOverrides)`,
+		"store-backed binary GET must thread headerOverrides through resolveRead")
+}
diff --git a/internal/generator/endpoint_is_write_test.go b/internal/generator/endpoint_is_write_test.go
index baa6af9d..18bc118a 100644
--- a/internal/generator/endpoint_is_write_test.go
+++ b/internal/generator/endpoint_is_write_test.go
@@ -249,7 +249,7 @@ func TestHasWriteCommands_PostAsQueryFlipsHasWriteFalse(t *testing.T) {
 
 // TestPromotedCommandVerbBranching covers the integration path: the
 // rendered promoted command emits the same HTTP verb the spec declared,
-// so a POST-only endpoint hits c.Post and a GET-only endpoint stays on
+// so a POST-only endpoint hits c.PostWithParams and a GET-only endpoint stays on
 // c.Get/resolveRead.
 func TestPromotedCommandVerbBranching(t *testing.T) {
 	cases := []struct {
@@ -262,7 +262,7 @@ func TestPromotedCommandVerbBranching(t *testing.T) {
 		mustNotHave  []string
 	}{
 		{
-			name:         "POST endpoint emits c.Post",
+			name:         "POST endpoint emits c.PostWithParams",
 			apiName:      "post-promoted",
 			resourceName: "queries",
 			endpointName: "searchAll",
@@ -272,7 +272,7 @@ func TestPromotedCommandVerbBranching(t *testing.T) {
 				Description: "Search collections by free text",
 				Body:        []spec.Param{{Name: "queryText", Type: "string"}},
 			},
-			mustContain: []string{"c.Post("},
+			mustContain: []string{"c.PostWithParams("},
 			mustNotHave: []string{"c.Get(path, params)"},
 		},
 		{
@@ -561,8 +561,8 @@ func TestPromotedCommandPlumbsBodyFields(t *testing.T) {
 		"promoted command must build a body map from body flags")
 	require.Contains(t, src, `body["name"] = bodyName`,
 		"body map must use the spec-declared field name, not the camelCased flag var")
-	require.Contains(t, src, `c.Post(path, body)`,
-		"promoted command must pass the body map to c.Post, not the params map")
+	require.Contains(t, src, `c.PostWithParams(path, params, body)`,
+		"promoted command must pass the body map to c.PostWithParams, not the params map")
 	require.NotContains(t, src, `c.Post(path, params)`,
 		"promoted command must NOT pass params (URL/path params) as the request body — that was the bug")
 }
diff --git a/internal/generator/form_test.go b/internal/generator/form_test.go
index 6c8940f5..1ce727c4 100644
--- a/internal/generator/form_test.go
+++ b/internal/generator/form_test.go
@@ -72,7 +72,7 @@ func TestGenerateFormRequestBodyUsesFormClient(t *testing.T) {
 	assert.Contains(t, endpointSrc, `fields := url.Values{}`)
 	assert.Contains(t, endpointSrc, `fields.Set("grant_type", bodyGrantType)`)
 	assert.Contains(t, endpointSrc, `fields.Set("client_id", bodyClientId)`)
-	assert.Contains(t, endpointSrc, `c.PostForm(path, fields)`)
+	assert.Contains(t, endpointSrc, `c.PostFormWithParams(path, params, fields)`)
 	assert.NotContains(t, endpointSrc, `var stdinBody bool`)
 	assert.NotContains(t, endpointSrc, `c.Post(path, body)`)
 	// Required-flag check should fire at top-level, not inside `if !stdinBody`.
@@ -84,7 +84,7 @@ func TestGenerateFormRequestBodyUsesFormClient(t *testing.T) {
 	venuesSrc := readGeneratedFile(t, outputDir, "internal", "cli", "promoted_venues.go")
 	assert.Contains(t, venuesSrc, `if !json.Valid([]byte(bodyStructData))`)
 	assert.Contains(t, venuesSrc, `fields.Set("struct_data", bodyStructData)`)
-	assert.Contains(t, venuesSrc, `c.PostForm(path, fields)`)
+	assert.Contains(t, venuesSrc, `c.PostFormWithParams(path, params, fields)`)
 
 	mcpSrc := readGeneratedFile(t, outputDir, "internal", "mcp", "tools.go")
 	assert.Contains(t, mcpSrc, `RequestContentType: "application/x-www-form-urlencoded"`)
diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index 7019a313..96bc93be 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -8268,7 +8268,7 @@ func TestGenerateOperationRoutingPathParamDefault(t *testing.T) {
 	mcpGo, err := os.ReadFile(filepath.Join(outputDir, "internal", "mcp", "tools.go"))
 	require.NoError(t, err)
 	assert.Regexp(t,
-		regexp.MustCompile(`makeAPIHandler\("GET",\s*"/graphql/\{pathQueryId\}/Followers",\s*\[]mcpParamBinding\{.*WireName: "pathQueryId".*\},\s*\[]string\{[^}]*"pathQueryId"`),
+		regexp.MustCompile(`makeAPIHandler\("GET",\s*"/graphql/\{pathQueryId\}/Followers",\s*false,\s*\[]mcpParamBinding\{.*WireName: "pathQueryId".*\},\s*\[]string\{[^}]*"pathQueryId"`),
 		string(mcpGo),
 		"MCP handler must receive the routing path param so it can substitute the URL")
 }
@@ -9141,9 +9141,9 @@ func TestGenerateResourceBaseURLOverrideRoutesToOverrideHost(t *testing.T) {
 	mcpTools, err := os.ReadFile(filepath.Join(outputDir, "internal", "mcp", "tools.go"))
 	require.NoError(t, err)
 	mcpToolsStr := string(mcpTools)
-	assert.Contains(t, mcpToolsStr, `makeAPIHandler("GET", "https://geocoding-api.example.com/v1/search"`,
+	assert.Contains(t, mcpToolsStr, `makeAPIHandler("GET", "https://geocoding-api.example.com/v1/search", false`,
 		"geocoding MCP endpoint mirror must emit the absolute override URL")
-	assert.Contains(t, mcpToolsStr, `makeAPIHandler("GET", "/forecast"`,
+	assert.Contains(t, mcpToolsStr, `makeAPIHandler("GET", "/forecast", false`,
 		"forecast MCP endpoint mirror must keep the relative path when its resource has no override")
 
 	// Must compile.
@@ -9231,7 +9231,7 @@ func TestGenerateEndpointBaseURLOverrideRoutesToOverrideHost(t *testing.T) {
 
 	mcpTools, err := os.ReadFile(filepath.Join(outputDir, "internal", "mcp", "tools.go"))
 	require.NoError(t, err)
-	assert.Contains(t, string(mcpTools), `makeAPIHandler("GET", "https://geocoding-api.example.com/v1/search"`,
+	assert.Contains(t, string(mcpTools), `makeAPIHandler("GET", "https://geocoding-api.example.com/v1/search", false`,
 		"typed MCP endpoint tool must use the endpoint override URL")
 }
 
@@ -9694,11 +9694,11 @@ func TestGenerateMCPHandlerPreservesQueryPositionals(t *testing.T) {
 	// Call sites still pass both names — the upstream emit is unchanged;
 	// the fix lives entirely inside the handler body.
 	assert.Regexp(t,
-		regexp.MustCompile(`makeAPIHandler\("GET",\s*"/search/movie",\s*\[]mcpParamBinding\{.*WireName: "query".*\},\s*\[]string\{[^}]*"query"`),
+		regexp.MustCompile(`makeAPIHandler\("GET",\s*"/search/movie",\s*false,\s*\[]mcpParamBinding\{.*WireName: "query".*\},\s*\[]string\{[^}]*"query"`),
 		tools,
 		"search call site must still pass `query` in positionalParams (handler decides path vs query at runtime)")
 	assert.Regexp(t,
-		regexp.MustCompile(`makeAPIHandler\("GET",\s*"/movie/\{movieId\}",\s*\[]mcpParamBinding\{.*WireName: "movieId".*\},\s*\[]string\{[^}]*"movieId"`),
+		regexp.MustCompile(`makeAPIHandler\("GET",\s*"/movie/\{movieId\}",\s*false,\s*\[]mcpParamBinding\{.*WireName: "movieId".*\},\s*\[]string\{[^}]*"movieId"`),
 		tools,
 		"get-by-id call site must pass `movieId` in positionalParams")
 
diff --git a/internal/generator/multipart_test.go b/internal/generator/multipart_test.go
index aba8d2e5..833d1cf9 100644
--- a/internal/generator/multipart_test.go
+++ b/internal/generator/multipart_test.go
@@ -67,18 +67,18 @@ func TestGenerateMultipartRequestBodyUsesMultipartClient(t *testing.T) {
 	assert.Contains(t, endpointSrc, `fileFields["assetData"] = bodyAssetData`)
 	assert.Contains(t, endpointSrc, `fields["filename"] = bodyFilename`)
 	assert.Contains(t, endpointSrc, `fields["metadata"] = bodyMetadata`)
-	assert.Contains(t, endpointSrc, `c.PostMultipart(path, fields, fileFields)`)
+	assert.Contains(t, endpointSrc, `c.PostMultipartWithParams(path, params, fields, fileFields)`)
 	assert.NotContains(t, endpointSrc, `"stdin"`)
 
 	promotedSrc := readGeneratedFile(t, outputDir, "internal", "cli", "promoted_avatars.go")
 	assert.Contains(t, promotedSrc, `return fmt.Errorf("required flag \"%s\" not set", "image")`)
 	assert.Contains(t, promotedSrc, `fileFields["image"] = bodyImage`)
 	assert.Contains(t, promotedSrc, `fields["label"] = bodyLabel`)
-	assert.Contains(t, promotedSrc, `c.PostMultipart(path, fields, fileFields)`)
+	assert.Contains(t, promotedSrc, `c.PostMultipartWithParams(path, params, fields, fileFields)`)
 	assert.NotContains(t, promotedSrc, `"stdin"`)
 
 	mcpSrc := readGeneratedFile(t, outputDir, "internal", "mcp", "tools.go")
-	assert.Contains(t, mcpSrc, `makeAPIHandler("POST", "/assets", []mcpParamBinding`)
+	assert.Contains(t, mcpSrc, `makeAPIHandler("POST", "/assets", false, []mcpParamBinding`)
 	assert.Contains(t, mcpSrc, `Format: "binary"`)
 	assert.Contains(t, mcpSrc, `RequestContentType: "multipart/form-data"`)
 	assert.Contains(t, mcpSrc, `multipartFileFields[binding.WireName] = fmt.Sprintf("%v", v)`)
@@ -87,3 +87,32 @@ func TestGenerateMultipartRequestBodyUsesMultipartClient(t *testing.T) {
 	runGoCommand(t, outputDir, "mod", "tidy")
 	runGoCommand(t, outputDir, "build", "./...")
 }
+
+func TestGenerateBinaryResponseWritesRawBytes(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("audioapi")
+	apiSpec.Resources = map[string]spec.Resource{
+		"audio": {
+			Description: "Audio",
+			Endpoints: map[string]spec.Endpoint{
+				"create": {
+					Method:         "POST",
+					Path:           "/audio",
+					Description:    "Create audio",
+					ResponseFormat: spec.ResponseFormatBinary,
+					Body: []spec.Param{
+						{Name: "text", Type: "string", Required: true, Description: "Text"},
+					},
+				},
+			},
+		},
+	}
+
+	outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+	require.NoError(t, New(apiSpec, outputDir).Generate())
+
+	endpointSrc := readGeneratedFile(t, outputDir, "internal", "cli", "promoted_audio.go")
+	assert.Contains(t, endpointSrc, `binary response cannot be rendered as structured output`)
+	assert.Contains(t, endpointSrc, `cmd.OutOrStdout().Write(data)`)
+}
diff --git a/internal/generator/mutating_query_params_test.go b/internal/generator/mutating_query_params_test.go
new file mode 100644
index 00000000..09746868
--- /dev/null
+++ b/internal/generator/mutating_query_params_test.go
@@ -0,0 +1,56 @@
+package generator
+
+import (
+	"path/filepath"
+	"testing"
+
+	"github.com/mvanhorn/cli-printing-press/v4/internal/naming"
+	"github.com/mvanhorn/cli-printing-press/v4/internal/spec"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestGenerateMutatingEndpointPassesQueryParams(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("audioapi")
+	apiSpec.Resources = map[string]spec.Resource{
+		"text-to-speech": {
+			Description: "Text to speech",
+			Endpoints: map[string]spec.Endpoint{
+				"create": {
+					Method:         "POST",
+					Path:           "/text-to-speech/{voice_id}",
+					Description:    "Create speech",
+					ResponseFormat: spec.ResponseFormatBinary,
+					Params: []spec.Param{
+						{Name: "voice_id", Type: "string", Required: true, Positional: true, PathParam: true, Description: "Voice ID"},
+						{Name: "output_format", Type: "string", Description: "Output format"},
+					},
+					Body: []spec.Param{
+						{Name: "text", Type: "string", Required: true, Description: "Text"},
+					},
+				},
+			},
+		},
+	}
+
+	outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+	require.NoError(t, New(apiSpec, outputDir).Generate())
+
+	clientSrc := readGeneratedFile(t, outputDir, "internal", "client", "client.go")
+	assert.Contains(t, clientSrc, `func (c *Client) PostWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error)`)
+
+	endpointSrc := readGeneratedFile(t, outputDir, "internal", "cli", "promoted_text-to-speech.go")
+	assert.Contains(t, endpointSrc, `params := map[string]string{}`)
+	assert.Contains(t, endpointSrc, `params["output_format"] = fmt.Sprintf("%v", flagOutputFormat)`)
+	assert.Contains(t, endpointSrc, `c.PostWithParamsAndHeaders(path, params, body, headerOverrides)`)
+
+	mcpSrc := readGeneratedFile(t, outputDir, "internal", "mcp", "tools.go")
+	assert.Contains(t, mcpSrc, `PublicName: "output_format", WireName: "output_format", Location: "query"`)
+	assert.Contains(t, mcpSrc, `data, _, err = c.PostWithParamsAndHeaders(path, params, bodyArgs, headers)`)
+	assert.Contains(t, mcpSrc, `"content_encoding": "base64"`)
+
+	runGoCommand(t, outputDir, "mod", "tidy")
+	runGoCommand(t, outputDir, "build", "./...")
+}
diff --git a/internal/generator/promoted_poststore_test.go b/internal/generator/promoted_poststore_test.go
index 9b5a463e..a93e9104 100644
--- a/internal/generator/promoted_poststore_test.go
+++ b/internal/generator/promoted_poststore_test.go
@@ -51,7 +51,7 @@ func TestPromotedHasStorePostRoutesThroughVerbBranch(t *testing.T) {
 
 	assert.NotContains(t, got, "resolveRead(",
 		"HasStore + POST must NOT route through resolveRead (GET-only internally)")
-	assert.Contains(t, got, "c.Post(path, body)",
+	assert.Contains(t, got, "c.PostWithParams(path, params, body)",
 		"HasStore + POST must route through the verb branch with a built body")
 	assert.Contains(t, got, `attachFreshness(DataProvenance{Source: "live"}, flags)`,
 		"non-GET HasStore commands must synthesize a live-call prov so the downstream HasStore block compiles")
@@ -97,7 +97,7 @@ func TestPromotedHasStoreGetStillUsesResolveRead(t *testing.T) {
 // other tests don't reach. The provenance synthesis lives in a single
 // post-chain block, so a regression that drops it from one verb shape
 // drops it from all of them — but DELETE has its own pre-existing
-// `data, _, err := c.Delete(path)` call site, so an explicit assertion
+// `data, _, err := c.DeleteWithParams(path, params)` call site, so an explicit assertion
 // guards against template refactors that re-introduce the dup.
 func TestPromotedHasStoreDeleteSynthesizesProv(t *testing.T) {
 	t.Parallel()
@@ -128,8 +128,8 @@ func TestPromotedHasStoreDeleteSynthesizesProv(t *testing.T) {
 
 	assert.NotContains(t, got, "resolveRead(",
 		"HasStore + DELETE must NOT route through resolveRead")
-	assert.Contains(t, got, "c.Delete(path)",
-		"HasStore + DELETE must route through c.Delete")
+	assert.Contains(t, got, "c.DeleteWithParams(path, params)",
+		"HasStore + DELETE must route through c.DeleteWithParams")
 	assert.Contains(t, got, `attachFreshness(DataProvenance{Source: "live"}, flags)`,
 		"HasStore + DELETE must synthesize a live-call prov for the downstream provenance block")
 }
diff --git a/internal/generator/templates/client.go.tmpl b/internal/generator/templates/client.go.tmpl
index 8fd8aa95..f1ca56ff 100644
--- a/internal/generator/templates/client.go.tmpl
+++ b/internal/generator/templates/client.go.tmpl
@@ -38,6 +38,8 @@ import (
 	"{{modulePath}}/internal/config"
 )
 
+const BinaryResponseHeader = "X-Printing-Press-Binary-Response"
+
 type Client struct {
 	BaseURL    string
 {{- if .BasePath}}
@@ -505,14 +507,14 @@ func (c *Client) Post(path string, body any) (json.RawMessage, int, error) {
 	return c.do("POST", path, nil, body, nil)
 }
 
-func (c *Client) PostWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("POST", path, nil, body, headers)
-}
-
 func (c *Client) PostWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, body, nil)
 }
 
+func (c *Client) PostWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("POST", path, nil, body, headers)
+}
+
 func (c *Client) PostWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, body, headers)
 }
@@ -522,14 +524,14 @@ func (c *Client) PostMultipart(path string, fields map[string]string, fileFields
 	return c.do("POST", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
-func (c *Client) PostMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("POST", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
-}
-
 func (c *Client) PostMultipartWithParams(path string, params map[string]string, fields map[string]string, fileFields map[string]string) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
+func (c *Client) PostMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("POST", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
+}
+
 func (c *Client) PostMultipartWithParamsAndHeaders(path string, params map[string]string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
 }
@@ -540,14 +542,14 @@ func (c *Client) PostForm(path string, fields url.Values) (json.RawMessage, int,
 	return c.do("POST", path, nil, formRequestBody{Fields: fields}, nil)
 }
 
-func (c *Client) PostFormWithHeaders(path string, fields url.Values, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("POST", path, nil, formRequestBody{Fields: fields}, headers)
-}
-
 func (c *Client) PostFormWithParams(path string, params map[string]string, fields url.Values) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, formRequestBody{Fields: fields}, nil)
 }
 
+func (c *Client) PostFormWithHeaders(path string, fields url.Values, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("POST", path, nil, formRequestBody{Fields: fields}, headers)
+}
+
 func (c *Client) PostFormWithParamsAndHeaders(path string, params map[string]string, fields url.Values, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, formRequestBody{Fields: fields}, headers)
 }
@@ -558,14 +560,14 @@ func (c *Client) Delete(path string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, nil, nil, nil)
 }
 
-func (c *Client) DeleteWithHeaders(path string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("DELETE", path, nil, nil, headers)
-}
-
 func (c *Client) DeleteWithParams(path string, params map[string]string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, params, nil, nil)
 }
 
+func (c *Client) DeleteWithHeaders(path string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("DELETE", path, nil, nil, headers)
+}
+
 func (c *Client) DeleteWithParamsAndHeaders(path string, params map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, params, nil, headers)
 }
@@ -574,14 +576,14 @@ func (c *Client) Put(path string, body any) (json.RawMessage, int, error) {
 	return c.do("PUT", path, nil, body, nil)
 }
 
-func (c *Client) PutWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PUT", path, nil, body, headers)
-}
-
 func (c *Client) PutWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, body, nil)
 }
 
+func (c *Client) PutWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PUT", path, nil, body, headers)
+}
+
 func (c *Client) PutWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, body, headers)
 }
@@ -591,14 +593,14 @@ func (c *Client) PutMultipart(path string, fields map[string]string, fileFields
 	return c.do("PUT", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
-func (c *Client) PutMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PUT", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
-}
-
 func (c *Client) PutMultipartWithParams(path string, params map[string]string, fields map[string]string, fileFields map[string]string) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
+func (c *Client) PutMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PUT", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
+}
+
 func (c *Client) PutMultipartWithParamsAndHeaders(path string, params map[string]string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
 }
@@ -609,14 +611,14 @@ func (c *Client) PutForm(path string, fields url.Values) (json.RawMessage, int,
 	return c.do("PUT", path, nil, formRequestBody{Fields: fields}, nil)
 }
 
-func (c *Client) PutFormWithHeaders(path string, fields url.Values, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PUT", path, nil, formRequestBody{Fields: fields}, headers)
-}
-
 func (c *Client) PutFormWithParams(path string, params map[string]string, fields url.Values) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, formRequestBody{Fields: fields}, nil)
 }
 
+func (c *Client) PutFormWithHeaders(path string, fields url.Values, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PUT", path, nil, formRequestBody{Fields: fields}, headers)
+}
+
 func (c *Client) PutFormWithParamsAndHeaders(path string, params map[string]string, fields url.Values, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, formRequestBody{Fields: fields}, headers)
 }
@@ -627,14 +629,14 @@ func (c *Client) Patch(path string, body any) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, nil, body, nil)
 }
 
-func (c *Client) PatchWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PATCH", path, nil, body, headers)
-}
-
 func (c *Client) PatchWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, body, nil)
 }
 
+func (c *Client) PatchWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PATCH", path, nil, body, headers)
+}
+
 func (c *Client) PatchWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, body, headers)
 }
@@ -644,14 +646,14 @@ func (c *Client) PatchMultipart(path string, fields map[string]string, fileField
 	return c.do("PATCH", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
-func (c *Client) PatchMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PATCH", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
-}
-
 func (c *Client) PatchMultipartWithParams(path string, params map[string]string, fields map[string]string, fileFields map[string]string) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
+func (c *Client) PatchMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PATCH", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
+}
+
 func (c *Client) PatchMultipartWithParamsAndHeaders(path string, params map[string]string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
 }
@@ -662,14 +664,14 @@ func (c *Client) PatchForm(path string, fields url.Values) (json.RawMessage, int
 	return c.do("PATCH", path, nil, formRequestBody{Fields: fields}, nil)
 }
 
-func (c *Client) PatchFormWithHeaders(path string, fields url.Values, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PATCH", path, nil, formRequestBody{Fields: fields}, headers)
-}
-
 func (c *Client) PatchFormWithParams(path string, params map[string]string, fields url.Values) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, formRequestBody{Fields: fields}, nil)
 }
 
+func (c *Client) PatchFormWithHeaders(path string, fields url.Values, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PATCH", path, nil, formRequestBody{Fields: fields}, headers)
+}
+
 func (c *Client) PatchFormWithParamsAndHeaders(path string, params map[string]string, fields url.Values, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, formRequestBody{Fields: fields}, headers)
 }
@@ -992,6 +994,10 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		for k, v := range headerOverrides {
 			req.Header.Set(k, v)
 		}
+		binaryResponse := strings.EqualFold(req.Header.Get(BinaryResponseHeader), "true")
+		if binaryResponse {
+			req.Header.Del(BinaryResponseHeader)
+		}
 {{- if not .UsesBrowserManagedUserAgent}}
 		if req.Header.Get("User-Agent") == "" {
 {{- if .UsesBrowserLikeUserAgent}}
@@ -1020,7 +1026,11 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		// per-endpoint headerOverrides, both of which run before this
 		// if-empty default.
 		if req.Header.Get("Accept") == "" {
-			req.Header.Set("Accept", "application/json")
+			if binaryResponse {
+				req.Header.Set("Accept", "*/*")
+			} else {
+				req.Header.Set("Accept", "application/json")
+			}
 		}
 {{- end}}
 
@@ -1035,7 +1045,9 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		if err != nil {
 			return nil, 0, fmt.Errorf("reading response: %w", err)
 		}
-		respBody = sanitizeJSONResponse(respBody)
+		if !binaryResponse {
+			respBody = sanitizeJSONResponse(respBody)
+		}
 
 		// Success
 		if resp.StatusCode < 400 {
diff --git a/internal/generator/templates/command_endpoint.go.tmpl b/internal/generator/templates/command_endpoint.go.tmpl
index 65375db7..ab24938b 100644
--- a/internal/generator/templates/command_endpoint.go.tmpl
+++ b/internal/generator/templates/command_endpoint.go.tmpl
@@ -158,10 +158,13 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 {{- end}}
 {{- end}}
 
-{{- if .Endpoint.HeaderOverrides}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			headerOverrides := map[string]string{
 {{- range .Endpoint.HeaderOverrides}}
 				"{{.Name}}": "{{.Value}}",
+{{- end}}
+{{- if .Endpoint.UsesBinaryResponse}}
+				"X-Printing-Press-Binary-Response": "true",
 {{- end}}
 			}
 {{- end}}
@@ -218,7 +221,7 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 				"{{paramWireName .}}": fmt.Sprintf("%v", flag{{camel (paramIdent .)}}),
 {{- end}}
 {{- end}}
-			}, {{if .Endpoint.HeaderOverrides}}headerOverrides{{else}}nil{{end}}, flagAll, "{{.Endpoint.Pagination.CursorParam}}", "{{.Endpoint.Pagination.NextCursorPath}}", "{{.Endpoint.Pagination.HasMoreField}}")
+			}, {{if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}headerOverrides{{else}}nil{{end}}, flagAll, "{{.Endpoint.Pagination.CursorParam}}", "{{.Endpoint.Pagination.NextCursorPath}}", "{{.Endpoint.Pagination.HasMoreField}}")
 {{- else}}
 			data, err := paginatedGet(c, path, map[string]string{
 {{- range .Endpoint.Params}}
@@ -229,7 +232,7 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 				"{{paramWireName .}}": fmt.Sprintf("%v", flag{{camel (paramIdent .)}}),
 {{- end}}
 {{- end}}
-			}, {{if .Endpoint.HeaderOverrides}}headerOverrides{{else}}nil{{end}}, flagAll, "{{.Endpoint.Pagination.CursorParam}}", "{{.Endpoint.Pagination.NextCursorPath}}", "{{.Endpoint.Pagination.HasMoreField}}")
+			}, {{if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}headerOverrides{{else}}nil{{end}}, flagAll, "{{.Endpoint.Pagination.CursorParam}}", "{{.Endpoint.Pagination.NextCursorPath}}", "{{.Endpoint.Pagination.HasMoreField}}")
 {{- end}}
 {{- else}}
 			params := map[string]string{}
@@ -244,9 +247,9 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 {{- end}}
 {{- end}}
 {{- if .HasStore}}
-			data, prov, err := resolveRead(cmd.Context(), c, flags, "{{lower .ResourceName}}", {{if .Endpoint.Pagination}}true{{else}}false{{end}}, path, params, {{if .Endpoint.HeaderOverrides}}headerOverrides{{else}}nil{{end}})
+			data, prov, err := resolveRead(cmd.Context(), c, flags, "{{lower .ResourceName}}", {{if .Endpoint.Pagination}}true{{else}}false{{end}}, path, params, {{if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}headerOverrides{{else}}nil{{end}})
 {{- else}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, err := c.GetWithHeaders(path, params, headerOverrides)
 {{- else}}
 			data, err := c.Get(path, params)
@@ -254,11 +257,6 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 {{- end}}
 {{- end}}
 {{- else if eq .Endpoint.Method "POST"}}
-{{- if $isMultipart}}
-			fields := map[string]string{}
-			fileFields := map[string]string{}
-{{multipartBodyMaps .Endpoint.Body "\t\t\t"}}
-{{- if endpointHasQueryFlags .Endpoint}}
 			params := map[string]string{}
 {{- range .Endpoint.Params}}
 {{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
@@ -270,45 +268,23 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 			}
 {{- end}}
 {{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if $isMultipart}}
+			fields := map[string]string{}
+			fileFields := map[string]string{}
+{{multipartBodyMaps .Endpoint.Body "\t\t\t"}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.PostMultipartWithParamsAndHeaders(path, params, fields, fileFields, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.PostMultipartWithParams(path, params, fields, fileFields)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.PostMultipartWithHeaders(path, fields, fileFields, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.PostMultipart(path, fields, fileFields)
-{{- end}}
-{{- end}}
 {{- else if $isForm}}
 			fields := url.Values{}
 {{formBodyMaps .Endpoint.Body "\t\t\t"}}
-{{- if endpointHasQueryFlags .Endpoint}}
-			params := map[string]string{}
-{{- range .Endpoint.Params}}
-{{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
-			params["{{.Name}}"] = args[{{positionalIndex $.Endpoint .Name}}]
-{{- end}}
-{{- if and (not .Positional) (not .PathParam)}}
-			if flag{{camel (paramIdent .)}} != {{zeroValForParam .Name .Type}} {
-				params["{{.Name}}"] = fmt.Sprintf("%v", flag{{camel (paramIdent .)}})
-			}
-{{- end}}
-{{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.PostFormWithParamsAndHeaders(path, params, fields, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.PostFormWithParams(path, params, fields)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.PostFormWithHeaders(path, fields, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.PostForm(path, fields)
-{{- end}}
-{{- end}}
 {{- else}}
 			var body map[string]any
 			if stdinBody {
@@ -324,33 +300,13 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 			} else {
 				body = map[string]any{}
 {{bodyMapForEndpoint .Endpoint "\t\t\t\t"}}			}
-{{- if endpointHasQueryFlags .Endpoint}}
-			params := map[string]string{}
-{{- range .Endpoint.Params}}
-{{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
-			params["{{.Name}}"] = args[{{positionalIndex $.Endpoint .Name}}]
-{{- end}}
-{{- if and (not .Positional) (not .PathParam)}}
-			if flag{{camel (paramIdent .)}} != {{zeroValForParam .Name .Type}} {
-				params["{{.Name}}"] = fmt.Sprintf("%v", flag{{camel (paramIdent .)}})
-			}
-{{- end}}
-{{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.PostWithParamsAndHeaders(path, params, body, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.PostWithParams(path, params, body)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.PostWithHeaders(path, body, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.Post(path, body)
-{{- end}}
-{{- end}}
 {{- end}}
 {{- else if eq .Endpoint.Method "DELETE"}}
-{{- if endpointHasQueryFlags .Endpoint}}
 			params := map[string]string{}
 {{- range .Endpoint.Params}}
 {{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
@@ -362,24 +318,12 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 			}
 {{- end}}
 {{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.DeleteWithParamsAndHeaders(path, params, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.DeleteWithParams(path, params)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.DeleteWithHeaders(path, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.Delete(path)
-{{- end}}
-{{- end}}
 {{- else if eq .Endpoint.Method "PUT"}}
-{{- if $isMultipart}}
-			fields := map[string]string{}
-			fileFields := map[string]string{}
-{{multipartBodyMaps .Endpoint.Body "\t\t\t"}}
-{{- if endpointHasQueryFlags .Endpoint}}
 			params := map[string]string{}
 {{- range .Endpoint.Params}}
 {{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
@@ -391,45 +335,23 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 			}
 {{- end}}
 {{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if $isMultipart}}
+			fields := map[string]string{}
+			fileFields := map[string]string{}
+{{multipartBodyMaps .Endpoint.Body "\t\t\t"}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.PutMultipartWithParamsAndHeaders(path, params, fields, fileFields, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.PutMultipartWithParams(path, params, fields, fileFields)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.PutMultipartWithHeaders(path, fields, fileFields, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.PutMultipart(path, fields, fileFields)
-{{- end}}
-{{- end}}
 {{- else if $isForm}}
 			fields := url.Values{}
 {{formBodyMaps .Endpoint.Body "\t\t\t"}}
-{{- if endpointHasQueryFlags .Endpoint}}
-			params := map[string]string{}
-{{- range .Endpoint.Params}}
-{{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
-			params["{{.Name}}"] = args[{{positionalIndex $.Endpoint .Name}}]
-{{- end}}
-{{- if and (not .Positional) (not .PathParam)}}
-			if flag{{camel (paramIdent .)}} != {{zeroValForParam .Name .Type}} {
-				params["{{.Name}}"] = fmt.Sprintf("%v", flag{{camel (paramIdent .)}})
-			}
-{{- end}}
-{{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.PutFormWithParamsAndHeaders(path, params, fields, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.PutFormWithParams(path, params, fields)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.PutFormWithHeaders(path, fields, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.PutForm(path, fields)
-{{- end}}
-{{- end}}
 {{- else}}
 			var body map[string]any
 			if stdinBody {
@@ -445,37 +367,13 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 			} else {
 				body = map[string]any{}
 {{bodyMapForEndpoint .Endpoint "\t\t\t\t"}}			}
-{{- if endpointHasQueryFlags .Endpoint}}
-			params := map[string]string{}
-{{- range .Endpoint.Params}}
-{{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
-			params["{{.Name}}"] = args[{{positionalIndex $.Endpoint .Name}}]
-{{- end}}
-{{- if and (not .Positional) (not .PathParam)}}
-			if flag{{camel (paramIdent .)}} != {{zeroValForParam .Name .Type}} {
-				params["{{.Name}}"] = fmt.Sprintf("%v", flag{{camel (paramIdent .)}})
-			}
-{{- end}}
-{{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.PutWithParamsAndHeaders(path, params, body, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.PutWithParams(path, params, body)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.PutWithHeaders(path, body, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.Put(path, body)
-{{- end}}
-{{- end}}
 {{- end}}
 {{- else if eq .Endpoint.Method "PATCH"}}
-{{- if $isMultipart}}
-			fields := map[string]string{}
-			fileFields := map[string]string{}
-{{multipartBodyMaps .Endpoint.Body "\t\t\t"}}
-{{- if endpointHasQueryFlags .Endpoint}}
 			params := map[string]string{}
 {{- range .Endpoint.Params}}
 {{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
@@ -487,45 +385,23 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 			}
 {{- end}}
 {{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if $isMultipart}}
+			fields := map[string]string{}
+			fileFields := map[string]string{}
+{{multipartBodyMaps .Endpoint.Body "\t\t\t"}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.PatchMultipartWithParamsAndHeaders(path, params, fields, fileFields, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.PatchMultipartWithParams(path, params, fields, fileFields)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.PatchMultipartWithHeaders(path, fields, fileFields, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.PatchMultipart(path, fields, fileFields)
-{{- end}}
-{{- end}}
 {{- else if $isForm}}
 			fields := url.Values{}
 {{formBodyMaps .Endpoint.Body "\t\t\t"}}
-{{- if endpointHasQueryFlags .Endpoint}}
-			params := map[string]string{}
-{{- range .Endpoint.Params}}
-{{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
-			params["{{.Name}}"] = args[{{positionalIndex $.Endpoint .Name}}]
-{{- end}}
-{{- if and (not .Positional) (not .PathParam)}}
-			if flag{{camel (paramIdent .)}} != {{zeroValForParam .Name .Type}} {
-				params["{{.Name}}"] = fmt.Sprintf("%v", flag{{camel (paramIdent .)}})
-			}
-{{- end}}
-{{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.PatchFormWithParamsAndHeaders(path, params, fields, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.PatchFormWithParams(path, params, fields)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.PatchFormWithHeaders(path, fields, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.PatchForm(path, fields)
-{{- end}}
-{{- end}}
 {{- else}}
 			var body map[string]any
 			if stdinBody {
@@ -541,30 +417,11 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 			} else {
 				body = map[string]any{}
 {{bodyMapForEndpoint .Endpoint "\t\t\t\t"}}			}
-{{- if endpointHasQueryFlags .Endpoint}}
-			params := map[string]string{}
-{{- range .Endpoint.Params}}
-{{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
-			params["{{.Name}}"] = args[{{positionalIndex $.Endpoint .Name}}]
-{{- end}}
-{{- if and (not .Positional) (not .PathParam)}}
-			if flag{{camel (paramIdent .)}} != {{zeroValForParam .Name .Type}} {
-				params["{{.Name}}"] = fmt.Sprintf("%v", flag{{camel (paramIdent .)}})
-			}
-{{- end}}
-{{- end}}
-{{- if .Endpoint.HeaderOverrides}}
+{{- if or .Endpoint.HeaderOverrides .Endpoint.UsesBinaryResponse}}
 			data, statusCode, err := c.PatchWithParamsAndHeaders(path, params, body, headerOverrides)
 {{- else}}
 			data, statusCode, err := c.PatchWithParams(path, params, body)
 {{- end}}
-{{- else}}
-{{- if .Endpoint.HeaderOverrides}}
-			data, statusCode, err := c.PatchWithHeaders(path, body, headerOverrides)
-{{- else}}
-			data, statusCode, err := c.Patch(path, body)
-{{- end}}
-{{- end}}
 {{- end}}
 {{- end}}
 {{- if eq .Endpoint.Method "DELETE"}}
@@ -576,6 +433,24 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 				return classifyAPIError(err, flags)
 			}
 {{- end}}
+{{- if .Endpoint.UsesBinaryResponse}}
+			_ = json.Valid
+			_ = os.Stderr
+{{- if or (eq .Endpoint.Method "POST") (eq .Endpoint.Method "PUT") (eq .Endpoint.Method "PATCH") (eq .Endpoint.Method "DELETE")}}
+			_ = statusCode
+{{- end}}
+{{- if and .HasStore (or (eq .Endpoint.Method "GET") (eq .Endpoint.Method "HEAD") .Endpoint.Pagination $isGraphQLEndpoint)}}
+			_ = prov
+{{- end}}
+			if flags.quiet {
+				return nil
+			}
+			if flags.asJSON || flags.csv || flags.compact || flags.plain || flags.selectFields != "" {
+				return fmt.Errorf("binary response cannot be rendered as structured output; redirect stdout or use --deliver file:<path>")
+			}
+			_, err = cmd.OutOrStdout().Write(data)
+			return err
+{{- else}}
 {{- if endpointNeedsClientLimit .Endpoint}}
 			// Honor --limit when the API accepts but ignores ?limit=N.
 			data = truncateJSONArray(data, flagLimit)
@@ -753,6 +628,7 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
 			}
 {{- end}}
 			return printOutputWithFlags(cmd.OutOrStdout(), data, flags)
+{{- end}}
 		},
 	}
 
diff --git a/internal/generator/templates/command_promoted.go.tmpl b/internal/generator/templates/command_promoted.go.tmpl
index e7811916..153bd68a 100644
--- a/internal/generator/templates/command_promoted.go.tmpl
+++ b/internal/generator/templates/command_promoted.go.tmpl
@@ -131,6 +131,9 @@ func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
 {{- end}}
 {{- end}}
 
+{{- if .Endpoint.UsesBinaryResponse}}
+			headerOverrides := map[string]string{"X-Printing-Press-Binary-Response": "true"}
+{{- end}}
 {{- if .Endpoint.Pagination}}
 {{- if .HasStore}}
 			data, prov, err := resolvePaginatedRead(cmd.Context(), c, flags, "{{lower .ResourceName}}", path, map[string]string{
@@ -142,7 +145,7 @@ func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
 				"{{paramWireName .}}": fmt.Sprintf("%v", flag{{camel (paramIdent .)}}),
 {{- end}}
 {{- end}}
-			}, nil, flagAll, "{{.Endpoint.Pagination.CursorParam}}", "{{.Endpoint.Pagination.NextCursorPath}}", "{{.Endpoint.Pagination.HasMoreField}}")
+			}, {{if .Endpoint.UsesBinaryResponse}}headerOverrides{{else}}nil{{end}}, flagAll, "{{.Endpoint.Pagination.CursorParam}}", "{{.Endpoint.Pagination.NextCursorPath}}", "{{.Endpoint.Pagination.HasMoreField}}")
 {{- else}}
 			data, err := paginatedGet(c, path, map[string]string{
 {{- range .Endpoint.Params}}
@@ -153,10 +156,9 @@ func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
 				"{{paramWireName .}}": fmt.Sprintf("%v", flag{{camel (paramIdent .)}}),
 {{- end}}
 {{- end}}
-			}, nil, flagAll, "{{.Endpoint.Pagination.CursorParam}}", "{{.Endpoint.Pagination.NextCursorPath}}", "{{.Endpoint.Pagination.HasMoreField}}")
+			}, {{if .Endpoint.UsesBinaryResponse}}headerOverrides{{else}}nil{{end}}, flagAll, "{{.Endpoint.Pagination.CursorParam}}", "{{.Endpoint.Pagination.NextCursorPath}}", "{{.Endpoint.Pagination.HasMoreField}}")
 {{- end}}
 {{- else}}
-{{- if or $isReadVerb (and $isWriteVerb $hasQueryFlags)}}
 			params := map[string]string{}
 {{- range .Endpoint.Params}}
 {{- if and .Positional (not (pathContainsParam $.Endpoint.Path .Name))}}
@@ -168,34 +170,37 @@ func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
 			}
 {{- end}}
 {{- end}}
-{{- end}}
-{{- if and .HasStore (eq $method "GET")}}
-			data, prov, err := resolveRead(cmd.Context(), c, flags, "{{lower .ResourceName}}", {{if .Endpoint.Pagination}}true{{else}}false{{end}}, path, params, nil)
+{{- if and .HasStore (eq (upper .Endpoint.Method) "GET")}}
+			data, prov, err := resolveRead(cmd.Context(), c, flags, "{{lower .ResourceName}}", {{if .Endpoint.Pagination}}true{{else}}false{{end}}, path, params, {{if .Endpoint.UsesBinaryResponse}}headerOverrides{{else}}nil{{end}})
 {{- else if eq $method "GET"}}
+{{- if .Endpoint.UsesBinaryResponse}}
+			data, err := c.GetWithHeaders(path, params, headerOverrides)
+{{- else}}
 			data, err := c.Get(path, params)
-{{- else if eq $method "DELETE"}}
-{{- if $hasQueryFlags}}
-			data, _, err := c.DeleteWithParams(path, params)
+{{- end}}
+{{- else if eq (upper .Endpoint.Method) "DELETE"}}
+{{- if .Endpoint.UsesBinaryResponse}}
+			data, _, err := c.DeleteWithParamsAndHeaders(path, params, headerOverrides)
 {{- else}}
-			data, _, err := c.Delete(path)
+			data, _, err := c.DeleteWithParams(path, params)
 {{- end}}
-{{- else if or (eq $method "POST") (eq $method "PUT") (eq $method "PATCH")}}
+{{- else if or (eq (upper .Endpoint.Method) "POST") (eq (upper .Endpoint.Method) "PUT") (eq (upper .Endpoint.Method) "PATCH")}}
 {{- if $isMultipart}}
 			fields := map[string]string{}
 			fileFields := map[string]string{}
 {{multipartBodyMaps .Endpoint.Body "\t\t\t"}}
-{{- if $hasQueryFlags}}
-			data, _, err := c.{{pascal (lower .Endpoint.Method)}}MultipartWithParams(path, params, fields, fileFields)
+{{- if .Endpoint.UsesBinaryResponse}}
+			data, _, err := c.{{pascal (lower .Endpoint.Method)}}MultipartWithParamsAndHeaders(path, params, fields, fileFields, headerOverrides)
 {{- else}}
-			data, _, err := c.{{pascal (lower .Endpoint.Method)}}Multipart(path, fields, fileFields)
+			data, _, err := c.{{pascal (lower .Endpoint.Method)}}MultipartWithParams(path, params, fields, fileFields)
 {{- end}}
-{{- else if $isForm}}{{/* HeaderOverrides intentionally omitted on promoted form/multipart/JSON paths — parity with the existing multipart promoted branch above. */}}
+{{- else if $isForm}}
 			fields := url.Values{}
 {{formBodyMaps .Endpoint.Body "\t\t\t"}}
-{{- if $hasQueryFlags}}
-			data, _, err := c.{{pascal (lower .Endpoint.Method)}}FormWithParams(path, params, fields)
+{{- if .Endpoint.UsesBinaryResponse}}
+			data, _, err := c.{{pascal (lower .Endpoint.Method)}}FormWithParamsAndHeaders(path, params, fields, headerOverrides)
 {{- else}}
-			data, _, err := c.{{pascal (lower .Endpoint.Method)}}Form(path, fields)
+			data, _, err := c.{{pascal (lower .Endpoint.Method)}}FormWithParams(path, params, fields)
 {{- end}}
 {{- else}}
 			// HasStore + non-GET falls through to a live API call here
@@ -203,16 +208,20 @@ func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
 			// body-aware cached read helper is filed as #425 for when a
 			// second store-backed POST-search consumer ships.
 			body := map[string]any{}
-{{bodyMapForEndpoint .Endpoint "\t\t\t"}}{{if $hasQueryFlags}}			data, _, err := c.{{pascal (lower .Endpoint.Method)}}WithParams(path, params, body)
-{{else}}			data, _, err := c.{{pascal (lower .Endpoint.Method)}}(path, body)
+{{bodyMapForEndpoint .Endpoint "\t\t\t"}}{{if .Endpoint.UsesBinaryResponse}}			data, _, err := c.{{pascal (lower .Endpoint.Method)}}WithParamsAndHeaders(path, params, body, headerOverrides)
+{{else}}			data, _, err := c.{{pascal (lower .Endpoint.Method)}}WithParams(path, params, body)
 {{end}}
 {{- end}}
 {{- else}}
 			// HEAD/OPTIONS and unknown verbs fall back to GET so generation
 			// stays compileable. Spec authors who genuinely need these verbs
 			// should add a typed client method and a dedicated template branch.
+{{- if .Endpoint.UsesBinaryResponse}}
+			data, err := c.GetWithHeaders(path, params, headerOverrides)
+{{- else}}
 			data, err := c.Get(path, params)
 {{- end}}
+{{- end}}
 {{- if and .HasStore (ne (upper .Endpoint.Method) "GET")}}
 			prov := attachFreshness(DataProvenance{Source: "live"}, flags)
 {{- end}}
@@ -220,6 +229,21 @@ func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
 			if err != nil {
 				return classifyAPIError(err, flags)
 			}
+{{- if .Endpoint.UsesBinaryResponse}}
+			_ = json.Valid
+			_ = os.Stderr
+{{- if .HasStore}}
+			_ = prov
+{{- end}}
+			if flags.quiet {
+				return nil
+			}
+			if flags.asJSON || flags.csv || flags.compact || flags.plain || flags.selectFields != "" {
+				return fmt.Errorf("binary response cannot be rendered as structured output; redirect stdout or use --deliver file:<path>")
+			}
+			_, err = cmd.OutOrStdout().Write(data)
+			return err
+{{- else}}
 {{- if endpointNeedsClientLimit .Endpoint}}
 			// Honor --limit when the API accepts but ignores ?limit=N.
 			data = truncateJSONArray(data, flagLimit)
@@ -297,6 +321,7 @@ func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
 				}
 			}
 			return printOutputWithFlags(cmd.OutOrStdout(), data, flags)
+{{- end}}
 		},
 	}
 
diff --git a/internal/generator/templates/mcp_tools.go.tmpl b/internal/generator/templates/mcp_tools.go.tmpl
index 46a507ec..c97aea10 100644
--- a/internal/generator/templates/mcp_tools.go.tmpl
+++ b/internal/generator/templates/mcp_tools.go.tmpl
@@ -9,6 +9,7 @@ package mcp
 
 import (
 	"context"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 {{- if $hasFormRequest}}
@@ -70,9 +71,9 @@ func RegisterTools(s *server.MCPServer) {
 {{- end}}
 		),
 {{- if $.HasTierRouting}}
-		makeAPIHandler({{printf "%q" (upper $endpoint.Method)}}, {{printf "%q" (effectiveEndpointPath $resource $endpoint)}}, {{printf "%q" (effectiveTier $.APISpec $resource $endpoint)}}, []mcpParamBinding{ {{- range mcpParamBindings $endpoint (effectiveEndpointPath $resource $endpoint)}}{PublicName: {{printf "%q" .PublicName}}, WireName: {{printf "%q" .WireName}}, Location: {{printf "%q" .Location}}{{if .Format}}, Format: {{printf "%q" .Format}}{{end}}{{if .RequestContentType}}, RequestContentType: {{printf "%q" .RequestContentType}}{{end}}},{{end}} }, []string{ {{- range $endpoint.Params}}{{if or .Positional .PathParam}}{{printf "%q" .Name}},{{end}}{{end}} }),
+		makeAPIHandler({{printf "%q" (upper $endpoint.Method)}}, {{printf "%q" (effectiveEndpointPath $resource $endpoint)}}, {{printf "%q" (effectiveTier $.APISpec $resource $endpoint)}}, {{if $endpoint.UsesBinaryResponse}}true{{else}}false{{end}}, []mcpParamBinding{ {{- range mcpParamBindings $endpoint (effectiveEndpointPath $resource $endpoint)}}{PublicName: {{printf "%q" .PublicName}}, WireName: {{printf "%q" .WireName}}, Location: {{printf "%q" .Location}}{{if .Format}}, Format: {{printf "%q" .Format}}{{end}}{{if .RequestContentType}}, RequestContentType: {{printf "%q" .RequestContentType}}{{end}}},{{end}} }, []string{ {{- range $endpoint.Params}}{{if or .Positional .PathParam}}{{printf "%q" .Name}},{{end}}{{end}} }),
 {{- else}}
-		makeAPIHandler({{printf "%q" (upper $endpoint.Method)}}, {{printf "%q" (effectiveEndpointPath $resource $endpoint)}}, []mcpParamBinding{ {{- range mcpParamBindings $endpoint (effectiveEndpointPath $resource $endpoint)}}{PublicName: {{printf "%q" .PublicName}}, WireName: {{printf "%q" .WireName}}, Location: {{printf "%q" .Location}}{{if .Format}}, Format: {{printf "%q" .Format}}{{end}}{{if .RequestContentType}}, RequestContentType: {{printf "%q" .RequestContentType}}{{end}}},{{end}} }, []string{ {{- range $endpoint.Params}}{{if or .Positional .PathParam}}{{printf "%q" .Name}},{{end}}{{end}} }),
+		makeAPIHandler({{printf "%q" (upper $endpoint.Method)}}, {{printf "%q" (effectiveEndpointPath $resource $endpoint)}}, {{if $endpoint.UsesBinaryResponse}}true{{else}}false{{end}}, []mcpParamBinding{ {{- range mcpParamBindings $endpoint (effectiveEndpointPath $resource $endpoint)}}{PublicName: {{printf "%q" .PublicName}}, WireName: {{printf "%q" .WireName}}, Location: {{printf "%q" .Location}}{{if .Format}}, Format: {{printf "%q" .Format}}{{end}}{{if .RequestContentType}}, RequestContentType: {{printf "%q" .RequestContentType}}{{end}}},{{end}} }, []string{ {{- range $endpoint.Params}}{{if or .Positional .PathParam}}{{printf "%q" .Name}},{{end}}{{end}} }),
 {{- end}}
 	)
 {{- end}}
@@ -105,9 +106,9 @@ func RegisterTools(s *server.MCPServer) {
 {{- end}}
 		),
 {{- if $.HasTierRouting}}
-		makeAPIHandler({{printf "%q" (upper $endpoint.Method)}}, {{printf "%q" (effectiveSubEndpointPath $resource $subResource $endpoint)}}, {{printf "%q" (effectiveSubTier $.APISpec $resource $subResource $endpoint)}}, []mcpParamBinding{ {{- range mcpParamBindings $endpoint (effectiveSubEndpointPath $resource $subResource $endpoint)}}{PublicName: {{printf "%q" .PublicName}}, WireName: {{printf "%q" .WireName}}, Location: {{printf "%q" .Location}}{{if .Format}}, Format: {{printf "%q" .Format}}{{end}}{{if .RequestContentType}}, RequestContentType: {{printf "%q" .RequestContentType}}{{end}}},{{end}} }, []string{ {{- range $endpoint.Params}}{{if or .Positional .PathParam}}{{printf "%q" .Name}},{{end}}{{end}} }),
+		makeAPIHandler({{printf "%q" (upper $endpoint.Method)}}, {{printf "%q" (effectiveSubEndpointPath $resource $subResource $endpoint)}}, {{printf "%q" (effectiveSubTier $.APISpec $resource $subResource $endpoint)}}, {{if $endpoint.UsesBinaryResponse}}true{{else}}false{{end}}, []mcpParamBinding{ {{- range mcpParamBindings $endpoint (effectiveSubEndpointPath $resource $subResource $endpoint)}}{PublicName: {{printf "%q" .PublicName}}, WireName: {{printf "%q" .WireName}}, Location: {{printf "%q" .Location}}{{if .Format}}, Format: {{printf "%q" .Format}}{{end}}{{if .RequestContentType}}, RequestContentType: {{printf "%q" .RequestContentType}}{{end}}},{{end}} }, []string{ {{- range $endpoint.Params}}{{if or .Positional .PathParam}}{{printf "%q" .Name}},{{end}}{{end}} }),
 {{- else}}
-		makeAPIHandler({{printf "%q" (upper $endpoint.Method)}}, {{printf "%q" (effectiveSubEndpointPath $resource $subResource $endpoint)}}, []mcpParamBinding{ {{- range mcpParamBindings $endpoint (effectiveSubEndpointPath $resource $subResource $endpoint)}}{PublicName: {{printf "%q" .PublicName}}, WireName: {{printf "%q" .WireName}}, Location: {{printf "%q" .Location}}{{if .Format}}, Format: {{printf "%q" .Format}}{{end}}{{if .RequestContentType}}, RequestContentType: {{printf "%q" .RequestContentType}}{{end}}},{{end}} }, []string{ {{- range $endpoint.Params}}{{if or .Positional .PathParam}}{{printf "%q" .Name}},{{end}}{{end}} }),
+		makeAPIHandler({{printf "%q" (upper $endpoint.Method)}}, {{printf "%q" (effectiveSubEndpointPath $resource $subResource $endpoint)}}, {{if $endpoint.UsesBinaryResponse}}true{{else}}false{{end}}, []mcpParamBinding{ {{- range mcpParamBindings $endpoint (effectiveSubEndpointPath $resource $subResource $endpoint)}}{PublicName: {{printf "%q" .PublicName}}, WireName: {{printf "%q" .WireName}}, Location: {{printf "%q" .Location}}{{if .Format}}, Format: {{printf "%q" .Format}}{{end}}{{if .RequestContentType}}, RequestContentType: {{printf "%q" .RequestContentType}}{{end}}},{{end}} }, []string{ {{- range $endpoint.Params}}{{if or .Positional .PathParam}}{{printf "%q" .Name}},{{end}}{{end}} }),
 {{- end}}
 	)
 {{- end}}
@@ -199,9 +200,9 @@ func mcpFormFieldValue(v any) string {
 
 // makeAPIHandler creates a generic MCP tool handler for an API endpoint.
 {{- if .HasTierRouting}}
-func makeAPIHandler(method, pathTemplate, tier string, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
+func makeAPIHandler(method, pathTemplate, tier string, binaryResponse bool, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
 {{- else}}
-func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
+func makeAPIHandler(method, pathTemplate string, binaryResponse bool, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
 {{- end}}
 	return func(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
 		c, err := newMCPClient()
@@ -225,6 +226,10 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		pathParams := make(map[string]bool, len(positionalParams))
 		params := make(map[string]string)
 		bodyArgs := make(map[string]any)
+		var headers map[string]string
+		if binaryResponse {
+			headers = map[string]string{client.BinaryResponseHeader: "true"}
+		}
 {{- if $hasMultipartRequest}}
 		multipartFields := make(map[string]string)
 		multipartFileFields := make(map[string]string)
@@ -330,68 +335,124 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		var data json.RawMessage
 		switch method {
 		case "GET":
+			if binaryResponse {
+				data, err = c.GetWithHeaders(path, params, headers)
+				break
+			}
 			data, err = c.Get(path, params)
 		case "POST":
 {{- if $hasMultipartRequest}}
 			if multipart {
+				if binaryResponse {
+					data, _, err = c.PostMultipartWithParamsAndHeaders(path, params, multipartFields, multipartFileFields, headers)
+					break
+				}
 				data, _, err = c.PostMultipartWithParams(path, params, multipartFields, multipartFileFields)
 				break
 			}
 {{- end}}
 {{- if $hasFormRequest}}
 			if formEncoded {
+				if binaryResponse {
+					data, _, err = c.PostFormWithParamsAndHeaders(path, params, formFields, headers)
+					break
+				}
 				data, _, err = c.PostFormWithParams(path, params, formFields)
 				break
 			}
 {{- end}}
 {{- if $hasBodyJSONFallback}}
 			if len(bodyJSONOverride) > 0 {
+				if binaryResponse {
+					data, _, err = c.PostWithParamsAndHeaders(path, params, bodyJSONOverride, headers)
+					break
+				}
 				data, _, err = c.PostWithParams(path, params, bodyJSONOverride)
 				break
 			}
 {{- end}}
+			if binaryResponse {
+				data, _, err = c.PostWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PostWithParams(path, params, bodyArgs)
 		case "PUT":
 {{- if $hasMultipartRequest}}
 			if multipart {
+				if binaryResponse {
+					data, _, err = c.PutMultipartWithParamsAndHeaders(path, params, multipartFields, multipartFileFields, headers)
+					break
+				}
 				data, _, err = c.PutMultipartWithParams(path, params, multipartFields, multipartFileFields)
 				break
 			}
 {{- end}}
 {{- if $hasFormRequest}}
 			if formEncoded {
+				if binaryResponse {
+					data, _, err = c.PutFormWithParamsAndHeaders(path, params, formFields, headers)
+					break
+				}
 				data, _, err = c.PutFormWithParams(path, params, formFields)
 				break
 			}
 {{- end}}
 {{- if $hasBodyJSONFallback}}
 			if len(bodyJSONOverride) > 0 {
+				if binaryResponse {
+					data, _, err = c.PutWithParamsAndHeaders(path, params, bodyJSONOverride, headers)
+					break
+				}
 				data, _, err = c.PutWithParams(path, params, bodyJSONOverride)
 				break
 			}
 {{- end}}
+			if binaryResponse {
+				data, _, err = c.PutWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PutWithParams(path, params, bodyArgs)
 		case "PATCH":
 {{- if $hasMultipartRequest}}
 			if multipart {
+				if binaryResponse {
+					data, _, err = c.PatchMultipartWithParamsAndHeaders(path, params, multipartFields, multipartFileFields, headers)
+					break
+				}
 				data, _, err = c.PatchMultipartWithParams(path, params, multipartFields, multipartFileFields)
 				break
 			}
 {{- end}}
 {{- if $hasFormRequest}}
 			if formEncoded {
+				if binaryResponse {
+					data, _, err = c.PatchFormWithParamsAndHeaders(path, params, formFields, headers)
+					break
+				}
 				data, _, err = c.PatchFormWithParams(path, params, formFields)
 				break
 			}
 {{- end}}
 {{- if $hasBodyJSONFallback}}
 			if len(bodyJSONOverride) > 0 {
+				if binaryResponse {
+					data, _, err = c.PatchWithParamsAndHeaders(path, params, bodyJSONOverride, headers)
+					break
+				}
 				data, _, err = c.PatchWithParams(path, params, bodyJSONOverride)
 				break
 			}
 {{- end}}
+			if binaryResponse {
+				data, _, err = c.PatchWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PatchWithParams(path, params, bodyArgs)
 		case "DELETE":
+			if binaryResponse {
+				data, _, err = c.DeleteWithParamsAndHeaders(path, params, headers)
+				break
+			}
 			data, _, err = c.DeleteWithParams(path, params)
 		default:
 			return mcplib.NewToolResultError("unsupported method: " + method), nil
@@ -490,6 +551,14 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 				}
 			}
 		}
+		if binaryResponse {
+			out, _ := json.Marshal(map[string]any{
+				"content_encoding": "base64",
+				"data_base64":      base64.StdEncoding.EncodeToString(data),
+				"byte_count":       len(data),
+			})
+			return mcplib.NewToolResultText(string(out)), nil
+		}
 		return mcplib.NewToolResultText(string(data)), nil
 	}
 }
diff --git a/internal/generator/tier_routing_test.go b/internal/generator/tier_routing_test.go
index 522f0af5..7312d880 100644
--- a/internal/generator/tier_routing_test.go
+++ b/internal/generator/tier_routing_test.go
@@ -102,8 +102,8 @@ func TestTierRoutingEmitsTierAwareClientAndCommands(t *testing.T) {
 	require.Contains(t, paidCmd, `c = c.WithTier("paid")`)
 
 	mcpSrc := readGeneratedFile(t, outputDir, "internal", "mcp", "tools.go")
-	require.Contains(t, mcpSrc, `makeAPIHandler("GET", "/items", "free"`)
-	require.Contains(t, mcpSrc, `makeAPIHandler("GET", "/items/premium", "paid"`)
+	require.Contains(t, mcpSrc, `makeAPIHandler("GET", "/items", "free", false`)
+	require.Contains(t, mcpSrc, `makeAPIHandler("GET", "/items/premium", "paid", false`)
 	require.Contains(t, mcpSrc, `c = c.WithTier(tier)`)
 	require.Contains(t, mcpSrc, `"tier_routing": map[string]any`)
 	require.Regexp(t, `"items_premium":\s+"paid"`, mcpSrc)
diff --git a/internal/openapi/parser.go b/internal/openapi/parser.go
index c94c118b..1cfee019 100644
--- a/internal/openapi/parser.go
+++ b/internal/openapi/parser.go
@@ -548,6 +548,9 @@ func mapAuthWithDescriptionInference(doc *openapi3.T, name string, allowDescript
 	schemeName, scheme := selectSecurityScheme(doc)
 	if scheme == nil {
 		result := inferQueryParamAuth(doc, name, auth)
+		if result.Type == "none" {
+			result = inferHeaderParamAPIKeyAuth(doc, name, result)
+		}
 		if result.Type == "none" && allowDescriptionInference {
 			result = inferDescriptionAuth(doc, name, result)
 		}
@@ -1209,6 +1212,22 @@ var commonAuthQueryParams = map[string]bool{
 	"token":        true,
 }
 
+func authLikeHeaderName(name string) bool {
+	normalized := strings.ToLower(strings.TrimSpace(name))
+	if normalized == "" {
+		return false
+	}
+	switch normalized {
+	case "authorization", "proxy-authorization", "cookie", "set-cookie":
+		return false
+	}
+	compact := strings.NewReplacer("-", "", "_", "", " ", "").Replace(normalized)
+	if strings.Contains(compact, "apikey") || strings.Contains(compact, "authkey") {
+		return true
+	}
+	return strings.Contains(compact, "token") && (strings.Contains(compact, "auth") || strings.Contains(compact, "access"))
+}
+
 // inferQueryParamAuth scans all operations for query parameters that look like
 // API keys. If more than 30% of operations carry one, we infer query-param auth.
 // This handles specs that omit securitySchemes but pass keys via query string.
@@ -1254,7 +1273,13 @@ func inferQueryParamAuth(doc *openapi3.T, name string, fallback spec.AuthConfig)
 	// Find the most common auth-like param name.
 	var best string
 	var bestCount int
-	for pName, cnt := range paramCounts {
+	paramNames := make([]string, 0, len(paramCounts))
+	for pName := range paramCounts {
+		paramNames = append(paramNames, pName)
+	}
+	sort.Strings(paramNames)
+	for _, pName := range paramNames {
+		cnt := paramCounts[pName]
 		if cnt > bestCount {
 			best = pName
 			bestCount = cnt
@@ -1274,6 +1299,71 @@ func inferQueryParamAuth(doc *openapi3.T, name string, fallback spec.AuthConfig)
 	}
 }
 
+// inferHeaderParamAPIKeyAuth handles OpenAPI specs that omit securitySchemes
+// but repeat an API-key-shaped header parameter across the operation surface.
+func inferHeaderParamAPIKeyAuth(doc *openapi3.T, name string, fallback spec.AuthConfig) spec.AuthConfig {
+	if doc == nil || doc.Paths == nil || hasTopLevelSecurityDeclaration(doc) {
+		return fallback
+	}
+
+	paramCounts := map[string]int{}
+	totalOps := 0
+
+	for _, pathKey := range doc.Paths.InMatchingOrder() {
+		pathItem := doc.Paths.Value(pathKey)
+		if pathItem == nil {
+			continue
+		}
+		for _, op := range pathItem.Operations() {
+			if op == nil {
+				continue
+			}
+			totalOps++
+			seen := false
+			for _, p := range mergeParameters(pathItem, op) {
+				if p == nil || p.In != openapi3.ParameterInHeader || seen {
+					continue
+				}
+				if authLikeHeaderName(p.Name) {
+					paramCounts[p.Name]++
+					seen = true
+				}
+			}
+		}
+	}
+
+	if totalOps == 0 {
+		return fallback
+	}
+
+	var best string
+	var bestCount int
+	paramNames := make([]string, 0, len(paramCounts))
+	for pName := range paramCounts {
+		paramNames = append(paramNames, pName)
+	}
+	sort.Strings(paramNames)
+	for _, pName := range paramNames {
+		cnt := paramCounts[pName]
+		if cnt > bestCount {
+			best = pName
+			bestCount = cnt
+		}
+	}
+	if bestCount == 0 || float64(bestCount)/float64(totalOps) <= 0.3 {
+		return fallback
+	}
+
+	envPrefix := naming.EnvPrefix(name)
+	return spec.AuthConfig{
+		Type:     "api_key",
+		In:       "header",
+		Header:   best,
+		EnvVars:  []string{envPrefix + "_API_KEY"},
+		Inferred: true,
+	}
+}
+
 // detectRequiredHeaders scans all operations for required header parameters
 // and returns those appearing on >80% of operations as global required headers.
 // Auth-related and dynamic headers are excluded via case-insensitive matching.
@@ -2035,6 +2125,9 @@ func mapResources(doc *openapi3.T, out *spec.APISpec, basePath string) {
 			// response-schema inference. Resolution happens at parse time so
 			// the profiler sees a single resolved value per endpoint and
 			// templates do not re-walk schemas at generation time.
+			if responseUsesBinary(op) {
+				endpoint.ResponseFormat = spec.ResponseFormatBinary
+			}
 			if pathResourceIDOverride != "" {
 				endpoint.IDField = pathResourceIDOverride
 			} else {
@@ -3220,6 +3313,49 @@ func selectResponseSchema(response *openapi3.Response) *openapi3.SchemaRef {
 	return nil
 }
 
+func responseUsesBinary(op *openapi3.Operation) bool {
+	if op == nil || op.Responses == nil {
+		return false
+	}
+	success := selectSuccessResponse(op.Responses)
+	if success == nil || success.Value == nil || success.Value.Content == nil {
+		return false
+	}
+	for _, contentType := range sortedContentTypes(success.Value.Content) {
+		media := success.Value.Content[contentType]
+		if media == nil {
+			continue
+		}
+		if schema := schemaRefValue(media.Schema); schema != nil {
+			if schema.Type != nil && schema.Type.Includes(openapi3.TypeString) && strings.EqualFold(schemaFormat(schema), "binary") {
+				return true
+			}
+		}
+		if binaryContentType(contentType) {
+			return true
+		}
+	}
+	return false
+}
+
+func binaryContentType(contentType string) bool {
+	base := strings.ToLower(strings.TrimSpace(strings.Split(contentType, ";")[0]))
+	if base == "" {
+		return false
+	}
+	if strings.HasPrefix(base, "audio/") ||
+		strings.HasPrefix(base, "video/") ||
+		strings.HasPrefix(base, "image/") {
+		return true
+	}
+	switch base {
+	case "application/octet-stream", "application/zip", "application/x-zip", "application/x-zip-compressed", "application/pdf", "multipart/mixed":
+		return true
+	default:
+		return false
+	}
+}
+
 // readPathItemResourceID reads the `x-resource-id` extension from a path item
 // and returns the resolved field name. Accepts only string values; non-string
 // values (numbers, booleans, malformed YAML) emit a warning and return "".
diff --git a/internal/openapi/parser_test.go b/internal/openapi/parser_test.go
index 696d9639..ad92360c 100644
--- a/internal/openapi/parser_test.go
+++ b/internal/openapi/parser_test.go
@@ -3590,6 +3590,34 @@ func TestInferOperationLevelBearer(t *testing.T) {
 	})
 }
 
+func TestInferHeaderParamAPIKeyAuth(t *testing.T) {
+	t.Parallel()
+
+	doc := &openapi3.T{
+		Info:  &openapi3.Info{Title: "test", Description: "no auth keywords"},
+		Paths: &openapi3.Paths{},
+	}
+	for _, path := range []string{"/a", "/b", "/c", "/d"} {
+		doc.Paths.Set(path, &openapi3.PathItem{
+			Get: &openapi3.Operation{
+				Responses: openapi3.NewResponses(),
+				Parameters: openapi3.Parameters{
+					&openapi3.ParameterRef{Value: &openapi3.Parameter{
+						Name: "xi-api-key", In: "header", Required: false,
+					}},
+				},
+			},
+		})
+	}
+
+	result := mapAuth(doc, "elevenlabs")
+	assert.Equal(t, "api_key", result.Type)
+	assert.Equal(t, "header", result.In)
+	assert.Equal(t, "xi-api-key", result.Header)
+	assert.Equal(t, []string{"ELEVENLABS_API_KEY"}, result.EnvVars)
+	assert.True(t, result.Inferred)
+}
+
 func TestAuthTierPrecedence(t *testing.T) {
 	t.Parallel()
 
@@ -5552,6 +5580,37 @@ paths:
 	assert.True(t, byName["filename"].Required)
 }
 
+func TestParseBinaryResponseFormat(t *testing.T) {
+	t.Parallel()
+	data := []byte(`
+openapi: 3.1.0
+info:
+  title: Audio API
+  version: 1.0.0
+servers:
+  - url: https://api.example.com
+paths:
+  /v1/audio:
+    post:
+      operationId: createAudio
+      responses:
+        "200":
+          description: Audio bytes
+          content:
+            audio/mpeg:
+              schema:
+                type: string
+                format: binary
+`)
+
+	parsed, err := Parse(data)
+	require.NoError(t, err)
+
+	endpoint := findParsedEndpointByPath(t, parsed, "POST", "/v1/audio")
+	assert.Equal(t, spec.ResponseFormatBinary, endpoint.ResponseFormat)
+	assert.True(t, endpoint.UsesBinaryResponse())
+}
+
 func TestParseFormUrlencodedRequestBodyPreservesContentType(t *testing.T) {
 	t.Parallel()
 	data := []byte(`
diff --git a/internal/pipeline/mcpsync/sync.go b/internal/pipeline/mcpsync/sync.go
index 0b440f5f..ca79c83e 100644
--- a/internal/pipeline/mcpsync/sync.go
+++ b/internal/pipeline/mcpsync/sync.go
@@ -10,6 +10,9 @@ import (
 	"regexp"
 	"strings"
 
+	catalogfs "github.com/mvanhorn/cli-printing-press/v4/catalog"
+	"github.com/mvanhorn/cli-printing-press/v4/internal/catalog"
+	"github.com/mvanhorn/cli-printing-press/v4/internal/catalogmeta"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/generator"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/graphql"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/mcpoverrides"
@@ -80,6 +83,7 @@ func Sync(cliDir string, opts Options) (Result, error) {
 	if prior := applyManifestNameOverride(cliDir, parsed); prior != "" {
 		fmt.Fprintf(os.Stderr, "mcp-sync: using manifest api_name %q over spec-derived slug %q\n", parsed.Name, prior)
 	}
+	applyCatalogMetadata(parsed)
 	// Validate that spec.yaml.name matches the directory's basename.
 	// Older library CLIs sometimes have drift (weather-goat's
 	// spec.yaml.name = "weather"; open-meteo's name diverges similarly)
@@ -511,10 +515,22 @@ func applyManifestNameOverride(cliDir string, parsed *spec.APISpec) string {
 	if parsed.Config.Path == fmt.Sprintf(defaultConfigPathFormat, naming.CLI(prior)) {
 		parsed.Config.Path = fmt.Sprintf(defaultConfigPathFormat, naming.CLI(m.APIName))
 	}
+	catalogmeta.RebaseAuthEnvPrefix(&parsed.Auth, prior, m.APIName)
 	parsed.Name = m.APIName
 	return prior
 }
 
+func applyCatalogMetadata(parsed *spec.APISpec) {
+	if parsed == nil {
+		return
+	}
+	entry, err := catalog.LookupFS(catalogfs.FS, parsed.Name)
+	if err != nil {
+		return
+	}
+	catalogmeta.ApplyRuntimeMetadata(parsed, entry)
+}
+
 // readExistingManifestDisplayName returns the display_name from an
 // existing manifest.json if it's a real brand name. The only form
 // rejected is the bare lowercase slug we'd otherwise emit as last
diff --git a/internal/pipeline/mcpsync/sync_test.go b/internal/pipeline/mcpsync/sync_test.go
index bc7625f7..4eba6b26 100644
--- a/internal/pipeline/mcpsync/sync_test.go
+++ b/internal/pipeline/mcpsync/sync_test.go
@@ -1247,6 +1247,18 @@ func TestApplyManifestNameOverrideReplacesParsedName(t *testing.T) {
 
 	parsed := &spec.APISpec{
 		Name: "telegram-bot",
+		Auth: spec.AuthConfig{
+			Type:    "api_key",
+			Header:  "X-API-Key",
+			EnvVars: []string{"TELEGRAM_BOT_API_KEY"},
+			EnvVarSpecs: []spec.AuthEnvVar{{
+				Name:      "TELEGRAM_BOT_API_KEY",
+				Kind:      spec.AuthEnvVarKindPerCall,
+				Required:  true,
+				Sensitive: true,
+				Inferred:  true,
+			}},
+		},
 		Config: spec.ConfigSpec{
 			Format: "toml",
 			Path:   "~/.config/telegram-bot-pp-cli/config.toml",
@@ -1257,6 +1269,34 @@ func TestApplyManifestNameOverrideReplacesParsedName(t *testing.T) {
 	assert.Equal(t, "telegram", parsed.Name, "parsed.Name should adopt the manifest's api_name")
 	assert.Equal(t, "~/.config/telegram-pp-cli/config.toml", parsed.Config.Path,
 		"spec-derived Config.Path must follow the override so README's Config file: line tracks the binary")
+	assert.Equal(t, []string{"TELEGRAM_API_KEY"}, parsed.Auth.EnvVars,
+		"inferred auth env vars should follow the manifest api_name")
+	require.Len(t, parsed.Auth.EnvVarSpecs, 1)
+	assert.Equal(t, "TELEGRAM_API_KEY", parsed.Auth.EnvVarSpecs[0].Name,
+		"inferred auth env var specs should follow the manifest api_name")
+}
+
+func TestApplyCatalogMetadataUsesCatalogBaseURL(t *testing.T) {
+	t.Parallel()
+
+	parsed := &spec.APISpec{
+		Name:                 "elevenlabs",
+		BaseURL:              spec.PlaceholderBaseURL,
+		BaseURLIsPlaceholder: true,
+		Auth: spec.AuthConfig{
+			Type:   "api_key",
+			Header: "xi-api-key",
+		},
+	}
+
+	applyCatalogMetadata(parsed)
+
+	assert.Equal(t, "https://api.elevenlabs.io", parsed.BaseURL)
+	assert.False(t, parsed.BaseURLIsPlaceholder)
+	assert.Equal(t, "ElevenLabs", parsed.DisplayName)
+	assert.Equal(t, "https://elevenlabs.io/app/settings/api-keys", parsed.Auth.KeyURL)
+	assert.Equal(t, "standard", parsed.HTTPTransport)
+	assert.Equal(t, "official", parsed.SpecSource)
 }
 
 // TestApplyManifestNameOverrideNoOpWhenManifestAgrees — most CLIs are
diff --git a/internal/pipeline/publish.go b/internal/pipeline/publish.go
index fee7801f..a81c676b 100644
--- a/internal/pipeline/publish.go
+++ b/internal/pipeline/publish.go
@@ -13,6 +13,7 @@ import (
 
 	"github.com/mvanhorn/cli-printing-press/v4/catalog"
 	catalogpkg "github.com/mvanhorn/cli-printing-press/v4/internal/catalog"
+	"github.com/mvanhorn/cli-printing-press/v4/internal/catalogmeta"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/graphql"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/naming"
 	"github.com/mvanhorn/cli-printing-press/v4/internal/openapi"
@@ -295,6 +296,7 @@ func writeCLIManifestForPublish(state *PipelineState, dir string) error {
 			parsed, parseErr = spec.ParseBytes(data)
 		}
 		if parseErr == nil {
+			applyPublishCatalogMetadata(parsed, state.APIName)
 			populateMCPMetadata(&m, parsed)
 		}
 
@@ -373,6 +375,23 @@ func writeCLIManifestForPublish(state *PipelineState, dir string) error {
 	return WriteCLIManifest(dir, m)
 }
 
+func applyPublishCatalogMetadata(parsed *spec.APISpec, apiName string) {
+	if parsed == nil || apiName == "" {
+		return
+	}
+	priorName := parsed.Name
+	if priorName != "" && priorName != apiName {
+		catalogmeta.RebaseAuthEnvPrefix(&parsed.Auth, priorName, apiName)
+	}
+	parsed.Name = apiName
+
+	entry, err := catalogpkg.LookupFS(catalog.FS, apiName)
+	if err != nil {
+		return
+	}
+	catalogmeta.ApplyRuntimeMetadata(parsed, entry)
+}
+
 // loadResearchForPromote returns the research.json relevant to the
 // current promote/publish call, plus the path it was loaded from (used
 // for non-canonical-source stderr visibility).
diff --git a/internal/pipeline/publish_test.go b/internal/pipeline/publish_test.go
index 5a6407af..09dc8e4f 100644
--- a/internal/pipeline/publish_test.go
+++ b/internal/pipeline/publish_test.go
@@ -269,6 +269,47 @@ paths: {}
 	assert.Equal(t, "Product Hunt", m.DisplayName)
 }
 
+func TestWriteCLIManifestForPublishAppliesCatalogMetadataAfterNameOverride(t *testing.T) {
+	tmp := t.TempDir()
+	t.Setenv("PRINTING_PRESS_HOME", tmp)
+	t.Setenv("PRINTING_PRESS_SCOPE", "test-scope")
+	t.Setenv("PRINTING_PRESS_REPO_ROOT", tmp)
+
+	state := NewStateWithRun("elevenlabs", filepath.Join(tmp, "working", "elevenlabs-pp-cli"), "20260513-elevenlabs", "test-scope")
+	require.NoError(t, os.MkdirAll(state.WorkingDir, 0o755))
+	require.NoError(t, os.WriteFile(filepath.Join(state.WorkingDir, "spec.yaml"), []byte(`
+openapi: "3.1.0"
+info:
+  title: ElevenLabs API Documentation
+  version: "1.0"
+paths:
+  /v1/models:
+    get:
+      operationId: getModels
+      parameters:
+        - name: xi-api-key
+          in: header
+          required: true
+          schema:
+            type: string
+      responses:
+        "200":
+          description: ok
+`), 0o644))
+
+	require.NoError(t, writeCLIManifestForPublish(state, state.WorkingDir))
+
+	m := readPublishedManifest(t, state.WorkingDir)
+	assert.Equal(t, []string{"ELEVENLABS_API_KEY"}, m.AuthEnvVars)
+	assert.Equal(t, "https://elevenlabs.io/app/settings/api-keys", m.AuthKeyURL)
+
+	tools, err := ReadToolsManifest(state.WorkingDir)
+	require.NoError(t, err)
+	assert.Equal(t, "elevenlabs", tools.APIName)
+	assert.Equal(t, "https://api.elevenlabs.io", tools.BaseURL)
+	assert.Equal(t, []string{"ELEVENLABS_API_KEY"}, tools.Auth.EnvVars)
+}
+
 func TestWriteCLIManifestForPublishPopulatesCategoryFromSpec(t *testing.T) {
 	tmp := t.TempDir()
 	t.Setenv("PRINTING_PRESS_HOME", tmp)
diff --git a/internal/spec/spec.go b/internal/spec/spec.go
index d522906b..3c00fb3e 100644
--- a/internal/spec/spec.go
+++ b/internal/spec/spec.go
@@ -32,8 +32,9 @@ const (
 )
 
 const (
-	ResponseFormatJSON = "json"
-	ResponseFormatHTML = "html"
+	ResponseFormatJSON   = "json"
+	ResponseFormatHTML   = "html"
+	ResponseFormatBinary = "binary"
 )
 
 const (
@@ -1270,6 +1271,10 @@ func (e Endpoint) UsesHTMLResponse() bool {
 	return e.EffectiveResponseFormat() == ResponseFormatHTML
 }
 
+func (e Endpoint) UsesBinaryResponse() bool {
+	return e.EffectiveResponseFormat() == ResponseFormatBinary
+}
+
 type HTMLExtract struct {
 	Mode         string   `yaml:"mode,omitempty" json:"mode,omitempty"`                   // page (default), links, or embedded-json
 	LinkPrefixes []string `yaml:"link_prefixes,omitempty" json:"link_prefixes,omitempty"` // URL path prefixes to keep when extracting links (mode: links)
@@ -2581,9 +2586,9 @@ func validateTierRoutingResource(s *APISpec, resourcePath string, resource Resou
 
 func validateEndpointResponseFormat(e Endpoint) error {
 	switch e.ResponseFormat {
-	case "", ResponseFormatJSON, ResponseFormatHTML:
+	case "", ResponseFormatJSON, ResponseFormatHTML, ResponseFormatBinary:
 	default:
-		return fmt.Errorf("response_format must be one of: json, html")
+		return fmt.Errorf("response_format must be one of: json, html, binary")
 	}
 	if !e.UsesHTMLResponse() {
 		return nil
diff --git a/testdata/golden/expected/catalog-list/stdout.txt b/testdata/golden/expected/catalog-list/stdout.txt
index 86be8809..398e4f31 100644
--- a/testdata/golden/expected/catalog-list/stdout.txt
+++ b/testdata/golden/expected/catalog-list/stdout.txt
@@ -1,3 +1,6 @@
+ai:
+  elevenlabs           Generate, transform, transcribe, dub, and manage AI audio, voices, music, and conversational agents with ElevenLabs.
+
 auth:
   stytch               Authentication and user management API
 
diff --git a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
index 278bf2e1..7ed57607 100644
--- a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
+++ b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
@@ -23,6 +23,8 @@ import (
 	"time"
 )
 
+const BinaryResponseHeader = "X-Printing-Press-Binary-Response"
+
 type Client struct {
 	BaseURL    string
 	Config     *config.Config
@@ -177,14 +179,14 @@ func (c *Client) Post(path string, body any) (json.RawMessage, int, error) {
 	return c.do("POST", path, nil, body, nil)
 }
 
-func (c *Client) PostWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("POST", path, nil, body, headers)
-}
-
 func (c *Client) PostWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, body, nil)
 }
 
+func (c *Client) PostWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("POST", path, nil, body, headers)
+}
+
 func (c *Client) PostWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, body, headers)
 }
@@ -193,14 +195,14 @@ func (c *Client) Delete(path string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, nil, nil, nil)
 }
 
-func (c *Client) DeleteWithHeaders(path string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("DELETE", path, nil, nil, headers)
-}
-
 func (c *Client) DeleteWithParams(path string, params map[string]string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, params, nil, nil)
 }
 
+func (c *Client) DeleteWithHeaders(path string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("DELETE", path, nil, nil, headers)
+}
+
 func (c *Client) DeleteWithParamsAndHeaders(path string, params map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, params, nil, headers)
 }
@@ -209,14 +211,14 @@ func (c *Client) Put(path string, body any) (json.RawMessage, int, error) {
 	return c.do("PUT", path, nil, body, nil)
 }
 
-func (c *Client) PutWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PUT", path, nil, body, headers)
-}
-
 func (c *Client) PutWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, body, nil)
 }
 
+func (c *Client) PutWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PUT", path, nil, body, headers)
+}
+
 func (c *Client) PutWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, body, headers)
 }
@@ -225,14 +227,14 @@ func (c *Client) Patch(path string, body any) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, nil, body, nil)
 }
 
-func (c *Client) PatchWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PATCH", path, nil, body, headers)
-}
-
 func (c *Client) PatchWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, body, nil)
 }
 
+func (c *Client) PatchWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PATCH", path, nil, body, headers)
+}
+
 func (c *Client) PatchWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, body, headers)
 }
@@ -306,6 +308,10 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		for k, v := range headerOverrides {
 			req.Header.Set(k, v)
 		}
+		binaryResponse := strings.EqualFold(req.Header.Get(BinaryResponseHeader), "true")
+		if binaryResponse {
+			req.Header.Del(BinaryResponseHeader)
+		}
 		if req.Header.Get("User-Agent") == "" {
 			req.Header.Set("User-Agent", "printing-press-oauth2-pp-cli/1.0.0")
 		}
@@ -321,7 +327,11 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		// per-endpoint headerOverrides, both of which run before this
 		// if-empty default.
 		if req.Header.Get("Accept") == "" {
-			req.Header.Set("Accept", "application/json")
+			if binaryResponse {
+				req.Header.Set("Accept", "*/*")
+			} else {
+				req.Header.Set("Accept", "application/json")
+			}
 		}
 
 		resp, err := c.HTTPClient.Do(req)
@@ -335,7 +345,9 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		if err != nil {
 			return nil, 0, fmt.Errorf("reading response: %w", err)
 		}
-		respBody = sanitizeJSONResponse(respBody)
+		if !binaryResponse {
+			respBody = sanitizeJSONResponse(respBody)
+		}
 
 		// Success
 		if resp.StatusCode < 400 {
diff --git a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/mcp/tools.go b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/mcp/tools.go
index 98853611..efe0916a 100644
--- a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/mcp/tools.go
+++ b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/mcp/tools.go
@@ -5,6 +5,7 @@ package mcp
 
 import (
 	"context"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"os"
@@ -31,7 +32,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/items", []mcpParamBinding{}, []string{}),
+		makeAPIHandler("GET", "/items", false, []mcpParamBinding{}, []string{}),
 	)
 	// SQL tool — ad-hoc analysis on synced data without API calls
 	s.AddTool(
@@ -67,7 +68,7 @@ type mcpParamBinding struct {
 }
 
 // makeAPIHandler creates a generic MCP tool handler for an API endpoint.
-func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
+func makeAPIHandler(method, pathTemplate string, binaryResponse bool, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
 	return func(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
 		c, err := newMCPClient()
 		if err != nil {
@@ -87,6 +88,10 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		pathParams := make(map[string]bool, len(positionalParams))
 		params := make(map[string]string)
 		bodyArgs := make(map[string]any)
+		var headers map[string]string
+		if binaryResponse {
+			headers = map[string]string{client.BinaryResponseHeader: "true"}
+		}
 		for _, binding := range bindings {
 			knownArgs[binding.PublicName] = true
 			v, ok := args[binding.PublicName]
@@ -130,14 +135,34 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		var data json.RawMessage
 		switch method {
 		case "GET":
+			if binaryResponse {
+				data, err = c.GetWithHeaders(path, params, headers)
+				break
+			}
 			data, err = c.Get(path, params)
 		case "POST":
+			if binaryResponse {
+				data, _, err = c.PostWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PostWithParams(path, params, bodyArgs)
 		case "PUT":
+			if binaryResponse {
+				data, _, err = c.PutWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PutWithParams(path, params, bodyArgs)
 		case "PATCH":
+			if binaryResponse {
+				data, _, err = c.PatchWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PatchWithParams(path, params, bodyArgs)
 		case "DELETE":
+			if binaryResponse {
+				data, _, err = c.DeleteWithParamsAndHeaders(path, params, headers)
+				break
+			}
 			data, _, err = c.DeleteWithParams(path, params)
 		default:
 			return mcplib.NewToolResultError("unsupported method: " + method), nil
@@ -190,6 +215,14 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 				}
 			}
 		}
+		if binaryResponse {
+			out, _ := json.Marshal(map[string]any{
+				"content_encoding": "base64",
+				"data_base64":      base64.StdEncoding.EncodeToString(data),
+				"byte_count":       len(data),
+			})
+			return mcplib.NewToolResultText(string(out)), nil
+		}
 		return mcplib.NewToolResultText(string(data)), nil
 	}
 }
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_avatar_upload-project.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_avatar_upload-project.go
index 9bf61373..3bddcf56 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_avatar_upload-project.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_avatar_upload-project.go
@@ -33,6 +33,10 @@ func newProjectsAvatarUploadProjectCmd(flags *rootFlags) *cobra.Command {
 
 			path := "/projects/{projectId}/avatar"
 			path = replacePathParam(path, "projectId", args[0])
+			params := map[string]string{}
+			if flagOverwrite != false {
+				params["overwrite"] = fmt.Sprintf("%v", flagOverwrite)
+			}
 			fields := map[string]string{}
 			fileFields := map[string]string{}
 			if bodyCaption != "" {
@@ -42,10 +46,6 @@ func newProjectsAvatarUploadProjectCmd(flags *rootFlags) *cobra.Command {
 				fileFields["file"] = bodyFile
 			}
 
-			params := map[string]string{}
-			if flagOverwrite != false {
-				params["overwrite"] = fmt.Sprintf("%v", flagOverwrite)
-			}
 			data, statusCode, err := c.PutMultipartWithParams(path, params, fields, fileFields)
 			if err != nil {
 				return classifyAPIError(err, flags)
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_create.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_create.go
index 952292b0..4f1e7b79 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_create.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_create.go
@@ -38,6 +38,7 @@ func newProjectsCreateCmd(flags *rootFlags) *cobra.Command {
 			}
 
 			path := "/projects"
+			params := map[string]string{}
 			var body map[string]any
 			if stdinBody {
 				stdinData, err := io.ReadAll(os.Stdin)
@@ -61,7 +62,7 @@ func newProjectsCreateCmd(flags *rootFlags) *cobra.Command {
 					body["visibility"] = bodyVisibility
 				}
 			}
-			data, statusCode, err := c.Post(path, body)
+			data, statusCode, err := c.PostWithParams(path, params, body)
 			if err != nil {
 				return classifyAPIError(err, flags)
 			}
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_tasks_update-project.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_tasks_update-project.go
index 86421865..0412e27f 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_tasks_update-project.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_tasks_update-project.go
@@ -42,6 +42,10 @@ func newProjectsTasksUpdateProjectCmd(flags *rootFlags) *cobra.Command {
 				return usageErr(fmt.Errorf("taskId is required\nUsage: %s <%s>", cmd.CommandPath(), "taskId"))
 			}
 			path = replacePathParam(path, "taskId", args[1])
+			params := map[string]string{}
+			if flagNotify != false {
+				params["notify"] = fmt.Sprintf("%v", flagNotify)
+			}
 			var body map[string]any
 			if stdinBody {
 				stdinData, err := io.ReadAll(os.Stdin)
@@ -65,10 +69,6 @@ func newProjectsTasksUpdateProjectCmd(flags *rootFlags) *cobra.Command {
 					body["title"] = bodyTitle
 				}
 			}
-			params := map[string]string{}
-			if flagNotify != false {
-				params["notify"] = fmt.Sprintf("%v", flagNotify)
-			}
 			data, statusCode, err := c.PatchWithParams(path, params, body)
 			if err != nil {
 				return classifyAPIError(err, flags)
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
index 12a6aba0..62713388 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
@@ -22,6 +22,8 @@ import (
 	"time"
 )
 
+const BinaryResponseHeader = "X-Printing-Press-Binary-Response"
+
 type Client struct {
 	BaseURL    string
 	Config     *config.Config
@@ -171,14 +173,14 @@ func (c *Client) Post(path string, body any) (json.RawMessage, int, error) {
 	return c.do("POST", path, nil, body, nil)
 }
 
-func (c *Client) PostWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("POST", path, nil, body, headers)
-}
-
 func (c *Client) PostWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, body, nil)
 }
 
+func (c *Client) PostWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("POST", path, nil, body, headers)
+}
+
 func (c *Client) PostWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, body, headers)
 }
@@ -186,14 +188,14 @@ func (c *Client) PostMultipart(path string, fields map[string]string, fileFields
 	return c.do("POST", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
-func (c *Client) PostMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("POST", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
-}
-
 func (c *Client) PostMultipartWithParams(path string, params map[string]string, fields map[string]string, fileFields map[string]string) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
+func (c *Client) PostMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("POST", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
+}
+
 func (c *Client) PostMultipartWithParamsAndHeaders(path string, params map[string]string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
 }
@@ -202,14 +204,14 @@ func (c *Client) Delete(path string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, nil, nil, nil)
 }
 
-func (c *Client) DeleteWithHeaders(path string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("DELETE", path, nil, nil, headers)
-}
-
 func (c *Client) DeleteWithParams(path string, params map[string]string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, params, nil, nil)
 }
 
+func (c *Client) DeleteWithHeaders(path string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("DELETE", path, nil, nil, headers)
+}
+
 func (c *Client) DeleteWithParamsAndHeaders(path string, params map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, params, nil, headers)
 }
@@ -218,14 +220,14 @@ func (c *Client) Put(path string, body any) (json.RawMessage, int, error) {
 	return c.do("PUT", path, nil, body, nil)
 }
 
-func (c *Client) PutWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PUT", path, nil, body, headers)
-}
-
 func (c *Client) PutWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, body, nil)
 }
 
+func (c *Client) PutWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PUT", path, nil, body, headers)
+}
+
 func (c *Client) PutWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, body, headers)
 }
@@ -233,14 +235,14 @@ func (c *Client) PutMultipart(path string, fields map[string]string, fileFields
 	return c.do("PUT", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
-func (c *Client) PutMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PUT", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
-}
-
 func (c *Client) PutMultipartWithParams(path string, params map[string]string, fields map[string]string, fileFields map[string]string) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
+func (c *Client) PutMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PUT", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
+}
+
 func (c *Client) PutMultipartWithParamsAndHeaders(path string, params map[string]string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
 }
@@ -249,14 +251,14 @@ func (c *Client) Patch(path string, body any) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, nil, body, nil)
 }
 
-func (c *Client) PatchWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PATCH", path, nil, body, headers)
-}
-
 func (c *Client) PatchWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, body, nil)
 }
 
+func (c *Client) PatchWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PATCH", path, nil, body, headers)
+}
+
 func (c *Client) PatchWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, body, headers)
 }
@@ -264,14 +266,14 @@ func (c *Client) PatchMultipart(path string, fields map[string]string, fileField
 	return c.do("PATCH", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
-func (c *Client) PatchMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PATCH", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
-}
-
 func (c *Client) PatchMultipartWithParams(path string, params map[string]string, fields map[string]string, fileFields map[string]string) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, nil)
 }
 
+func (c *Client) PatchMultipartWithHeaders(path string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PATCH", path, nil, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
+}
+
 func (c *Client) PatchMultipartWithParamsAndHeaders(path string, params map[string]string, fields map[string]string, fileFields map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, multipartRequestBody{Fields: fields, FileFields: fileFields}, headers)
 }
@@ -399,6 +401,10 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		for k, v := range headerOverrides {
 			req.Header.Set(k, v)
 		}
+		binaryResponse := strings.EqualFold(req.Header.Get(BinaryResponseHeader), "true")
+		if binaryResponse {
+			req.Header.Del(BinaryResponseHeader)
+		}
 		if req.Header.Get("User-Agent") == "" {
 			req.Header.Set("User-Agent", "printing-press-golden-pp-cli/2026.04")
 		}
@@ -414,7 +420,11 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		// per-endpoint headerOverrides, both of which run before this
 		// if-empty default.
 		if req.Header.Get("Accept") == "" {
-			req.Header.Set("Accept", "application/json")
+			if binaryResponse {
+				req.Header.Set("Accept", "*/*")
+			} else {
+				req.Header.Set("Accept", "application/json")
+			}
 		}
 
 		resp, err := c.HTTPClient.Do(req)
@@ -428,7 +438,9 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		if err != nil {
 			return nil, 0, fmt.Errorf("reading response: %w", err)
 		}
-		respBody = sanitizeJSONResponse(respBody)
+		if !binaryResponse {
+			respBody = sanitizeJSONResponse(respBody)
+		}
 
 		// Success
 		if resp.StatusCode < 400 {
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/mcp/tools.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/mcp/tools.go
index 67b50870..615a7001 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/mcp/tools.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/mcp/tools.go
@@ -5,6 +5,7 @@ package mcp
 
 import (
 	"context"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"os"
@@ -31,7 +32,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/currencies", []mcpParamBinding{}, []string{}),
+		makeAPIHandler("GET", "/currencies", false, []mcpParamBinding{}, []string{}),
 	)
 	s.AddTool(
 		mcplib.NewTool("projects_create",
@@ -42,7 +43,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("POST", "/projects", []mcpParamBinding{{PublicName: "name", WireName: "name", Location: "body"}, {PublicName: "owner_email", WireName: "owner_email", Location: "body"}, {PublicName: "visibility", WireName: "visibility", Location: "body"}}, []string{}),
+		makeAPIHandler("POST", "/projects", false, []mcpParamBinding{{PublicName: "name", WireName: "name", Location: "body"}, {PublicName: "owner_email", WireName: "owner_email", Location: "body"}, {PublicName: "visibility", WireName: "visibility", Location: "body"}}, []string{}),
 	)
 	s.AddTool(
 		mcplib.NewTool("projects_get",
@@ -52,7 +53,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/projects/{projectId}", []mcpParamBinding{{PublicName: "projectId", WireName: "projectId", Location: "path"}}, []string{"projectId"}),
+		makeAPIHandler("GET", "/projects/{projectId}", false, []mcpParamBinding{{PublicName: "projectId", WireName: "projectId", Location: "path"}}, []string{"projectId"}),
 	)
 	s.AddTool(
 		mcplib.NewTool("projects_list",
@@ -64,7 +65,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/projects", []mcpParamBinding{{PublicName: "status", WireName: "status", Location: "query"}, {PublicName: "limit", WireName: "limit", Location: "query"}, {PublicName: "cursor", WireName: "cursor", Location: "query"}}, []string{}),
+		makeAPIHandler("GET", "/projects", false, []mcpParamBinding{{PublicName: "status", WireName: "status", Location: "query"}, {PublicName: "limit", WireName: "limit", Location: "query"}, {PublicName: "cursor", WireName: "cursor", Location: "query"}}, []string{}),
 	)
 	s.AddTool(
 		mcplib.NewTool("projects_avatar_upload-project",
@@ -75,7 +76,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithString("file", mcplib.Description("File")),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("PUT", "/projects/{projectId}/avatar", []mcpParamBinding{{PublicName: "projectId", WireName: "projectId", Location: "path", RequestContentType: "multipart/form-data"}, {PublicName: "overwrite", WireName: "overwrite", Location: "query", RequestContentType: "multipart/form-data"}, {PublicName: "caption", WireName: "caption", Location: "body", RequestContentType: "multipart/form-data"}, {PublicName: "file", WireName: "file", Location: "body", Format: "binary", RequestContentType: "multipart/form-data"}}, []string{"projectId"}),
+		makeAPIHandler("PUT", "/projects/{projectId}/avatar", false, []mcpParamBinding{{PublicName: "projectId", WireName: "projectId", Location: "path", RequestContentType: "multipart/form-data"}, {PublicName: "overwrite", WireName: "overwrite", Location: "query", RequestContentType: "multipart/form-data"}, {PublicName: "caption", WireName: "caption", Location: "body", RequestContentType: "multipart/form-data"}, {PublicName: "file", WireName: "file", Location: "body", Format: "binary", RequestContentType: "multipart/form-data"}}, []string{"projectId"}),
 	)
 	s.AddTool(
 		mcplib.NewTool("projects_tasks_list-project",
@@ -88,7 +89,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/projects/{projectId}/tasks", []mcpParamBinding{{PublicName: "projectId", WireName: "projectId", Location: "path"}, {PublicName: "priority", WireName: "priority", Location: "query"}, {PublicName: "limit", WireName: "limit", Location: "query"}, {PublicName: "cursor", WireName: "cursor", Location: "query"}}, []string{"projectId"}),
+		makeAPIHandler("GET", "/projects/{projectId}/tasks", false, []mcpParamBinding{{PublicName: "projectId", WireName: "projectId", Location: "path"}, {PublicName: "priority", WireName: "priority", Location: "query"}, {PublicName: "limit", WireName: "limit", Location: "query"}, {PublicName: "cursor", WireName: "cursor", Location: "query"}}, []string{"projectId"}),
 	)
 	s.AddTool(
 		mcplib.NewTool("projects_tasks_update-project",
@@ -101,7 +102,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithString("title", mcplib.Description("Title")),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("PATCH", "/projects/{projectId}/tasks/{taskId}", []mcpParamBinding{{PublicName: "projectId", WireName: "projectId", Location: "path"}, {PublicName: "taskId", WireName: "taskId", Location: "path"}, {PublicName: "notify", WireName: "notify", Location: "query"}, {PublicName: "completed", WireName: "completed", Location: "body"}, {PublicName: "priority", WireName: "priority", Location: "body"}, {PublicName: "title", WireName: "title", Location: "body"}}, []string{"projectId", "taskId"}),
+		makeAPIHandler("PATCH", "/projects/{projectId}/tasks/{taskId}", false, []mcpParamBinding{{PublicName: "projectId", WireName: "projectId", Location: "path"}, {PublicName: "taskId", WireName: "taskId", Location: "path"}, {PublicName: "notify", WireName: "notify", Location: "query"}, {PublicName: "completed", WireName: "completed", Location: "body"}, {PublicName: "priority", WireName: "priority", Location: "body"}, {PublicName: "title", WireName: "title", Location: "body"}}, []string{"projectId", "taskId"}),
 	)
 	s.AddTool(
 		mcplib.NewTool("public_get-status",
@@ -110,7 +111,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/public/status", []mcpParamBinding{}, []string{}),
+		makeAPIHandler("GET", "/public/status", false, []mcpParamBinding{}, []string{}),
 	)
 	s.AddTool(
 		mcplib.NewTool("reports_summary_get-report-year",
@@ -120,7 +121,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/reports/{year}/summary", []mcpParamBinding{{PublicName: "year", WireName: "year", Location: "path"}}, []string{"year"}),
+		makeAPIHandler("GET", "/reports/{year}/summary", false, []mcpParamBinding{{PublicName: "year", WireName: "year", Location: "path"}}, []string{"year"}),
 	)
 	// Search tool — faster than iterating list endpoints for finding specific items
 	s.AddTool(
@@ -179,7 +180,7 @@ func mcpMultipartFieldValue(v any) string {
 }
 
 // makeAPIHandler creates a generic MCP tool handler for an API endpoint.
-func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
+func makeAPIHandler(method, pathTemplate string, binaryResponse bool, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
 	return func(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
 		c, err := newMCPClient()
 		if err != nil {
@@ -199,6 +200,10 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		pathParams := make(map[string]bool, len(positionalParams))
 		params := make(map[string]string)
 		bodyArgs := make(map[string]any)
+		var headers map[string]string
+		if binaryResponse {
+			headers = map[string]string{client.BinaryResponseHeader: "true"}
+		}
 		multipartFields := make(map[string]string)
 		multipartFileFields := make(map[string]string)
 		multipart := false
@@ -258,26 +263,58 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		var data json.RawMessage
 		switch method {
 		case "GET":
+			if binaryResponse {
+				data, err = c.GetWithHeaders(path, params, headers)
+				break
+			}
 			data, err = c.Get(path, params)
 		case "POST":
 			if multipart {
+				if binaryResponse {
+					data, _, err = c.PostMultipartWithParamsAndHeaders(path, params, multipartFields, multipartFileFields, headers)
+					break
+				}
 				data, _, err = c.PostMultipartWithParams(path, params, multipartFields, multipartFileFields)
 				break
 			}
+			if binaryResponse {
+				data, _, err = c.PostWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PostWithParams(path, params, bodyArgs)
 		case "PUT":
 			if multipart {
+				if binaryResponse {
+					data, _, err = c.PutMultipartWithParamsAndHeaders(path, params, multipartFields, multipartFileFields, headers)
+					break
+				}
 				data, _, err = c.PutMultipartWithParams(path, params, multipartFields, multipartFileFields)
 				break
 			}
+			if binaryResponse {
+				data, _, err = c.PutWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PutWithParams(path, params, bodyArgs)
 		case "PATCH":
 			if multipart {
+				if binaryResponse {
+					data, _, err = c.PatchMultipartWithParamsAndHeaders(path, params, multipartFields, multipartFileFields, headers)
+					break
+				}
 				data, _, err = c.PatchMultipartWithParams(path, params, multipartFields, multipartFileFields)
 				break
 			}
+			if binaryResponse {
+				data, _, err = c.PatchWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PatchWithParams(path, params, bodyArgs)
 		case "DELETE":
+			if binaryResponse {
+				data, _, err = c.DeleteWithParamsAndHeaders(path, params, headers)
+				break
+			}
 			data, _, err = c.DeleteWithParams(path, params)
 		default:
 			return mcplib.NewToolResultError("unsupported method: " + method), nil
@@ -330,6 +367,14 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 				}
 			}
 		}
+		if binaryResponse {
+			out, _ := json.Marshal(map[string]any{
+				"content_encoding": "base64",
+				"data_base64":      base64.StdEncoding.EncodeToString(data),
+				"byte_count":       len(data),
+			})
+			return mcplib.NewToolResultText(string(out)), nil
+		}
 		return mcplib.NewToolResultText(string(data)), nil
 	}
 }
diff --git a/testdata/golden/expected/generate-graphql-shared-endpoint/graphql-shared-golden/internal/cli/things_get.go b/testdata/golden/expected/generate-graphql-shared-endpoint/graphql-shared-golden/internal/cli/things_get.go
index faed9b13..2f04632c 100644
--- a/testdata/golden/expected/generate-graphql-shared-endpoint/graphql-shared-golden/internal/cli/things_get.go
+++ b/testdata/golden/expected/generate-graphql-shared-endpoint/graphql-shared-golden/internal/cli/things_get.go
@@ -33,6 +33,8 @@ func newThingsGetCmd(flags *rootFlags) *cobra.Command {
 
 			path := "/graphql"
 			_ = path
+			params := map[string]string{}
+			params["thing_id"] = args[0]
 			var body map[string]any
 			if stdinBody {
 				stdinData, err := io.ReadAll(os.Stdin)
@@ -47,7 +49,7 @@ func newThingsGetCmd(flags *rootFlags) *cobra.Command {
 			} else {
 				body = map[string]any{}
 			}
-			data, statusCode, err := c.Post(path, body)
+			data, statusCode, err := c.PostWithParams(path, params, body)
 			if err != nil {
 				return classifyAPIError(err, flags)
 			}
diff --git a/testdata/golden/expected/generate-graphql-shared-endpoint/graphql-shared-golden/internal/cli/things_list.go b/testdata/golden/expected/generate-graphql-shared-endpoint/graphql-shared-golden/internal/cli/things_list.go
index 5263ff0d..b12420c0 100644
--- a/testdata/golden/expected/generate-graphql-shared-endpoint/graphql-shared-golden/internal/cli/things_list.go
+++ b/testdata/golden/expected/generate-graphql-shared-endpoint/graphql-shared-golden/internal/cli/things_list.go
@@ -30,6 +30,7 @@ func newThingsListCmd(flags *rootFlags) *cobra.Command {
 
 			path := "/graphql"
 			_ = path
+			params := map[string]string{}
 			var body map[string]any
 			if stdinBody {
 				stdinData, err := io.ReadAll(os.Stdin)
@@ -44,7 +45,7 @@ func newThingsListCmd(flags *rootFlags) *cobra.Command {
 			} else {
 				body = map[string]any{}
 			}
-			data, statusCode, err := c.Post(path, body)
+			data, statusCode, err := c.PostWithParams(path, params, body)
 			if err != nil {
 				return classifyAPIError(err, flags)
 			}
diff --git a/testdata/golden/expected/generate-mcp-api/mcp-cloudflare/internal/mcp/tools.go b/testdata/golden/expected/generate-mcp-api/mcp-cloudflare/internal/mcp/tools.go
index e49b33b9..8264a877 100644
--- a/testdata/golden/expected/generate-mcp-api/mcp-cloudflare/internal/mcp/tools.go
+++ b/testdata/golden/expected/generate-mcp-api/mcp-cloudflare/internal/mcp/tools.go
@@ -5,6 +5,7 @@ package mcp
 
 import (
 	"context"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"os"
@@ -61,7 +62,7 @@ type mcpParamBinding struct {
 }
 
 // makeAPIHandler creates a generic MCP tool handler for an API endpoint.
-func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
+func makeAPIHandler(method, pathTemplate string, binaryResponse bool, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
 	return func(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
 		c, err := newMCPClient()
 		if err != nil {
@@ -81,6 +82,10 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		pathParams := make(map[string]bool, len(positionalParams))
 		params := make(map[string]string)
 		bodyArgs := make(map[string]any)
+		var headers map[string]string
+		if binaryResponse {
+			headers = map[string]string{client.BinaryResponseHeader: "true"}
+		}
 		for _, binding := range bindings {
 			knownArgs[binding.PublicName] = true
 			v, ok := args[binding.PublicName]
@@ -124,14 +129,34 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		var data json.RawMessage
 		switch method {
 		case "GET":
+			if binaryResponse {
+				data, err = c.GetWithHeaders(path, params, headers)
+				break
+			}
 			data, err = c.Get(path, params)
 		case "POST":
+			if binaryResponse {
+				data, _, err = c.PostWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PostWithParams(path, params, bodyArgs)
 		case "PUT":
+			if binaryResponse {
+				data, _, err = c.PutWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PutWithParams(path, params, bodyArgs)
 		case "PATCH":
+			if binaryResponse {
+				data, _, err = c.PatchWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PatchWithParams(path, params, bodyArgs)
 		case "DELETE":
+			if binaryResponse {
+				data, _, err = c.DeleteWithParamsAndHeaders(path, params, headers)
+				break
+			}
 			data, _, err = c.DeleteWithParams(path, params)
 		default:
 			return mcplib.NewToolResultError("unsupported method: " + method), nil
@@ -184,6 +209,14 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 				}
 			}
 		}
+		if binaryResponse {
+			out, _ := json.Marshal(map[string]any{
+				"content_encoding": "base64",
+				"data_base64":      base64.StdEncoding.EncodeToString(data),
+				"byte_count":       len(data),
+			})
+			return mcplib.NewToolResultText(string(out)), nil
+		}
 		return mcplib.NewToolResultText(string(data)), nil
 	}
 }
diff --git a/testdata/golden/expected/generate-public-param-names/public-param-golden/internal/cli/stores_create.go b/testdata/golden/expected/generate-public-param-names/public-param-golden/internal/cli/stores_create.go
index 8fe13fd8..c8070688 100644
--- a/testdata/golden/expected/generate-public-param-names/public-param-golden/internal/cli/stores_create.go
+++ b/testdata/golden/expected/generate-public-param-names/public-param-golden/internal/cli/stores_create.go
@@ -33,6 +33,7 @@ func newStoresCreateCmd(flags *rootFlags) *cobra.Command {
 			}
 
 			path := "/stores"
+			params := map[string]string{}
 			var body map[string]any
 			if stdinBody {
 				stdinData, err := io.ReadAll(os.Stdin)
@@ -50,7 +51,7 @@ func newStoresCreateCmd(flags *rootFlags) *cobra.Command {
 					body["store_code"] = bodyStoreCode
 				}
 			}
-			data, statusCode, err := c.Post(path, body)
+			data, statusCode, err := c.PostWithParams(path, params, body)
 			if err != nil {
 				return classifyAPIError(err, flags)
 			}
diff --git a/testdata/golden/expected/generate-public-param-names/public-param-golden/internal/mcp/tools.go b/testdata/golden/expected/generate-public-param-names/public-param-golden/internal/mcp/tools.go
index 25b3c856..4c14a760 100644
--- a/testdata/golden/expected/generate-public-param-names/public-param-golden/internal/mcp/tools.go
+++ b/testdata/golden/expected/generate-public-param-names/public-param-golden/internal/mcp/tools.go
@@ -5,6 +5,7 @@ package mcp
 
 import (
 	"context"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"os"
@@ -29,7 +30,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("POST", "/stores", []mcpParamBinding{{PublicName: "store-code", WireName: "store_code", Location: "body"}}, []string{}),
+		makeAPIHandler("POST", "/stores", false, []mcpParamBinding{{PublicName: "store-code", WireName: "store_code", Location: "body"}}, []string{}),
 	)
 	s.AddTool(
 		mcplib.NewTool("stores_find",
@@ -40,7 +41,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/power/store-locator", []mcpParamBinding{{PublicName: "address", WireName: "s", Location: "query"}, {PublicName: "city", WireName: "c", Location: "query"}}, []string{}),
+		makeAPIHandler("GET", "/power/store-locator", false, []mcpParamBinding{{PublicName: "address", WireName: "s", Location: "query"}, {PublicName: "city", WireName: "c", Location: "query"}}, []string{}),
 	)
 
 	// Context tool — front-loaded domain knowledge for agents.
@@ -66,7 +67,7 @@ type mcpParamBinding struct {
 }
 
 // makeAPIHandler creates a generic MCP tool handler for an API endpoint.
-func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
+func makeAPIHandler(method, pathTemplate string, binaryResponse bool, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
 	return func(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
 		c, err := newMCPClient()
 		if err != nil {
@@ -86,6 +87,10 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		pathParams := make(map[string]bool, len(positionalParams))
 		params := make(map[string]string)
 		bodyArgs := make(map[string]any)
+		var headers map[string]string
+		if binaryResponse {
+			headers = map[string]string{client.BinaryResponseHeader: "true"}
+		}
 		for _, binding := range bindings {
 			knownArgs[binding.PublicName] = true
 			v, ok := args[binding.PublicName]
@@ -129,14 +134,34 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 		var data json.RawMessage
 		switch method {
 		case "GET":
+			if binaryResponse {
+				data, err = c.GetWithHeaders(path, params, headers)
+				break
+			}
 			data, err = c.Get(path, params)
 		case "POST":
+			if binaryResponse {
+				data, _, err = c.PostWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PostWithParams(path, params, bodyArgs)
 		case "PUT":
+			if binaryResponse {
+				data, _, err = c.PutWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PutWithParams(path, params, bodyArgs)
 		case "PATCH":
+			if binaryResponse {
+				data, _, err = c.PatchWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PatchWithParams(path, params, bodyArgs)
 		case "DELETE":
+			if binaryResponse {
+				data, _, err = c.DeleteWithParamsAndHeaders(path, params, headers)
+				break
+			}
 			data, _, err = c.DeleteWithParams(path, params)
 		default:
 			return mcplib.NewToolResultError("unsupported method: " + method), nil
@@ -182,6 +207,14 @@ func makeAPIHandler(method, pathTemplate string, bindings []mcpParamBinding, pos
 				}
 			}
 		}
+		if binaryResponse {
+			out, _ := json.Marshal(map[string]any{
+				"content_encoding": "base64",
+				"data_base64":      base64.StdEncoding.EncodeToString(data),
+				"byte_count":       len(data),
+			})
+			return mcplib.NewToolResultText(string(out)), nil
+		}
 		return mcplib.NewToolResultText(string(data)), nil
 	}
 }
diff --git a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
index 585769f7..e5f04e40 100644
--- a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
+++ b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
@@ -21,6 +21,8 @@ import (
 	"time"
 )
 
+const BinaryResponseHeader = "X-Printing-Press-Binary-Response"
+
 type Client struct {
 	BaseURL     string
 	Config      *config.Config
@@ -266,14 +268,14 @@ func (c *Client) Post(path string, body any) (json.RawMessage, int, error) {
 	return c.do("POST", path, nil, body, nil)
 }
 
-func (c *Client) PostWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("POST", path, nil, body, headers)
-}
-
 func (c *Client) PostWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, body, nil)
 }
 
+func (c *Client) PostWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("POST", path, nil, body, headers)
+}
+
 func (c *Client) PostWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("POST", path, params, body, headers)
 }
@@ -282,14 +284,14 @@ func (c *Client) Delete(path string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, nil, nil, nil)
 }
 
-func (c *Client) DeleteWithHeaders(path string, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("DELETE", path, nil, nil, headers)
-}
-
 func (c *Client) DeleteWithParams(path string, params map[string]string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, params, nil, nil)
 }
 
+func (c *Client) DeleteWithHeaders(path string, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("DELETE", path, nil, nil, headers)
+}
+
 func (c *Client) DeleteWithParamsAndHeaders(path string, params map[string]string, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("DELETE", path, params, nil, headers)
 }
@@ -298,14 +300,14 @@ func (c *Client) Put(path string, body any) (json.RawMessage, int, error) {
 	return c.do("PUT", path, nil, body, nil)
 }
 
-func (c *Client) PutWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PUT", path, nil, body, headers)
-}
-
 func (c *Client) PutWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, body, nil)
 }
 
+func (c *Client) PutWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PUT", path, nil, body, headers)
+}
+
 func (c *Client) PutWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PUT", path, params, body, headers)
 }
@@ -314,14 +316,14 @@ func (c *Client) Patch(path string, body any) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, nil, body, nil)
 }
 
-func (c *Client) PatchWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
-	return c.do("PATCH", path, nil, body, headers)
-}
-
 func (c *Client) PatchWithParams(path string, params map[string]string, body any) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, body, nil)
 }
 
+func (c *Client) PatchWithHeaders(path string, body any, headers map[string]string) (json.RawMessage, int, error) {
+	return c.do("PATCH", path, nil, body, headers)
+}
+
 func (c *Client) PatchWithParamsAndHeaders(path string, params map[string]string, body any, headers map[string]string) (json.RawMessage, int, error) {
 	return c.do("PATCH", path, params, body, headers)
 }
@@ -403,6 +405,10 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		for k, v := range headerOverrides {
 			req.Header.Set(k, v)
 		}
+		binaryResponse := strings.EqualFold(req.Header.Get(BinaryResponseHeader), "true")
+		if binaryResponse {
+			req.Header.Del(BinaryResponseHeader)
+		}
 		if req.Header.Get("User-Agent") == "" {
 			req.Header.Set("User-Agent", "tier-routing-golden-pp-cli/1.0.0")
 		}
@@ -418,7 +424,11 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		// per-endpoint headerOverrides, both of which run before this
 		// if-empty default.
 		if req.Header.Get("Accept") == "" {
-			req.Header.Set("Accept", "application/json")
+			if binaryResponse {
+				req.Header.Set("Accept", "*/*")
+			} else {
+				req.Header.Set("Accept", "application/json")
+			}
 		}
 
 		resp, err := c.HTTPClient.Do(req)
@@ -432,7 +442,9 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		if err != nil {
 			return nil, 0, fmt.Errorf("reading response: %w", err)
 		}
-		respBody = sanitizeJSONResponse(respBody)
+		if !binaryResponse {
+			respBody = sanitizeJSONResponse(respBody)
+		}
 
 		// Success
 		if resp.StatusCode < 400 {
diff --git a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/mcp/tools.go b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/mcp/tools.go
index aa25d051..d9c8b975 100644
--- a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/mcp/tools.go
+++ b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/mcp/tools.go
@@ -5,6 +5,7 @@ package mcp
 
 import (
 	"context"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"os"
@@ -31,7 +32,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/items/enterprise", "enterprise", []mcpParamBinding{}, []string{}),
+		makeAPIHandler("GET", "/items/enterprise", "enterprise", false, []mcpParamBinding{}, []string{}),
 	)
 	s.AddTool(
 		mcplib.NewTool("items_list",
@@ -40,7 +41,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/items", "free", []mcpParamBinding{}, []string{}),
+		makeAPIHandler("GET", "/items", "free", false, []mcpParamBinding{}, []string{}),
 	)
 	s.AddTool(
 		mcplib.NewTool("items_premium",
@@ -49,7 +50,7 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDestructiveHintAnnotation(false),
 			mcplib.WithOpenWorldHintAnnotation(true),
 		),
-		makeAPIHandler("GET", "/items/premium", "paid", []mcpParamBinding{}, []string{}),
+		makeAPIHandler("GET", "/items/premium", "paid", false, []mcpParamBinding{}, []string{}),
 	)
 	// SQL tool — ad-hoc analysis on synced data without API calls
 	s.AddTool(
@@ -85,7 +86,7 @@ type mcpParamBinding struct {
 }
 
 // makeAPIHandler creates a generic MCP tool handler for an API endpoint.
-func makeAPIHandler(method, pathTemplate, tier string, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
+func makeAPIHandler(method, pathTemplate, tier string, binaryResponse bool, bindings []mcpParamBinding, positionalParams []string) server.ToolHandlerFunc {
 	return func(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
 		c, err := newMCPClient()
 		if err != nil {
@@ -106,6 +107,10 @@ func makeAPIHandler(method, pathTemplate, tier string, bindings []mcpParamBindin
 		pathParams := make(map[string]bool, len(positionalParams))
 		params := make(map[string]string)
 		bodyArgs := make(map[string]any)
+		var headers map[string]string
+		if binaryResponse {
+			headers = map[string]string{client.BinaryResponseHeader: "true"}
+		}
 		for _, binding := range bindings {
 			knownArgs[binding.PublicName] = true
 			v, ok := args[binding.PublicName]
@@ -149,14 +154,34 @@ func makeAPIHandler(method, pathTemplate, tier string, bindings []mcpParamBindin
 		var data json.RawMessage
 		switch method {
 		case "GET":
+			if binaryResponse {
+				data, err = c.GetWithHeaders(path, params, headers)
+				break
+			}
 			data, err = c.Get(path, params)
 		case "POST":
+			if binaryResponse {
+				data, _, err = c.PostWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PostWithParams(path, params, bodyArgs)
 		case "PUT":
+			if binaryResponse {
+				data, _, err = c.PutWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PutWithParams(path, params, bodyArgs)
 		case "PATCH":
+			if binaryResponse {
+				data, _, err = c.PatchWithParamsAndHeaders(path, params, bodyArgs, headers)
+				break
+			}
 			data, _, err = c.PatchWithParams(path, params, bodyArgs)
 		case "DELETE":
+			if binaryResponse {
+				data, _, err = c.DeleteWithParamsAndHeaders(path, params, headers)
+				break
+			}
 			data, _, err = c.DeleteWithParams(path, params)
 		default:
 			return mcplib.NewToolResultError("unsupported method: " + method), nil
@@ -209,6 +234,14 @@ func makeAPIHandler(method, pathTemplate, tier string, bindings []mcpParamBindin
 				}
 			}
 		}
+		if binaryResponse {
+			out, _ := json.Marshal(map[string]any{
+				"content_encoding": "base64",
+				"data_base64":      base64.StdEncoding.EncodeToString(data),
+				"byte_count":       len(data),
+			})
+			return mcplib.NewToolResultText(string(out)), nil
+		}
 		return mcplib.NewToolResultText(string(data)), nil
 	}
 }

← 55f5b26b fix(cli): expand pm_stale timestamp-field list for non-updat  ·  back to Cli Printing Press  ·  fix(cli): short-circuit sync on dry-run sentinel response (# f8d3ba80 →