{
  "openapi": "3.1.0",
  "info": {
    "title": "Phulax AI-app connector",
    "version": "1.0.0",
    "summary": "Remote MCP server and OAuth 2.1 endpoints for using Phulax from ChatGPT, Claude and other MCP clients.",
    "description": "Phulax exposes construction-estimating tools over the Model Context Protocol (Streamable HTTP, stateless) at /mcp. Every /mcp request needs an OAuth 2.1 Bearer access token issued by Phulax for the resource https://phulax.pro/mcp. MCP tools: create_estimate, list_projects, get_project, estimate_change_order, ask_project, create_proposal, get_account. See https://phulax.pro/developers.",
    "contact": { "name": "Phulax", "email": "phulaxpro@gmail.com", "url": "https://phulax.pro/developers" },
    "termsOfService": "https://phulax.pro/terms"
  },
  "servers": [{ "url": "https://phulax.pro" }],
  "externalDocs": { "description": "Developer docs", "url": "https://phulax.pro/developers" },
  "components": {
    "securitySchemes": {
      "phulaxOAuth": {
        "type": "oauth2",
        "description": "Authorization code flow with PKCE (S256 only). Clients register dynamically (RFC 7591) or use a Client ID Metadata Document URL as client_id. Send resource=https://phulax.pro/mcp (RFC 8707).",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://phulax.pro/oauth/authorize",
            "tokenUrl": "https://phulax.pro/oauth/token",
            "refreshUrl": "https://phulax.pro/oauth/token",
            "scopes": { "phulax": "Create and read estimate projects, price change orders, ask about projects, save proposal drafts, read plan and limits." }
          }
        }
      }
    },
    "schemas": {
      "JsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "method"],
        "properties": {
          "jsonrpc": { "const": "2.0" },
          "id": { "type": ["string", "integer"] },
          "method": { "type": "string", "examples": ["initialize", "server/discover", "tools/list", "tools/call", "ping"] },
          "params": { "type": "object" }
        }
      },
      "OAuthError": {
        "type": "object",
        "properties": { "error": { "type": "string" }, "error_description": { "type": "string" } }
      }
    }
  },
  "paths": {
    "/mcp": {
      "post": {
        "summary": "MCP Streamable HTTP endpoint",
        "description": "One JSON-RPC request or notification per POST. Supports protocol 2026-07-28 (per-request _meta, MCP-Protocol-Version / Mcp-Method / Mcp-Name headers, server/discover) and 2025-03-26 to 2025-11-25 clients (initialize handshake; no sessions are issued). Long-running tools/call requests may be answered as text/event-stream when the client accepts it.",
        "security": [{ "phulaxOAuth": ["phulax"] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcRequest" } } } },
        "responses": {
          "200": { "description": "JSON-RPC response (application/json or text/event-stream)" },
          "202": { "description": "Notification accepted" },
          "400": { "description": "Malformed request, header mismatch (-32020) or unsupported protocol version (-32022)" },
          "401": { "description": "Missing/invalid token. WWW-Authenticate: Bearer resource_metadata=\"https://phulax.pro/.well-known/oauth-protected-resource\", scope=\"phulax\"" },
          "403": { "description": "Origin not allowed" },
          "404": { "description": "Unknown method (protocol 2026-07-28)" }
        }
      },
      "get": { "summary": "Not supported (stateless server)", "responses": { "405": { "description": "Method not allowed" } } }
    },
    "/.well-known/oauth-protected-resource": {
      "get": { "summary": "Protected resource metadata (RFC 9728)", "description": "Also served at /.well-known/oauth-protected-resource/mcp.", "responses": { "200": { "description": "Metadata JSON with resource=https://phulax.pro/mcp" } } }
    },
    "/.well-known/oauth-authorization-server": {
      "get": { "summary": "Authorization server metadata (RFC 8414)", "responses": { "200": { "description": "Metadata JSON (issuer, endpoints, S256, client_id_metadata_document_supported, authorization_response_iss_parameter_supported)" } } }
    },
    "/oauth/register": {
      "post": {
        "summary": "Dynamic client registration (RFC 7591)",
        "description": "redirect_uris must be https, or http on localhost/127.0.0.1. token_endpoint_auth_method: none, client_secret_post or client_secret_basic (default).",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["redirect_uris"], "properties": { "client_name": { "type": "string" }, "redirect_uris": { "type": "array", "items": { "type": "string", "format": "uri" } }, "token_endpoint_auth_method": { "type": "string" }, "grant_types": { "type": "array", "items": { "type": "string" } } } } } } },
        "responses": { "201": { "description": "Client registered" }, "400": { "description": "Invalid metadata", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OAuthError" } } } }, "429": { "description": "Rate limited" } }
      }
    },
    "/oauth/authorize": {
      "get": {
        "summary": "Authorization endpoint (sign-in + consent page)",
        "parameters": [
          { "name": "response_type", "in": "query", "required": true, "schema": { "const": "code" } },
          { "name": "client_id", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "redirect_uri", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "code_challenge", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "code_challenge_method", "in": "query", "required": true, "schema": { "const": "S256" } },
          { "name": "state", "in": "query", "schema": { "type": "string" } },
          { "name": "resource", "in": "query", "schema": { "type": "string", "examples": ["https://phulax.pro/mcp"] } },
          { "name": "scope", "in": "query", "schema": { "type": "string", "examples": ["phulax"] } }
        ],
        "responses": { "200": { "description": "Consent page (after sign-in)" }, "307": { "description": "Redirect to sign-in, or to redirect_uri with code/error + state + iss" } }
      }
    },
    "/oauth/token": {
      "post": {
        "summary": "Token endpoint",
        "description": "grant_type=authorization_code (code, redirect_uri, client_id, code_verifier, resource) or refresh_token (refresh_token, client_id). Refresh tokens rotate on every use and last 90 days from last use.",
        "requestBody": { "required": true, "content": { "application/x-www-form-urlencoded": { "schema": { "type": "object" } }, "application/json": { "schema": { "type": "object" } } } },
        "responses": { "200": { "description": "access_token (1 hour), refresh_token, token_type=Bearer, expires_in, scope" }, "400": { "description": "OAuth error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OAuthError" } } } }, "401": { "description": "invalid_client" } }
      }
    },
    "/oauth/revoke": {
      "post": {
        "summary": "Token revocation (RFC 7009)",
        "description": "Revoking an access or refresh token revokes the whole grant.",
        "requestBody": { "required": true, "content": { "application/x-www-form-urlencoded": { "schema": { "type": "object", "required": ["token"], "properties": { "token": { "type": "string" }, "token_type_hint": { "type": "string" }, "client_id": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Revoked (also returned for unknown tokens)" } }
      }
    }
  }
}
