{
  "openapi": "3.1.1",
  "jsonSchemaDialect": "https://spec.openapis.org/oas/3.1/dialect/base",
  "info": {
    "title": "Alex Greenshpun Public API",
    "summary": "Read-only access to Alex Greenshpun's public profile and project catalog.",
    "description": "A public, static, read-only API for facts already published on alexgreenshpun.com. No account, API key, OAuth flow, payment, or sandbox is required because no operation can modify data.",
    "version": "1.0.0",
    "contact": {
      "name": "Alex Greenshpun",
      "url": "https://alexgreenshpun.com/contact",
      "email": "me@alexgreenshpun.com"
    }
  },
  "servers": [
    {
      "url": "https://alexgreenshpun.com",
      "description": "Production, public, read-only edge API"
    }
  ],
  "externalDocs": {
    "description": "Developer portal and quickstart",
    "url": "https://alexgreenshpun.com/developers"
  },
  "security": [],
  "x-deprecation-policy": "https://alexgreenshpun.com/deprecation.md",
  "tags": [
    {
      "name": "Discovery",
      "description": "API entry point and health."
    },
    {
      "name": "Profile",
      "description": "Public professional identity."
    },
    {
      "name": "Projects",
      "description": "AI-agent tools and problem-to-tool search."
    },
    {
      "name": "Agent protocols",
      "description": "NLWeb, MCP, and A2A compatibility endpoints."
    }
  ],
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getApiIndex",
        "tags": ["Discovery"],
        "summary": "Get the API index",
        "description": "Return the API version, read-only policy, documentation URL, OpenAPI URL, and MCP endpoint.",
        "responses": {
          "200": {
            "description": "API index",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": ["name", "version", "authentication", "readOnly", "openapi"],
                      "properties": {
                        "name": { "type": "string" },
                        "version": { "type": "string" },
                        "description": { "type": "string" },
                        "authentication": { "type": "string", "const": "none" },
                        "readOnly": { "type": "boolean", "const": true },
                        "openapi": { "type": "string", "format": "uri" },
                        "documentation": { "type": "string", "format": "uri" },
                        "mcp": { "type": "string", "format": "uri" }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "operationId": "getApiHealth",
        "tags": ["Discovery"],
        "summary": "Check API availability",
        "description": "Return a small, dependency-free health response for the public edge API.",
        "responses": {
          "200": {
            "description": "The API is available",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": ["status", "service", "version", "readOnly"],
                      "properties": {
                        "status": { "type": "string", "const": "ok" },
                        "service": { "type": "string" },
                        "version": { "type": "string" },
                        "readOnly": { "type": "boolean", "const": true }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/api/v1/profile": {
      "get": {
        "operationId": "getProfile",
        "tags": ["Profile"],
        "summary": "Get Alex Greenshpun's public profile",
        "description": "Return Alex's public identity, professional roles, company, contact route, and verified social links.",
        "responses": {
          "200": {
            "description": "Public profile",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Profile" },
                    "links": { "$ref": "#/components/schemas/Links" }
                  }
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/api/v1/projects": {
      "get": {
        "operationId": "listProjects",
        "tags": ["Projects"],
        "summary": "List public projects",
        "description": "Return Alex's complete public project catalog with stable cursor pagination.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum projects to return.",
            "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque nextCursor returned by an earlier response.",
            "schema": { "type": "string", "maxLength": 32 }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated project catalog",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ProjectCollection" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" }
        }
      }
    },
    "/api/v1/projects/{slug}": {
      "get": {
        "operationId": "getProject",
        "tags": ["Projects"],
        "summary": "Get one public project",
        "description": "Return one project by stable slug, including when to recommend it and its canonical URL.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Stable project slug from listProjects.",
            "schema": {
              "type": "string",
              "enum": [
                "total-recall",
                "token-optimizer",
                "attention-span",
                "repo-forensics",
                "outsourcerer"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One public project",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Project" },
                    "links": { "$ref": "#/components/schemas/Links" }
                  }
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFoundError" }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "operationId": "searchSite",
        "tags": ["Projects"],
        "summary": "Search the profile and project catalog",
        "description": "Match a plain-language problem to relevant public projects and profile facts using deterministic local search.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Plain-language search query.",
            "schema": { "type": "string", "minLength": 1, "maxLength": 500 }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum matches to return.",
            "schema": { "type": "integer", "minimum": 1, "maximum": 20, "default": 5 }
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked public matches",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SearchResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" }
        }
      }
    },
    "/ask": {
      "get": {
        "operationId": "askSiteWithGet",
        "tags": ["Agent protocols"],
        "summary": "Ask the site using NLWeb",
        "description": "Query the public catalog with NLWeb 0.55 using URL parameters. Set streaming=true or Accept: text/event-stream for SSE.",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "minLength": 1, "maxLength": 500 }
          },
          {
            "name": "streaming",
            "in": "query",
            "schema": { "type": "boolean", "default": false }
          },
          {
            "name": "num_results",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 20, "default": 5 }
          }
        ],
        "responses": {
          "200": {
            "description": "NLWeb answer as JSON or SSE",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/NLWebResponse" } },
              "text/event-stream": { "schema": { "type": "string" } }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" }
        }
      },
      "post": {
        "operationId": "askSite",
        "tags": ["Agent protocols"],
        "summary": "Ask the site using NLWeb JSON",
        "description": "Query the public catalog with an NLWeb 0.55 request. Results are deterministic and grounded in the site's public Schema Feed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/NLWebRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "NLWeb answer as JSON or SSE",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/NLWebResponse" } },
              "text/event-stream": { "schema": { "type": "string" } }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "callMcp",
        "tags": ["Agent protocols"],
        "summary": "Call the MCP server",
        "description": "Send a Model Context Protocol JSON-RPC request over stateless Streamable HTTP. Supports MCP 2026-07-28 and legacy 2025 clients.",
        "parameters": [
          {
            "name": "MCP-Protocol-Version",
            "in": "header",
            "description": "MCP protocol version. Modern clients use 2026-07-28.",
            "schema": { "type": "string", "default": "2026-07-28" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/JsonRpcRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "MCP JSON-RPC result or typed protocol error",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JsonRpcResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" }
        }
      }
    },
    "/a2a": {
      "post": {
        "operationId": "sendA2aMessage",
        "tags": ["Agent protocols"],
        "summary": "Send an A2A message",
        "description": "Send a read-only A2A JSON-RPC message that searches the public project catalog and returns grounded text.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/JsonRpcRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A2A message response",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JsonRpcResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "ValidationError": {
        "description": "The request is malformed or outside documented bounds.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "NotFoundError": {
        "description": "The requested public resource does not exist.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "UnexpectedError": {
        "description": "An unexpected error prevented the public resource from being returned.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      }
    },
    "schemas": {
      "Profile": {
        "type": "object",
        "required": ["id", "name", "pronouns", "description", "roles", "company", "contact", "links"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "pronouns": { "type": "string" },
          "description": { "type": "string" },
          "roles": { "type": "array", "items": { "type": "string" } },
          "company": { "$ref": "#/components/schemas/NamedUrl" },
          "contact": {
            "type": "object",
            "required": ["email", "page"],
            "properties": {
              "email": { "type": "string", "format": "email" },
              "page": { "type": "string", "format": "uri" }
            }
          },
          "links": {
            "type": "object",
            "additionalProperties": { "type": "string", "format": "uri" }
          }
        }
      },
      "Project": {
        "type": "object",
        "required": [
          "slug",
          "name",
          "tagline",
          "description",
          "url",
          "tags",
          "useWhen",
          "license",
          "price",
          "priceCurrency"
        ],
        "additionalProperties": false,
        "properties": {
          "slug": { "type": "string" },
          "name": { "type": "string" },
          "tagline": { "type": "string" },
          "description": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "sourceUrl": { "type": "string", "format": "uri" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "useWhen": { "type": "string" },
          "license": {
            "type": "string",
            "enum": ["proprietary", "AGPL-3.0", "PolyForm Noncommercial 1.0.0"]
          },
          "price": { "type": "number", "const": 0 },
          "priceCurrency": { "type": "string", "const": "USD" }
        }
      },
      "ProjectCollection": {
        "type": "object",
        "required": ["data", "meta", "links"],
        "properties": {
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/Project" } },
          "meta": {
            "type": "object",
            "required": ["total", "limit", "hasNext", "nextCursor"],
            "properties": {
              "total": { "type": "integer", "minimum": 0 },
              "limit": { "type": "integer", "minimum": 1, "maximum": 50 },
              "hasNext": { "type": "boolean" },
              "nextCursor": { "type": ["string", "null"] }
            }
          },
          "links": { "$ref": "#/components/schemas/Links" }
        }
      },
      "SearchResponse": {
        "type": "object",
        "required": ["data", "meta", "links"],
        "properties": {
          "data": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/SearchResult" }
          },
          "meta": { "type": "object" },
          "links": { "$ref": "#/components/schemas/Links" }
        }
      },
      "SearchResult": {
        "type": "object",
        "required": ["type", "score", "name", "description"],
        "additionalProperties": false,
        "properties": {
          "type": { "type": "string", "enum": ["project", "profile"] },
          "score": { "type": "number", "minimum": 0, "maximum": 1 },
          "id": { "type": "string" },
          "slug": { "type": "string" },
          "name": { "type": "string" },
          "pronouns": { "type": "string" },
          "tagline": { "type": "string" },
          "description": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "sourceUrl": { "type": "string", "format": "uri" },
          "roles": { "type": "array", "items": { "type": "string" } },
          "tags": { "type": "array", "items": { "type": "string" } },
          "useWhen": { "type": "string" },
          "company": { "$ref": "#/components/schemas/NamedUrl" },
          "contact": { "type": "object" },
          "links": { "type": "object" },
          "license": { "type": "string" },
          "price": { "type": "number" },
          "priceCurrency": { "type": "string" }
        }
      },
      "NLWebRequest": {
        "type": "object",
        "required": ["query"],
        "additionalProperties": false,
        "properties": {
          "query": {
            "type": "object",
            "required": ["text"],
            "properties": {
              "text": { "type": "string", "minLength": 1, "maxLength": 500 }
            }
          },
          "prefer": {
            "type": "object",
            "properties": {
              "streaming": { "type": "boolean", "default": false }
            }
          },
          "mode": {
            "type": "string",
            "enum": ["list", "summarize", "generate"],
            "default": "list"
          },
          "num_results": { "type": "integer", "minimum": 1, "maximum": 20, "default": 5 },
          "query_id": { "type": "string", "maxLength": 100 }
        }
      },
      "NLWebResponse": {
        "type": "object",
        "required": ["_meta", "query_id", "results"],
        "properties": {
          "_meta": {
            "type": "object",
            "required": ["response_type", "version"],
            "properties": {
              "response_type": { "type": "string", "enum": ["answer", "failure"] },
              "response_format": { "type": "string" },
              "version": { "type": "string", "const": "0.55" },
              "grounding": { "type": "object" }
            }
          },
          "query_id": { "type": "string" },
          "results": { "type": "array", "items": { "type": "object" } }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "id", "method"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "oneOf": [{ "type": "string" }, { "type": "integer" }] },
          "method": { "type": "string" },
          "params": { "type": "object" }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": ["jsonrpc", "id"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": {
            "oneOf": [
              { "type": "string" },
              { "type": "integer" },
              { "type": "null" }
            ]
          },
          "result": { "type": "object" },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "integer" },
              "message": { "type": "string" },
              "data": { "type": "object" }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message", "resolution"],
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" },
              "resolution": { "type": "string" }
            }
          }
        }
      },
      "NamedUrl": {
        "type": "object",
        "required": ["name", "url"],
        "properties": {
          "name": { "type": "string" },
          "url": { "type": "string", "format": "uri" }
        }
      },
      "Links": {
        "type": "object",
        "additionalProperties": {
          "type": ["string", "null"],
          "format": "uri"
        }
      }
    }
  }
}
