{
  "openapi": "3.1.0",
  "info": {
    "title": "Rootstrap public API",
    "version": "1.0.0",
    "summary": "The public HTTP surface of rootstrap.com: a Model Context Protocol server and a health endpoint.",
    "description": "Rootstrap exposes one programmatic interface: a public, unauthenticated MCP server (Streamable HTTP, stateless) at /api/mcp. It answers JSON-RPC 2.0 messages with JSON. Tools: get_company_profile, get_page_markdown, list_case_studies, list_insights, search_site, start_conversation. Human documentation: https://www.rootstrap.com/developers and https://www.rootstrap.com/mcp.md. Any HTML page of the site can also be fetched as Markdown with `Accept: text/markdown`. Rate limit: 120 requests per 60 seconds per client IP (best effort per serving instance), advertised in RateLimit-* headers; a 429 carries Retry-After. The edge firewall also enforces the same 120/60 s per IP globally and answers 403 text/plain with the header x-vercel-mitigated: deny; treat it as throttling and retry after the window.",
    "contact": {
      "name": "Rootstrap",
      "url": "https://www.rootstrap.com/contact",
      "email": "info@rootstrap.com"
    },
    "termsOfService": "https://www.rootstrap.com/privacy",
    "license": {
      "name": "Public content; see privacy policy",
      "url": "https://www.rootstrap.com/privacy"
    }
  },
  "externalDocs": {
    "description": "Developer page",
    "url": "https://www.rootstrap.com/developers"
  },
  "servers": [
    {
      "url": "https://www.rootstrap.com",
      "description": "Production"
    },
    {
      "url": "https://mcp.rootstrap.com",
      "description": "Production (agent host, same deployment)"
    }
  ],
  "tags": [
    {
      "name": "mcp",
      "description": "Model Context Protocol server",
      "externalDocs": {
        "url": "https://www.rootstrap.com/.well-known/mcp/server-card.json",
        "description": "MCP Server Card"
      }
    },
    {
      "name": "meta",
      "description": "Health and discovery"
    }
  ],
  "paths": {
    "/api/mcp": {
      "post": {
        "tags": [
          "mcp"
        ],
        "operationId": "mcpMessage",
        "summary": "Send a JSON-RPC 2.0 message to the MCP server",
        "description": "Stateless MCP Streamable HTTP endpoint. Supported methods: initialize, ping, tools/list, tools/call, resources/list, prompts/list. Notifications (messages without an id) are accepted with 202 and no body. A JSON array is treated as a batch.",
        "parameters": [
          {
            "name": "MCP-Protocol-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "2025-11-25",
                "2025-06-18",
                "2025-03-26"
              ]
            },
            "description": "Protocol version negotiated in initialize."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/JsonRpcRequest"
                  },
                  {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/JsonRpcRequest"
                    }
                  }
                ]
              },
              "examples": {
                "initialize": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "initialize",
                    "params": {
                      "protocolVersion": "2025-06-18",
                      "capabilities": {},
                      "clientInfo": {
                        "name": "my-agent",
                        "version": "1.0"
                      }
                    }
                  }
                },
                "toolsList": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/list"
                  }
                },
                "searchSite": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 3,
                    "method": "tools/call",
                    "params": {
                      "name": "search_site",
                      "arguments": {
                        "query": "ground truth agents",
                        "limit": 5
                      }
                    }
                  }
                },
                "startConversation": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 4,
                    "method": "tools/call",
                    "params": {
                      "name": "start_conversation",
                      "arguments": {
                        "name": "Ada Lovelace",
                        "email": "ada@example.com",
                        "company": "Analytical Engines",
                        "message": "We want to take an AI prototype to production."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response (or an array for a batch). Tool errors are reported inside `result.isError`, protocol errors in `error`.",
            "headers": {
              "MCP-Protocol-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/JsonRpcResponse"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JsonRpcResponse"
                      }
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "Notification accepted; no body."
          },
          "400": {
            "description": "Unparseable body or invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "403": {
            "$ref": "#/components/responses/RateLimitedEdge"
          }
        }
      },
      "get": {
        "tags": [
          "mcp"
        ],
        "operationId": "mcpGet",
        "summary": "Not supported (stateless server, no server-initiated stream)",
        "responses": {
          "405": {
            "description": "Use POST.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string",
                  "example": "POST, OPTIONS"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "403": {
            "$ref": "#/components/responses/RateLimitedEdge"
          }
        }
      },
      "options": {
        "tags": [
          "mcp"
        ],
        "operationId": "mcpPreflight",
        "summary": "CORS preflight",
        "responses": {
          "204": {
            "description": "Allowed for any origin."
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "tags": [
          "meta"
        ],
        "operationId": "health",
        "summary": "Liveness",
        "responses": {
          "200": {
            "description": "The API is up.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "ok"
                    },
                    "service": {
                      "type": "string"
                    },
                    "time": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "403": {
            "$ref": "#/components/responses/RateLimitedEdge"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string"
              }
            ],
            "description": "Omit for a notification."
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "ping",
              "tools/list",
              "tools/call",
              "resources/list",
              "prompts/list",
              "notifications/initialized",
              "notifications/cancelled"
            ]
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc",
          "id"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "type": "object",
            "additionalProperties": true
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "integer",
                "description": "-32700 parse error, -32600 invalid request, -32601 method not found, -32602 invalid params"
              },
              "message": {
                "type": "string"
              },
              "data": {}
            }
          }
        }
      },
      "ApiError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "examples": [
                  "not_found",
                  "method_not_allowed"
                ]
              },
              "message": {
                "type": "string"
              },
              "hint": {
                "type": "string",
                "description": "What to do next."
              },
              "docs": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    },
    "headers": {
      "RateLimit-Policy": {
        "description": "Quota policy: `<limit>;w=<window seconds>` (IETF RateLimit header fields draft).",
        "schema": {
          "type": "string",
          "example": "120;w=60"
        }
      },
      "RateLimit-Limit": {
        "description": "Requests allowed per window for this client.",
        "schema": {
          "type": "integer",
          "example": 120
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying (on 429).",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "RateLimited": {
        "description": "Too many requests from this client (120 per 60 s per IP, best effort per instance).",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      },
      "RateLimitedEdge": {
        "description": "Throttled by the edge firewall (global 120 per 60 s per IP). Plain-text body, header `x-vercel-mitigated: deny`. Retry when the window rolls over (up to 60 s).",
        "headers": {
          "x-vercel-mitigated": {
            "schema": {
              "type": "string",
              "const": "deny"
            }
          }
        },
        "content": {
          "text/plain": {
            "schema": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}
