{
  "openapi": "3.1.0",
  "info": {
    "title": "Meta Agent Tools",
    "version": "1.0.0",
    "description": "Meta Agent Tools — registro de MCP, skills e plugins."
  },
  "servers": [
    {
      "url": "https://agentalog.com"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Guest mr_…, sessão sess_… ou ADMIN_TOKEN."
      }
    },
    "schemas": {
      "PaginaDeAnuncios": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Anuncio"
            },
            "description": "Os registros desta página."
          },
          "limit": {
            "type": "integer",
            "description": "Tamanho de página aplicado."
          },
          "offset": {
            "type": "integer",
            "description": "Deslocamento aplicado."
          },
          "next_offset": {
            "type": "integer",
            "description": "Offset da próxima página; `null` quando acabou.",
            "nullable": true
          },
          "low_count": {
            "type": "integer",
            "description": "Quantos `low` batem no `q` (teto 200). Zero sem termo."
          },
          "low_capped": {
            "type": "boolean",
            "description": "`true` quando a contagem bateu no teto — há pelo menos esses."
          },
          "low_included": {
            "type": "boolean",
            "description": "`true` quando `low=1` misturou a cauda nesta página."
          },
          "api": {
            "type": "string",
            "description": "URL absoluta desta listagem."
          }
        },
        "required": [
          "items",
          "limit",
          "offset",
          "next_offset",
          "low_count",
          "low_capped",
          "low_included",
          "api"
        ],
        "description": "Página do mosaico público. Não traz `total`: o catálogo tem dezenas de milhares de registros e contar tudo a cada busca sairia caro sem mudar decisão nenhuma."
      },
      "Anuncio": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID do registro; é a chave em toda a API."
          },
          "kind": {
            "type": "string",
            "description": "O que é este registro."
          },
          "category": {
            "type": "string",
            "description": "Categoria escolhida por quem publicou.",
            "nullable": true
          },
          "name": {
            "type": "string",
            "description": "Nome de exibição."
          },
          "tagline": {
            "type": "string",
            "description": "Uma linha dizendo para que serve.",
            "nullable": true
          },
          "body": {
            "type": "string",
            "description": "Descrição longa, quando quem publicou escreveu uma.",
            "nullable": true
          },
          "url": {
            "type": "string",
            "description": "Onde o recurso vive — o endpoint MCP, o SKILL.md ou o repositório."
          },
          "status": {
            "type": "string",
            "description": "Estado no catálogo."
          },
          "origin": {
            "type": "string",
            "description": "De onde o registro veio: `official`, `marketplace`, `directory` ou envio da comunidade."
          },
          "origin_id": {
            "type": "string",
            "description": "Identificador do registro na fonte de origem.",
            "nullable": true
          },
          "install": {
            "type": "string",
            "description": "Como instalar, quando a fonte diz.",
            "nullable": true
          },
          "source": {
            "type": "string",
            "description": "URL do código-fonte, quando conhecida.",
            "nullable": true
          },
          "transporte": {
            "type": "string",
            "description": "Transporte do MCP: `stdio`, `http`, `sse`.",
            "nullable": true
          },
          "ns": {
            "type": "string",
            "description": "Namespace do servidor no registro oficial.",
            "nullable": true
          },
          "versao": {
            "type": "string",
            "description": "Versão declarada pela fonte.",
            "nullable": true
          },
          "oficial_status": {
            "type": "string",
            "description": "Estado no registro oficial de MCP, quando aplicável.",
            "nullable": true
          },
          "repo_host": {
            "type": "string",
            "description": "Onde o repositório está hospedado, ex. `github`.",
            "nullable": true
          },
          "topico": {
            "type": "string",
            "description": "Tópico inferido do repositório, usado nas facetas.",
            "nullable": true
          },
          "stars": {
            "type": "integer",
            "description": "Estrelas do repositório na última apuração.",
            "nullable": true
          },
          "forks": {
            "type": "integer",
            "description": "Forks do repositório na última apuração.",
            "nullable": true
          },
          "prs_abertos": {
            "type": "integer",
            "description": "Pull requests abertos na última apuração.",
            "nullable": true
          },
          "pushed_at": {
            "type": "string",
            "description": "Último push no repositório (UTC).",
            "nullable": true
          },
          "repo_estado": {
            "type": "string",
            "description": "Como o repositório está.",
            "nullable": true
          },
          "likes": {
            "type": "integer",
            "description": "Quantas pessoas curtiram — o like é reversível e conta pessoas."
          },
          "comments": {
            "type": "integer",
            "description": "Comentários públicos no registro."
          },
          "visits": {
            "type": "integer",
            "description": "Visitas contadas pelo hop; no máximo 1 por dono por dia."
          },
          "created_at": {
            "type": "string",
            "description": "Quando entrou no catálogo (UTC)."
          },
          "updated_at": {
            "type": "string",
            "description": "Última alteração (UTC).",
            "nullable": true
          },
          "mine": {
            "type": "boolean",
            "description": "`true` quando o registro é seu — só então dá para editar."
          },
          "api": {
            "type": "string",
            "description": "URL absoluta da ficha deste registro."
          },
          "go": {
            "type": "string",
            "description": "URL do hop: redireciona para `url` e conta a visita."
          },
          "comments_api": {
            "type": "string",
            "description": "URL absoluta dos comentários deste registro."
          }
        },
        "required": [
          "id",
          "kind",
          "category",
          "name",
          "tagline",
          "body",
          "url",
          "status",
          "origin",
          "origin_id",
          "install",
          "source",
          "transporte",
          "ns",
          "versao",
          "oficial_status",
          "repo_host",
          "topico",
          "stars",
          "forks",
          "prs_abertos",
          "pushed_at",
          "repo_estado",
          "likes",
          "comments",
          "visits",
          "created_at",
          "updated_at",
          "mine",
          "api",
          "go",
          "comments_api"
        ],
        "description": "Um registro do catálogo: servidor MCP, Agent Skill ou plugin do Claude Code."
      },
      "Facetas": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Registros live no catálogo."
          },
          "facetas": {
            "type": "object",
            "description": "Mapa de faceta → lista de `{ v, n }` (valor e contagem): `kind`, `origin`, `category`, `repo_estado`, `transporte`…"
          },
          "topico_inferido": {
            "type": "object",
            "description": "Tópicos deduzidos dos repositórios, com a contagem de cada um."
          },
          "api": {
            "type": "string",
            "description": "URL absoluta desta rota."
          }
        },
        "required": [
          "total",
          "facetas",
          "topico_inferido",
          "api"
        ],
        "description": "Os recortes do catálogo inteiro, para montar filtro sem varrer os registros."
      },
      "PaginaV01": {
        "type": "object",
        "properties": {
          "servers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServidorV01"
            },
            "description": "Os servidores desta página."
          },
          "metadata": {
            "type": "object",
            "description": "`next_cursor` e `count`, no formato da spec."
          }
        },
        "required": [
          "servers",
          "metadata"
        ],
        "description": "A página do subregistry, paginada por cursor como manda a spec v0.1."
      },
      "ServidorV01": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Nome do servidor no formato namespace/nome."
          },
          "description": {
            "type": "string",
            "description": "O que o servidor faz.",
            "nullable": true
          },
          "version": {
            "type": "string",
            "description": "Versão declarada.",
            "nullable": true
          },
          "repository": {
            "type": "object",
            "description": "Onde o código vive.",
            "nullable": true
          },
          "remotes": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Endpoints remotos do servidor, quando existem.",
            "nullable": true
          },
          "packages": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Pacotes instaláveis do servidor, quando existem.",
            "nullable": true
          }
        },
        "required": [
          "name",
          "description",
          "version",
          "repository",
          "remotes",
          "packages"
        ],
        "description": "Um servidor MCP no formato do Official Registry (spec v0.1) — é o que um cliente MCP genérico espera ler."
      },
      "Comentario": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID do comentário, para apagar."
          },
          "body": {
            "type": "string",
            "description": "O texto do comentário."
          },
          "author": {
            "type": "string",
            "description": "Apelido de quem escreveu.",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "description": "Quando foi escrito (UTC)."
          },
          "mine": {
            "type": "boolean",
            "description": "`true` se é seu — só você pode apagar."
          }
        },
        "required": [
          "id",
          "body",
          "author",
          "created_at",
          "mine"
        ],
        "description": "Comentário público num registro."
      },
      "Like": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true`."
          },
          "liked": {
            "type": "boolean",
            "description": "Se VOCÊ está curtindo agora."
          },
          "likes": {
            "type": "integer",
            "description": "Total de pessoas curtindo o registro."
          }
        },
        "required": [
          "ok",
          "liked",
          "likes"
        ],
        "description": "O estado do like depois da chamada. Ligar e desligar devolvem a mesma forma."
      },
      "Conta": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID da conta."
          },
          "email": {
            "type": "string",
            "description": "E-mail confirmado por código."
          }
        },
        "required": [
          "id",
          "email"
        ],
        "description": "A pessoa por trás da sessão."
      },
      "Carga": {
        "type": "object",
        "properties": {
          "fontes": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Cada fonte com o último lote e a contagem que ela trouxe."
          },
          "falhas": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Falhas de importação ainda não marcadas como vistas."
          },
          "runs": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "As execuções recentes, da mais nova para a mais antiga."
          }
        },
        "required": [
          "fontes",
          "falhas",
          "runs"
        ],
        "description": "O estado das fontes de carga do catálogo — de onde os registros vêm e quando vieram."
      },
      "PreferenciasUi": {
        "type": "object",
        "properties": {
          "prefs": {
            "type": "object",
            "description": "As preferências gravadas, como a interface as escreveu. `{}` quando nunca houve gravação."
          },
          "api": {
            "type": "string",
            "description": "URL absoluta desta rota."
          }
        },
        "required": [
          "prefs",
          "api"
        ],
        "description": "O que a pessoa arrumou na tela e precisa sobreviver a um F5: recorte de filtro, aba ativa e abas de item abertas. O conteúdo é OPACO — o servidor não interpreta o JSON, só guarda e devolve, para a tela mudar de campo sem migração de banco."
      },
      "Ok": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true` — a falha vem como status 4xx/5xx, não como `ok:false`."
          }
        },
        "required": [
          "ok"
        ],
        "description": "Confirmação de escrita que não tem corpo próprio a devolver."
      },
      "Billing": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "description": "Sempre `x402` — é o único protocolo de cobrança aceito."
          },
          "mode": {
            "type": "string",
            "description": "Modo do vendedor: `live` cobra de verdade, `dev` libera sem pagar."
          },
          "network": {
            "type": "string",
            "description": "Rede da USDC: `base` em produção, `base-sepolia` em homologação."
          },
          "chain_id": {
            "type": "integer",
            "description": "Chain ID EVM da rede acima, para a carteira assinar na cadeia certa."
          },
          "pay_to": {
            "type": "string",
            "description": "Endereço que recebe o pagamento.",
            "nullable": true
          },
          "homolog": {
            "type": "boolean",
            "description": "Seam de homologação ligado: dá para fechar o loop sem gastar USDC."
          },
          "dev": {
            "type": "boolean",
            "description": "Modo de desenvolvimento: o 402 é simulado."
          },
          "dev_gate": {
            "type": "string",
            "description": "Como o modo dev é destravado, quando existe.",
            "nullable": true
          },
          "facilitator": {
            "type": "string",
            "description": "URL do facilitador que verifica e liquida o pagamento."
          },
          "asset": {
            "type": "string",
            "description": "Moeda aceita — sempre `USDC`."
          },
          "asset_address": {
            "type": "string",
            "description": "Contrato da USDC na rede acima."
          },
          "faucet": {
            "type": "string",
            "description": "Torneira de USDC de teste; só em base-sepolia.",
            "nullable": true
          },
          "wallets": {
            "type": "object",
            "description": "Links de carteiras que falam x402 (metamask, coinbase, base_app)."
          },
          "product": {
            "type": "string",
            "description": "Nome do produto que está cobrando."
          },
          "prices": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Precos"
              }
            ],
            "description": "Quanto custa cada ação paga, em USD."
          }
        },
        "required": [
          "provider",
          "mode",
          "network",
          "chain_id",
          "pay_to",
          "homolog",
          "dev",
          "dev_gate",
          "facilitator",
          "asset",
          "asset_address",
          "faucet",
          "wallets",
          "product",
          "prices"
        ],
        "description": "Configuração x402 em vigor e os preços do produto. Ler, curtir, comentar e visitar são grátis; o que custa para agente é publicar."
      },
      "Precos": {
        "type": "object",
        "properties": {
          "contact_agent_usd": {
            "type": "number",
            "description": "Contato de agente."
          },
          "listing_agent_usd": {
            "type": "number",
            "description": "Publicar um registro sendo agente (humano com conta publica de graça)."
          }
        },
        "required": [
          "contact_agent_usd",
          "listing_agent_usd"
        ],
        "description": "Preços em vigor, em dólar. Leia daqui, não da documentação."
      },
      "Metricas": {
        "type": "object",
        "properties": {
          "app": {
            "type": "string",
            "description": "Nome do produto."
          },
          "today": {
            "type": "string",
            "description": "Dia de referência (UTC, AAAA-MM-DD)."
          },
          "today_visits": {
            "type": "integer",
            "description": "Visitas contadas hoje."
          },
          "today_contacts": {
            "type": "integer",
            "description": "Mensagens de contato recebidas hoje. Só com `METRICS_TOKEN`: contato não sai sem token."
          },
          "days": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Um registro por dia da janela, com as contagens de cada métrica."
          },
          "usage": {
            "type": "object",
            "description": "Uso por recurso do produto — aqui, registros criados pela superfície (origin community); carga de catálogo do próprio registro não conta como uso."
          },
          "accounts": {
            "type": "object",
            "description": "Total de convidados e contas."
          },
          "financeiro": {
            "type": "object",
            "description": "Agregado do dia: `hoje_usd`, `hoje_count`, `rede`. Só com `METRICS_TOKEN`: dinheiro não sai sem token; a série completa é `payments`."
          },
          "payments": {
            "type": "object",
            "description": "Resumo financeiro; só com METRICS_TOKEN."
          }
        },
        "required": [
          "app",
          "today",
          "today_visits",
          "days",
          "usage",
          "accounts"
        ],
        "description": "Painel de 7 dias. `payments` só aparece com o token do operador e só em Base mainnet."
      },
      "EstadoFila": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true` quando há retrato."
          },
          "fila": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RetratoFila"
              }
            ],
            "description": "Os números da fila."
          }
        },
        "required": [
          "ok",
          "fila"
        ],
        "description": "O retrato da fila de enriquecimento: quantos registros já foram apurados e o que se sabe deles."
      },
      "RetratoFila": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Registros na fila."
          },
          "concluidos": {
            "type": "integer",
            "description": "Já apurados."
          },
          "vencidos": {
            "type": "integer",
            "description": "Com apuração vencida, esperando nova passada."
          },
          "com_falha": {
            "type": "integer",
            "description": "Que falharam na apuração."
          },
          "processados": {
            "type": "integer",
            "description": "Processados na janela corrente."
          },
          "estados": {
            "type": "object",
            "description": "Contagem por `repo_estado`: ativo, parado, arquivado, renomeado, sumiu."
          },
          "motivos": {
            "type": "object",
            "description": "Contagem por motivo de o registro estar no estado em que está."
          }
        },
        "required": [
          "total",
          "concluidos",
          "vencidos",
          "com_falha",
          "processados",
          "estados",
          "motivos"
        ],
        "description": "Contadores da fila de enriquecimento, empurrados pelo robô que apura os repositórios."
      }
    }
  },
  "paths": {
    "/okf/{arquivo}": {
      "get": {
        "operationId": "get_okf_by_arquivo",
        "summary": "Bundle OKF (Open Knowledge Format v0.1): markdown com frontmatter para o agente ler o produto inteiro sem parsear HTML.",
        "description": "Devolve: `text/markdown`. Comece por `/okf/index.md`, que lista o bundle.",
        "security": [],
        "parameters": [
          {
            "name": "arquivo",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`text/markdown`. Comece por `/okf/index.md`, que lista o bundle."
          },
          "404": {
            "description": "Arquivo fora do bundle."
          }
        }
      }
    },
    "/.well-known/{arquivo}": {
      "get": {
        "operationId": "get_well_known_by_arquivo",
        "summary": "Descoberta de máquina antes da home: `api-catalog` (RFC 9727, linkset com a API e o MCP), `security.txt` (RFC 9116) e `mcp-registry-auth` (chave do registro oficial de MCP).",
        "description": "Devolve: `application/linkset+json` no api-catalog; `text/plain` nos outros dois.",
        "security": [],
        "parameters": [
          {
            "name": "arquivo",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`application/linkset+json` no api-catalog; `text/plain` nos outros dois."
          },
          "404": {
            "description": "Nome fora dos quatro publicados."
          }
        }
      }
    },
    "/apis.json": {
      "get": {
        "operationId": "get_apis_json",
        "summary": "APIs.json (apisjson.org, 0.19): o índice que o APIs.io colhe — a API, o MCP, OpenAPI, guia e bundle OKF num arquivo só. Também em `/.well-known/apis.json`.",
        "description": "Devolve: `application/json` no formato APIs.json 0.19: `apis[]` com `baseURL`, `humanURL` e `properties[]`.",
        "security": [],
        "responses": {
          "200": {
            "description": "`application/json` no formato APIs.json 0.19: `apis[]` com `baseURL`, `humanURL` e `properties[]`."
          }
        }
      }
    },
    "/feed.xml": {
      "get": {
        "operationId": "get_feed_xml",
        "summary": "RSS 2.0 dos registros publicados mais recentemente.",
        "description": "Devolve: `application/rss+xml`.",
        "security": [],
        "responses": {
          "200": {
            "description": "`application/rss+xml`."
          }
        }
      }
    },
    "/feed.json": {
      "get": {
        "operationId": "get_feed_json",
        "summary": "JSON Feed 1.1 dos registros publicados mais recentemente — o mesmo stream do RSS.",
        "description": "Devolve: `application/feed+json`.",
        "security": [],
        "responses": {
          "200": {
            "description": "`application/feed+json`."
          }
        }
      }
    },
    "/api/": {
      "get": {
        "operationId": "api_index",
        "summary": "Índice auto-descrito: toda a superfície da API, com cota e quickstart.",
        "description": "Devolve: { name, description, auth, docs, endpoints, quota, mcp, quickstart }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ name, description, auth, docs, endpoints, quota, mcp, quickstart }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Nome do produto."
                    },
                    "description": {
                      "type": "string",
                      "description": "O que o registro é e o que ele não é."
                    },
                    "auth": {
                      "type": "object",
                      "description": "Cada modo de autenticação e como obtê-lo."
                    },
                    "docs": {
                      "type": "object",
                      "description": "Links para llms.txt, llms-full.txt, openapi.json, MCP e a UI."
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Todo endpoint com método, caminho, auth, URL absoluta e o que devolve."
                    },
                    "quota": {
                      "type": "object",
                      "description": "O que é grátis, o que custa e como pagar — antes de você gastar chamada."
                    },
                    "mcp": {
                      "type": "object",
                      "description": "Endereço e transporte do servidor MCP."
                    },
                    "quickstart": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "As chamadas que levam do zero ao primeiro registro publicado."
                    }
                  },
                  "required": [
                    "name",
                    "description",
                    "auth",
                    "docs",
                    "endpoints",
                    "quota",
                    "mcp",
                    "quickstart"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "health",
        "summary": "Liveness e o commit publicado agora — é como o smoke espera o próprio deploy.",
        "description": "Devolve: { ok, app, build, ts }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ ok, app, build, ts }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true` quando o Worker responde."
                    },
                    "app": {
                      "type": "string",
                      "description": "Nome do produto."
                    },
                    "build": {
                      "type": "string",
                      "description": "Commit publicado; o CI passa o SHA curto no deploy."
                    },
                    "ts": {
                      "type": "string",
                      "description": "Momento da resposta (UTC, ISO-8601)."
                    }
                  },
                  "required": [
                    "ok",
                    "app",
                    "build",
                    "ts"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "post_mcp",
        "summary": "Servidor MCP por HTTP (Streamable HTTP, JSON-RPC 2.0) — pluga no cliente sem instalar nada.",
        "description": "As tools são as operações deste mesmo catálogo; o MCP não tem backend próprio. `GET /mcp` devolve o cartão do servidor.\nDevolve: Resposta JSON-RPC 2.0 (`initialize`, `tools/list` ou `tools/call`).\nCredencial vai nos headers de sempre (X-Guest-Token, Authorization, X-PAYMENT) e é repassada à API.\nCota estourada chega como 402 com accepts[] dentro do resultado da tool — pague e repita.",
        "security": [],
        "responses": {
          "200": {
            "description": "Resposta JSON-RPC 2.0 (`initialize`, `tools/list` ou `tools/call`)."
          }
        }
      }
    },
    "/api/guest": {
      "post": {
        "operationId": "create_guest",
        "summary": "Cria um convidado `mr_…` — é a identidade que curte, comenta e visita.",
        "description": "Não pede e-mail. Publicar registro é que exige conta (ou pagamento).\nDevolve: { token }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ token }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "O convidado, prefixo `mr_`. Mande em `X-Guest-Token` ou como Bearer."
                    }
                  },
                  "required": [
                    "token"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/listings": {
      "get": {
        "operationId": "list_listings",
        "summary": "O mosaico público: busca paginada dos registros live do catálogo.",
        "description": "Só `live` no mosaico. Com `q`, `low_count` diz quantos `low` (poucas estrelas ou sem licença clara) batem no termo — LIKE com teto 200. `low=1` inclui essa cauda; sem `q` o parâmetro é ignorado.\nDevolve: { items[{id,kind,category,name,tagline,body,url,status,origin,origin_id,install,source,transporte,ns,versao,oficial_status,repo_host,topico,stars,forks,prs_abertos,pushed_at,repo_estado,likes,comments,visits,created_at,updated_at,mine,api,go,comments_api}], limit, offset, next_offset, low_count, low_capped, low_included, api }",
        "security": [],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Texto livre no nome, na tagline e na descrição.",
            "example": "postgres"
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "mcp",
                "skill",
                "plugin"
              ]
            },
            "description": "Que tipo de recurso trazer."
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Categoria declarada por quem publicou."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "recent",
              "enum": [
                "recent",
                "likes",
                "visits"
              ]
            },
            "description": "Ordem do resultado."
          },
          {
            "name": "low",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "`1` inclui registros `low` no resultado. Só vale junto com `q`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 24
            },
            "description": "Registros por página."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Quantos registros pular. Use `next_offset` da resposta anterior."
          }
        ],
        "responses": {
          "200": {
            "description": "{ items[{id,kind,category,name,tagline,body,url,status,origin,origin_id,install,source,transporte,ns,versao,oficial_status,repo_host,topico,stars,forks,prs_abertos,pushed_at,repo_estado,likes,comments,visits,created_at,updated_at,mine,api,go,comments_api}], limit, offset, next_offset, low_count, low_capped, low_included, api }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDeAnuncios"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "create_listing",
        "summary": "Registra um MCP, uma skill ou um plugin no catálogo. Entra como `pending`.",
        "description": "Duas portas para a mesma ação. Humano com sessão: grátis, 1 por dia, no máximo 3 na fila. Agente (com ou sem convidado): **402 com `accepts[]`**, $0.10 — pague e repita. Para skill, a URL do `SKILL.md` basta; o resto é apurado. **Valida antes de cobrar:** corpo recusado (400) e cota estourada (429) vêm ANTES do 402, então nenhum pagamento liquida por um registro que já se sabe que não entra. Corpo válido sem pagamento continua recebendo o 402 com o preço.\nDevolve: { ok, id, status }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "description": "O que está sendo registrado."
                  },
                  "url": {
                    "type": "string",
                    "description": "Endpoint MCP, URL do SKILL.md ou repositório do plugin."
                  },
                  "name": {
                    "type": "string",
                    "description": "Nome de exibição; sem ele, sai da fonte."
                  },
                  "tagline": {
                    "type": "string",
                    "description": "Uma linha dizendo para que serve."
                  },
                  "body": {
                    "type": "string",
                    "description": "Descrição longa, opcional."
                  },
                  "category": {
                    "type": "string",
                    "description": "Categoria para o registro aparecer no filtro certo."
                  }
                },
                "required": [
                  "kind",
                  "url"
                ]
              },
              "example": {
                "kind": "mcp",
                "category": "ferramentas",
                "name": "Nome",
                "tagline": "Uma linha",
                "url": "https://exemplo.com/mcp"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, id, status }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true` quando o pedido entrou."
                    },
                    "id": {
                      "type": "string",
                      "description": "ID do registro criado."
                    },
                    "status": {
                      "type": "string",
                      "description": "Sempre `pending`: tudo passa pela fila antes de virar live."
                    }
                  },
                  "required": [
                    "ok",
                    "id",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`kind` ou `url` ausentes, URL inválida, URL que não responde ou campo recusado."
          },
          "402": {
            "description": "Cota estourada. A resposta traz `accepts[]` (x402, USDC na Base): pague e repita a mesma chamada com `X-PAYMENT`."
          },
          "429": {
            "description": "1 por dia, máximo 3 pendentes — vale para as duas portas, e é conferido antes de cobrar."
          },
          "502": {
            "description": "O pagamento liquidou e a gravação falhou. O corpo traz `transaction`: guarde e fale com o suporte."
          }
        }
      }
    },
    "/api/facets": {
      "get": {
        "operationId": "get_api_facets",
        "summary": "As contagens do catálogo inteiro por tipo, procedência, categoria e estado do repositório.",
        "description": "Existe para montar filtro sem varrer os registros: são dezenas de milhares, e pedir a lista só para contar sairia caro. Traz também `topico_inferido`, deduzido dos repositórios.\nDevolve: { total, facetas, topico_inferido, api }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ total, facetas, topico_inferido, api }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Facetas"
                }
              }
            }
          }
        }
      }
    },
    "/v0.1/servers": {
      "get": {
        "operationId": "list_mcp_servers",
        "summary": "Subregistry MCP no formato do Official Registry (spec v0.1), paginado por cursor.",
        "description": "Só `kind=mcp` e só `live`. É a rota que um cliente MCP genérico sabe ler sem conhecer este produto. `GET /v0/servers` é alias do mesmo recurso, mantido para quem já apontava para lá — a URL canônica é esta.\nDevolve: { servers[{name,description,version,repository,remotes,packages}], metadata }",
        "security": [],
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Texto livre no nome e na descrição do servidor."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Cursor opaco da página seguinte, vindo de `metadata.next_cursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 30
            },
            "description": "Servidores por página."
          }
        ],
        "responses": {
          "200": {
            "description": "{ servers[{name,description,version,repository,remotes,packages}], metadata }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaV01"
                }
              }
            }
          }
        }
      }
    },
    "/api/listings/{id}": {
      "get": {
        "operationId": "get_listing",
        "summary": "Ficha de um registro. O dono vê a própria mesmo pendente ou escondida.",
        "description": "Devolve: { id, kind, category, name, tagline, body, url, status, origin, origin_id, install, source, transporte, ns, versao, oficial_status, repo_host, topico, stars, forks, prs_abertos, pushed_at, repo_estado, likes, comments, visits, created_at, updated_at, mine, api, go, comments_api }",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ id, kind, category, name, tagline, body, url, status, origin, origin_id, install, source, transporte, ns, versao, oficial_status, repo_host, topico, stars, forks, prs_abertos, pushed_at, repo_estado, likes, comments, visits, created_at, updated_at, mine, api, go, comments_api }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Anuncio"
                }
              }
            }
          },
          "404": {
            "description": "Registro não existe, ou não é seu e não está live/low."
          }
        }
      },
      "patch": {
        "operationId": "patch_api_listings_by_id",
        "summary": "Edita um registro seu. Mudar a URL faz ele voltar para a fila.",
        "description": "A URL é o que a moderação olha; trocá-la depois de aprovado seria burlar a fila, então o registro volta a `pending`.\nDevolve: { ok, id, status }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Novo nome de exibição."
                  },
                  "tagline": {
                    "type": "string",
                    "description": "Nova linha de resumo."
                  },
                  "body": {
                    "type": "string",
                    "description": "Nova descrição longa."
                  },
                  "category": {
                    "type": "string",
                    "description": "Nova categoria."
                  },
                  "url": {
                    "type": "string",
                    "description": "Nova URL — trocar isto devolve o registro para `pending`."
                  }
                }
              },
              "example": {
                "tagline": "…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, id, status }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true`."
                    },
                    "id": {
                      "type": "string",
                      "description": "ID do registro editado."
                    },
                    "status": {
                      "type": "string",
                      "description": "Estado depois da edição; volta a `pending` se a URL mudou."
                    }
                  },
                  "required": [
                    "ok",
                    "id",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Campo inválido no corpo."
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          },
          "404": {
            "description": "O registro não é seu ou não existe."
          }
        }
      }
    },
    "/api/go/{id}": {
      "get": {
        "operationId": "go_listing",
        "summary": "Hop para a URL do registro: redireciona e conta a visita.",
        "description": "Conta no máximo 1 visita por dono por dia. O header `X-Visit-Counted` diz se esta chamada contou — é como o cliente sabe sem contar duas vezes.\nDevolve: `302` com `Location` para a URL do registro, e o header `X-Visit-Counted`.",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`302` com `Location` para a URL do registro, e o header `X-Visit-Counted`."
          },
          "404": {
            "description": "Registro não existe ou não está live/low."
          }
        }
      }
    },
    "/api/listings/{id}/comments": {
      "get": {
        "operationId": "list_comments",
        "summary": "Comentários públicos de um registro live.",
        "description": "Com credencial na chamada, cada comentário seu vem com `mine: true`.\nDevolve: { items[{id,body,author,created_at,mine}], total }",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ items[{id,body,author,created_at,mine}], total }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Comentario"
                      },
                      "description": "Os comentários, do mais novo para o mais antigo."
                    },
                    "total": {
                      "type": "integer",
                      "description": "Quantos comentários o registro tem."
                    }
                  },
                  "required": [
                    "items",
                    "total"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Registro não existe ou não está live."
          }
        }
      },
      "post": {
        "operationId": "post_api_listings_by_id_comments",
        "summary": "Escreve um comentário no registro. Teto de 20 por hora por dono.",
        "description": "Devolve: { ok, id, body }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "O texto do comentário."
                  }
                },
                "required": [
                  "body"
                ]
              },
              "example": {
                "body": "texto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, id, body }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true` quando o comentário entrou."
                    },
                    "id": {
                      "type": "string",
                      "description": "ID do comentário criado."
                    },
                    "body": {
                      "type": "string",
                      "description": "O texto gravado."
                    }
                  },
                  "required": [
                    "ok",
                    "id",
                    "body"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Texto vazio ou longo demais."
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          },
          "404": {
            "description": "Registro não existe."
          },
          "429": {
            "description": "Passou de 20 comentários na hora."
          }
        }
      }
    },
    "/api/comments/{id}": {
      "delete": {
        "operationId": "delete_api_comments_by_id",
        "summary": "Apaga um comentário seu. Comentário alheio responde 404, não 403.",
        "description": "O 404 é de propósito: a API não confirma que existe um comentário com aquele id se ele não é seu.\nDevolve: { ok, id }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, id }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true`."
                    },
                    "id": {
                      "type": "string",
                      "description": "O id que saiu."
                    }
                  },
                  "required": [
                    "ok",
                    "id"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          },
          "404": {
            "description": "Recurso não existe (ou não é seu — a API não distingue os dois de propósito)."
          }
        }
      }
    },
    "/api/listings/{id}/like": {
      "post": {
        "operationId": "like_listing",
        "summary": "Curte o registro. Chamar de novo não soma: o contador conta pessoas.",
        "description": "Devolve: { ok, liked, likes }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, liked, likes }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Like"
                }
              }
            }
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          },
          "404": {
            "description": "Recurso não existe (ou não é seu — a API não distingue os dois de propósito)."
          }
        }
      },
      "delete": {
        "operationId": "delete_api_listings_by_id_like",
        "summary": "Descurte e devolve o ponto ao contador público.",
        "description": "Devolve: { ok, liked, likes }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, liked, likes }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Like"
                }
              }
            }
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          },
          "404": {
            "description": "Recurso não existe (ou não é seu — a API não distingue os dois de propósito)."
          }
        }
      }
    },
    "/api/me/listings": {
      "get": {
        "operationId": "get_api_me_listings",
        "summary": "Os registros do dono em qualquer estado, inclusive pendente e escondido.",
        "description": "É a única rota que mostra o que ainda não é live — o mosaico público nunca mostra.\nDevolve: { items[{id,kind,category,name,tagline,body,url,status,origin,origin_id,install,source,transporte,ns,versao,oficial_status,repo_host,topico,stars,forks,prs_abertos,pushed_at,repo_estado,likes,comments,visits,created_at,updated_at,mine,api,go,comments_api}] }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ items[{id,kind,category,name,tagline,body,url,status,origin,origin_id,install,source,transporte,ns,versao,oficial_status,repo_host,topico,stars,forks,prs_abertos,pushed_at,repo_estado,likes,comments,visits,created_at,updated_at,mine,api,go,comments_api}] }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Anuncio"
                      },
                      "description": "Os registros da conta, de qualquer estado."
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          }
        }
      }
    },
    "/api/me": {
      "get": {
        "operationId": "get_api_me",
        "summary": "A conta da sessão e, para o admin, o estado da carga.",
        "description": "Devolve: { user{id,email}, admin, carga?{fontes,falhas,runs} }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ user{id,email}, admin, carga?{fontes,falhas,runs} }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Conta"
                        }
                      ],
                      "description": "A pessoa dona da sessão."
                    },
                    "admin": {
                      "type": "boolean",
                      "description": "Se esta conta é o `ADMIN_EMAIL`."
                    },
                    "carga": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Carga"
                        }
                      ],
                      "description": "Estado das fontes de carga; só para o admin."
                    }
                  },
                  "required": [
                    "user",
                    "admin"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          }
        }
      }
    },
    "/api/me/ui": {
      "get": {
        "operationId": "get_api_me_ui",
        "summary": "As preferências de tela do dono: busca, filtro, ordenação e tema.",
        "description": "Existe para o que a pessoa arrumou sobreviver a um F5 e a reabrir o app em outro aparelho. Refresh não é tela nova — ver AGENTS-UI.md. Convidado não tem: sem conta não há a quem devolver o dado depois, então o guest fica só no `localStorage` do navegador.\nDevolve: { prefs, api }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ prefs, api }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreferenciasUi"
                }
              }
            }
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          }
        }
      },
      "put": {
        "operationId": "put_api_me_ui",
        "summary": "Grava as preferências de tela do dono, substituindo as anteriores.",
        "description": "É PUT e não PATCH de propósito: o cliente manda o estado inteiro da tela, não um delta. Teto de 8 KB — isto é preferência de tela, não depósito de blob.\nDevolve: { ok }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "prefs": {
                    "type": "object",
                    "description": "O estado da interface a guardar. Opaco para o servidor: qualquer JSON dentro do teto serve."
                  }
                },
                "required": [
                  "prefs"
                ]
              },
              "example": {
                "prefs": {
                  "q": "postgres",
                  "kind": "mcp",
                  "sort": "likes",
                  "tema": "escuro"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "Corpo que não é JSON (`bad_json`) ou sem a chave `prefs` (`prefs`)."
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          },
          "405": {
            "description": "Método diferente de GET ou PUT nesta rota."
          },
          "413": {
            "description": "Preferências acima do teto de 8 KB (`grande`)."
          }
        }
      }
    },
    "/api/auth/start": {
      "post": {
        "operationId": "post_api_auth_start",
        "summary": "Envia o código de 6 dígitos por e-mail para criar a conta ou entrar nela.",
        "description": "Devolve: { ok }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "E-mail que vai receber o código."
                  }
                },
                "required": [
                  "email"
                ]
              },
              "example": {
                "email": "voce@exemplo.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "E-mail ausente ou malformado."
          },
          "429": {
            "description": "Pedidos demais para o mesmo e-mail."
          }
        }
      }
    },
    "/api/auth/verify": {
      "post": {
        "operationId": "post_api_auth_verify",
        "summary": "Troca o código por uma sessão `sess_…`.",
        "description": "Devolve: { ok, token, user{id,email} }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "O mesmo e-mail do `/api/auth/start`."
                  },
                  "code": {
                    "type": "string",
                    "description": "Os 6 dígitos que chegaram por e-mail."
                  }
                },
                "required": [
                  "email",
                  "code"
                ]
              },
              "example": {
                "email": "voce@exemplo.com",
                "code": "123456"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, token, user{id,email} }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true` quando o código conferiu."
                    },
                    "token": {
                      "type": "string",
                      "description": "Sessão `sess_…` para usar em `Authorization: Bearer`."
                    },
                    "user": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Conta"
                        }
                      ],
                      "description": "A pessoa que acabou de entrar."
                    }
                  },
                  "required": [
                    "ok",
                    "token",
                    "user"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Código errado ou expirado."
          },
          "429": {
            "description": "Tentativas demais."
          }
        }
      }
    },
    "/api/auth/claim": {
      "post": {
        "operationId": "post_api_auth_claim",
        "summary": "Amarra um convidado à conta: likes e comentários dele passam a ser dela.",
        "description": "Devolve: { ok, claimed }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "guest_token": {
                    "type": "string",
                    "description": "Convidado `mr_…` a ligar na conta."
                  }
                },
                "required": [
                  "guest_token"
                ]
              },
              "example": {
                "guest_token": "mr_…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, claimed }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true`."
                    },
                    "claimed": {
                      "type": "integer",
                      "description": "Quantos registros mudaram de dono."
                    }
                  },
                  "required": [
                    "ok",
                    "claimed"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`guest_token` ausente."
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "operationId": "post_api_auth_logout",
        "summary": "Invalida a sessão em curso.",
        "description": "Devolve: { ok }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          }
        }
      }
    },
    "/api/billing": {
      "get": {
        "operationId": "billing",
        "summary": "Configuração x402 em vigor e os preços de contato e de publicação por agente.",
        "description": "Devolve: { provider, mode, network, chain_id, pay_to, homolog, dev, dev_gate, facilitator, asset, asset_address, faucet, wallets, product, prices{contact_agent_usd,listing_agent_usd} }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ provider, mode, network, chain_id, pay_to, homolog, dev, dev_gate, facilitator, asset, asset_address, faucet, wallets, product, prices{contact_agent_usd,listing_agent_usd} }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          }
        }
      }
    },
    "/api/contact": {
      "post": {
        "operationId": "contact",
        "summary": "Fala com o suporte: humano resolve Turnstile, agente paga $0.10 em x402.",
        "description": "O primeiro envio de agente é livre; depois o backoff é 60s dobrando até o teto de 1 hora, informado em `Retry-After`.\nDevolve: { ok }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Como chamar quem escreveu."
                  },
                  "email": {
                    "type": "string",
                    "description": "Para onde responder."
                  },
                  "message": {
                    "type": "string",
                    "description": "O que você quer dizer."
                  },
                  "form_ts": {
                    "type": "integer",
                    "description": "Momento em que o formulário abriu; é anti-robô do caminho humano."
                  }
                },
                "required": [
                  "name",
                  "email",
                  "message"
                ]
              },
              "example": {
                "name": "…",
                "email": "a@example.com",
                "message": "…",
                "form_ts": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "Campo obrigatório faltando."
          },
          "402": {
            "description": "Cota estourada. A resposta traz `accepts[]` (x402, USDC na Base): pague e repita a mesma chamada com `X-PAYMENT`."
          },
          "429": {
            "description": "Backoff de agente: espere o `Retry-After`."
          }
        }
      }
    },
    "/api/visit": {
      "post": {
        "operationId": "post_api_visit",
        "summary": "Ping da interface que incrementa a visita do dia. Agente não precisa chamar.",
        "description": "Smoke não conta: `X-MM-Smoke`, User-Agent `mm-smoke` ou `smoke: true` no corpo entram como `counted: false`.\nDevolve: { ok, counted, reason? }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "p": {
                    "type": "string",
                    "description": "Caminho da página visitada."
                  },
                  "smoke": {
                    "type": "boolean",
                    "description": "`true` marca a chamada como teste e ela não entra na contagem."
                  }
                }
              },
              "example": {
                "p": "/"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, counted, reason? }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true`."
                    },
                    "counted": {
                      "type": "boolean",
                      "description": "Se a visita entrou na contagem do dia."
                    },
                    "reason": {
                      "type": "string",
                      "description": "Por que não contou, quando `counted` é `false`."
                    }
                  },
                  "required": [
                    "ok",
                    "counted"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/metrics": {
      "get": {
        "operationId": "get_api_metrics",
        "summary": "Métricas dos últimos 7 dias. Com o token do operador, inclui os pagamentos.",
        "description": "Sem credencial devolve visitas, uso e contas. Com `METRICS_TOKEN` em Bearer acrescenta `payments` — e só em Base mainnet, porque número de homologação em painel financeiro engana.\nDevolve: { app, today, today_visits, today_contacts?, days, usage, accounts, financeiro?, payments? }",
        "security": [],
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer <METRICS_TOKEN>` para incluir o bloco financeiro."
          }
        ],
        "responses": {
          "200": {
            "description": "{ app, today, today_visits, today_contacts?, days, usage, accounts, financeiro?, payments? }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Metricas"
                }
              }
            }
          }
        }
      }
    },
    "/api/fila": {
      "get": {
        "operationId": "get_api_fila",
        "summary": "O retrato público da fila de enriquecimento: quanto do catálogo já foi apurado.",
        "description": "É público porque é sobre a saúde do catálogo, não sobre ninguém: só números agregados, nenhum registro identificável.\nDevolve: { ok, fila{total,concluidos,vencidos,com_falha,processados,estados,motivos} }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ ok, fila{total,concluidos,vencidos,com_falha,processados,estados,motivos} }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EstadoFila"
                }
              }
            }
          }
        }
      }
    },
    "/api/admin/fila": {
      "post": {
        "operationId": "post_api_admin_fila",
        "summary": "O robô de enriquecimento empurra aqui o retrato da própria fila.",
        "description": "Só a credencial do robô: isto não escreve no catálogo, então não há motivo para aceitar admin.\nDevolve: { ok }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "fila": {
                    "type": "object",
                    "description": "O retrato da fila: totais, estados e motivos."
                  }
                },
                "required": [
                  "fila"
                ]
              },
              "example": {
                "fila": {
                  "total": 20182,
                  "concluidos": 11675
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "Corpo sem `fila`."
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          }
        }
      }
    },
    "/api/admin/repos": {
      "post": {
        "operationId": "post_api_admin_repos",
        "summary": "O enriquecedor grava aqui o que apurou de um repositório: estrelas, forks, estado.",
        "description": "Aceita a credencial do enriquecedor OU a de admin — o robô tem a dele, de menor privilégio, e o admin entra para poder operar na mão sem depender do robô. O Worker não coleta nada: só grava o que já foi apurado fora dele.\nDevolve: { ok }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "repos": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "description": "Um item por repositório apurado, com estrelas, forks, PRs, `pushed_at` e estado."
                  }
                },
                "required": [
                  "repos"
                ]
              },
              "example": {
                "repos": [
                  {
                    "url": "https://github.com/x/y",
                    "stars": 120,
                    "repo_estado": "ativo"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "Corpo sem `repos` ou item malformado."
          },
          "401": {
            "description": "Nem credencial do enriquecedor nem de admin."
          }
        }
      }
    },
    "/api/admin/listings": {
      "get": {
        "operationId": "get_api_admin_listings",
        "summary": "A fila de moderação. Sem filtro, traz o que está pendente.",
        "description": "Aceita `ADMIN_TOKEN` em Bearer ou a sessão do `ADMIN_EMAIL`.\nDevolve: { items[{id,kind,category,name,tagline,body,url,status,origin,origin_id,install,source,transporte,ns,versao,oficial_status,repo_host,topico,stars,forks,prs_abertos,pushed_at,repo_estado,likes,comments,visits,created_at,updated_at,mine,api,go,comments_api}] }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "pending",
              "enum": [
                "pending",
                "live",
                "hidden",
                "blocked"
              ]
            },
            "description": "Qual estado listar."
          }
        ],
        "responses": {
          "200": {
            "description": "{ items[{id,kind,category,name,tagline,body,url,status,origin,origin_id,install,source,transporte,ns,versao,oficial_status,repo_host,topico,stars,forks,prs_abertos,pushed_at,repo_estado,likes,comments,visits,created_at,updated_at,mine,api,go,comments_api}] }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Anuncio"
                      },
                      "description": "Os registros naquele estado."
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          }
        }
      }
    },
    "/api/admin/listings/{id}": {
      "post": {
        "operationId": "post_api_admin_listings_by_id",
        "summary": "Decide o destino de um registro na fila: aprovar, esconder ou bloquear.",
        "description": "Devolve: { ok, status }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "description": "O que fazer com o registro."
                  }
                },
                "required": [
                  "action"
                ]
              },
              "example": {
                "action": "approve"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, status }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true`."
                    },
                    "status": {
                      "type": "string",
                      "description": "O estado em que o registro ficou."
                    }
                  },
                  "required": [
                    "ok",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`action` fora da lista."
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          },
          "404": {
            "description": "Recurso não existe (ou não é seu — a API não distingue os dois de propósito)."
          }
        }
      }
    },
    "/api/admin/carga": {
      "get": {
        "operationId": "get_api_admin_carga",
        "summary": "Estado das fontes de carga do catálogo: último lote, contagem e falhas.",
        "description": "Aceita `ADMIN_TOKEN` em Bearer ou a sessão do `ADMIN_EMAIL`.\nDevolve: { fontes, falhas, runs }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "fonte",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restringe a uma fonte, ex. `official_mcp`."
          }
        ],
        "responses": {
          "200": {
            "description": "{ fontes, falhas, runs }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Carga"
                }
              }
            }
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          }
        }
      },
      "post": {
        "operationId": "post_api_admin_carga",
        "summary": "Dispara um lote de carga ou marca um alerta de falha como visto.",
        "description": "Devolve: { ok }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "description": "O que fazer."
                  },
                  "fonte": {
                    "type": "string",
                    "description": "Qual fonte carregar ou marcar, ex. `official_mcp`."
                  }
                },
                "required": [
                  "action"
                ]
              },
              "example": {
                "action": "run",
                "fonte": "official_mcp"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "`action` fora da lista ou fonte desconhecida."
          },
          "401": {
            "description": "Sem credencial ou credencial inválida. Veja a auth deste endpoint."
          }
        }
      }
    },
    "/api/credito": {
      "post": {
        "operationId": "post_api_credito",
        "summary": "Recarrega crédito pré-pago: paga uma vez com x402 e recebe o token que desconta em qualquer API da casa.",
        "description": "Devolve: { token, saldo_usd, guarde, usar, saldo_em }",
        "security": [],
        "parameters": [
          {
            "name": "usd",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Pacote: 1, 5, 10 ou 25 dólares."
          }
        ],
        "responses": {
          "200": {
            "description": "{ token, saldo_usd, guarde, usar, saldo_em }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "Token portador do saldo (`cred_…`). Mostrado UMA vez — não há como recuperá-lo."
                    },
                    "saldo_usd": {
                      "type": "string",
                      "description": "Saldo creditado."
                    },
                    "guarde": {
                      "type": "string",
                      "description": "Aviso de que o token é o portador do crédito."
                    },
                    "usar": {
                      "type": "string",
                      "description": "Como apresentar o token nas rotas pagas."
                    },
                    "saldo_em": {
                      "type": "string",
                      "description": "Onde consultar saldo e extrato."
                    }
                  },
                  "required": [
                    "token",
                    "saldo_usd",
                    "guarde",
                    "usar",
                    "saldo_em"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Pacote fora da lista (1, 5, 10 ou 25)."
          },
          "402": {
            "description": "Sem pagamento — o corpo traz `accepts[]` do x402."
          }
        }
      },
      "get": {
        "operationId": "get_api_credito",
        "summary": "Saldo e extrato do crédito — as últimas movimentações, sem devolver o token.",
        "description": "Devolve: { saldo_micros, saldo_usd, criado_em, movimentos }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ saldo_micros, saldo_usd, criado_em, movimentos }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "saldo_micros": {
                      "type": "integer",
                      "description": "Saldo em micro-dólares (1e-6 USD)."
                    },
                    "saldo_usd": {
                      "type": "string",
                      "description": "Saldo formatado."
                    },
                    "criado_em": {
                      "type": "string",
                      "description": "Quando o crédito foi aberto."
                    },
                    "movimentos": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Entradas e saídas recentes, com produto e recurso."
                    }
                  },
                  "required": [
                    "saldo_micros",
                    "saldo_usd",
                    "criado_em",
                    "movimentos"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sem token ou token desconhecido."
          }
        }
      }
    }
  }
}