{
  "openapi": "3.1.0",
  "info": {
    "title": "CrawlCoach API",
    "version": "1.0.0",
    "summary": "Read your fix list and the checked pages Google left out, then ask CrawlCoach to recheck the pages you fixed.",
    "description": "CrawlCoach's API is its MCP server. Send JSON-RPC 2.0 messages to /mcp over the MCP Streamable HTTP transport. Most MCP clients do this for you. Docs: https://crawlcoach.com/docs",
    "contact": {
      "name": "CrawlCoach",
      "url": "https://crawlcoach.com"
    }
  },
  "externalDocs": {
    "description": "CrawlCoach developer docs",
    "url": "https://crawlcoach.com/docs"
  },
  "servers": [
    {
      "url": "https://crawlcoach.com"
    }
  ],
  "tags": [
    {
      "name": "MCP",
      "description": "The MCP server for AI agents."
    },
    {
      "name": "Discovery",
      "description": "Public files that describe the API and the docs."
    }
  ],
  "paths": {
    "/mcp": {
      "post": {
        "tags": [
          "MCP"
        ],
        "operationId": "mcpRequest",
        "summary": "Send one MCP JSON-RPC message",
        "description": "Methods: initialize, tools/list, tools/call, prompts/list, and prompts/get. Tools: list_sites, get_fix_list, get_pages (500 pages an answer), and recheck_pages (25 pages a call, 200 a site each day, paid plans, needs crawlcoach:write). One message per request; batches are refused. The body can be up to 512 KB.",
        "security": [
          {
            "crawlcoachOAuth": [
              "crawlcoach:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Mcp-Protocol-Version",
            "in": "header",
            "required": false,
            "description": "The MCP protocol version the client negotiated in initialize.",
            "schema": {
              "type": "string",
              "example": "2025-06-18"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              },
              "examples": {
                "listSites": {
                  "summary": "List your sites",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/call",
                    "params": {
                      "name": "list_sites",
                      "arguments": {}
                    }
                  }
                },
                "getFixList": {
                  "summary": "Read a site's fix list",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/call",
                    "params": {
                      "name": "get_fix_list",
                      "arguments": {
                        "siteId": "SITE_ID"
                      }
                    }
                  }
                },
                "getPages": {
                  "summary": "List missing pages for one reason",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/call",
                    "params": {
                      "name": "get_pages",
                      "arguments": {
                        "siteId": "SITE_ID",
                        "status": "missing",
                        "reason": "crawled-currently-not-indexed"
                      }
                    }
                  }
                },
                "recheckPages": {
                  "summary": "Recheck changed pages",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/call",
                    "params": {
                      "name": "recheck_pages",
                      "arguments": {
                        "siteId": "SITE_ID",
                        "urls": [
                          "https://example.com/pricing"
                        ]
                      }
                    }
                  }
                },
                "listTools": {
                  "summary": "List the tools",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/list"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The JSON-RPC response. A tool that can't answer returns a result with isError true.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "400": {
            "description": "The body is not one JSON-RPC message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Send one JSON-RPC message per request."
                }
              }
            }
          },
          "401": {
            "description": "No token, or the token is expired, revoked, or not for CrawlCoach.",
            "headers": {
              "WWW-Authenticate": {
                "description": "Points the client to the protected resource metadata, so it can sign in.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Connect your CrawlCoach account.",
                  "code": "TOKEN_REQUIRED"
                }
              }
            }
          },
          "403": {
            "description": "The Google account has no CrawlCoach account, or the token lacks the scope the tool needs.",
            "headers": {
              "WWW-Authenticate": {
                "description": "Points the client to the protected resource metadata, so it can sign in.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "This tool needs additional permission.",
                  "code": "INSUFFICIENT_SCOPE"
                }
              }
            }
          },
          "413": {
            "description": "The body is over 512 KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request body."
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this token or account.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Too many requests. Wait a minute and try again.",
                  "code": "RATE_LIMITED"
                }
              }
            }
          },
          "503": {
            "description": "CrawlCoach can't check the token right now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Account connection is unavailable. Try again later.",
                  "code": "AUTHENTICATION_UNAVAILABLE"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource/mcp": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "operationId": "getProtectedResourceMetadata",
        "summary": "Get the OAuth protected resource metadata",
        "description": "Names the authorization server and the scopes for /mcp (RFC 9728).",
        "security": [],
        "responses": {
          "200": {
            "description": "The metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProtectedResourceMetadata"
                },
                "example": {
                  "resource": "https://crawlcoach.com/mcp",
                  "authorization_servers": [
                    "https://clerk.crawlcoach.com"
                  ],
                  "scopes_supported": [
                    "crawlcoach:read",
                    "crawlcoach:write"
                  ],
                  "bearer_methods_supported": [
                    "header"
                  ],
                  "resource_name": "CrawlCoach"
                }
              }
            }
          },
          "404": {
            "description": "Sign-in for AI agents is not set up on this server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Sign-in for AI agents is not set up yet."
                }
              }
            }
          }
        }
      }
    },
    "/docs/llms.txt": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "operationId": "getDocsIndex",
        "summary": "Get the docs index for AI agents",
        "security": [],
        "responses": {
          "200": {
            "description": "Every docs page, linked to its Markdown copy.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "operationId": "getSiteSummary",
        "summary": "Get the site summary for AI assistants",
        "security": [],
        "responses": {
          "200": {
            "description": "CrawlCoach's plans and indexing guides.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "crawlcoachOAuth": {
        "type": "oauth2",
        "description": "OAuth 2.0 authorization code flow with PKCE, run by CrawlCoach's sign-in. The client identifies itself with a client ID metadata document (an HTTPS URL as client_id). CrawlCoach doesn't issue API keys.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://clerk.crawlcoach.com/oauth/authorize",
            "tokenUrl": "https://clerk.crawlcoach.com/oauth/token",
            "refreshUrl": "https://clerk.crawlcoach.com/oauth/token",
            "scopes": {
              "crawlcoach:read": "Read your sites, fix lists, and pages.",
              "crawlcoach:write": "Ask CrawlCoach to recheck pages."
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "A message that says what to do next."
          },
          "code": {
            "type": "string",
            "enum": [
              "TOKEN_REQUIRED",
              "INVALID_TOKEN",
              "ACCOUNT_REQUIRED",
              "INSUFFICIENT_SCOPE",
              "RATE_LIMITED",
              "AUTHENTICATION_UNAVAILABLE"
            ]
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer"
            ]
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "notifications/initialized",
              "tools/list",
              "tools/call",
              "prompts/list",
              "prompts/get",
              "ping"
            ]
          },
          "params": {
            "type": "object"
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc"
        ],
        "properties": {
          "jsonrpc": {
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer"
            ]
          },
          "result": {
            "$ref": "#/components/schemas/ToolResult"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "ToolResult": {
        "type": "object",
        "description": "A tools/call result. Other methods return their MCP result objects.",
        "properties": {
          "content": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "const": "text"
                },
                "text": {
                  "type": "string"
                }
              }
            }
          },
          "structuredContent": {
            "type": "object",
            "description": "The same answer as an object."
          },
          "isError": {
            "type": "boolean",
            "description": "True when the tool ran but couldn't answer."
          }
        }
      },
      "ProtectedResourceMetadata": {
        "type": "object",
        "properties": {
          "resource": {
            "type": "string",
            "format": "uri"
          },
          "authorization_servers": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "bearer_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "resource_name": {
            "type": "string"
          }
        }
      }
    }
  }
}