{
  "openapi": "3.1.1",
  "info": {
    "title": "Website Change Explainer API",
    "version": "1.0.0",
    "summary": "Compare two versions of a page and see which changes matter: prices, terms, dates, links.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://websitechangeexplainer.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Compare two versions of a page and see which changes matter: prices, terms, dates, links.",
        "description": "Give a \"before\" and an \"after\" version of a web page (fetch a URL now or paste saved HTML/text). The tool extracts readable blocks, title, meta description, headings, links, prices and dates, diffs them block by block and word by word, and ranks meaningful changes above noise like whitespace, tracking parameters, timestamps or copyright years.\n\nMethodology:\n- Each side is parsed (tolerant HTML tree builder; plain text is split into lines). Text is collected per block-level element (p, li, h1–h6, td, …); scripts, styles and navigation-less noise like <svg> are skipped.\n- Blocks are aligned with a longest-common-subsequence diff over whitespace-normalised text; adjacent removed/added blocks with ≥ 40 % word overlap (Dice) are paired as \"modified\" and diffed word by word.\n- A change is trivial when it disappears after masking whitespace/case, clock times, \"N minutes ago\", copyright years and long hex/random tokens; links differing only in tracking parameters (utm_*, gclid, fbclid, …) are trivial.\n- Meaningful categories and scores: price 100, legal/terms keyword 90, contact (email/phone) 75, link target 70, title 65, date 60, heading 55, other number 45, meta description 40, link removed/added 35/30, other text 20 (+ up to 15 for size). Changes are ranked by score, then document order.\n- Every change lists the rule ids and matched tokens that produced its category.\n\nRule-based comparison of static HTML. Pages that build content with JavaScript may show fewer blocks than a browser; always read the actual wording of legal changes.",
        "tags": [
          "internet"
        ],
        "x-mcp-tool": "website_change_explainer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebsiteChangeExplainerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Pricing page: price rise, new auto-renewal clause, tracking link",
                  "value": {
                    "beforeContent": "<html><head><title>Pricing – Acme Cloud</title><meta name=\"description\" content=\"Simple pricing for teams.\"></head><body>\n<h1>Pricing</h1><h2>Pro plan</h2><p>Pro costs $12 per month per user.</p><p>Cancel anytime from your dashboard.</p>\n<p>Questions? Email sales@acme.example or call +1 555 010 2000.</p>\n<a href=\"https://acme.example/terms?utm_source=footer\">Terms</a> <a href=\"https://acme.example/signup\">Start trial</a>\n<footer><p>© 2025 Acme Inc. Page generated at 10:15</p></footer></body></html>",
                    "afterContent": "<html><head><title>Pricing – Acme Cloud</title><meta name=\"description\" content=\"Simple pricing for teams.\"></head><body>\n<h1>Pricing</h1><h2>Pro plan</h2><p>Pro costs $15 per month per user.</p><p>Plans renew automatically each year; cancellation requires 30 days notice.</p>\n<p>Questions? Email sales@acme.example or call +1 555 010 2000.</p>\n<a href=\"https://acme.example/terms?utm_source=header\">Terms</a> <a href=\"https://acme.example/signup-2026\">Start trial</a>\n<h2>Enterprise</h2><p>Contact us for volume pricing.</p>\n<footer><p>© 2026 Acme Inc. Page generated at 09:02</p></footer></body></html>",
                    "includeTrivial": true,
                    "maxChanges": 50
                  }
                },
                "example2": {
                  "summary": "Plain-text policy: changed deadline",
                  "value": {
                    "beforeContent": "Returns policy\nYou can return items within 30 days of delivery.\nRefunds are issued within 14 days.\nLast reviewed: 2026-01-10",
                    "afterContent": "Returns policy\nYou can return items within 14 days of delivery.\nRefunds are issued within 14 days.\nLast reviewed: 2026-09-01",
                    "includeTrivial": false,
                    "maxChanges": 20
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebsiteChangeExplainerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "WebsiteChangeExplainerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "beforeUrl": {
            "title": "Before: URL",
            "description": "Fetched now. Usually you paste the old version below (e.g. from a saved copy or web archive).",
            "type": "string",
            "format": "uri"
          },
          "beforeContent": {
            "title": "Before: pasted HTML or text",
            "type": "string",
            "maxLength": 400000
          },
          "afterUrl": {
            "title": "After: URL",
            "description": "Fetched now (the live page).",
            "type": "string",
            "format": "uri"
          },
          "afterContent": {
            "title": "After: pasted HTML or text",
            "type": "string",
            "maxLength": 400000
          },
          "includeTrivial": {
            "title": "List trivial changes too",
            "type": "boolean",
            "default": false
          },
          "maxChanges": {
            "title": "Max changes listed",
            "type": "integer",
            "minimum": 5,
            "maximum": 500,
            "default": 50
          }
        },
        "required": []
      },
      "WebsiteChangeExplainerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "before",
              "after",
              "counts",
              "changes",
              "truncated",
              "unifiedDiff"
            ],
            "properties": {
              "before": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "origin",
                  "title",
                  "metaDescription",
                  "blockCount",
                  "headings",
                  "linkCount",
                  "prices",
                  "dates"
                ],
                "properties": {
                  "origin": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "kind",
                      "truncated"
                    ],
                    "properties": {
                      "kind": {
                        "type": "string",
                        "enum": [
                          "url",
                          "pasted"
                        ]
                      },
                      "requestedUrl": {
                        "type": "string"
                      },
                      "finalUrl": {
                        "type": "string"
                      },
                      "status": {
                        "type": "integer"
                      },
                      "contentType": {
                        "type": "string"
                      },
                      "fetchedAt": {
                        "type": "string"
                      },
                      "truncated": {
                        "type": "boolean"
                      }
                    }
                  },
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "metaDescription": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "blockCount": {
                    "type": "integer"
                  },
                  "headings": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 50
                  },
                  "linkCount": {
                    "type": "integer"
                  },
                  "prices": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 50
                  },
                  "dates": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 50
                  }
                }
              },
              "after": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "origin",
                  "title",
                  "metaDescription",
                  "blockCount",
                  "headings",
                  "linkCount",
                  "prices",
                  "dates"
                ],
                "properties": {
                  "origin": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "kind",
                      "truncated"
                    ],
                    "properties": {
                      "kind": {
                        "type": "string",
                        "enum": [
                          "url",
                          "pasted"
                        ]
                      },
                      "requestedUrl": {
                        "type": "string"
                      },
                      "finalUrl": {
                        "type": "string"
                      },
                      "status": {
                        "type": "integer"
                      },
                      "contentType": {
                        "type": "string"
                      },
                      "fetchedAt": {
                        "type": "string"
                      },
                      "truncated": {
                        "type": "boolean"
                      }
                    }
                  },
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "metaDescription": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "blockCount": {
                    "type": "integer"
                  },
                  "headings": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 50
                  },
                  "linkCount": {
                    "type": "integer"
                  },
                  "prices": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 50
                  },
                  "dates": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 50
                  }
                }
              },
              "counts": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "meaningful",
                  "trivial",
                  "high",
                  "medium",
                  "low",
                  "blocksBefore",
                  "blocksAfter",
                  "unchangedBlocks"
                ],
                "properties": {
                  "meaningful": {
                    "type": "integer"
                  },
                  "trivial": {
                    "type": "integer"
                  },
                  "high": {
                    "type": "integer"
                  },
                  "medium": {
                    "type": "integer"
                  },
                  "low": {
                    "type": "integer"
                  },
                  "blocksBefore": {
                    "type": "integer"
                  },
                  "blocksAfter": {
                    "type": "integer"
                  },
                  "unchangedBlocks": {
                    "type": "integer"
                  }
                }
              },
              "changes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "rank",
                    "kind",
                    "category",
                    "categories",
                    "significance",
                    "level",
                    "element",
                    "before",
                    "after",
                    "wordDiff",
                    "evidence"
                  ],
                  "properties": {
                    "rank": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "modified",
                        "added",
                        "removed",
                        "title",
                        "meta-description",
                        "link"
                      ]
                    },
                    "category": {
                      "type": "string",
                      "enum": [
                        "price",
                        "legal-terms",
                        "contact",
                        "link-target",
                        "title",
                        "date",
                        "heading",
                        "number",
                        "meta-description",
                        "link-removed",
                        "link-added",
                        "text",
                        "trivial"
                      ]
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "price",
                          "legal-terms",
                          "contact",
                          "link-target",
                          "title",
                          "date",
                          "heading",
                          "number",
                          "meta-description",
                          "link-removed",
                          "link-added",
                          "text",
                          "trivial"
                        ]
                      }
                    },
                    "significance": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100
                    },
                    "level": {
                      "type": "string",
                      "enum": [
                        "high",
                        "medium",
                        "low",
                        "trivial"
                      ]
                    },
                    "element": {
                      "type": "string"
                    },
                    "before": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "after": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "wordDiff": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Inline word diff: [-removed-] {+added+}."
                    },
                    "evidence": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "truncated": {
                "type": "boolean"
              },
              "unifiedDiff": {
                "type": "string"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}