spec

package
v3.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Index

Constants

View Source
const (
	KindREST      = "rest"      // default; strict path-validity against the spec
	KindSynthetic = "synthetic" // multi-source / combo CLI; dogfood + scorecard relax path-validity
)

Valid values for APISpec.Kind. A bare string with no const was the established convention for sibling fields (SpecSource, ClientPattern), but Kind is compared in production code at multiple sites, so the constant prevents typos from silently falling through to the default-rest path.

View Source
const (
	HTTPTransportStandard        = "standard"          // default for official API clients
	HTTPTransportBrowserChrome   = "browser-chrome"    // Chrome-impersonated transport for browser-facing web surfaces
	HTTPTransportBrowserChromeH3 = "browser-chrome-h3" // Chrome-impersonated transport forced through HTTP/3 for stricter bot screens
)
View Source
const (
	ResponseFormatJSON = "json"
	ResponseFormatHTML = "html"
)
View Source
const (
	TierAuthTypeNone        = "none"
	TierAuthTypeAPIKey      = "api_key"
	TierAuthTypeBearerToken = "bearer_token"

	TierAuthPlacementHeader = "header"
	TierAuthPlacementQuery  = "query"
)
View Source
const (
	HTMLExtractModePage         = "page"
	HTMLExtractModeLinks        = "links"
	HTMLExtractModeEmbeddedJSON = "embedded-json"
)
View Source
const DefaultEmbeddedJSONScriptSelector = "script#__NEXT_DATA__"

DefaultEmbeddedJSONScriptSelector is the script-tag selector used when `html_extract.mode: embedded-json` is set without an explicit `script_selector`. Targets Next.js's pages-router `<script id="__NEXT_DATA__">` block — the most common shape and the one the food52 retro surfaced. Other SSR frameworks declare different selectors:

  • Nuxt: script#__NUXT__
  • Remix: script:contains("window.__remixContext") (use selector with type or id when available)
  • Astro: site-specific; declare per spec
View Source
const DefaultOrchestrationThreshold = 50

DefaultOrchestrationThreshold is the endpoint-count above which the generator recommends (but does not require) code-orchestration. At 50+ endpoints, even intent-grouped tools tend to overflow an agent's usable context; code-orchestration covers the full surface in a pair of tools.

View Source
const OAuth2GrantAuthorizationCode = "authorization_code"

OAuth2GrantAuthorizationCode is the 3-legged user-OAuth flow (browser redirect, callback server, code exchange at TokenURL).

View Source
const OAuth2GrantClientCredentials = "client_credentials"

OAuth2GrantClientCredentials is the 2-legged server-to-server flow used by M2M APIs (Auth0 Management, Microsoft Graph daemon apps): POST to TokenURL with form-encoded client_id/client_secret, no user redirect.

Variables

View Source
var ReservedCLIResourceNames = map[string]struct{}{
	"agent_context":    {},
	"api_discovery":    {},
	"auth":             {},
	"auto_refresh":     {},
	"cache":            {},
	"channel_workflow": {},
	"client":           {},
	"data_source":      {},
	"deliver":          {},
	"doctor":           {},
	"export":           {},
	"feedback":         {},
	"helpers":          {},
	"html_extract":     {},
	"import":           {},
	"profile":          {},
	"refresh_bearer":   {},
	"root":             {},
	"search":           {},
	"share_commands":   {},
	"sync":             {},
	"tail":             {},
	"types":            {},
	"which":            {},
	"workflow":         {},
}

ReservedCLIResourceNames is the set of resource names that would collide with reserved single-file templates emitted into the printed CLI's internal/cli/ directory. Two collisions occur if a spec uses one of these as a resource name: the resource template's <name>.go overwrites the reserved file (losing helpers like FeedbackEndpointConfigured() from feedback.go), AND the resource's `new<Name>Cmd` cobra-builder shadows the reserved template's same-named function, breaking the build with a redeclaration error.

Renaming the file alone is not enough; the function-name collision still breaks the build. Reject at parse time and ask the author to rename the resource (e.g., `feedback` → `customer_feedback`, `auth` → `accounts`).

The contract is intentionally stable: removing an entry is allowed only when the corresponding reserved template is also removed from the generator.

View Source
var ReservedCobraUseNames = map[string]struct{}{
	"about":          {},
	"agent-context":  {},
	"analytics":      {},
	"api":            {},
	"auth":           {},
	"completion":     {},
	"doctor":         {},
	"export":         {},
	"feedback":       {},
	"health":         {},
	"help":           {},
	"import":         {},
	"jobs":           {},
	"load":           {},
	"orphans":        {},
	"profile":        {},
	"refresh-bearer": {},
	"search":         {},
	"share":          {},
	"similar":        {},
	"sql":            {},
	"stale":          {},
	"sync":           {},
	"tail":           {},
	"version":        {},
	"which":          {},
	"workflow":       {},
}

ReservedCobraUseNames is the set of cobra command names registered at the top level of every printed CLI's root cobra tree. A spec resource whose name maps to one of these would shadow the framework command at runtime. Distinct from ReservedCLIResourceNames above: that set protects template-file overwrites (snake_case); this set protects cobra-Use shadowing (kebab-case). Hand-maintained; drift invariants enforced by tests in reserved_drift_test.go.

Functions

func AllAuthEnvVarSpecsInferred added in v3.10.0

func AllAuthEnvVarSpecsInferred(envVarSpecs []AuthEnvVar) bool

Types

type APISpec

type APISpec struct {
	Name string `yaml:"name" json:"name"`
	// DisplayName is the human-readable brand name used in user-facing
	// surfaces that aren't a kebab-case slug — Claude Desktop's connector
	// list, MCPB manifest display_name, the MCP server's protocol-level
	// name in `server.NewMCPServer(...)`. Authors can set it explicitly
	// (e.g. "Company GOAT", "Cal.com", "PokéAPI") to preserve unusual
	// capitalization or punctuation; when empty the generator title-cases
	// Name as a fallback. The generate command also fills this from a
	// matching catalog entry's display_name when available.
	DisplayName string `yaml:"display_name,omitempty" json:"display_name,omitempty"`
	// Description describes the API itself ("REST API for ordering pizza").
	// It flows into generated docs and SKILL.md but is intentionally NOT used
	// as the printed CLI's --help text; that's CLIDescription's job.
	Description string `yaml:"description" json:"description"`
	// CLIDescription, when set, becomes the printed CLI's root cobra command
	// `Short:` text. Spec authors should phrase it as what the CLI does
	// ("Order Seattle pizza from the terminal"), not what the API is. When
	// blank the generator falls back to the research narrative's headline,
	// then to a generic "Manage <api> resources via the <api> API". Adding
	// this field eliminates a recurring manual rewrite step that the main
	// skill used to instruct Claude to perform after every generation.
	CLIDescription string `yaml:"cli_description,omitempty" json:"cli_description,omitempty"`
	Version        string `yaml:"version" json:"version"`
	BaseURL        string `yaml:"base_url" json:"base_url"`
	BasePath       string `yaml:"base_path,omitempty" json:"base_path,omitempty"`
	// GraphQLEndpointPath is the path appended to BaseURL for GraphQL POSTs.
	// REST specs leave it empty; GraphQL specs default it to "/graphql" but
	// can override (e.g., Shopify's "/admin/api/{version}/graphql.json").
	// The split exists because some GraphQL APIs put the endpoint behind a
	// per-tenant subdomain or version segment, and the old single-BaseURL
	// model couldn't represent that without hardcoding "/graphql" in the
	// generated client.
	GraphQLEndpointPath string `yaml:"graphql_endpoint_path,omitempty" json:"graphql_endpoint_path,omitempty"`
	// EndpointTemplateVars lists placeholder names embedded in BaseURL or
	// GraphQLEndpointPath as {var} (e.g., ["shop", "version"]). The
	// generator emits per-variable env-var lookups in the printed CLI's
	// config so users can resolve them at runtime. PR-1 carries this field
	// as plumbing only; PR-2 wires the runtime substitution.
	EndpointTemplateVars []string            `yaml:"endpoint_template_vars,omitempty" json:"endpoint_template_vars,omitempty"`
	Owner                string              `yaml:"owner,omitempty" json:"owner,omitempty"`                   // GitHub owner for import paths and Homebrew tap
	Kind                 string              `yaml:"kind,omitempty" json:"kind,omitempty"`                     // "rest" (default) or "synthetic" — synthetic CLIs aggregate multiple sources beyond the spec; dogfood's path-validity check is relaxed accordingly
	SpecSource           string              `yaml:"spec_source,omitempty" json:"spec_source,omitempty"`       // official, community, sniffed, docs — affects generated client defaults
	ClientPattern        string              `yaml:"client_pattern,omitempty" json:"client_pattern,omitempty"` // rest (default), proxy-envelope — affects generated HTTP client
	HTTPTransport        string              `yaml:"http_transport,omitempty" json:"http_transport,omitempty"` // standard (default for official APIs), browser-chrome, or browser-chrome-h3
	ProxyRoutes          map[string]string   `yaml:"proxy_routes,omitempty" json:"proxy_routes,omitempty"`     // path prefix → service name for proxy-envelope routing
	BearerRefresh        BearerRefreshConfig `yaml:"bearer_refresh,omitempty" json:"bearer_refresh,omitzero"`  // live-source metadata for rotating public client bearer tokens
	WebsiteURL           string              `yaml:"website_url,omitempty" json:"website_url,omitempty"`       // product/company website (not the API base URL)
	Category             string              `yaml:"category,omitempty" json:"category,omitempty"`             // catalog category (e.g., productivity, developer-tools) — used for library install path
	Auth                 AuthConfig          `yaml:"auth" json:"auth"`
	TierRouting          TierRoutingConfig   `yaml:"tier_routing,omitempty" json:"tier_routing,omitzero"`
	RequiredHeaders      []RequiredHeader    `yaml:"required_headers,omitempty" json:"required_headers,omitempty"`
	Config               ConfigSpec          `yaml:"config" json:"config"`
	Resources            map[string]Resource `yaml:"resources" json:"resources"`
	Types                map[string]TypeDef  `yaml:"types" json:"types"`
	ExtraCommands        []ExtraCommand      `yaml:"extra_commands,omitempty" json:"extra_commands,omitempty"` // hand-written cobra commands declared so SKILL.md can document them; spec-only metadata, no code generated
	Cache                CacheConfig         `yaml:"cache,omitempty" json:"cache"`                             // cache freshness + auto-refresh config; when enabled, generated read commands auto-refresh stale local data before serving
	Share                ShareConfig         `yaml:"share,omitempty" json:"share"`                             // git-backed snapshot sharing config; when enabled, emits a `share` subcommand that publishes/subscribes to a git repo
	MCP                  MCPConfig           `yaml:"mcp,omitempty" json:"mcp"`                                 // MCP server generation config; when unset, the emitted MCP binary is stdio-only (today's default). Opting into http adds a --transport/--addr flag surface so the same binary can serve cloud-hosted agents.
	Throttling           ThrottlingConfig    `yaml:"throttling,omitempty" json:"throttling"`                   // cost-based throttling config; when Enabled with a recognized Shape, the generator emits a ThrottleState (generic harness) plus a per-Shape parser that reads the API's cost bucket. Only the "shopify" Shape ships in v1.
}

func Parse

func Parse(path string) (*APISpec, error)

func ParseBytes

func ParseBytes(data []byte) (*APISpec, error)

func (*APISpec) CountMCPTools

func (s *APISpec) CountMCPTools() (total, public int)

CountMCPTools counts total endpoints and public (NoAuth) endpoints across all resources and sub-resources.

func (*APISpec) EffectiveDisplayName

func (s *APISpec) EffectiveDisplayName() string

EffectiveDisplayName returns the human-readable brand name for this CLI. Explicit DisplayName wins (preserves "Company GOAT", "Cal.com", "PokéAPI" shape); otherwise we title-case Name. Used by the MCP server's protocol name, the MCPB manifest, and any surface that wants a friendly identity instead of the kebab-case slug.

func (*APISpec) EffectiveEndpointAuth added in v3.9.0

func (s *APISpec) EffectiveEndpointAuth(resource Resource, endpoint Endpoint) (authType string, noAuth bool)

func (*APISpec) EffectiveHTTPTransport

func (s *APISpec) EffectiveHTTPTransport() string

func (*APISpec) EffectiveSubEndpointAuth added in v3.9.0

func (s *APISpec) EffectiveSubEndpointAuth(parent Resource, subResource Resource, endpoint Endpoint) (authType string, noAuth bool)

func (*APISpec) EffectiveTier added in v3.9.0

func (s *APISpec) EffectiveTier(resource Resource, endpoint Endpoint) string

func (*APISpec) EffectiveTierConfig added in v3.9.0

func (s *APISpec) EffectiveTierConfig(resource Resource, endpoint Endpoint) (string, TierConfig, bool)

func (*APISpec) HasCostThrottling added in v3.1.0

func (s *APISpec) HasCostThrottling() bool

HasCostThrottling reports whether the spec opts into cost-based throttling primitives. Used by the generator to gate emission of throttle.go and the related conditional blocks in client.go / graphql_client.go / root.go. Specs without this flag regenerate byte-identical to the pre-PR-3 output.

func (*APISpec) HasHTMLExtractMode

func (s *APISpec) HasHTMLExtractMode(mode string) bool

HasHTMLExtractMode reports whether any endpoint in the spec declares html_extract with the given effective mode. Used by the html_extract template to gate per-mode helpers: a CLI that uses only HTMLExtractModeEmbeddedJSON does not need the page-mode DOM walkers or links-mode anchor parsing, and vice versa.

`mode` should be one of the HTMLExtractMode* constants. Modes that don't appear in any endpoint return false; modes are matched by their effective value (so an unset Mode counts as page).

func (*APISpec) HasHTMLExtraction

func (s *APISpec) HasHTMLExtraction() bool

func (*APISpec) HasResourceBaseURLOverride

func (s *APISpec) HasResourceBaseURLOverride() bool

HasResourceBaseURLOverride reports whether any resource (top-level or nested sub-resource) declares a BaseURL override. Used by the client template to gate the absolute-URL detection branch — specs that don't opt in regenerate byte-identically.

func (*APISpec) HasTierRouting added in v3.9.0

func (s *APISpec) HasTierRouting() bool

func (*APISpec) IsSynthetic

func (s *APISpec) IsSynthetic() bool

IsSynthetic reports whether this spec declares a multi-source / combo CLI where hand-built commands intentionally go beyond the spec. Dogfood skips strict path-validity and scorecard marks path_validity as unscored.

func (*APISpec) NormalizeAuthEnvVarSpecs added in v3.10.0

func (s *APISpec) NormalizeAuthEnvVarSpecs()

func (*APISpec) UsesBrowserHTTP3Transport

func (s *APISpec) UsesBrowserHTTP3Transport() bool

func (*APISpec) UsesBrowserHTTPTransport

func (s *APISpec) UsesBrowserHTTPTransport() bool

func (*APISpec) UsesBrowserManagedUserAgent

func (s *APISpec) UsesBrowserManagedUserAgent() bool

func (*APISpec) Validate

func (s *APISpec) Validate() error

type AuthConfig

type AuthConfig struct {
	Type             string       `yaml:"type" json:"type"` // api_key, oauth2, bearer_token, cookie, composed, session_handshake, none
	Header           string       `yaml:"header" json:"header"`
	Format           string       `yaml:"format" json:"format"`
	EnvVars          []string     `yaml:"env_vars" json:"env_vars"`
	EnvVarSpecs      []AuthEnvVar `yaml:"env_var_specs,omitempty" json:"env_var_specs,omitempty"`
	Optional         bool         `yaml:"optional,omitempty" json:"optional,omitempty"` // true when the key enhances a subset of features (e.g., USDA nutrition backfill) rather than gating core functionality; doctor treats unconfigured optional auth as INFO not FAIL and README frames the section as "Optional"
	Scheme           string       `yaml:"scheme,omitempty" json:"scheme,omitempty"`     // OpenAPI security scheme name
	In               string       `yaml:"in,omitempty" json:"in,omitempty"`             // header, query, cookie
	KeyURL           string       `yaml:"key_url,omitempty" json:"key_url,omitempty"`   // URL where users can register for an API key
	Title            string       `yaml:"title,omitempty" json:"title,omitempty"`       // user-facing credential field title for install/config surfaces
	Description      string       `yaml:"description,omitempty" json:"description,omitempty"`
	AuthorizationURL string       `yaml:"authorization_url,omitempty" json:"authorization_url,omitempty"`
	TokenURL         string       `yaml:"token_url,omitempty" json:"token_url,omitempty"`
	Scopes           []string     `yaml:"scopes,omitempty" json:"scopes,omitempty"`
	CookieDomain     string       `yaml:"cookie_domain,omitempty" json:"cookie_domain,omitempty"` // domain to read browser cookies from (e.g. ".notion.so")
	Cookies          []string     `yaml:"cookies,omitempty" json:"cookies,omitempty"`             // named cookies to extract for composed auth (e.g. ["customerId", "authToken"])
	Inferred         bool         `yaml:"inferred,omitempty" json:"inferred,omitempty"`           // true when auth was inferred from spec description, not declared in securitySchemes

	// VerifyPath is an optional path appended to base_url that the doctor
	// command probes to validate credentials. Set this to a known-good
	// authenticated GET endpoint that returns 2xx for any valid token (e.g.
	// "/me?fields=id" for Meta, "/v1/account" for Stripe, "/user" for GitHub,
	// "/users/@me" for Discord). When empty, doctor falls back to probing
	// the bare base URL and classifies 401/403 as "inconclusive" rather than
	// "invalid", because many versioned API roots return 401 regardless of
	// token validity (the path isn't a routed endpoint, but the gateway
	// still demands credentials in a meaningful context).
	VerifyPath string `yaml:"verify_path,omitempty" json:"verify_path,omitempty"`

	// Browser-session verification fields. Used when a website-facing CLI
	// depends on browser-derived cookies or clearance state for its required
	// happy path. The generator emits validation and proof handling, and the
	// shipcheck pipeline treats a missing proof as a blocker.
	RequiresBrowserSession         bool   `yaml:"requires_browser_session,omitempty" json:"requires_browser_session,omitempty"`
	BrowserSessionReason           string `yaml:"browser_session_reason,omitempty" json:"browser_session_reason,omitempty"`
	BrowserSessionValidationPath   string `yaml:"browser_session_validation_path,omitempty" json:"browser_session_validation_path,omitempty"`
	BrowserSessionValidationMethod string `yaml:"browser_session_validation_method,omitempty" json:"browser_session_validation_method,omitempty"`

	// Session-handshake fields. Used only when Type == "session_handshake".
	// The pattern: GET BootstrapURL to seed cookies → GET TokenURL to receive
	// an anti-CSRF token (the "crumb" on Yahoo Finance, similarly named on
	// Walmart, some streaming APIs, Facebook's internal graph) → pass that
	// token on every subsequent data request as TokenParamName in TokenParamIn.
	// The generator emits a cookie jar, disk-persisted session file, and auto-
	// invalidation on InvalidateOnStatus responses.
	BootstrapURL       string `yaml:"bootstrap_url,omitempty" json:"bootstrap_url,omitempty"`               // optional GET to seed cookies before token fetch (e.g. "https://fc.yahoo.com/")
	SessionTokenURL    string `yaml:"session_token_url,omitempty" json:"session_token_url,omitempty"`       // endpoint that returns the token (e.g. "https://query2.finance.yahoo.com/v1/test/getcrumb"); distinct from TokenURL (OAuth) to avoid conflation
	TokenFormat        string `yaml:"token_format,omitempty" json:"token_format,omitempty"`                 // "text" (raw body) or "json" (extract via TokenJSONPath); default "text"
	TokenJSONPath      string `yaml:"token_json_path,omitempty" json:"token_json_path,omitempty"`           // when TokenFormat is "json", dot-path to the token field (e.g. "data.crumb")
	TokenParamName     string `yaml:"token_param_name,omitempty" json:"token_param_name,omitempty"`         // parameter name to attach to requests (e.g. "crumb")
	TokenParamIn       string `yaml:"token_param_in,omitempty" json:"token_param_in,omitempty"`             // "query" or "header"; default "query"
	InvalidateOnStatus []int  `yaml:"invalidate_on_status,omitempty" json:"invalidate_on_status,omitempty"` // HTTP status codes that should invalidate the cached token and re-bootstrap (e.g. [401, 403])
	SessionTTLHours    int    `yaml:"session_ttl_hours,omitempty" json:"session_ttl_hours,omitempty"`       // how long to trust a cached session (default 24)

	// OAuth2Grant selects the OAuth2 sub-flow when Type=="oauth2". Defaults
	// to authorization_code; ignored for non-oauth2 types. Read via
	// EffectiveOAuth2Grant() so the default lives in one place.
	OAuth2Grant string `yaml:"oauth2_grant,omitempty" json:"oauth2_grant,omitempty"`
}

func (*AuthConfig) CanonicalEnvVar added in v3.10.0

func (c *AuthConfig) CanonicalEnvVar() *AuthEnvVar

CanonicalEnvVar returns the deterministic canonical entry for human-prose surfaces.

func (AuthConfig) EffectiveOAuth2Grant added in v3.8.0

func (c AuthConfig) EffectiveOAuth2Grant() string

EffectiveOAuth2Grant returns the configured OAuth2 grant type, defaulting to OAuth2GrantAuthorizationCode when unset.

func (*AuthConfig) IsAuthEnvVarORCase added in v3.10.0

func (c *AuthConfig) IsAuthEnvVarORCase() bool

IsAuthEnvVarORCase reports whether all EnvVarSpecs are non-required per_call vars. In this shape, no single var is the canonical credential; the runtime tries each in turn and returns the first non-empty value. Returns false when EnvVarSpecs has fewer than 2 entries, any entry is Required, or any entry is not Kind=per_call.

func (*AuthConfig) NormalizeEnvVarSpecs added in v3.10.0

func (c *AuthConfig) NormalizeEnvVarSpecs(context string)

type AuthEnvVar added in v3.10.0

type AuthEnvVar struct {
	Name        string         `yaml:"name" json:"name"`
	Kind        AuthEnvVarKind `yaml:"kind,omitempty" json:"kind,omitempty"`
	Required    bool           `yaml:"required" json:"required"`
	Sensitive   bool           `yaml:"sensitive" json:"sensitive"` // orthogonal to Kind; drives redaction policy
	Description string         `yaml:"description,omitempty" json:"description,omitempty"`
	Inferred    bool           `yaml:"inferred,omitempty" json:"inferred,omitempty"`
}

func (AuthEnvVar) EffectiveKind added in v3.10.0

func (v AuthEnvVar) EffectiveKind() AuthEnvVarKind

EffectiveKind treats legacy empty kinds as per-call credentials.

func (AuthEnvVar) IsRequestCredential added in v3.10.0

func (v AuthEnvVar) IsRequestCredential() bool

IsRequestCredential reports whether this env var can satisfy request auth.

func (AuthEnvVar) MarkdownDescription added in v3.10.0

func (v AuthEnvVar) MarkdownDescription() string

type AuthEnvVarKind added in v3.10.0

type AuthEnvVarKind string
const (
	AuthEnvVarKindPerCall       AuthEnvVarKind = "per_call"
	AuthEnvVarKindAuthFlowInput AuthEnvVarKind = "auth_flow_input"
	AuthEnvVarKindHarvested     AuthEnvVarKind = "harvested"
)

func (AuthEnvVarKind) SensitivePlaceholder added in v3.10.0

func (k AuthEnvVarKind) SensitivePlaceholder() string

type BearerRefreshConfig added in v3.9.0

type BearerRefreshConfig struct {
	BundleURL string `yaml:"bundle_url,omitempty" json:"bundle_url,omitempty"`
	Pattern   string `yaml:"pattern,omitempty" json:"pattern,omitempty"`
}

func (BearerRefreshConfig) Enabled added in v3.9.0

func (c BearerRefreshConfig) Enabled() bool

type CacheCommand

type CacheCommand struct {
	Name      string   `yaml:"name" json:"name"`           // lowercase cobra command path, without the binary name (e.g., "today" or "insights stale")
	Resources []string `yaml:"resources" json:"resources"` // resource names to refresh before serving the command
}

CacheCommand declares that a hand-authored command path reads one or more syncable resources and should participate in the generated freshness hook.

type CacheConfig

type CacheConfig struct {
	Enabled        bool              `yaml:"enabled,omitempty" json:"enabled,omitempty"`                 // master switch; when false, freshness helpers and pre-run refresh hook are not emitted
	StaleAfter     string            `yaml:"stale_after,omitempty" json:"stale_after,omitempty"`         // default duration after which any resource's last_synced_at is considered stale (e.g., "6h"). Blank means runtime default (6h).
	RefreshTimeout string            `yaml:"refresh_timeout,omitempty" json:"refresh_timeout,omitempty"` // max wall-clock the pre-run refresh may block the command (e.g., "30s"). On timeout the command serves stale data with a stderr warning. Blank means runtime default (30s).
	EnvOptOut      string            `yaml:"env_opt_out,omitempty" json:"env_opt_out,omitempty"`         // env var name that disables auto-refresh when set to "1" (e.g., LINEAR_NO_AUTO_REFRESH). Blank lets the template derive {{upper name}}_NO_AUTO_REFRESH.
	Resources      map[string]string `yaml:"resources,omitempty" json:"resources,omitempty"`             // per-resource override of stale_after (e.g., quotes: "5m", channels: "24h"). Resources not listed inherit StaleAfter.
	Commands       []CacheCommand    `yaml:"commands,omitempty" json:"commands,omitempty"`               // optional custom command-path coverage for hand-authored store-backed reads. Generated resource commands are covered automatically.
}

CacheConfig gates the auto-refresh machinery emitted into a printed CLI. Opt-in — CLIs whose local store is per-user state (carts, drafts) should leave Enabled at its zero value so reads never silently replace the user's state with a snapshot from a different session.

StaleAfter and RefreshTimeout are strings parsed to time.Duration at CLI runtime; keeping them as strings lets spec authors write "6h" or "30s" and preserves the yaml-level representation for round-trip tooling.

type ConfigSpec

type ConfigSpec struct {
	Format string `yaml:"format" json:"format"` // toml, yaml
	Path   string `yaml:"path" json:"path"`
}

type Endpoint

type Endpoint struct {
	Method          string            `yaml:"method" json:"method"`
	Path            string            `yaml:"path" json:"path"`
	Description     string            `yaml:"description" json:"description"`
	Params          []Param           `yaml:"params" json:"params"`
	Body            []Param           `yaml:"body" json:"body"`
	Response        ResponseDef       `yaml:"response" json:"response"`
	ResponseFormat  string            `yaml:"response_format,omitempty" json:"response_format,omitempty"` // json (default) or html
	HTMLExtract     *HTMLExtract      `yaml:"html_extract,omitempty" json:"html_extract,omitempty"`       // extraction options when response_format is html
	Pagination      *Pagination       `yaml:"pagination" json:"pagination"`
	ResponsePath    string            `yaml:"response_path,omitempty" json:"response_path,omitempty"`       // path to extract data array from response (e.g., "data", "results.items")
	Meta            map[string]string `yaml:"meta,omitempty" json:"meta,omitempty"`                         // per-endpoint metadata (e.g., source_tier, source_count from crowd-sniff)
	HeaderOverrides []RequiredHeader  `yaml:"header_overrides,omitempty" json:"header_overrides,omitempty"` // per-endpoint header overrides (e.g., different api-version)
	NoAuth          bool              `yaml:"no_auth,omitempty" json:"no_auth,omitempty"`                   // true when the endpoint does not require authentication
	Tier            string            `yaml:"tier,omitempty" json:"tier,omitempty"`
	// IDField is the resolved primary-key field name for items returned by this
	// endpoint, populated either by a path-item-level `x-resource-id` extension
	// or, for OpenAPI specs, by walking the response schema (id → name → first
	// required scalar). Empty when no key could be resolved; templates fall back
	// to runtime list scanning. Internal YAML specs may set this directly.
	IDField string `yaml:"id_field,omitempty" json:"id_field,omitempty"`
	// Critical flags this endpoint's resource as essential to a sync run. When
	// true, a per-resource failure is treated as a hard failure even under the
	// new (non-strict) exit-code policy. Populated from the path-item-level
	// `x-critical` extension on OpenAPI specs; defaults to false.
	Critical bool   `yaml:"critical,omitempty" json:"critical,omitempty"`
	Alias    string `yaml:"-" json:"-"` // computed, not from YAML
}

func (Endpoint) EffectiveResponseFormat

func (e Endpoint) EffectiveResponseFormat() string

func (Endpoint) UsesHTMLResponse

func (e Endpoint) UsesHTMLResponse() bool

type ExtraCommand

type ExtraCommand struct {
	Name        string `yaml:"name" json:"name"`                     // command path, e.g. "boxscore" or "tv airing-today"
	Description string `yaml:"description" json:"description"`       // one-line description rendered after a dash
	Args        string `yaml:"args,omitempty" json:"args,omitempty"` // optional positional arg signature, e.g. "<event_id>" or "<team1> <team2>"
}

ExtraCommand declares a hand-written cobra command so the SKILL.md Command Reference can list it alongside spec-driven resources. The generator does not emit code for these — authors hand-write the command in internal/cli/. Without this declaration the SKILL.md template only sees .Resources and silently omits hand-written commands, which is the drift class that motivated this field.

type HTMLExtract

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)
	Limit        int      `yaml:"limit,omitempty" json:"limit,omitempty"`                 // max links to return; defaults at runtime (mode: links)
	// ScriptSelector identifies the <script> tag containing serialized
	// page state when mode is embedded-json. Defaults to
	// DefaultEmbeddedJSONScriptSelector ("script#__NEXT_DATA__") when
	// empty — the most common Next.js pages-router shape. Other SSR
	// frameworks declare per-site selectors (Nuxt: "script#__NUXT__",
	// etc.). Selector grammar is the simple "tag" / "tag#id" form
	// supported by the runtime extractor; expand later if needed.
	ScriptSelector string `yaml:"script_selector,omitempty" json:"script_selector,omitempty"`
	// JSONPath is a dot-notation walk into the parsed JSON inside the
	// matched script tag (mode: embedded-json). For Next.js the typical
	// value is "props.pageProps.<route-data>"; for Nuxt "data.<route>".
	// Empty path returns the entire parsed JSON. Missing intermediate
	// keys yield a typed-empty result rather than an error.
	JSONPath string `yaml:"json_path,omitempty" json:"json_path,omitempty"`
}

func (*HTMLExtract) EffectiveMode

func (h *HTMLExtract) EffectiveMode() string

func (*HTMLExtract) EffectiveScriptSelector

func (h *HTMLExtract) EffectiveScriptSelector() string

EffectiveScriptSelector returns the configured script selector, or the default Next.js pages-router selector when unset. Only meaningful when EffectiveMode() == HTMLExtractModeEmbeddedJSON.

type Intent

type Intent struct {
	Name        string        `yaml:"name" json:"name"`                           // MCP tool name; snake_case, unique within the spec
	Description string        `yaml:"description" json:"description"`             // agent-facing description; should name the *intent*, not the endpoints
	Params      []IntentParam `yaml:"params,omitempty" json:"params,omitempty"`   // input parameters the intent tool exposes to MCP callers
	Steps       []IntentStep  `yaml:"steps" json:"steps"`                         // ordered list of endpoint calls; at least one required
	Returns     string        `yaml:"returns,omitempty" json:"returns,omitempty"` // capture name whose value is returned to the caller; defaults to the last step's capture when blank
}

Intent declares an MCP tool that composes multiple endpoint calls into a single agent-facing operation. The generator emits one handler per intent that resolves bindings, calls each endpoint in order against the CLI's existing HTTP client, and returns the captured value named by Returns.

Binding syntax — each value in a step's Bind map is a string expression:

  • `${input.<name>}` resolves to the MCP request's input parameter
  • `${<capture>.<field>}` resolves to a field of a previous step's captured JSON response
  • anything else is used as a string literal

Type coercion: all bound values are rendered as strings at runtime; JSON bodies for POST/PUT/PATCH are built from the resolved map. The intent surface intentionally does not support array indexing, conditional branching, or looping in v1 — those escapes belong in U3's code-orchestration pattern, not here.

type IntentParam

type IntentParam struct {
	Name        string `yaml:"name" json:"name"`
	Type        string `yaml:"type" json:"type"` // one of: string, integer, boolean
	Required    bool   `yaml:"required,omitempty" json:"required,omitempty"`
	Description string `yaml:"description" json:"description"`
}

IntentParam mirrors a narrow slice of the endpoint Param type. Kept small by design: intents are compositions, so parameter shapes should be simple string/int/bool inputs that bind into step calls, not full nested bodies.

type IntentStep

type IntentStep struct {
	Endpoint string            `yaml:"endpoint" json:"endpoint"`                   // dotted path into the spec's resources, e.g., "messages.get_thread"
	Bind     map[string]string `yaml:"bind,omitempty" json:"bind,omitempty"`       // map of endpoint param name -> binding expression
	Capture  string            `yaml:"capture,omitempty" json:"capture,omitempty"` // name to bind this step's response under for subsequent steps / returns; must be unique within the intent
}

IntentStep declares one endpoint call inside an intent. Endpoint references are `resource.endpoint` or `resource.sub_resource.endpoint`; the validator confirms the path resolves against APISpec.Resources at spec load.

type MCPConfig

type MCPConfig struct {
	Transport              []string `yaml:"transport,omitempty" json:"transport,omitempty"`                             // allowed transports the generated binary compiles support for; empty == [stdio]. Runtime transport is chosen via the --transport flag and PP_MCP_TRANSPORT env.
	Addr                   string   `yaml:"addr,omitempty" json:"addr,omitempty"`                                       // default bind address for the http transport (e.g., ":7777"). Blank means runtime default (":7777"). Ignored unless http is in Transport.
	Intents                []Intent `yaml:"intents,omitempty" json:"intents,omitempty"`                                 // higher-level MCP tools that compose multiple endpoint calls. The agent sees one intent tool; the generator emits a handler that fans out to the declared endpoints sequentially. Anti-pattern to fight: one-tool-per-endpoint mirrors that force agents to stitch primitives.
	EndpointTools          string   `yaml:"endpoint_tools,omitempty" json:"endpoint_tools,omitempty"`                   // "visible" (default) keeps the per-endpoint MCP tools; "hidden" suppresses them so only intents + generator-emitted tools appear. Use "hidden" when intents fully cover the surface and raw endpoints would be noise.
	Orchestration          string   `yaml:"orchestration,omitempty" json:"orchestration,omitempty"`                     // "endpoint-mirror" (default), "intent", or "code". Code-orchestration emits a thin <api>_search + <api>_execute pair covering the full surface in ~1K tokens; used for very large APIs where even intent-grouped tools would overflow context. Mutually exclusive with endpoint-mirror at emission time.
	OrchestrationThreshold int      `yaml:"orchestration_threshold,omitempty" json:"orchestration_threshold,omitempty"` // endpoint count above which the generator warns that code-orchestration would be a better default. Zero means use the built-in default (50).
}

MCPConfig declares how the generated MCP server binary is shaped. When empty the generator emits today's behavior: a stdio-only server via server.ServeStdio. Opting http into Transport adds a --transport flag (stdio|http) and, for http, an --addr flag so the same binary can also serve an HTTP streamable transport.

Rationale: stdio-only servers can only reach clients that share a filesystem and can spawn a subprocess. Cloud-hosted agents (hosted Claude Code sessions, Managed Agents, web clients) cannot, so they need a remote transport option. Declaring transports in the spec rather than inferring at generate time keeps the decision visible and reviewable in the published CLI's source spec.

Allowed Transport values: "stdio", "http". An empty list is treated as ["stdio"] for backward compatibility. Unknown values are rejected at spec load; this prevents silent drift when new transports are introduced.

func (MCPConfig) EffectiveOrchestrationThreshold

func (m MCPConfig) EffectiveOrchestrationThreshold() int

EffectiveOrchestrationThreshold returns the resolved threshold, applying the built-in default when the spec leaves it unset.

func (MCPConfig) EffectiveTransports

func (m MCPConfig) EffectiveTransports() []string

EffectiveTransports returns the transports the generated binary should support, defaulting to stdio when none are declared. Using this helper from templates and the generator avoids sprinkling the default in two places.

func (MCPConfig) HasTransport

func (m MCPConfig) HasTransport(t string) bool

HasTransport reports whether t is among the effective transports for this MCPConfig. Case-insensitive on the comparison since spec authors may write "HTTP" and the generator normalizes to lowercase at validation time.

func (MCPConfig) IsCodeOrchestration

func (m MCPConfig) IsCodeOrchestration() bool

IsCodeOrchestration reports whether this MCP config opts into the code-orchestration thin surface. Templates branch on this to emit only <api>_search + <api>_execute instead of the endpoint-mirror.

type Pagination

type Pagination struct {
	Type           string `yaml:"type" json:"type"`                         // cursor, offset, page_token
	LimitParam     string `yaml:"limit_param" json:"limit_param"`           // query param name for page size (limit, maxResults, pageSize)
	CursorParam    string `yaml:"cursor_param" json:"cursor_param"`         // query param name for cursor (after, pageToken, offset)
	NextCursorPath string `yaml:"next_cursor_path" json:"next_cursor_path"` // response field with next cursor (nextPageToken, cursor)
	HasMoreField   string `yaml:"has_more_field" json:"has_more_field"`     // response field indicating more pages (has_more)
}

type Param

type Param struct {
	Name        string   `yaml:"name" json:"name"`
	Type        string   `yaml:"type" json:"type"`
	Required    bool     `yaml:"required" json:"required"`
	Positional  bool     `yaml:"positional" json:"positional"`
	PathParam   bool     `yaml:"path_param,omitempty" json:"path_param,omitempty"` // true for path params rendered as flags (e.g., pagination)
	Default     any      `yaml:"default" json:"default"`
	Description string   `yaml:"description" json:"description"`
	Fields      []Param  `yaml:"fields" json:"fields"`                     // for nested objects
	Enum        []string `yaml:"enum,omitempty" json:"enum,omitempty"`     // enum constraints for the parameter
	Format      string   `yaml:"format,omitempty" json:"format,omitempty"` // OpenAPI format hints (date-time, email, uri, etc.)
	// IdentName, when set, overrides Name for Go identifier and CLI flag
	// derivation (camel/flagName). Name remains the wire-side parameter name
	// used in URLs, JSON keys, and path substitution. Populated by the
	// generator's flag-collision dedup pass when two params on the same
	// endpoint would otherwise produce identical Go identifiers or CLI flag
	// names — for example Twilio's StartTime/StartTime>/StartTime< all
	// collapsing to "StartTime" through camelization. Most params leave this
	// empty and template helpers fall back to Name.
	IdentName string `yaml:"-" json:"-"`
}

type RequiredHeader

type RequiredHeader struct {
	Name  string `yaml:"name" json:"name"`
	Value string `yaml:"value" json:"value"`
}

RequiredHeader represents a non-auth header that the API requires on most requests (e.g., cal-api-version, Stripe-Version, anthropic-version). Detected automatically from OpenAPI specs when a required header parameter appears on >80% of operations.

type Resource

type Resource struct {
	Description string   `yaml:"description" json:"description"`
	Path        string   `yaml:"path,omitempty" json:"path,omitempty"`             // base path for operations shorthand (e.g., /api/items)
	Operations  []string `yaml:"operations,omitempty" json:"operations,omitempty"` // shorthand: list, get, create, update, delete, search
	// BaseURL overrides the spec-level BaseURL for this resource's
	// endpoints. Fixed at generation time. Incompatible with the
	// proxy-envelope client pattern, which POSTs every request to a
	// single URL.
	BaseURL      string              `yaml:"base_url,omitempty" json:"base_url,omitempty"`
	Tier         string              `yaml:"tier,omitempty" json:"tier,omitempty"`
	Endpoints    map[string]Endpoint `yaml:"endpoints" json:"endpoints"`
	SubResources map[string]Resource `yaml:"sub_resources,omitempty" json:"sub_resources,omitempty"`
}

type ResponseDef

type ResponseDef struct {
	Type          string                 `yaml:"type" json:"type"` // object, array
	Item          string                 `yaml:"item" json:"item"` // type name
	Discriminator *ResponseDiscriminator `yaml:"discriminator,omitempty" json:"discriminator,omitempty"`
}

type ResponseDiscriminator added in v3.5.0

type ResponseDiscriminator struct {
	Field   string            `yaml:"field" json:"field"`
	Mapping map[string]string `yaml:"mapping,omitempty" json:"mapping,omitempty"` // discriminator value -> schema/resource name
}

type ShareConfig

type ShareConfig struct {
	Enabled        bool     `yaml:"enabled,omitempty" json:"enabled,omitempty"`                 // master switch; when false, the share package and command are not emitted
	SnapshotTables []string `yaml:"snapshot_tables,omitempty" json:"snapshot_tables,omitempty"` // explicit allowlist of SQLite tables included in the snapshot. Required when Enabled. Names matching denylisted patterns (*_cache, *_secrets, auth_*) are rejected at parse time.
	DefaultRepo    string   `yaml:"default_repo,omitempty" json:"default_repo,omitempty"`       // optional default git remote (e.g., "git@github.com:acme/linear-snapshots.git"); command-line --repo flag always wins
	DefaultBranch  string   `yaml:"default_branch,omitempty" json:"default_branch,omitempty"`   // optional default branch for push/pull; blank means "main"
}

ShareConfig gates the git-backed snapshot share surface emitted into a printed CLI. When Enabled, the generator emits an internal/share package plus a `share` cobra command (publish, subscribe, export, import). Share is off by default because it is a multi-user feature and most CLIs are single-user; enabling also requires an explicit SnapshotTables allowlist to prevent accidental export of auth or per-user state.

type ThrottleShape added in v3.1.0

type ThrottleShape string

ThrottleShape names the API-specific cost-bucket parser the generator wires into the GraphQL client. The generic harness (bucket math, retry, --throttle-mode flag) is shape-agnostic; only the parser that reads the API's response into a ThrottleStatus differs per shape, because every API surfaces its calculated cost in a different place. Adding a new shape means: (1) add a constant here, (2) extend validateThrottling to accept it, (3) add the parser block to graphql_client.go.tmpl gated on `eq .Throttling.Shape "<name>"`. No core code changes.

const (
	// ThrottleShapeShopify reads `extensions.cost.throttleStatus.{maximumAvailable,
	// currentlyAvailable,restoreRate}` from each GraphQL response. This is the
	// only shape supported in v1; GitHub's queryable `rateLimit` field and
	// Datadog's header-based cost limits will need their own shapes (and the
	// GitHub case will need a query-rewrite layer, since rateLimit is a schema
	// field rather than a response extension).
	ThrottleShapeShopify ThrottleShape = "shopify"
)

type ThrottlingConfig added in v3.1.0

type ThrottlingConfig struct {
	Enabled bool          `yaml:"enabled,omitempty" json:"enabled,omitempty"`
	Shape   ThrottleShape `yaml:"shape,omitempty" json:"shape,omitempty"`
}

ThrottlingConfig opts a printed CLI into the cost-based throttling primitives. Enabled turns the surface on (--throttle-mode flag, ThrottleState, budget projection, retry helper); default off so existing CLIs regenerate byte-identically when this field is unset. Shape selects the per-API parser and is required when Enabled is true; see ThrottleShape for the valid values and how to add new ones.

Authors opt in by writing `throttling: { enabled: true, shape: shopify }`.

type TierConfig added in v3.9.0

type TierConfig struct {
	BaseURL            string     `yaml:"base_url,omitempty" json:"base_url,omitempty"`
	Auth               AuthConfig `yaml:"auth,omitempty" json:"auth,omitzero"`
	AllowCrossHostAuth bool       `yaml:"allow_cross_host_auth,omitempty" json:"allow_cross_host_auth,omitempty"`
}

type TierRoutingConfig added in v3.9.0

type TierRoutingConfig struct {
	DefaultTier string                `yaml:"default_tier,omitempty" json:"default_tier,omitempty"`
	Tiers       map[string]TierConfig `yaml:"tiers,omitempty" json:"tiers,omitempty"`
}

type TypeDef

type TypeDef struct {
	Fields []TypeField `yaml:"fields" json:"fields"`
}

type TypeField

type TypeField struct {
	Name string   `yaml:"name" json:"name"`
	Type string   `yaml:"type" json:"type"`
	Enum []string `yaml:"enum,omitempty" json:"enum,omitempty"`
	// Selection is an optional GraphQL sub-selection rendered when this field
	// is used in a generated GraphQL query. It lets wrapper specs keep the Go
	// field simple (for example, totalPriceSet as json.RawMessage) while still
	// issuing valid nested GraphQL selections.
	Selection string `yaml:"selection,omitempty" json:"selection,omitempty"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL