{
  "openapi": "3.1.0",
  "info": {
    "title": "OwnerSpec read-only content API",
    "version": "1.0.0",
    "summary": "Machine-readable access to every OwnerSpec page. No authentication, no write operations.",
    "description": "OwnerSpec is a static reference site. Every HTML page has a Markdown twin at the same URL plus `index.md`, and the same Markdown is returned for an `Accept: text/markdown` request (RFC 9110 content negotiation). The site index is `/llms.txt`, the full text of every page is `/llms-full.txt`, and the canonical URL list is `/sitemap.xml`. All endpoints are GET, public and anonymous; see `/auth.md`. An MCP server (JSON-RPC 2.0 over POST) lives at `/mcp` and an A2A agent at `/a2a`; both are described below in outline and documented at `/mcp-server/`.",
    "contact": {
      "name": "OwnerSpec",
      "url": "https://ownerspec.com/contact/"
    },
    "license": {
      "name": "Quoting with attribution and a link is permitted",
      "url": "https://ownerspec.com/legal/terms/"
    }
  },
  "servers": [
    {
      "url": "https://ownerspec.com"
    }
  ],
  "security": [],
  "paths": {
    "/llms.txt": {
      "get": {
        "operationId": "getSiteIndex",
        "summary": "Site index for agents: every page with its one-line description, grouped by category and layer.",
        "responses": {
          "200": {
            "description": "Markdown-formatted plain text.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms-full.txt": {
      "get": {
        "operationId": "getFullText",
        "summary": "Full text of every page in one document.",
        "responses": {
          "200": {
            "description": "Markdown-formatted plain text.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "operationId": "getSitemap",
        "summary": "Canonical URL of every indexable page with its last-modified date (from front matter, not git).",
        "responses": {
          "200": {
            "description": "Sitemaps.org XML.",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/index.xml": {
      "get": {
        "operationId": "getFeed",
        "summary": "RSS feed of the newest pages.",
        "responses": {
          "200": {
            "description": "RSS 2.0.",
            "content": {
              "application/rss+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/{path}/": {
      "get": {
        "operationId": "getPage",
        "summary": "One page. Send `Accept: text/markdown` (without text/html) to receive the Markdown representation instead of HTML.",
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "Page path without leading or trailing slash, for example `water/guides/iron-in-well-water`. Article URLs are always `<category>/<layer>/<slug>` where layer is guides, calculators, parts or reviews.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Accept",
            "in": "header",
            "required": false,
            "description": "`text/markdown` returns Markdown; anything that accepts text/html returns HTML.",
            "schema": {
              "type": "string",
              "examples": [
                "text/markdown",
                "text/html"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The page. `Vary: Accept` is set; the Markdown variant carries `Link: <canonical>; rel=\"canonical\"`.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such page."
          }
        }
      }
    },
    "/{path}/index.md": {
      "get": {
        "operationId": "getPageMarkdown",
        "summary": "The Markdown twin of a page, addressable directly without content negotiation.",
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "Same path as the HTML page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown with a front-matter block naming the canonical URL, title and dates.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such page."
          }
        }
      }
    },
    "/index.json": {
      "get": {
        "operationId": "getSearchIndex",
        "summary": "Search index: one record per page with title, canonical URL, layer, topic, dates, keywords, the cited quick answer, FAQ, sources, product picks (Amazon links carry the site's Associates tag) and plain text.",
        "responses": {
          "200": {
            "description": "JSON object with `site`, `generated`, `count` and `pages[]`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "mcp",
        "summary": "MCP server, Streamable HTTP transport (stateless, no SSE). Methods: initialize, ping, tools/list, tools/call, resources/list, resources/read, prompts/list, prompts/get. Tools: search_pages, get_page, get_quick_answer, diagnose_water_problem, find_replacement_part, get_product_picks, convert_water_hardness, size_water_softener.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "A JSON-RPC 2.0 request or batch."
              },
              "example": {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "tools/call",
                "params": {
                  "name": "diagnose_water_problem",
                  "arguments": {
                    "symptom": "rotten egg smell"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Notification accepted, no body."
          },
          "405": {
            "description": "Any method other than POST."
          }
        }
      }
    },
    "/a2a": {
      "post": {
        "operationId": "a2a",
        "summary": "A2A agent endpoint. SendMessage (or message/send) with a text part returns a completed task whose artifact holds the matching pages, quick answers and URLs to cite. Card: /.well-known/agent-card.json.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "SendMessage",
                "params": {
                  "message": {
                    "messageId": "m1",
                    "role": "ROLE_USER",
                    "parts": [
                      {
                        "text": "orange stains from well water"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response with a Task.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/agent/auth": {
      "post": {
        "operationId": "agentAuth",
        "summary": "auth.md anonymous registration. Returns an anonymous grant with no credential; nothing is stored. Exists so the auth.md flow terminates in one honest step.",
        "responses": {
          "200": {
            "description": "Anonymous grant.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "A non-anonymous identity type was requested; only anonymous exists."
          }
        }
      }
    }
  }
}
