{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://freesail.ai/schemas/catalog-schema-v1.json",
  "title": "Freesail Catalog Definition",
  "description": "Meta-schema for catalog.json files. Describes the structure required by the Freesail Gateway and the Freesail CLI. Set \"$schema\" to this file's URI to enable IDE validation and autocompletion.",
  "type": "object",
  "required": ["catalogId", "title", "components"],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "URI of the meta-schema used to validate this catalog file."
    },
    "$id": {
      "type": "string",
      "format": "uri",
      "description": "Optional JSON Schema identifier. When present should match catalogId."
    },
    "catalogId": {
      "type": "string",
      "format": "uri",
      "description": "REQUIRED. Unique URI identifying this catalog version. Used verbatim in the gateway system prompt and by clients in create_surface. Use a stable versioned URI, e.g. https://example.com/catalogs/my_catalog_v1.json."
    },
    "title": {
      "type": "string",
      "minLength": 1,
      "description": "REQUIRED. Human-readable catalog name. Appears as the section header in the gateway system prompt."
    },
    "description": {
      "type": "string",
      "description": "Overview of this catalog's domain and purpose. Shown in the system prompt below the title."
    },
    "components": {
      "type": "object",
      "description": "REQUIRED. Map of component names to their schema definitions. At least one entry is required. The gateway converter generates one MCP tool per entry.",
      "minProperties": 1,
      "additionalProperties": {
        "$ref": "#/$defs/CatalogComponent"
      }
    },
    "functions": {
      "type": "object",
      "description": "Optional map of client-side function declarations keyed by function name. Described to the agent in the system prompt so it knows what function expressions are valid.",
      "additionalProperties": {
        "$ref": "#/$defs/CatalogFunction"
      }
    },
    "$defs": {
      "type": "object",
      "description": "Local reusable schema definitions. CatalogComponentCommon is the conventional entry — it defines properties shared by all components in this catalog. Entries here are resolved by the gateway converter when components use \"$ref\": \"#/$defs/Name\".",
      "additionalProperties": {
        "$ref": "#/$defs/PropertySchema"
      }
    }
  },

  "$defs": {

    "CatalogComponent": {
      "type": "object",
      "description": "A single component definition. Must have either allOf (standard inheritance pattern) or properties (inline pattern). The gateway converter traverses allOf and properties recursively to build the MCP tool input schema and system prompt.",
      "anyOf": [
        { "required": ["allOf"] },
        { "required": ["properties"] }
      ],
      "properties": {
        "type": {
          "const": "object",
          "description": "Must be 'object'. All A2UI components are JSON objects."
        },
        "description": {
          "type": "string",
          "description": "Component description surfaced in the system prompt. The agent relies on this to understand when and how to use the component."
        },
        "allOf": {
          "type": "array",
          "description": "Composition array. Standard pattern: [ComponentCommon ref, CatalogComponentCommon ref, optional Checkable ref, primary inline schema with description + properties + required].",
          "minItems": 1,
          "items": { "$ref": "#/$defs/ComponentSubSchema" }
        },
        "properties": {
          "type": "object",
          "description": "Direct property map. Use allOf instead for the standard inheritance pattern.",
          "additionalProperties": { "$ref": "#/$defs/PropertySchema" }
        },
        "required": {
          "$ref": "#/$defs/RequiredList"
        },
        "unevaluatedProperties": {
          "type": "boolean",
          "description": "Set to false to disallow properties beyond those declared. Recommended — prevents the agent from sending undeclared properties."
        }
      }
    },

    "ComponentSubSchema": {
      "description": "A single entry in a component's allOf array. Either a $ref to a named type, or an inline object schema defining this component's own properties.",
      "oneOf": [
        {
          "type": "object",
          "description": "Inherits a named type via $ref. The gateway converter resolves ComponentCommon, CatalogComponentCommon, and Checkable from its bundled definitions. Other refs are resolved against the catalog's $defs.",
          "required": ["$ref"],
          "properties": {
            "$ref": {
              "type": "string",
              "description": "Examples: '../common/common_types.json#/$defs/ComponentCommon', '#/$defs/CatalogComponentCommon', '../common/common_types.json#/$defs/Checkable'"
            }
          },
          "additionalProperties": false
        },
        {
          "type": "object",
          "description": "The primary component-specific inline schema. Should be the last entry in allOf. Must include type: 'object', description, properties, and required.",
          "required": ["type"],
          "properties": {
            "type": { "const": "object" },
            "description": {
              "type": "string",
              "description": "Component purpose shown in the system prompt. Put it here on the inline sub-schema, not at the top-level CatalogComponent."
            },
            "properties": {
              "type": "object",
              "additionalProperties": { "$ref": "#/$defs/PropertySchema" }
            },
            "required": { "$ref": "#/$defs/RequiredList" },
            "unevaluatedProperties": { "type": "boolean" }
          }
        }
      ]
    },

    "PropertySchema": {
      "type": "object",
      "description": "Schema for a single component property. Supports all standard JSON Schema keywords. The converter specifically recognises $ref values containing DynamicString, DynamicNumber, DynamicBoolean, and ChildList, mapping them to their respective anyOf schemas in the MCP tool.",
      "properties": {
        "$ref": {
          "type": "string",
          "description": "Reference to a shared type. Recognised patterns: '../common/common_types.json#/$defs/DynamicString', '#/$defs/DynamicNumber', './common_types.json#/$defs/ChildList'."
        },
        "type": {
          "enum": ["string", "number", "boolean", "array", "object"],
          "description": "JSON Schema type."
        },
        "const": {
          "description": "Constant value constraint. Used for the component type discriminator, e.g. { \"const\": \"Button\" }."
        },
        "description": {
          "type": "string",
          "description": "Property description shown in the system prompt."
        },
        "enum": {
          "type": "array",
          "items": { "type": "string" },
          "minItems": 1,
          "description": "Allowed string values. The gateway enforces this — the agent gets a tool error for unknown values."
        },
        "default": {
          "description": "Default value if the property is omitted."
        },
        "minimum": { "type": "number" },
        "maximum": { "type": "number" },
        "exclusiveMinimum": { "type": "number" },
        "exclusiveMaximum": { "type": "number" },
        "minLength": { "type": "integer", "minimum": 0 },
        "maxLength": { "type": "integer", "minimum": 0 },
        "pattern": { "type": "string" },
        "items": {
          "description": "Schema for each array element, or a tuple array where each entry validates the element at the same index.",
          "oneOf": [
            { "$ref": "#/$defs/PropertySchema" },
            { "type": "array", "items": { "$ref": "#/$defs/PropertySchema" } }
          ]
        },
        "additionalItems": { "$ref": "#/$defs/PropertySchema" },
        "minItems": { "type": "integer", "minimum": 0 },
        "maxItems": { "type": "integer", "minimum": 0 },
        "properties": {
          "type": "object",
          "additionalProperties": { "$ref": "#/$defs/PropertySchema" }
        },
        "required": { "$ref": "#/$defs/RequiredList" },
        "additionalProperties": {
          "oneOf": [
            { "type": "boolean" },
            { "$ref": "#/$defs/PropertySchema" }
          ]
        },
        "unevaluatedProperties": { "type": "boolean" },
        "oneOf": {
          "type": "array",
          "items": { "$ref": "#/$defs/PropertySchema" }
        },
        "anyOf": {
          "type": "array",
          "items": { "$ref": "#/$defs/PropertySchema" }
        },
        "allOf": {
          "type": "array",
          "items": { "$ref": "#/$defs/PropertySchema" }
        }
      }
    },

    "CatalogFunction": {
      "type": "object",
      "description": "A client-side function declaration. Each definition IS a JSON Schema for its own call object, enabling discriminated union validation via anyFunction.",
      "additionalProperties": false,
      "properties": {
        "type": {
          "const": "object",
          "description": "Always 'object' — each function definition is a JSON Schema for its call object."
        },
        "description": {
          "type": "string",
          "description": "What the function does. Shown in the system prompt."
        },
        "properties": {
          "type": "object",
          "description": "JSON Schema properties for the call object: call (const discriminator), args (named arguments), returnType (const return type).",
          "properties": {
            "call": {
              "type": "object",
              "properties": { "const": { "type": "string" } },
              "required": ["const"]
            },
            "args": {
              "type": "object",
              "additionalProperties": true
            },
            "returnType": {
              "type": "object",
              "properties": { "const": { "type": "string" } }
            }
          },
          "additionalProperties": false
        },
        "required": {
          "$ref": "#/$defs/RequiredList"
        },
        "unevaluatedProperties": {
          "type": "boolean"
        }
      }
    },

    "RequiredList": {
      "type": "array",
      "description": "Array of property names that must be present. Standard JSON Schema 'required' keyword.",
      "items": { "type": "string" },
      "uniqueItems": true
    }
  }
}
