{
  "openapi": "3.1.0",
  "info": {
    "title": "Heart of the Village Inn — MCP API",
    "description": "Structured data query API for Heart of the Village Inn, Shelburne, Vermont. Provides 7 tools for AI agents to query room data, property policies, FAQ, booking guidance, canonical facts, and experience routing. Supports single-tool GET requests and multi-tool POST chains with parallel execution.\n\nAll data is sourced from static JSON datasets at heartofthevillage.com. No authentication required. All queries return JSON.\n\nProperty: Adults-only (21+) bed and breakfast. 9 rooms / 11 booking configurations. Free parking. Custom-made breakfast included.",
    "version": "1.1.0",
    "contact": {
      "name": "Heart of the Village Inn",
      "url": "https://www.heartofthevillage.com",
      "email": "innkeeper@heartofthevillage.com"
    },
    "license": {
      "name": "Public data — no license required for read access",
      "url": "https://www.heartofthevillage.com"
    }
  },
  "servers": [
    {
      "url": "https://www.heartofthevillage.com",
      "description": "Production — Netlify Edge"
    }
  ],
  "paths": {
    "/mcp": {
      "get": {
        "operationId": "mcpSingleTool",
        "summary": "Execute a single MCP tool query",
        "description": "Dispatches one tool and returns its result. Omit the `tool` parameter to receive a service discovery response listing all available tools.\n\n**Available tools:**\n- `get_room_by_name` — full data for a specific room by name\n- `filter_rooms` — list rooms matching bed type, occupancy, building, feature, or persona\n- `get_property_policy` — policy data for a specific topic (check-in, cancellation, pets, etc.)\n- `search_faq` — search FAQ entries by keyword or question\n- `get_booking_guidance` — booking URL, direct booking advantages, booking context\n- `get_canonical_fact` — single authoritative fact by dot-notation key (distances, times, counts, ratings)\n- `route_experience` — route a traveler intent to matching rooms, pages, and answer snippet\n\nFor multi-tool queries in a single request, use POST.",
        "parameters": [
          {
            "name": "tool",
            "in": "query",
            "required": false,
            "description": "Tool to execute. Omit to receive service discovery.",
            "schema": {
              "type": "string",
              "enum": [
                "get_room_by_name",
                "filter_rooms",
                "get_property_policy",
                "search_faq",
                "get_booking_guidance",
                "get_canonical_fact",
                "route_experience"
              ]
            }
          },
          {
            "name": "room_name",
            "in": "query",
            "required": false,
            "description": "**get_room_by_name** — exact room or suite name.",
            "schema": {
              "type": "string",
              "example": "Webb Suite",
              "enum": [
                "Webb Suite",
                "Barstow Suite",
                "Barstow Room",
                "Bostwick Room",
                "Fletcher Room",
                "Harrington Suite",
                "Harrington Room",
                "Shelburne Room",
                "Tracy Room",
                "Van Vliet Room",
                "Deyett Room"
              ]
            }
          },
          {
            "name": "bed_type",
            "in": "query",
            "required": false,
            "description": "**filter_rooms** — filter by bed configuration.",
            "schema": {
              "type": "string",
              "example": "King",
              "enum": ["King", "Queen", "King + Queen", "King + King"]
            }
          },
          {
            "name": "max_occupancy",
            "in": "query",
            "required": false,
            "description": "**filter_rooms** — minimum guest capacity required (integer 1–4).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 4,
              "example": 2
            }
          },
          {
            "name": "building",
            "in": "query",
            "required": false,
            "description": "**filter_rooms** — filter by building.",
            "schema": {
              "type": "string",
              "enum": ["Main Building", "Carriage House"]
            }
          },
          {
            "name": "feature",
            "in": "query",
            "required": false,
            "description": "**filter_rooms** — required room feature.",
            "schema": {
              "type": "string",
              "example": "jetted tub",
              "examples": {
                "jetted_tub": { "value": "jetted tub" },
                "accessibility": { "value": "accessibility" },
                "grab_bars": { "value": "grab bars" },
                "ground_floor": { "value": "ground floor" },
                "skylight": { "value": "skylight" },
                "bathtub": { "value": "bathtub" },
                "walk_in_shower": { "value": "walk-in shower" }
              }
            }
          },
          {
            "name": "best_for",
            "in": "query",
            "required": false,
            "description": "**filter_rooms** — guest persona or occasion.",
            "schema": {
              "type": "string",
              "example": "romantic couples",
              "examples": {
                "romantic": { "value": "romantic couples" },
                "anniversary": { "value": "anniversary" },
                "honeymoon": { "value": "honeymoon" },
                "accessibility": { "value": "accessibility needs" },
                "two_couples": { "value": "two couples" },
                "quiet": { "value": "quiet stay" }
              }
            }
          },
          {
            "name": "policy_topic",
            "in": "query",
            "required": false,
            "description": "**get_property_policy** — policy category to retrieve. Omit for all policies.",
            "schema": {
              "type": "string",
              "example": "check_in",
              "enum": [
                "check_in",
                "check_out",
                "cancellation",
                "adults_only",
                "pets",
                "minimum_stay",
                "breakfast",
                "parking",
                "luggage_storage"
              ]
            }
          },
          {
            "name": "query",
            "in": "query",
            "required": false,
            "description": "**search_faq** — keyword or phrase to search FAQ entries. Omit for FAQ index.",
            "schema": {
              "type": "string",
              "example": "check-in time"
            }
          },
          {
            "name": "context",
            "in": "query",
            "required": false,
            "description": "**get_booking_guidance** — booking context or question.",
            "schema": {
              "type": "string",
              "example": "book direct benefits",
              "enum": [
                "how to book",
                "book direct benefits",
                "check availability",
                "cancellation before booking",
                "group booking"
              ]
            }
          },
          {
            "name": "fact_key",
            "in": "query",
            "required": false,
            "description": "**get_canonical_fact** — dot-notation path to a canonical fact. Omit for available sections index.",
            "schema": {
              "type": "string",
              "example": "distances.shelburne_farms_miles",
              "examples": {
                "shelburne_farms_dist": { "value": "distances.shelburne_farms_miles" },
                "burlington_dist": { "value": "distances.burlington_downtown_miles" },
                "museum_walkable": { "value": "distances.shelburne_museum_walkable" },
                "rating": { "value": "ratings.rating_value" },
                "review_count": { "value": "ratings.review_count" },
                "check_in": { "value": "policies.check_in" },
                "minimum_age": { "value": "policies.minimum_age" },
                "parking": { "value": "amenities.parking" },
                "room_count": { "value": "rooms.physical_count" },
                "differentiators": { "value": "differentiators" }
              }
            }
          },
          {
            "name": "query_intent",
            "in": "query",
            "required": false,
            "description": "**route_experience** — canonical traveler intent key.",
            "schema": {
              "type": "string",
              "example": "romantic_getaway",
              "enum": [
                "romantic_getaway",
                "adults_only_inn_near_burlington",
                "quiet_stay_near_burlington",
                "stay_near_shelburne_farms",
                "bed_and_breakfast_near_burlington",
                "anniversary_or_honeymoon_vermont",
                "accessible_lodging_near_burlington",
                "group_or_multi_guest_lodging",
                "lake_champlain_area_lodging"
              ]
            }
          },
          {
            "name": "raw_query",
            "in": "query",
            "required": false,
            "description": "**route_experience** — free-text traveler query for intent matching when exact intent key is unknown.",
            "schema": {
              "type": "string",
              "example": "romantic B&B near Burlington Vermont"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tool result or service discovery response",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/SingleToolResponse" },
                    { "$ref": "#/components/schemas/DiscoveryResponse" }
                  ]
                },
                "examples": {
                  "room_by_name": {
                    "summary": "get_room_by_name — Webb Suite",
                    "value": {
                      "tool": "get_room_by_name",
                      "result": {
                        "room_name": "Webb Suite",
                        "building": "Carriage House",
                        "bed_type": "King",
                        "max_occupancy": 2,
                        "unique_features": ["jetted tub", "skylight", "private entrance"]
                      },
                      "source": "https://www.heartofthevillage.com/rooms-detailed.json",
                      "timestamp": "2026-04-27T12:00:00.000Z"
                    }
                  },
                  "canonical_fact": {
                    "summary": "get_canonical_fact — shelburne_farms_miles",
                    "value": {
                      "tool": "get_canonical_fact",
                      "result": {
                        "fact_key": "distances.shelburne_farms_miles",
                        "value": 1.7
                      },
                      "source": "https://www.heartofthevillage.com/property-facts.json",
                      "timestamp": "2026-04-27T12:00:00.000Z"
                    }
                  },
                  "discovery": {
                    "summary": "Service discovery (no tool param)",
                    "value": {
                      "service": "Heart of the Village Inn — MCP Query Endpoint",
                      "version": "1.1",
                      "manifest": "https://www.heartofthevillage.com/mcp-manifest.json",
                      "openapi": "https://www.heartofthevillage.com/openapi.json",
                      "available_tools": [
                        "get_room_by_name",
                        "filter_rooms",
                        "get_property_policy",
                        "search_faq",
                        "get_booking_guidance",
                        "get_canonical_fact",
                        "route_experience"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "502": { "$ref": "#/components/responses/BadGateway" }
        }
      },
      "post": {
        "operationId": "mcpToolChain",
        "summary": "Execute a chain of MCP tools in a single request",
        "description": "Runs multiple tools in one request with parallel execution and deduplicated data source fetches. Tools sharing a data source (e.g. `filter_rooms` + `get_room_by_name`) make only one fetch call.\n\nErrors in individual tool calls are returned per-call — they do not fail the entire chain. Maximum 7 calls per request.\n\n**Typical chain patterns:**\n- `route_experience` + `get_canonical_fact` — intent routing plus authoritative distance/fact\n- `filter_rooms` + `get_canonical_fact` — matching rooms plus policy facts\n- `route_experience` + `get_property_policy` — routing plus policy details for a page\n- `get_room_by_name` + `get_canonical_fact` — room details plus a key proximity fact",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ChainRequest" },
              "examples": {
                "romantic_getaway_with_fact": {
                  "summary": "Route romantic intent + get Shelburne Farms distance",
                  "value": {
                    "calls": [
                      {
                        "tool": "route_experience",
                        "params": { "query_intent": "romantic_getaway" }
                      },
                      {
                        "tool": "get_canonical_fact",
                        "params": { "fact_key": "distances.shelburne_farms_miles" }
                      }
                    ]
                  }
                },
                "rooms_and_policy": {
                  "summary": "Filter rooms for 2 guests + get check-in policy",
                  "value": {
                    "calls": [
                      {
                        "tool": "filter_rooms",
                        "params": { "max_occupancy": "2", "best_for": "anniversary" }
                      },
                      {
                        "tool": "get_property_policy",
                        "params": { "policy_topic": "check_in" }
                      }
                    ]
                  }
                },
                "room_detail_plus_distance": {
                  "summary": "Webb Suite details + Burlington drive time",
                  "value": {
                    "calls": [
                      {
                        "tool": "get_room_by_name",
                        "params": { "room_name": "Webb Suite" }
                      },
                      {
                        "tool": "get_canonical_fact",
                        "params": { "fact_key": "distances.burlington_drive_time" }
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chain results — one entry per call",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ChainResponse" },
                "examples": {
                  "chain_result": {
                    "summary": "Two-tool chain result",
                    "value": {
                      "chain": true,
                      "call_count": 2,
                      "results": [
                        {
                          "tool": "route_experience",
                          "result": {
                            "query_intent": "romantic_getaway",
                            "property_match": true,
                            "top_room": "Webb Suite",
                            "booking_url": "https://www.heartofthevillage.com/book"
                          },
                          "source": "https://www.heartofthevillage.com/experience-routing.json",
                          "error": null
                        },
                        {
                          "tool": "get_canonical_fact",
                          "result": {
                            "fact_key": "distances.shelburne_farms_miles",
                            "value": 1.7
                          },
                          "source": "https://www.heartofthevillage.com/property-facts.json",
                          "error": null
                        }
                      ],
                      "timestamp": "2026-04-27T12:00:00.000Z"
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" }
        }
      },
      "options": {
        "operationId": "mcpCors",
        "summary": "CORS preflight",
        "description": "Returns CORS headers. Handled automatically by the edge function.",
        "responses": {
          "204": {
            "description": "No content — CORS headers returned",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": { "type": "string", "example": "*" }
              },
              "Access-Control-Allow-Methods": {
                "schema": { "type": "string", "example": "GET, POST, OPTIONS" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ToolName": {
        "type": "string",
        "description": "One of the 7 available MCP tools",
        "enum": [
          "get_room_by_name",
          "filter_rooms",
          "get_property_policy",
          "search_faq",
          "get_booking_guidance",
          "get_canonical_fact",
          "route_experience"
        ]
      },
      "ChainCall": {
        "type": "object",
        "required": ["tool"],
        "description": "A single tool call within a chain request",
        "properties": {
          "tool": { "$ref": "#/components/schemas/ToolName" },
          "params": {
            "type": "object",
            "description": "Tool parameters as key-value pairs. Values are coerced to strings internally.",
            "additionalProperties": {
              "oneOf": [
                { "type": "string" },
                { "type": "number" },
                { "type": "boolean" }
              ]
            },
            "examples": [
              { "room_name": "Webb Suite" },
              { "query_intent": "romantic_getaway" },
              { "fact_key": "distances.shelburne_farms_miles" },
              { "max_occupancy": 2, "best_for": "anniversary" },
              { "policy_topic": "check_in" },
              { "query": "check-in time" }
            ]
          }
        }
      },
      "ChainRequest": {
        "type": "object",
        "required": ["calls"],
        "description": "POST body for a multi-tool chain request",
        "properties": {
          "calls": {
            "type": "array",
            "description": "Ordered list of tool calls to execute. Maximum 7.",
            "minItems": 1,
            "maxItems": 7,
            "items": { "$ref": "#/components/schemas/ChainCall" }
          }
        }
      },
      "ChainCallResult": {
        "type": "object",
        "description": "Result for a single tool call within a chain response",
        "properties": {
          "tool": { "$ref": "#/components/schemas/ToolName" },
          "result": {
            "description": "Tool output. Null if the call errored.",
            "oneOf": [
              { "type": "object" },
              { "type": "array" },
              { "type": "null" }
            ]
          },
          "source": {
            "type": "string",
            "format": "uri",
            "description": "Data source URL used for this tool call"
          },
          "error": {
            "type": ["string", "null"],
            "description": "Error message if this call failed, null otherwise"
          }
        }
      },
      "ChainResponse": {
        "type": "object",
        "description": "Response from a POST tool chain request",
        "properties": {
          "chain": {
            "type": "boolean",
            "const": true,
            "description": "Always true — identifies this as a chain response"
          },
          "call_count": {
            "type": "integer",
            "description": "Number of tool calls executed"
          },
          "results": {
            "type": "array",
            "description": "Ordered results corresponding to each call in the request",
            "items": { "$ref": "#/components/schemas/ChainCallResult" }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp of the response"
          }
        }
      },
      "SingleToolResponse": {
        "type": "object",
        "description": "Response from a GET single-tool request",
        "properties": {
          "tool": { "$ref": "#/components/schemas/ToolName" },
          "result": {
            "description": "Tool-specific result object",
            "oneOf": [
              { "type": "object" },
              { "type": "array" }
            ]
          },
          "source": {
            "type": "string",
            "format": "uri",
            "description": "Data source URL used"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DiscoveryResponse": {
        "type": "object",
        "description": "Service discovery response — returned when no tool parameter is provided",
        "properties": {
          "service": { "type": "string" },
          "version": { "type": "string" },
          "manifest": { "type": "string", "format": "uri" },
          "openapi": { "type": "string", "format": "uri" },
          "available_tools": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ToolName" }
          },
          "usage": { "type": "object" },
          "examples": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "description": "Human-readable error message" },
          "status": { "type": "integer" },
          "tool": { "type": ["string", "null"] }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad request — missing or invalid parameter",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "MethodNotAllowed": {
        "description": "Method not allowed",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "ServerError": {
        "description": "Internal server error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "BadGateway": {
        "description": "Data source fetch failed",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "MCP manifest — tool parameter reference and data source index",
    "url": "https://www.heartofthevillage.com/mcp-manifest.json"
  }
}
