Files
Trevin Chow 48082dbd14 fix(cli): synthesize happy-args instead of TODO examples (#4650)
* fix(cli): synthesize happy-args instead of TODO examples

Derive runnable pp:happy-args and Example strings from spec
example/enum/default/format at generate time. Omit example-value
placeholders from emitted Example lines rather than shipping
copy-paste 400s. Leave opaque IDs and other underivable required
inputs unset so live dogfood does not invent values.

Closes #4258

Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com>

* test(cli): update goldens for synthesized happy-args

Refresh generate-golden-api and generate-public-param-names fixtures
so required params with spec defaults emit runnable Example strings
and pp:happy-args, and underivable placeholders are omitted from
Cobra Example lines.

Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com>

* fix(cli): omit invented Cobra examples; reject unencodable happy-args

Apply the same required-input derivability gate to synthesized Cobra
Example strings as to pp:happy-args, and refuse values with literal
backslashes that splitHappyArgs cannot round-trip.

Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com>

* fix(cli): skip underivable optional positionals in happy-args

Optional positionals no longer fail closed during synthesis. Required
positionals still block; underivable optionals are omitted from
pp:happy-args and synthesized Cobra examples.

Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com>

* fix(cli): fail closed on non-trailing optional positionals

Happy-args overlays positionals by index after dropping labels, so
omitting a mid-list optional rebinds later values. Skip only a trailing
underivable-optional suffix; otherwise leave synthesis unset.

Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com>
2026-09-09 13:06:34 +00:00

70 KiB

OpenAPI Extensions

This document is the canonical reference for Printing Press-specific OpenAPI x-* extensions. OpenAPI allows extension fields anywhere, but the Printing Press only reads the extensions listed here.

Source of truth: internal/openapi/parser.go. This document should be updated in the same change as any new Extensions["x-*"] lookup in that file.

Summary

Extension Location Parsed field Required
x-api-name info APISpec.Name No
x-display-name info APISpec.DisplayName No
x-website info APISpec.WebsiteURL No
x-proxy-routes info APISpec.ProxyRoutes No
x-origin info Google Discovery resource fallback No
x-providerName info Google Discovery resource fallback No
x-roles root or info APISpec.Roles No
x-tier-routing root or info APISpec.TierRouting No
x-rate-class root or info APISpec.RateClass No
x-pp-default-rate-limit root or info APISpec.DefaultRateLimit No
x-mcp root or info APISpec.MCP No
x-cache root or info APISpec.Cache No
x-learn root or info APISpec.Learn No
x-pp-query root APISpec.QuerySync No
x-pp-response-envelope root or info APISpec.ResponseEnvelopeKey No
x-auth-type components.securitySchemes.<name> APISpec.Auth.Type No
x-auth-format components.securitySchemes.<name> APISpec.Auth.Format No
x-prefix components.securitySchemes.<name> APISpec.Auth.Format No
x-auth-env-vars components.securitySchemes.<name> APISpec.Auth.EnvVars No
x-auth-vars components.securitySchemes.<name> APISpec.Auth.EnvVarSpecs No
x-speakeasy-example components.securitySchemes.<name> APISpec.Auth.EnvVars No
x-auth-optional components.securitySchemes.<name> APISpec.Auth.Optional No
x-auth-key-url components.securitySchemes.<name> APISpec.Auth.KeyURL No
x-auth-title components.securitySchemes.<name> APISpec.Auth.Title No
x-auth-description components.securitySchemes.<name> APISpec.Auth.Description No
x-auth-subtype components.securitySchemes.<name> APISpec.Auth.Subtype No
x-auth-cookie-domain components.securitySchemes.<name> APISpec.Auth.CookieDomain No
x-auth-cookies components.securitySchemes.<name> APISpec.Auth.Cookies No
x-auth-companion components.securitySchemes.<name> or info APISpec.Auth.LoginURL, LoginCompleteSelector, JWTCarrierCookie No
x-oauth-device-flow components.securitySchemes.<name> APISpec.Auth.OAuth2Grant, DeviceAuthorizationURL, TokenURL, Scopes, DefaultClientID No
x-oauth-refresh-token-mechanism components.securitySchemes.<name> APISpec.Auth.RefreshTokenMechanism No
x-resource-id path item Endpoint.IDField No
x-critical path item Endpoint.Critical No
x-tier path item or operation Endpoint.Tier No
x-data-source-strategy path item or operation Endpoint.DataSourceStrategy No
x-live-dogfood-requires-tier path item or operation Endpoint.LiveDogfoodRequiresTier No
x-requires-role operation Endpoint.RequiresRole No
x-happy-args operation Endpoint.HappyArgs No
x-happy-stdin operation Endpoint.HappyStdin No
x-pp-example operation Endpoint.Example (verbatim Cobra example override) No
x-pp-resource operation resource name override No
x-pp-pagination operation Endpoint.Pagination No
x-pp-mutation operation Endpoint.Mutation No
x-pp-safe-probe operation skill guidance only; not parsed in parser.go No
x-pp-sync-walker operation Endpoint.Walker No
x-sync-params operation Endpoint.SyncParams No
x-pp-dispatch-param parameter Param.DispatchParam No
x-pp-tenant-scope-column path item Endpoint.TenantScopeColumn No
x-pp-membership-field path item Endpoint.MembershipField No

info Extensions

x-api-name

Overrides the API slug only when info.title does not fold to a usable slug. The parser first applies its normal name cleaning to info.title; x-api-name is only consulted when that result is empty or api.

Parsed field: APISpec.Name

Rules:

  • Optional.
  • Must be a string.
  • Cleaned with the same slug normalization as info.title.
  • Ignored when the cleaned value is empty or api.
  • Ignored when info.title already produced a usable slug.

Example:

info:
  title: API
  version: "1.0"
  x-api-name: example-service

x-display-name

Preserves the human-readable brand name when slug-derived title casing would deform it.

Parsed field: APISpec.DisplayName

Rules:

  • Optional.
  • Must be a string.
  • Leading and trailing whitespace is trimmed.
  • Empty or non-string values leave DisplayName empty, so downstream code falls back to spec metadata or slug-derived naming.
  • The parser does not enforce a length cap for x-display-name. The separate registry.json display-name fallback used by mcp-sync rejects registry values longer than 40 characters, but that limit does not apply here.

Example:

info:
  title: Cal Com
  version: "1.0"
  x-display-name: Cal.com

x-website

Provides a product or vendor website URL when standard OpenAPI metadata does not carry one.

Parsed field: APISpec.WebsiteURL

Rules:

  • Optional.
  • Must be a string.
  • Used only when info.contact.url is absent.
  • externalDocs.url is used after x-website if no website URL has been found.
  • The parser does not validate the URL shape.

Example:

info:
  title: Example Service
  version: "1.0"
  x-website: https://www.example.com

x-proxy-routes

Declares route-to-service mapping for the proxy-envelope client pattern.

Parsed field: APISpec.ProxyRoutes

Rules:

  • Optional.
  • Must be a map.
  • Map keys are path prefixes.
  • Map values must be strings; non-string values are skipped.
  • A missing or malformed map leaves ProxyRoutes empty.

Example:

info:
  title: Example Service
  version: "1.0"
  x-proxy-routes:
    /v1/search: search
    /v1/publish: publishing

x-origin / x-providerName

Recognized on Google Discovery specs converted by apis.guru. These extensions do not populate an APISpec field; they gate the parser's operationId-based resource fallback for paths such as /v2/{name} and /{resource}:getIamPolicy.

Rules:

  • Optional.
  • x-providerName: googleapis.com enables Google Discovery resource fallback.
  • x-origin enables the fallback when any entry has format: google or a Discovery URL under googleapis.com/$discovery.
  • Ignored for non-Google specs.

x-tier-routing

Declares opt-in free/paid credential routing for APIs where some endpoints work without credentials and other endpoints require a separate paid key or token.

Parsed field: APISpec.TierRouting

Rules:

  • Optional.
  • May be declared at the OpenAPI root or under info.
  • Requires a tiers map when present.
  • default_tier is optional; endpoints without x-tier use global auth when it is absent.
  • V1 tier auth supports only none, api_key, and bearer_token.
  • Credential-bearing tier base_url values must be HTTPS and cannot point at loopback, private, link-local, or unrelated hosts unless allow_cross_host_auth: true documents explicit review.
  • Incompatible with client_pattern: proxy-envelope and with resource- or endpoint-level base_url overrides when any tier declares its own base_url.
  • Tier credential env vars are read from the environment at request time; they are not serialized into generated config files.

Example:

x-tier-routing:
  default_tier: free
  tiers:
    free:
      auth:
        type: none
    paid:
      base_url: https://paid.api.example.com
      auth:
        type: api_key
        in: query
        header: api_key
        env_vars: [EXAMPLE_PAID_KEY]

x-roles

Declares the authenticated persona labels that operation-level RBAC gates may reference.

Parsed field: APISpec.Roles

Rules:

  • Optional.
  • May be declared at the OpenAPI root or under info.
  • Must be a string list.
  • Each role must match ^[A-Za-z][A-Za-z0-9_-]*$.
  • Every operation-level x-requires-role value must name one declared role.

Example:

x-roles: [parent, student, teacher, admin]

x-rate-class

Declares the API's rate-limit operating point so generated sync defaults can avoid wasteful parallelism on low-total-budget APIs.

Parsed field: APISpec.RateClass

Rules:

  • Optional.
  • May be declared at the OpenAPI root or under info. Root takes precedence when both are present.
  • Must be a string.
  • Accepted values are per-second, daily, monthly, and unlimited.
  • daily and monthly generate sync --concurrency with a default of 1. per-second, unlimited, and absent keep the default of 4.
  • This only changes generated sync worker defaults. It does not add runtime rate limiting, retries, or backoff.

Example:

info:
  title: Low Quota API
  version: "1.0"
  x-rate-class: monthly

x-pp-default-rate-limit

Sets the built-in default for the generated CLI's --rate-limit flag from an OpenAPI spec, mirroring the internal YAML default_rate_limit field.

Parsed field: APISpec.DefaultRateLimit

Rules:

  • Optional.
  • May be declared at the OpenAPI root or under info. Root takes precedence when both are present.
  • The value is either the string "auto" (case-insensitive) or a non-negative number (a JSON number or a numeric string). Any other shape is rejected with a validation error.
  • "auto" selects the header-driven adaptive limiter so the CLI paces itself to the server's X-Ratelimit-* headers with no hardcoded ceiling. A numeric value (e.g. 2) pins a fixed requests-per-second ceiling.
  • When absent, the generated --rate-limit default is client.RateLimitAuto (the same as "auto"), for both sniffed and documented specs. This only sets the default; the generated --rate-limit flag still overrides it at runtime.

Example:

info:
  title: Throttled API
  version: "1.0"
  x-pp-default-rate-limit: auto

x-mcp

Declares MCP server shape for the generated CLI. Mirrors the internal YAML spec's top-level mcp: block so OpenAPI specs can opt into the same pre-generation MCP enrichment recipe (notably the code-orchestration pattern for large surfaces: transport: [stdio, http] + orchestration: code + endpoint_tools: hidden).

Parsed field: APISpec.MCP (spec.MCPConfig)

Rules:

  • Optional. Specs without x-mcp get the endpoint-mirror surface while they remain at or below the orchestration threshold. Small APIs (typed-endpoint count at or below spec.DefaultRemoteTransportEndpointThreshold, currently 30) also get the http transport compiled in alongside stdio so the same binary can reach cloud-hosted agents. Large APIs above spec.DefaultOrchestrationThreshold (currently 50) default to the Cloudflare MCP pattern (transport: [stdio, http], orchestration: code, and endpoint_tools: hidden) when orchestration is unset. Set orchestration: endpoint-mirror to opt out. Setting transport explicitly (including transport: [stdio]) bypasses the transport default and is honored as-is.
  • May be declared at the OpenAPI root or under info. Root takes precedence when both are present.
  • For backwards compatibility, root-level mcp: is also accepted when canonical x-mcp is absent. The parser emits a warning asking authors to rename it to x-mcp. x-mcp and mcp are never merged; any canonical x-mcp declaration, including under info, wins as a complete config.
  • Shape mirrors the internal YAML mcp: block field-for-field: transport, addr, intents, endpoint_tools, orchestration, orchestration_threshold.
  • Validated by validateMCP at spec load (same allowlist as internal YAML): unknown transports and malformed addresses are rejected.

Example:

x-mcp:
  transport: [stdio, http]
  orchestration: code
  endpoint_tools: hidden

x-cache

Declares cache-freshness and auto-refresh behavior for generated CLIs. Mirrors the internal YAML spec's top-level cache: block so OpenAPI specs with a store-backed sync surface can opt into the same freshness machinery.

Parsed field: APISpec.Cache (spec.CacheConfig)

Rules:

  • Optional. Specs without x-cache keep today's behavior: no freshness helper or auto-refresh hook is emitted unless cache is configured elsewhere.
  • May be declared at the OpenAPI root or under info. Root takes precedence when both are present.
  • Shape mirrors the internal YAML cache: block field-for-field: enabled, stale_after, refresh_timeout, env_opt_out, resources, commands.
  • Validated by the same cache/share validation as internal YAML specs. Duration fields must be Go duration strings, commands require enabled: true, and command resource names must refer to parsed resources.

Example:

x-cache:
  enabled: true
  stale_after: 6h
  refresh_timeout: 30s
  env_opt_out: EXAMPLE_NO_AUTO_REFRESH
  resources:
    quotes: 5m
  commands:
    - name: dashboard
      resources: [quotes]

x-learn

Declares the self-learning loop configuration for generated CLIs. Mirrors the internal YAML spec's top-level learn: block so OpenAPI-sourced prints can author ticker patterns, stopwords, synonym folds, and entity-lookup seeds the same way internal specs do.

Parsed field: APISpec.Learn (spec.LearnConfig)

Rules:

  • Optional. Specs without x-learn express no learn preference and take the generator's learn-loop default. enabled: false is likewise treated as unset under that default (a plain bool cannot express "explicitly off"); the authoritative opt-out is disabled: true.
  • May be declared at the OpenAPI root or under info. Root takes precedence when both are present.
  • Shape mirrors the internal YAML learn: block field-for-field: enabled, disabled, ticker_patterns, stopwords, synonyms, entity_lookup_seeds.
  • disabled: true is the generation-time opt-out. Combining it with an explicit enabled: true is rejected at parse time as contradictory.
  • Validated by the same learn validation as internal YAML specs: ticker patterns must compile as Go regexps and must not classify every remaining content token in seeded playbook query_family_examples as a ticker (that empties QueryFamily and makes recall unreachable), seed kinds must be lowercase identifiers, canonicals must be non-empty and unique within a kind, and synonym pairs must be non-empty lowercase single-hop folds (no chains, no self-references).

Example:

x-learn:
  enabled: true
  ticker_patterns:
    - "[A-Z]{2,6}-[0-9]+"
  stopwords: [the, of]
  synonyms:
    last night: yesterday
  entity_lookup_seeds:
    country:
      - canonical: USA
        aliases: [united states, america]

x-pp-example

Overrides the generated command's --help Example with a verbatim, authored invocation. The synthesized example only includes required params, so an endpoint whose params are all optional — the common "pass one of channelId / handle / url" shape — otherwise advertises a bare command the API rejects with a 4xx. That broken example also fails the live-dogfood happy-path and json-fidelity probes, which run the Example verbatim.

Parsed field: Endpoint.Example (the same field the internal YAML spec sets via example:).

Rules:

  • Optional. Endpoints without x-pp-example keep today's synthesized example byte-for-byte.
  • Operation-level only. The value is the full invocation including the binary name (<api-slug>-pp-cli <command> <args>); the parser normalizes it to the canonical two-space indent on each line, so authors may omit the leading spaces.
  • Use it instead of marking a param required to force it into the example — marking it required would also make the generated CLI flag mandatory, which a one-of endpoint must not be.
  • Whitespace-only values are ignored (treated as absent); non-string values warn and are ignored.

Example:

paths:
  /v1/youtube/channel:
    get:
      operationId: getYoutubeChannel
      x-pp-example: "scrape-creators-pp-cli youtube list-channel --handle mkbhd"
      parameters:
        - { name: channelId, in: query, required: false, schema: { type: string } }
        - { name: handle, in: query, required: false, schema: { type: string } }
        - { name: url, in: query, required: false, schema: { type: string } }

x-pp-response-envelope

Declares the exact top-level key used by an API's single-key JSON response wrapper, such as result in {"result": {"items": [...]}}. The generated client removes that wrapper before the response reaches commands, pagination, or sync. This is opt-in because a top-level key can also be part of a legitimate payload.

Parsed field: APISpec.ResponseEnvelopeKey

Rules:

  • Optional. Specs without this extension keep the response body unchanged.
  • Declared at the OpenAPI root or under info.
  • Must be a string; surrounding whitespace is trimmed.
  • Unwrapping applies only to successful JSON responses whose body is an object with exactly one property matching the configured key. Other bodies pass through unchanged.

Example:

x-pp-response-envelope: result

x-pp-query

Declares the SQL-query-endpoint sync shape (QuickBooks Online, Salesforce SOQL): an API where every list resource is read through one shared endpoint with an injected SELECT-style query, results wrapped in an entity-named envelope, and paging carried inside the query text. Mirrors the internal YAML spec's top-level query_sync: block. When present, the sync generator emits the query injection, the response-envelope unwrap, and the in-query offset-paging loop by construction — gated so a normal REST list API's generated output is byte-identical to today.

Parsed field: APISpec.QuerySync (spec.QuerySyncConfig)

Rules:

  • Optional. Specs without x-pp-query keep today's REST sync behavior exactly.
  • Declared at the OpenAPI root (the internal YAML form lives in the top-level query_sync: block). All query dialect text lives in the hint, never the generator — the template substitutes the {entity}, {start}, and {limit} placeholders at runtime.
  • A resource participates only when its list endpoint's path equals path AND it declares a response_path (e.g. QueryResponse.<Entity>); the per-resource entity name is taken from that endpoint's response item (e.g. Invoice). Raw passthrough resources on the same path with no response_path are skipped.
  • Fields: path (required, the shared query endpoint, e.g. /query); query_param (the param carrying the SELECT, default query); query_template (required, the SELECT + paging clause with {entity}/{start}/{limit} placeholders); version_param + version_value (an optional extra param sent on every query call); envelope_key (the result-envelope object key joined to the runtime extractor list, e.g. QueryResponse); page_size (in-query page size and offset stride, default 1000).

Example:

x-pp-query:
  path: /query
  query_param: query
  query_template: "select * from {entity} startposition {start} maxresults {limit}"
  version_param: minorversion
  version_value: "75"
  envelope_key: QueryResponse
  page_size: 1000

x-tenant-env-var

Declares the env-var name that resolves the implicit {tenant} path placeholder for multi-tenant SaaS APIs whose every path is /tenant/{tenant}/<resource>. Without this annotation, the generator classifies tenant-templated paths as parent-context-dependent and emits an empty defaultSyncResources / syncResourcePath map; sync silently no-ops and every downstream offline command ships broken.

Parsed fields: APISpec.EndpointTemplateVars (tenant added), APISpec.EndpointTemplateEnvOverrides["tenant"] (env-var name), and APISpec.GlobalPathTemplateVars when {tenant} is present on at least 80% of endpoints and can safely map to a root persistent flag.

Rules:

  • Optional. Specs without x-tenant-env-var keep single-tenant behavior; no {tenant}-aware emission, no spurious env reads.
  • Accepted at the document root or under info (path-positional templates are spec-wide either way); the root value wins when both are set. Press operators commonly place this at the document root, so the parser must accept it there — an info-only reader silently drops the extension, and the affected print loses tenant-aware sync, config.go, and url.go emission with no error, only a sync warning at runtime that reads like a resource-specific problem.
  • Value must be a non-empty string after TrimSpace. Whitespace-only values are treated as absent.
  • The placeholder name is tenant. Specs that use a different placeholder ({workspace}, {org}) should set EndpointTemplateVars + EndpointTemplateEnvOverrides directly in internal YAML until this extension generalizes.

Effect on generated output (when set):

  • The profiler treats /.../{tenant}/... paths as standalone-listable, so the resource becomes a flat SyncableResource rather than a DependentSyncResource.
  • The emitted config.go reads the override env-var name (e.g. ST_TENANT_ID) into Config.TemplateVars["tenant"] at Load() time.
  • The emitted url.go buildURL substitutes {tenant} from Config.TemplateVars at request time and names the override env var in the actionable error when the value is missing.
  • When {tenant} appears on at least 80% of endpoints and its public flag name does not collide with existing root flags, the emitted root command exposes --tenant as an optional override for the same Config.TemplateVars["tenant"] value. Matching per-command {tenant} positionals are removed; sparse path params remain per-command inputs.
  • Typed MCP endpoint tools for tenant-scoped paths expose optional tenant input that overrides the env/config value for that one call.
  • The emitted sync.go filters {tenant} out of the unresolved-key warning so per-tenant paths don't get skipped as "requires parent context".

Example:

info:
  title: ServiceTitan CRM
  version: 1.0.0
  x-tenant-env-var: ST_TENANT_ID

x-pp-tenant-scope-column

Declares, on a collection's list path-item, the column or field name that identifies the tenant (e.g. workspace) scope for each row returned by that collection. It is self-declaring: the annotated collection's own rows carry this column. Use it on list path-items whose synced rows are partitioned by a workspace or organization identifier.

Parsed field: Endpoint.TenantScopeColumn

The value flows into the resource profile and is consumed in two places:

  • Tenant-scoped dependent fan-out (parent tables): a parent collection carrying a tenant column is surfaced via APIProfile.TenantScopedParents() into the generated parentTenantScopeColumns map, so dependent fan-out targets only rows belonging to the active tenant.
  • Flat tenant-scoped reconcile: a flat resource becomes reconcilable (ReconcileMode = "flat") when it carries a tenant column, has a stable primary key, and is not routed through a discriminator dispatcher.
  • Single-tenant whole-table reconcile: when a print has zero TenantScopeColumn annotations, eligible flat resources (stable PK, no discriminator) are classified ReconcileMode = "flat_global" and the table is the partition. Unscoped resources in a mixed print stay "none".

Rules:

  • Optional. Absence means no tenant scoping is recorded for the collection.
  • Placed on the list path-item object (same level as get:, post:, etc.), not on an individual operation.
  • Must be a string naming the response field that holds the tenant scope (e.g. workspace, workspace_slug, org_id); non-string values are ignored with a warning.
  • The field names the foreign-key column whose values identify tenant boundaries in the synced rows.

Example:

paths:
  /projects/:
    x-pp-tenant-scope-column: workspace
    get:
      operationId: list_projects
      summary: List or retrieve projects

x-pp-membership-field

Declares, on a parent collection's list path-item, the boolean field in that collection's own row payload that indicates whether the authenticated user is a member of the resource (e.g. is_member). Dependent fan-out over the parent table skips rows whose field is false — their sub-resources would 403 — so a sync reports one clear "not a member" summary instead of a 403 per (sub-resource, parent).

Parsed field: Endpoint.MembershipField

Rules:

  • Optional. Absence means no membership filtering is recorded.
  • Placed on the list path-item object (same level as get:, post:, etc.), not on an individual operation.
  • Must be a string naming a boolean field in the row payload; non-string values are ignored with a warning.
  • The field name must be a simple identifier (^[a-zA-Z_][a-zA-Z0-9_]*$). It is interpolated into the generated store's non-member query, so dotted or nested-path field names are rejected at runtime and the membership skip becomes a no-op rather than filtering rows.
  • Consumed by the profiler when building dependent-sync resource metadata.

Example:

paths:
  /workspaces/:
    x-pp-membership-field: is_member
    get:
      operationId: list_workspaces
      summary: List workspaces

x-path-template-env-vars

Generic, map-shaped successor to x-tenant-env-var. Each entry binds a path placeholder to an object with two optional fields. The env field registers a runtime env-var override for the placeholder, flowing into the same EndpointTemplateVars / EndpointTemplateEnvOverrides bucket that x-tenant-env-var populates — suitable for BaseURL placeholders such as Atlassian's {workspace} or GitHub's {org}. The default field bakes a literal into operation paths at generation time and drops the matching path parameter, suitable for canonical always-valid values such as Gmail's userId='me' for the authenticated user. When both are set on the same entry, default wins and env is ignored — the placeholder is fully resolved before runtime substitution sees it.

Parsed fields: APISpec.EndpointTemplateVars, APISpec.EndpointTemplateEnvOverrides, APISpec.EndpointPathParamDefaults, and APISpec.GlobalPathTemplateVars for env-backed placeholders that meet the same 80% common-path promotion rule.

Rules:

  • Optional. Specs without this extension keep prior behavior; the new field stays empty and no generated output changes.
  • Accepted at the document root or under info (path-positional templates are spec-wide either way); the root value wins when both are set. Same root-or-info lookup as x-tenant-env-var.
  • Coexists with x-tenant-env-var; both feed the same template-vars bucket. The tenant placeholder may be set by either extension.
  • env and default values must be non-empty after TrimSpace. Whitespace-only values are treated as absent on the entry.
  • Entries with neither env nor default set are skipped silently.
  • Env-backed placeholders that appear in at least 80% of endpoint paths are promoted to optional root persistent flags (for example {workspace} -> --workspace) when the derived flag and Go field names do not collide with existing root command flags. Matching per-command path positionals are removed, while same-named non-path positionals and sparse path params remain command inputs.
  • Typed MCP endpoint tools for promoted env-backed path placeholders expose optional per-call inputs that override env/config values before URL substitution.

Effect on generated output (when set):

  • env-set entries behave exactly like x-tenant-env-var for the declared placeholder: the emitted config.go, url.go, and sync.go resolve the placeholder against the named env var at runtime, and the profiler treats /.../{placeholder}/... paths as standalone-listable.
  • default-set entries are baked in at parse time: every operation path under Resources has {placeholder} replaced with the literal, and the matching path parameter is dropped from each endpoint's Params. The printed CLI exposes neither a placeholder nor a flag for the resolved parameter.

Examples:

Runtime env-var override for a BaseURL placeholder, parallel to x-tenant-env-var:

info:
  title: Atlassian API
  version: 1.0.0
  x-path-template-env-vars:
    workspace:
      env: ATLASSIAN_WORKSPACE

Build-time literal substitution that bakes a canonical value into every operation path and drops the matching path parameter:

info:
  title: Gmail Users API
  version: 1.0.0
  x-path-template-env-vars:
    userId:
      default: me

Security Scheme Extensions

Security scheme extensions are read from components.securitySchemes.<scheme-name>. They can declare composed cookie auth or override install/config metadata when the API spec's service identity differs from the product identity exposed by the printed CLI.

When components.securitySchemes is absent, the parser may infer simple bearer auth from clear API-wide prose such as Authorization: Bearer, personal access token, fine-grained PAT, app installation token, or OAuth app token. An explicitly empty block disables that prose fallback:

components:
  securitySchemes: {}

x-auth-type

Marks an API key scheme as composed auth.

Parsed field: APISpec.Auth.Type

Rules:

  • Optional.
  • Must be the exact string composed to take effect.
  • Only read for OpenAPI apiKey security schemes.
  • Any other value leaves the normal API key mapping in place.

x-auth-format

Template used to assemble the composed auth header or cookie value.

Parsed field: APISpec.Auth.Format

Rules:

  • Optional.
  • Only read when x-auth-type: composed.
  • Must be a string.

x-prefix

Declares a literal token prefix for header API key schemes.

Parsed field: APISpec.Auth.Format

Rules:

  • Optional.
  • Only read for OpenAPI apiKey security schemes with in: header.
  • Must be a string.
  • Leading and trailing whitespace is trimmed.
  • When present, the parser stores "<prefix> {token}" in Auth.Format.
  • Ignored for query API keys and non-API-key auth schemes.

Example:

components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: Authorization
      x-prefix: Klaviyo-API-Key

x-auth-basic-username / x-auth-basic-password

Declares the literal username or password half for HTTP Basic auth schemes where only the other half should be supplied by the user.

Parsed field: APISpec.Auth.Format

Rules:

  • Optional.
  • Only read for OpenAPI http security schemes with scheme: basic.
  • Must be a string.
  • Leading and trailing whitespace is trimmed.
  • When only x-auth-basic-username is present, the parser stores "Basic <username>:{token}" in Auth.Format.
  • When only x-auth-basic-password is present, the parser stores "Basic {token}:<password>" in Auth.Format.
  • If both are present or both are absent, the normal Basic auth format remains "Basic {username}:{password}".

Example:

components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      x-auth-basic-username: API_KEY

x-auth-env-vars

Overrides the generated credential environment variable names.

Parsed field: APISpec.Auth.EnvVars

Rules:

  • Optional.
  • Must be a list of strings. A single string is also accepted for convenience.
  • Leading and trailing whitespace is trimmed from each item.
  • Empty and non-string list items are ignored.
  • When at least one non-empty item is present, the list replaces the parser's generated env var names.
  • On apiKey/header schemes that appear beside the selected auth scheme in the same AND security requirement, the first non-empty item supplies the generated per-call env var for that sibling header. Use x-auth-vars for richer metadata or when more than one credential variable belongs to the same scheme.
  • If a sibling apiKey/header scheme omits x-auth-env-vars and x-auth-vars, the parser derives a required per-call env var from the API slug and header name, for example DISPATCH_ST_APP_KEY for ST-App-Key.

x-auth-vars

Overrides the generated credential environment variable metadata.

Parsed field: APISpec.Auth.EnvVarSpecs

Rules:

  • Optional.
  • Must be a list of objects.
  • Each object must include name, kind, required, and sensitive.
  • name must be a non-empty string.
  • kind must be one of per_call, auth_flow_input, or harvested.
  • required and sensitive must be booleans.
  • description is optional and must be a string when present.
  • Group IDs and legacy aliases are not parsed. Express OR relationships in description text and by marking each alternative required: false.
  • Use either x-auth-env-vars for legacy name-only overrides or x-auth-vars for rich metadata. If both are present, x-auth-vars wins.
  • Malformed values are ignored with a warning, and the parser falls back to the generated auth env-var defaults.

Example:

components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: Authorization
      x-auth-vars:
        - name: TODOIST_API_KEY
          kind: per_call
          required: true
          sensitive: true
          description: Todoist API key.

x-speakeasy-example

Uses a Speakeasy security-scheme example as the credential environment variable name when it is shaped like a shell env var.

Parsed field: APISpec.Auth.EnvVars

Rules:

  • Optional.
  • Must be a string shaped like an uppercase environment variable name, for example DUB_API_KEY.
  • Ignored when x-auth-env-vars is present.
  • Ignored when the selected auth config has multiple env vars.
  • Ignored when the value looks like a token value instead of an env var name.

x-auth-optional

Marks the credential as optional for install/config surfaces.

Parsed field: APISpec.Auth.Optional

Rules:

  • Optional.
  • Must be a boolean.
  • true makes MCPB user_config.required false even for auth types that normally require credentials.

x-auth-key-url

Declares the page where users can get a credential.

Parsed field: APISpec.Auth.KeyURL

Rules:

  • Optional.
  • Must be a string.
  • Leading and trailing whitespace is trimmed.
  • The parser does not validate the URL shape.

When the extension is absent and the spec has any auth, the parser falls back through the following sources in order:

  1. The selected security scheme's description (extracted via regex).
  2. info.description, but only when the surrounding text mentions credential-related cues (token, api key, credential, register, sign up, etc.) so an unrelated URL doesn't get picked.

Within either description, generic protocol references such as MDN, RFC/IETF, HTTPWG, and Wikipedia links are ignored. A clear credential-management surface (console, dashboard, api-keys/apikeys, or settings/integrations) wins over an earlier neutral URL; otherwise the first accepted non-generic URL is kept as the fallback.

externalDocs.url and info.contact.url are intentionally not fallbacks for KeyURL. Those almost always point at the API's docs landing page or the company homepage, neither of which is where users actually create a token. When KeyURL ends up empty, the printed CLI uses WebsiteURL (already populated from externalDocs.url, info.contact.url, and x-website) under a separate See API docs: <URL> line — honest framing for those URLs.

The result drives the printed CLI's Get a key at: <URL> output in auth prompts and doctor.

x-auth-instructions

Free-form one-line guidance shown alongside x-auth-key-url, e.g. "Settings → Personal access tokens → Generate new". The printed CLI surfaces this under the URL in auth prompts, doctor, and the auth setup command.

Parsed field: APISpec.Auth.Instructions

Rules:

  • Optional.
  • Must be a string.
  • Leading and trailing whitespace is trimmed.
  • Use this when x-auth-key-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.

x-auth-title

Overrides the title shown for the credential field in install/config surfaces.

Parsed field: APISpec.Auth.Title

Rules:

  • Optional.
  • Must be a string.
  • Leading and trailing whitespace is trimmed.
  • Used when the selected auth scheme has a single env var. Multiple env vars keep env-var-name titles to avoid duplicate field labels.

x-auth-description

Overrides the full description shown for the credential field in install/config surfaces.

Parsed field: APISpec.Auth.Description

Rules:

  • Optional.
  • Must be a string.
  • Leading and trailing whitespace is trimmed.
  • Used as the complete description when the selected auth scheme has a single env var. When omitted, the generator builds a description from env var name, display name, optionality, and x-auth-key-url.

Example:

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-apikey
      x-auth-env-vars:
        - FLIGHTAWARE_API_KEY
      x-auth-optional: true
      x-auth-key-url: https://flightaware.com/commercial/aeroapi/
      x-auth-title: FlightAware AeroAPI Key
      x-auth-description: Optional FlightAware AeroAPI credential for enriched flight data.

x-auth-subtype

Refines Auth.Type for runtime flows that need a different credential-capture path than the base type implies. Recognized values include google_service_account, which selects the generated Google service-account JWT bearer exchange scaffold, and auth0_spa_in_memory, a bearer-token spec whose access token is held by the Auth0 SPA SDK with cacheLocation: memory. Cookie/localStorage extractors have no path to the latter token (it lives in JS heap only), so the generator emits a --auth0-spa flag on auth login --chrome that drives a Chrome DevTools Protocol outbound-Authorization interceptor.

Parsed field: APISpec.Auth.Subtype

Rules:

  • Optional.
  • Must be a string.
  • Recognized values: google_service_account, auth0_spa_in_memory. Other values are silently dropped by the parser; the in-spec value never round-trips unless it matches a known subtype.
  • google_service_account is valid only with a bearer-token auth type. It emits auth service-account, accepts a service-account JSON key through GOOGLE_APPLICATION_CREDENTIALS, and supports a pre-minted bearer override through GOOGLE_OAUTH_ACCESS_TOKEN.
  • Spec-level validation rejects auth.subtype: auth0_spa_in_memory paired with any non-empty auth.type other than bearer_token. Auth0 SPA tokens are always Authorization-bearer values; combining the subtype with api_key or cookie would silently emit a CDP path against a credential that isn't a JWT.
  • Sniff-time detection (internal/browsersniff/auth0_spa.go) sets the subtype automatically when an /oauth/token response carries access_token in the JSON body without a JWT-shaped Set-Cookie on the same response. Authors rarely need to set it by hand.

Example:

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      x-auth-subtype: auth0_spa_in_memory

Domain used when extracting named cookies for composed auth.

Parsed field: APISpec.Auth.CookieDomain

Rules:

  • Optional.
  • Only read when x-auth-type: composed.
  • Must be a string.

x-auth-cookies

Cookie names required to fill the composed auth format.

Parsed field: APISpec.Auth.Cookies

Rules:

  • Optional.
  • Only read when x-auth-type: composed.
  • Must be a list.
  • List items must be strings; non-string items are skipped.

Example:

components:
  securitySchemes:
    browserSession:
      type: apiKey
      in: header
      name: Authorization
      x-auth-type: composed
      x-auth-format: "Session {session_id}:{csrf_token}"
      x-auth-cookie-domain: app.example.com
      x-auth-cookies:
        - session_id
        - csrf_token

x-auth-companion

Declares the deterministic hints the generated CLI needs to hand off to press-auth login non-interactively. With these set, auth login --chrome --auto-login shells out to press-auth login <domain> --login-url ... --jwt-carrier-cookie ... and the user never has to remember those values.

Parsed fields:

  • login_url -> APISpec.Auth.LoginURL
  • login_complete_selector -> APISpec.Auth.LoginCompleteSelector
  • jwt_carrier_cookie -> APISpec.Auth.JWTCarrierCookie

Placement:

  • Allowed on components.securitySchemes.<name> and on info. Scheme-level fields win over info-level when both are set. Info-level is intended for specs that declare composed auth without a single named security scheme.

Rules:

  • All three sub-fields are optional. When absent, the generated CLI prints a hint instructing the user to invoke press-auth login manually.
  • login_url must parse as a URL and use https:// (or http://localhost / http://127.0.0.1). Plain http:// against other hosts would leak the captured cookies to a network sniffer; the parser rejects it.
  • login_complete_selector is opaque — pass through verbatim as a CSS selector. The parser does not validate it beyond non-emptiness.
  • jwt_carrier_cookie should match one of the names listed in cookies. A mismatch surfaces as a stderr warning at parse time (typo surfacing) rather than a hard error.
  • Only these three public hints are embedded into the generated CLI's source. Cookie values, JWT tokens, and other user-secret material never appear in generated constants — press-auth captures and stores those itself.

Internal YAML equivalent (under the auth: block):

auth:
  type: composed
  cookie_domain: example.com
  cookies:
    - session_id
    - guestsession
  login_url: https://www.example.com/account/login
  login_complete_selector: "a[href*=signout]"
  jwt_carrier_cookie: guestsession

OpenAPI security-scheme placement:

components:
  securitySchemes:
    browserSession:
      type: apiKey
      in: cookie
      name: guestsession
      x-auth-companion:
        login_url: https://www.example.com/account/login
        login_complete_selector: "a[href*=signout]"
        jwt_carrier_cookie: guestsession

OpenAPI info-level placement (for specs without a named scheme):

info:
  title: Example
  x-auth-companion:
    login_url: https://www.example.com/account/login
    login_complete_selector: "a[href*=signout]"
    jwt_carrier_cookie: guestsession

x-oauth-device-flow

Declares OAuth 2.0 device authorization grant metadata for CLI-first OAuth flows. OpenAPI 3.0 does not have a native deviceCode flow, so the Printing Press reads this extension from an OAuth2 security scheme and emits a generated auth login --device-code command plus refresh-token handling.

Parsed fields: APISpec.Auth.OAuth2Grant=device_code, APISpec.Auth.DeviceAuthorizationURL, APISpec.Auth.TokenURL, APISpec.Auth.Scopes, APISpec.Auth.DefaultClientID.

Rules:

  • Optional. When present, the parser treats the security scheme as a bearer OAuth flow backed by stored access tokens.
  • Must be an object.
  • deviceAuthorizationUrl (or device_authorization_url) and tokenUrl (or token_url) are required by APISpec.Validate() when oauth2_grant: device_code.
  • scopes may be a string or list of strings. Lists are sorted for stable generation.
  • defaultClientId (or default_client_id) is optional. When absent, the generated CLI prompts for --client-id or the inferred <API>_CLIENT_ID environment variable.

Example:

components:
  securitySchemes:
    OAuth2:
      type: oauth2
      x-oauth-device-flow:
        deviceAuthorizationUrl: https://login.example.com/common/oauth2/v2.0/devicecode
        tokenUrl: https://login.example.com/common/oauth2/v2.0/token
        defaultClientId: public-client-id
        scopes:
          - Calendars.Read
          - Mail.Read

x-oauth-refresh-token-mechanism

Declares how the authorization endpoint should be asked to issue a refresh token. Providers diverge: Google reads access_type=offline as a query parameter, while WHOOP, X/Twitter, and others read a magic scope value (offline, offline.access, offline_access) instead. The generator emits neither by default because a Google-shaped silent default silently breaks other providers (broken refresh path is invisible until access-token TTL expires).

Parsed field: APISpec.Auth.RefreshTokenMechanism

Rules:

  • Optional. Only consumed by the authorization_code grant template; ignored for other grants and non-OAuth2 auth.
  • Must be a string. Leading and trailing whitespace on the whole value is trimmed.
  • Two exact-match prefixes are accepted:
    • scope:<value> appends <value> to the scope list. No query param is added.
    • query:<key>=<value> sets the query parameter exactly once. No scope change.
  • Malformed values (empty key, empty value, missing = for query, unknown prefix, uppercase prefix) are ignored and produce no emission.
  • For query:<key>=<value>, the reserved authorization-URL parameter names client_id, redirect_uri, response_type, state, and scope are rejected. Permitting them would let a spec author silently overwrite the generator's CSRF state token or core OAuth params.
  • Note: the single-mechanism shape cannot express Google's two-param recipe (access_type=offline + prompt=consent). The first param is sufficient for refresh-token issuance on initial consent; the second forces re-consent on subsequent logins to keep the refresh-token contract alive. Specs that need both should declare one via this extension and add the other through a future multi-mechanism syntax (out of scope here).

Example:

components:
  securitySchemes:
    OAuth2:
      type: oauth2
      x-oauth-refresh-token-mechanism: scope:offline
      flows:
        authorizationCode:
          authorizationUrl: https://api.example.com/oauth/authorize
          tokenUrl: https://api.example.com/oauth/token
          scopes:
            read: Read access

x-url-name and x-param-url-names

Overrides the URL query key for a parameter without changing the public CLI/MCP input name. Use this for APIs whose documented flag name or shared OpenAPI component name differs from the exact wire query key a specific endpoint accepts.

Parsed field: Param.URLName

Rules:

  • x-url-name is allowed on an OpenAPI Parameter Object and applies wherever that parameter object is used.
  • x-param-url-names is allowed on a Path Item Object or Operation Object. It is an object mapping parameter names to URL query keys. Operation entries override path-item entries.
  • The parameter's name remains the public input identity used for generated Go identifiers, CLI flags, MCP public names, and manifest public names.
  • The override only changes generated URL query emission, MCP WireName, and manifest wire_name.
  • Empty names, empty URL keys, and non-string override values are ignored with warnings.

Example:

paths:
  /opportunities/search:
    get:
      x-param-url-names:
        locationId: location_id
      parameters:
        - $ref: "#/components/parameters/LocationId"

  /opportunities/pipelines:
    get:
      parameters:
        - $ref: "#/components/parameters/LocationId"

components:
  parameters:
    LocationId:
      name: locationId
      in: query
      schema:
        type: string

In this example both endpoints keep the same public locationId input, but only /opportunities/search sends ?location_id= on the wire.

x-pp-dispatch-param

Marks a query parameter as a fixed dispatch discriminator whose default value selects the upstream route rather than tuning the request. Generated runnable examples keep that default instead of substituting a synthetic dogfood value.

Parsed field: Param.DispatchParam

Use this for shared-path APIs where a query parameter such as type or action selects the report or operation. Do not use it for ordinary filters, limits, page sizes, or other tunable inputs.

Example:

paths:
  /:
    get:
      operationId: getDomainRank
      parameters:
        - name: report
          in: query
          x-pp-dispatch-param: true
          schema:
            type: string
            default: domain_rank

Path Item Extensions

Path item extensions are read from a path object, beside its HTTP operations. They apply to every operation under that path because sync identity and critical resource status are resource-scoped, and operation-level data-source strategy can override the path default.

x-resource-id

Declares the response field that should be used as the primary key when sync stores resources locally.

Parsed field: Endpoint.IDField

Rules:

  • Optional.
  • Must be a string. A dotted path (entityInfo.entityId) is stored verbatim and walked at runtime by LookupFieldValue; it is not limited to a single top-level key. A +-separated list (date+model_permaslug) is a composite identity: generated ExtractResourceID joins the part values with + at storage time. Date-shaped parts are allowed inside a composite; a solo date field is not, because CanonicalResourceID rejects ISO-date values.
  • Leading and trailing whitespace is trimmed.
  • Non-string values emit a warning and are ignored.
  • A non-empty x-resource-id wins over every automatic response-schema fallback.
  • An empty or missing value falls through to the parser's automatic resolveIDFieldFromResponseSchema chain: bare id; then a resource-derived singular key ending in _id, _uuid, _guid, or _uid (matched through snake-case normalization, so camelCase and PascalCase spellings such as widgetId are preserved when emitted); then vendor identifier keys gid, sid, uid, uuid, and guid; then a sole remaining <stem>_uid / camelCase stemUid field whose stem is the resource's own collection noun (so alertUid matches account-alerts-open, while a foreign accountUid on /sites falls through; two own-stem spellings stay ambiguous); then URL-shaped identifier keys uri, self, selfLink, href, and url; then name; then the first solo-usable required scalar. A date-shaped required field (OpenAPI format: date / date-time, a date-like name such as date / created_at, or an ISO-date example) is never selected as a solo IDField: if later required identity fields exist (string or numeric, excluding mutable/metric names such as status or total_tokens), they are joined with + as a composite identity; otherwise IDField stays empty so runtime fallbacks apply. Generated ExtractResourceID joins part values with + after escaping \ and + inside each part so distinct tuples cannot collide. Date-shaped parts are allowed inside a composite; a solo date field is not, because CanonicalResourceID rejects ISO-date values. URL-shaped keys qualify only when the field schema is a plausible ID and either the field is required or its name, title, description, or format carries an identifier hint; an optional generic url therefore falls through to name.
  • URL-shaped keys intentionally trail id-shaped keys, so APIs that expose both id and self keep the compact primary key.
  • Qualified URL-shaped keys intentionally win over display name when no id-shaped key is available. A resource URL or URI is usually record-unique, while display names can collide; keying on name would collapse two records with the same display label into one local row.
  • Applies to every operation on the path item.

Example:

paths:
  /widgets:
    x-resource-id: widget_uid
    get:
      operationId: listWidgets
      responses:
        "200":
          description: OK

Nested identifiers use the same extension with a dotted path. Generated LookupFieldValue walks each segment with snake/camel/Pascal spellings and a trailing-underscore variant, so entityInfo.entityId reaches a payload shaped like {"entityInfo":{"entityId":"..."}} without a hand-written override:

paths:
  /entities:
    x-resource-id: entityInfo.entityId
    get:
      operationId: listEntities
      responses:
        "200":
          description: OK

Composite identities use the same extension with +-separated field names. Generated extraction joins the part values with + so a date-shaped column can participate in the row key without becoming a solo ID:

paths:
  /datasets/rankings-daily:
    x-resource-id: date+model_permaslug
    get:
      operationId: listRankingsDaily
      responses:
        "200":
          description: OK

Generated dependent sync uses the resolved resource ID field when substituting parent IDs into child path parameters. When the resolved ID field is URL-shaped (uri, self, selfLink, href, or url), generated replaceURLIDPathParam reduces a full URL value to its trailing path segment before normal path escaping. This is intentional: dependent fetches whose upstream path expects {id} must not send a full URL as that path segment. Do not restore scheme, host, query, or earlier path components in generated path params.

store.BareResourceID is a separate storage-key helper for stripping the NUL-delimited parent suffix from composite dependent-resource storage IDs. It is not part of response-schema identity selection and should not be changed to alter URL-shaped record identity behavior.

x-critical

Marks a syncable resource as essential. Generated sync commands fail the run when a critical resource fails, while non-critical resource failures can be reported as warnings unless --strict is used.

Parsed field: Endpoint.Critical

Rules:

  • Optional.
  • Defaults to false.
  • Accepts native booleans.
  • Also accepts the strings "true" and "1" as true, case-insensitive after trimming.
  • The strings "false", "0", and "" are false.
  • Other string values emit a warning and are false.
  • Non-boolean, non-string values emit a warning and are false.
  • Applies to every operation on the path item.

Example:

paths:
  /accounts:
    x-critical: true
    get:
      operationId: listAccounts
      responses:
        "200":
          description: OK

x-pp-syncable

Opts a list endpoint into generated default sync even when the profiler would normally exclude it because required path or query parameters are not automatically satisfiable.

Parsed field: Endpoint.Syncable

Rules:

  • Optional.
  • Defaults to false.
  • Accepts native booleans.
  • May be set on a path item or a single operation.
  • Use only when required inputs are supplied by defaults, endpoint template variables, or another generated runtime mechanism.

Example:

paths:
  /tenant/{tenant_id}/items:
    get:
      operationId: listTenantItems
      x-pp-syncable: true
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK

x-pp-mutation

Overrides the generator's read/write classification when the HTTP method is not enough. true marks a GET action as state-changing (start, stop, restart, deploy). false marks a POST (or other non-GET) endpoint as a read so the generated command prints the response body instead of the mutation acknowledgment envelope. Unset RPC-over-GET operations without a read token fail closed (not mcp:read-only).

Parsed field: Endpoint.Mutation

Rules:

  • Optional.
  • Unset means classify from the HTTP verb, operation name, path shape, and body shape. RPC-over-GET without a read signal is a write.
  • Must be a native boolean.
  • Applies only at the operation level.
  • When set, the generator uses this value before HTTP-verb and operation-name fallbacks. Do not infer reads from filter-shaped parameter names alone; use this flag or a read-shaped operation name (search, list, query).
  • Internal YAML uses the same boolean as mutation:.

Example:

paths:
  /applications/{id}/restart:
    get:
      operationId: restartApplication
      x-pp-mutation: true
      responses:
        "204":
          description: Restarted
  /sets/items:
    post:
      operationId: projectItems
      x-pp-mutation: false
      responses:
        "200":
          description: Matched rows

x-tier

Selects a tier declared by x-tier-routing for a path item or one operation.

Parsed field: Endpoint.Tier

Rules:

  • Optional.
  • Must be a string.
  • Operation-level x-tier overrides path-item-level x-tier.
  • The value must name a tier in x-tier-routing.tiers.
  • security: [] / security: [{}] must not be combined with an auth-bearing tier. Use a none tier for anonymous endpoints.

Example:

paths:
  /public/search:
    x-tier: free
    get:
      responses:
        "200": {description: ok}
  /premium/search:
    get:
      x-tier: paid
      responses:
        "200": {description: ok}

x-data-source-strategy

Declares how a generated read command should honor the global --data-source auto|local|live flag.

Parsed field: Endpoint.DataSourceStrategy

Rules:

  • Optional.
  • May be declared on a path item or operation.
  • Operation-level values override path-item-level values.
  • Must be one of auto, local, or live.
  • auto keeps the normal live-with-local-fallback behavior for store-backed reads.
  • local makes the command use local synced data for auto and local, and reject --data-source live with a clear no-live-equivalent error.
  • live makes the command use the remote API for auto and live, and reject --data-source local with a clear no-local-data-source error.

Example:

paths:
  /reports/snapshot:
    get:
      x-data-source-strategy: local
      responses:
        "200": {description: ok}

x-live-dogfood-requires-tier

Declares the runner credential tier required before cli-printing-press dogfood --live should probe an endpoint.

Parsed field: Endpoint.LiveDogfoodRequiresTier

Rules:

  • Optional.
  • May be declared on a path item or operation.
  • Operation-level values override path-item-level values.
  • Must be a string; non-string values are ignored with a warning.
  • This is dogfood-only. It does not select an upstream auth route and should not be confused with x-tier.
  • When absent, the parser may infer streaming for obvious GET streaming endpoints such as paths ending in /stream or responses with text/event-stream.

The generator emits the value as the Cobra annotation pp:requires-tier. Live dogfood skips annotated commands unless --auth-tier or PP_AUTH_TIER matches the value.

Example:

paths:
  /2/tweets/firehose/stream:
    get:
      x-live-dogfood-requires-tier: enterprise
      responses:
        "200":
          description: Streaming response
          content:
            text/event-stream:
              schema:
                type: string

x-requires-role

Requires the authenticated account to have one of the declared x-roles before the generated command calls the API.

Parsed field: Endpoint.RequiresRole

Rules:

  • Optional.
  • Must be on an operation, not the root, info, or path item.
  • Must be a string naming a role declared by x-roles.
  • The generator emits the guard framework and endpoint call site. How a printed CLI discovers the authenticated account's role remains API-specific.

Example:

x-roles: [parent, student, teacher, admin]
paths:
  /users:
    get:
      operationId: listUsers
      x-requires-role: admin
      responses:
        "200": {description: ok}

x-pp-resource

Overrides the resource bucket for one OpenAPI operation. Use it when the path parser would derive a reserved Printing Press template name such as search, or when the upstream path shape does not expose the intended resource name.

Parsed field: resource map key in APISpec.Resources

Rules:

  • Optional.
  • Must be a string.
  • The value is sanitized to the same resource-name form the parser uses for path-derived names.
  • Non-string values emit a warning and are ignored.
  • Applies only to the operation where it appears.

Example:

paths:
  /search:
    post:
      operationId: searchNotes
      x-pp-resource: notes_search
      responses:
        "200": {description: ok}

x-pp-pagination

Overrides pagination detection for one GET operation.

Parsed field: Endpoint.Pagination

Rules:

  • Optional.
  • Must be on an operation, not the root, info, or path item.
  • Must be a string.
  • Accepted value: none.
  • none tells generated sync not to send inferred cursor, page, offset, or page-size query parameters for this endpoint, even when the operation exposes page-looking filters such as page, page_size, or limit.
  • Use for list endpoints that return the whole collection in one response and reject pagination keys as invalid filters.
  • Unsupported values emit a warning and fall back to normal pagination detection.

Example:

paths:
  /ip_addresses:
    get:
      operationId: listIPAddresses
      x-pp-pagination: none
      parameters:
        - name: page_size
          in: query
          schema: {type: integer}
      responses:
        "200": {description: ok}

x-pp-safe-probe

Marks a mutation endpoint as explicitly safe for the Phase 1.9 reachability gate to call once as an optional second probe after the low-risk GET/body capture. This extension is consumed by Printing Press skill guidance rather than the Go OpenAPI parser; it documents author intent for agents reviewing a resolved spec.

Parsed field: none; consumed by skill guidance only

Rules:

  • Optional.
  • Must be on an operation, not the root, info, or path item.
  • Accepts native boolean true only.
  • Use only for idempotent or otherwise harmless operations for the real account being used.
  • Absence or any value other than native boolean true means mutation probing is not allowed; agents must stop after the GET/body reachability capture.

Example:

paths:
  /webhooks/test:
    post:
      x-pp-safe-probe: true
      responses:
        "200": {description: ok}

x-happy-args

Declares live-dogfood happy-path fixture arguments for one operation. Use it when generate-time synthesis cannot satisfy the endpoint contract: the generator already emits pp:happy-args from parameter example, enum, default, and format when every required input is derivable. Keep the extension for opaque IDs, coordinates with no schema hint, or conditional query flags that generic values cannot satisfy.

Parsed field: Endpoint.HappyArgs

Rules:

  • Optional.
  • Must be on an operation, not the root, info, or path item.
  • Must be a string in the runtime annotation format consumed by pp:happy-args.
  • Tokens are separated by unescaped semicolons. Escape a literal semicolon as \; (write \\; inside a YAML double-quoted string). <label>=value or label=value overlays synthesized positional args, --flag=value replaces the matching example flag or adds a new flag/value pair, and bare --flag tokens are treated as boolean --flag=true.
  • Negative numeric flag values are emitted in --flag=-12.3 form so Cobra does not parse the value as a shorthand flag cluster.
  • Empty or whitespace-only values behave the same as absence.

Example:

paths:
  /referents:
    get:
      operationId: listReferents
      x-happy-args: "--song-id=378195"
      responses:
        "200": {description: ok}

x-happy-stdin

Declares a JSON request-body fixture for a live-dogfood command that reads its input from stdin. The generator emits the fixture as the pp:happy-stdin annotation, and the matrix pipes it to both the happy-path and JSON-fidelity probes.

Parsed field: Endpoint.HappyStdin

Rules:

  • Optional.
  • Must be on an operation, not the root, info, or path item.
  • Must be a string containing valid JSON.
  • Commands that require stdin but do not declare this fixture are skipped with reason no-stdin-fixture; the runner never synthesizes a request body.

Example:

paths:
  /widgets/inspect:
    post:
      x-happy-stdin: '{"name":"synthetic"}'
      responses:
        "200": {description: ok}

x-pp-sync-walker

Declares a hierarchical-walk dependency for a child endpoint. Synthesizes (or augments) a dependent-resource entry so the generator's existing parent-child sync machinery handles the fan-out — fetch the parent, extract the named field from each parent record, substitute it into the child path, fetch each child.

Use this when the auto-detected parent-child link in the profiler would miss your endpoint or pick the wrong parent. Common cases:

  • The child path's placeholder name does not match a parent resource (e.g. /games/{game_key}/leaguesgame_key does not stem to "games" via the default _id/_key stripping).
  • The parent placeholder lives in a matrix or query parameter rather than the path, so the path has no {placeholder} for auto-detection to read.
  • The child path uses a parent field that is not the parent's primary key (e.g. Yahoo Fantasy's game_key, Reddit's subreddit name).

Parsed field: Endpoint.Walker (a *spec.WalkerConfig)

Rules:

  • Optional.
  • Operation-level only. (No path-item-level form today.)
  • parent (string, required): the resource name to iterate. The parent must itself be a syncable resource (i.e., have a flat-list endpoint). Walkers pointing at non-syncable parents emit a warning: to stderr at generate time and are dropped.
  • key_field (string, optional): the field to extract from each parent record for substitution into the child path. Defaults to the parent's primary key. Set this when the child path needs a non-PK field.
  • key_param (string, optional): the child request slot that receives the extracted value. When that name is a {placeholder} in the child path, generated sync substitutes it into the URL. When it is not — a query parameter such as GET /messages?roomId= — generated sync writes the parent-row value into the request params map. Defaults to the first (and only) {placeholder} in the child path when there is exactly one. Required explicitly when the child path has 0 or 2+ placeholders — the single-placeholder default would otherwise pick the wrong slot (or no slot at all). The generator warns and drops the walker when it's ambiguous and key_param is missing.
  • Walker-emitted dependents flow through the same syncDependentResource machinery as auto-detected ones, so concurrency/retry/cursor/Upsert behavior is identical.

Internal YAML emits this as walker: on the endpoint with the same sub-field names (parent, key_field, key_param). Both surfaces parse to the same WalkerConfig struct.

Example:

paths:
  /games:
    get:
      summary: List games (parent for the walker below)
      responses:
        "200": {description: ok}
  /games/{game_key}/leagues:
    get:
      summary: List leagues for a game
      x-pp-sync-walker:
        parent: games
        key_field: game_key
        key_param: game_key
      parameters:
        - name: game_key
          in: path
          required: true
          schema: {type: string}
      responses:
        "200": {description: ok}
  /standings:
    get:
      summary: List standings for a game (query-param parent key)
      x-pp-sync-walker:
        parent: games
        key_field: game_key
        key_param: gameId
      parameters:
        - name: gameId
          in: query
          required: true
          schema: {type: string}
      responses:
        "200": {description: ok}

x-sync-params

Query parameters applied only during generated sync. List/get endpoint commands keep the API's documented defaults; sync uses these values so a naive sync does not treat a default-filtered slice (status=open, state=open) as the complete resource.

Parsed field: Endpoint.SyncParams

Rules:

  • Optional. Absent the field, generated sync still auto-widens a status/state query param whose spec default: is open when the param's enum includes all (preferred) or any. That is the GitHub issues / Alpaca orders / Shopify orders idiom. Other defaults (tenant scope, sort, include_*) are not widened.
  • Operation-level only.
  • Must be an object mapping parameter names to string values. Non-string values are stringified. Empty names or values are skipped. Malformed values warn and are ignored rather than failing the parse.
  • Overlay order: spec default: (with history-hiding widen) first, then x-sync-params, then user --param / --resource-param at runtime. Sync-owned paging/since/sort keys are skipped, same as spec defaults.
  • An explicit x-sync-params entry for a key suppresses auto-widen for that key, including status: open when the intended sync scope really is the open slice.
  • When a resource still has an unwidened status/state=open default (no all/any in the enum and no overlay) and sync stores 0 rows, generated sync emits sync_warning with reason default_filter_hides_history instead of silent success-with-zero.

Internal YAML emits this as sync_params: on the endpoint with the same map shape.

Example:

paths:
  /v2/orders:
    get:
      summary: List orders
      x-sync-params:
        status: all
      parameters:
        - name: status
          in: query
          schema:
            type: string
            default: open
            enum: [open, closed, all]
      responses:
        "200": {description: ok}

x-streaming

Declares that a spec has WebSocket-primary ingest with REST metadata refresh. Internal YAML uses the same shape as streaming: at the top level.

Parsed field: APISpec.Streaming (spec.StreamingConfig)

Rules:

  • Optional. When omitted, REST-only generation is unchanged.
  • transport must currently be websocket.
  • url must be an absolute ws:// or wss:// URL.
  • framing may be single_object_per_frame or newline_delimited_json. Empty defaults to single_object_per_frame.
  • metadata.endpoint is optional, but required when any metadata sub-field is declared.
  • metadata.refresh_cadence is a Go duration. Empty defaults to 30s.
  • metadata.statuses defaults to [live, pending].
  • metadata.primary_key defaults to id.

When present, the generator emits live ws sync, live rest sync, internal/wsclient, and local SQLite tables for stream frames, stream metadata, and <api>_rebase_log lifecycle events.

Example:

x-streaming:
  transport: websocket
  url: "wss://api.example.com/v1/ws"
  subscribe_shape: '{"type":"subscribe","channels":["events"]}'
  framing: newline_delimited_json
  metadata:
    endpoint: "/v1/events"
    refresh_cadence: 30s
    statuses: [live, pending]
    primary_key: event_id