{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Jacob White Films Developer API",
    "version": "1.0.0",
    "description": "Public discovery resources and the read-only Jacob White Films MCP endpoint. The current public API contract is v1: /api/v1/mcp is the versioned path and /api/mcp remains a compatibility alias. Deprecation changes will be announced in this document and through Sunset headers before a version is retired. Contact and Studio operations remain separately controlled.",
    "contact": {
      "name": "Jacob White Films",
      "email": "hello@jacobwhitefilms.co.uk",
      "url": "https://www.jacobwhitefilms.co.uk/contact"
    },
    "license": {
      "name": "All rights reserved"
    }
  },
  "servers": [
    {
      "url": "https://www.jacobwhitefilms.co.uk",
      "description": "Canonical production website"
    },
    {
      "url": "https://www.jacobwhitefilms.co.uk/api/v1",
      "description": "Versioned v1 API base path"
    }
  ],
  "x-api-versioning": {
    "strategy": "URL path",
    "currentVersion": "v1",
    "versionedBasePath": "/api/v1",
    "compatibilityAlias": "/api/mcp",
    "deprecationPolicy": "Announce planned retirement in this specification and with a Sunset response header before removal."
  },
  "tags": [
    {
      "name": "MCP",
      "description": "Read-only business-information tools for agents"
    },
    {
      "name": "Discovery",
      "description": "Machine-readable site and MCP discovery documents"
    },
    {
      "name": "Contact",
      "description": "Human enquiry handoff"
    }
  ],
  "paths": {
    "/api/mcp": {
      "get": {
        "operationId": "mcpStream",
        "tags": ["MCP"],
        "summary": "Open the Streamable HTTP MCP transport",
        "description": "Use the public MCP endpoint with a compatible MCP client. The server is stateless and exposes public, read-only business-information tools.",
        "responses": {
          "200": {
            "description": "An MCP event stream when supported by the client.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/McpMethodNotAllowed"
          }
        }
      },
      "post": {
        "operationId": "mcpJsonRpc",
        "tags": ["MCP"],
        "summary": "Send an MCP JSON-RPC request",
        "description": "Send a Streamable HTTP MCP request. Clients may use the 2025-era initialize exchange or the current stateless MCP request format supported by the server.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "MCP JSON-RPC response.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/McpBadRequest"
          },
          "429": {
            "$ref": "#/components/responses/McpRateLimited"
          }
        }
      }
    },
    "/api/v1/mcp": {
      "get": {
        "operationId": "mcpV1Stream",
        "tags": ["MCP"],
        "summary": "Open the versioned v1 Streamable HTTP MCP transport",
        "description": "Versioned alias for the public, stateless, read-only Jacob White Films MCP transport.",
        "responses": {
          "200": {
            "description": "An MCP event stream when supported by the client.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/McpMethodNotAllowed"
          }
        }
      },
      "post": {
        "operationId": "mcpV1JsonRpc",
        "tags": ["MCP"],
        "summary": "Send a versioned v1 MCP JSON-RPC request",
        "description": "Versioned alias for the public Streamable HTTP MCP request surface. It exposes read-only business-information tools and no client records, bookings, invoices, tasks or email operations.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "MCP JSON-RPC response.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/McpBadRequest"
          },
          "429": {
            "$ref": "#/components/responses/McpRateLimited"
          }
        }
      }
    },
    "/api/studio/mcp": {
      "post": {
        "operationId": "studioMcpJsonRpc",
        "tags": ["MCP"],
        "summary": "Authenticated Studio MCP transport",
        "description": "Private operator integration for Jacob White Films Studio. It is not a public recommendation API and requires a separately issued bearer service key.",
        "security": [
          {
            "StudioBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authenticated Studio MCP response."
          },
          "401": {
            "description": "A valid Studio bearer key is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/mcp": {
      "get": {
        "operationId": "getMcpManifest",
        "tags": ["Discovery"],
        "summary": "Get the Jacob White Films MCP manifest",
        "responses": {
          "200": {
            "description": "MCP server card and connection metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpServerCard"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/mcp.json": {
      "get": {
        "operationId": "getLegacyMcpManifest",
        "tags": ["Discovery"],
        "summary": "Get the compatible MCP JSON manifest alias",
        "responses": {
          "200": {
            "$ref": "#/components/responses/McpManifest"
          }
        }
      }
    },
    "/.well-known/mcp/server-card.json": {
      "get": {
        "operationId": "getMcpServerCard",
        "tags": ["Discovery"],
        "summary": "Get the MCP server card",
        "responses": {
          "200": {
            "$ref": "#/components/responses/McpManifest"
          }
        }
      }
    },
    "/.well-known/ai-catalog.json": {
      "get": {
        "operationId": "getAiCatalog",
        "tags": ["Discovery"],
        "summary": "Get the AI catalog entry for Jacob White Films MCP",
        "responses": {
          "200": {
            "description": "AI catalog with the public MCP server-card URL.",
            "content": {
              "application/ai-catalog+json": {
                "schema": {
                  "$ref": "#/components/schemas/AiCatalog"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiCatalog"
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "operationId": "getLlmsIndex",
        "tags": ["Discovery"],
        "summary": "Get the Markdown site index and agent guidance",
        "responses": {
          "200": {
            "description": "Markdown site index.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/agent-instructions.md": {
      "get": {
        "operationId": "getAgentInstructions",
        "tags": ["Discovery"],
        "summary": "Get when-to-use instructions for agents",
        "responses": {
          "200": {
            "description": "Markdown agent instructions.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/contact": {
      "get": {
        "operationId": "contactMethodNotAllowed",
        "tags": ["Contact"],
        "summary": "Describe the contact method boundary",
        "description": "The public contact endpoint accepts deliberate human enquiries with POST; GET is a structured method error.",
        "responses": {
          "405": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      },
      "post": {
        "operationId": "submitContactEnquiry",
        "tags": ["Contact"],
        "summary": "Submit a human wedding-film enquiry",
        "description": "Use this only when the person has chosen to contact Jacob White Films. It does not check availability or generate a final quote.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactEnquiry"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The enquiry was accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnquiryAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiRateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "StudioBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "jwf_mcp service key",
        "description": "A service key issued through the private Studio settings. Never publish a key in prompts, source code, or public documentation."
      }
    },
    "headers": {
      "RateLimit": {
        "description": "The public MCP request budget and window, expressed using the RFC RateLimit field format.",
        "schema": {
          "type": "string",
          "example": "60;w=60"
        }
      },
      "ContactRateLimit": {
        "description": "The contact enquiry budget and one-hour window.",
        "schema": {
          "type": "string",
          "example": "5;w=3600"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying after a 429 response.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "responses": {
      "McpManifest": {
        "description": "MCP server card and connection metadata.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/McpServerCard"
            }
          }
        }
      },
      "McpMethodNotAllowed": {
        "description": "The client must use POST for JSON-RPC requests.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/McpErrorResponse"
            }
          }
        }
      },
      "McpBadRequest": {
        "description": "Malformed or unsupported JSON-RPC request.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/McpErrorResponse"
            }
          }
        }
      },
      "McpRateLimited": {
        "description": "The public MCP request budget was exceeded.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/McpErrorResponse"
            }
          }
        }
      },
      "ApiError": {
        "description": "A structured error with a stable code, human-readable message and recovery guidance.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/ContactRateLimit"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      },
      "ApiRateLimited": {
        "description": "The contact enquiry rate limit was reached.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/ContactRateLimit"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      }
    },
    "schemas": {
      "ApiError": {
        "type": "object",
        "required": ["error", "code", "message", "resolution"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Legacy-compatible error summary."
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code."
          },
          "message": {
            "type": "string"
          },
          "resolution": {
            "type": "string",
            "description": "A safe next step for the caller."
          }
        },
        "additionalProperties": false
      },
      "McpErrorResponse": {
        "type": "object",
        "required": ["jsonrpc", "error", "id"],
        "properties": {
          "jsonrpc": {
            "const": "2.0"
          },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              },
              "data": {
                "type": "object",
                "additionalProperties": true
              }
            }
          },
          "id": {
            "type": ["string", "integer", "null"]
          }
        },
        "additionalProperties": false
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "method"],
        "properties": {
          "jsonrpc": {
            "const": "2.0"
          },
          "id": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              }
            ]
          },
          "method": {
            "type": "string"
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "JsonRpcResponse": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "const": "2.0"
          },
          "id": {},
          "result": {},
          "error": {}
        },
        "additionalProperties": true
      },
      "McpServerCard": {
        "type": "object",
        "required": [
          "$schema",
          "version",
          "protocolVersion",
          "serverInfo",
          "transport",
          "capabilities"
        ],
        "properties": {
          "$schema": {
            "type": "string",
            "format": "uri"
          },
          "version": {
            "type": "string"
          },
          "protocolVersion": {
            "type": "string"
          },
          "serverInfo": {
            "type": "object",
            "required": ["name", "version"],
            "properties": {
              "name": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "transport": {
            "type": "object",
            "required": ["type", "endpoint"],
            "properties": {
              "type": {
                "const": "streamable-http"
              },
              "endpoint": {
                "type": "string",
                "format": "uri-reference"
              }
            }
          },
          "capabilities": {
            "type": "object"
          }
        }
      },
      "AiCatalog": {
        "type": "object",
        "required": ["specVersion", "entries"],
        "properties": {
          "specVersion": {
            "type": "string"
          },
          "entries": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["identifier", "type", "url"],
              "properties": {
                "identifier": {
                  "type": "string"
                },
                "type": {
                  "const": "application/mcp-server-card+json"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          }
        }
      },
      "EnquiryAccepted": {
        "type": "object",
        "required": ["ok"],
        "properties": {
          "ok": {
            "const": true
          }
        },
        "additionalProperties": false
      },
      "ContactEnquiry": {
        "type": "object",
        "required": ["name", "email", "date", "venue", "message"],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "venue": {
            "type": "string",
            "maxLength": 200
          },
          "message": {
            "type": "string",
            "maxLength": 4000
          },
          "phone": {
            "type": "string",
            "maxLength": 50
          },
          "budget": {
            "type": "string",
            "maxLength": 100
          }
        },
        "additionalProperties": false
      }
    }
  },
  "externalDocs": {
    "description": "Model Context Protocol documentation",
    "url": "https://modelcontextprotocol.io/specification/2025-06-18/basic/transports"
  }
}
