{
  "openapi": "3.1.0",
  "info": {
    "title": "Rootshift public API",
    "version": "1.0.0",
    "summary": "The public HTTP surface of rootshift.ai: a read-only Model Context Protocol server and a health endpoint.",
    "description": "Rootshift exposes one programmatic interface: a public, unauthenticated, read-only MCP server (Streamable HTTP, stateless) at /api/mcp that explains the method. Tools: get_overview, get_page_markdown, get_ground_truth_model, get_method, get_constitution, contact_rootstrap. Human documentation: https://rootshift.ai/developers and https://rootshift.ai/mcp.md. Any HTML page can also be fetched as Markdown with `Accept: text/markdown`. Rate limit: 120 requests per 60 seconds per client IP, advertised in RateLimit-* headers; a 429 carries Retry-After. Rootshift is created by Rootstrap (https://www.rootstrap.com).",
    "contact": { "name": "Rootstrap", "url": "https://www.rootstrap.com/contact" },
    "termsOfService": "https://www.rootstrap.com/privacy"
  },
  "externalDocs": { "description": "Developer page", "url": "https://rootshift.ai/developers" },
  "servers": [{ "url": "https://rootshift.ai", "description": "Production" }],
  "tags": [{ "name": "mcp", "description": "Model Context Protocol server" }, { "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. Methods: initialize, ping, tools/list, tools/call, resources/list, prompts/list. Notifications (no id) are accepted with 202 and no body. A JSON array is a batch.",
        "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" } },
            "constitution": { "value": { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "get_constitution", "arguments": {} } } } } } } },
        "responses": {
          "200": { "description": "JSON-RPC response (or an array for a batch).", "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" }
        }
      },
      "get": { "tags": ["mcp"], "operationId": "mcpGet", "summary": "Not supported (stateless server)", "responses": { "405": { "description": "Use POST.", "headers": { "Allow": { "schema": { "type": "string", "example": "POST, OPTIONS" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }, "429": { "$ref": "#/components/responses/RateLimited" } } },
      "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.", "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" } }, "content": { "application/json": { "schema": { "type": "object", "required": ["status"], "properties": { "status": { "type": "string", "const": "ok" }, "service": { "type": "string" }, "time": { "type": "string", "format": "date-time" } } } } } }, "429": { "$ref": "#/components/responses/RateLimited" } } }
    }
  },
  "components": {
    "headers": {
      "RateLimit-Policy": { "description": "Quota policy: `<limit>;w=<window seconds>`.", "schema": { "type": "string", "example": "120;w=60" } },
      "RateLimit-Limit": { "description": "Requests allowed per window.", "schema": { "type": "integer" } },
      "RateLimit-Remaining": { "description": "Requests left in the window.", "schema": { "type": "integer" } },
      "RateLimit-Reset": { "description": "Seconds until the window resets.", "schema": { "type": "integer" } },
      "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer" } }
    },
    "responses": {
      "RateLimited": { "description": "Too many requests (120 per 60 s per IP).", "headers": { "Retry-After": { "$ref": "#/components/headers/Retry-After" }, "RateLimit-Policy": { "$ref": "#/components/headers/RateLimit-Policy" }, "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimit-Remaining" }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
    },
    "schemas": {
      "JsonRpcRequest": { "type": "object", "required": ["jsonrpc", "method"], "properties": { "jsonrpc": { "type": "string", "const": "2.0" }, "id": { "oneOf": [{ "type": "integer" }, { "type": "string" }] }, "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" }, "message": { "type": "string" }, "data": {} } } } },
      "ApiError": { "type": "object", "required": ["error"], "properties": { "error": { "type": "object", "required": ["code", "message"], "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "hint": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } }
    }
  }
}
