guias·Fabricio Telles

Como Publicar ai-catalog.json: Torne Seus Agentes Descobríveis

Guia prático para criar e hospedar um ai-catalog.json — o manifesto que permite que agentes de IA descubram seus serviços em runtime.

Como Publicar ai-catalog.json: Torne Seus Agentes Descobríveis

Você construiu um MCP server, publicou uma skill, expôs uma API com capacidades de IA. Mas como um agente descobre que seu serviço existe? A resposta, desde junho de 2026, é o ai-catalog.json — um manifesto padronizado que funciona como o DNS do ecossistema de agentes. Você hospeda, agentes descobrem. Sem cadastro em marketplace, sem intermediários.

O ai-catalog.json é para agentes o que o robots.txt foi para crawlers: um contrato público que diz “aqui estão minhas capacidades, é assim que você se conecta”. A diferença é que, em vez de dizer o que não acessar, você declara o que pode ser usado.

Pré-requisitos

Antes de criar seu catálogo, você precisa ter algo para expor. O ai-catalog.json é um manifesto — ele descreve o que existe, não cria funcionalidade. Você precisa de pelo menos um destes:

  • Um MCP server rodando (local ou remoto)
  • Uma skill publicada em repositório
  • Um agente A2A com endpoint acessível
  • Uma API que agentes podem consumir

Se você ainda não tem nenhum desses, volte quando tiver. O catálogo vem depois da implementação.

Anatomia do ai-catalog.json

O manifesto tem uma estrutura enxuta: um objeto host que identifica quem publica, e um array entries com os serviços disponíveis.

{
  "specVersion": "1.0",
  "host": {
    "displayName": "Nome do Publicador"
  },
  "entries": []
}

Esse é o esqueleto mínimo válido. Vamos detalhar cada parte.

O objeto host

O host descreve quem está publicando o catálogo. Apenas displayName é obrigatório — o resto é opcional mas recomendado para catálogos em produção.

CampoObrigatórioDescrição
displayNameSimNome legível do publicador
identifierNãoDID ou domínio do publicador
documentationUrlNãoURL da documentação geral
logoUrlNãoURL do logotipo
trustManifestNãoManifesto de confiança para verificação

Cada entry no array

Cada item em entries representa um serviço, agente ou skill que você expõe. Quatro campos são obrigatórios:

CampoObrigatórioDescrição
identifierSimURN único no formato urn:air:<publisher>:<namespace>:<agent-name>
displayNameSimNome legível para humanos e agentes
typeSimMedia type do serviço
url ou dataSim (um dos dois)Referência externa OU dados inline — mutuamente exclusivos

Os campos opcionais adicionam contexto para melhorar o discovery:

CampoDescrição
descriptionTexto livre sobre o que o serviço faz
tagsArray de tags para categorização
capabilitiesCapacidades específicas do serviço
representativeQueries2-5 exemplos de perguntas que o serviço responde
versionVersão semântica do serviço
updatedAtTimestamp ISO 8601 da última atualização
metadataObjeto livre para dados extras
trustManifestManifesto de confiança específico da entry

O formato URN

O identificador segue o padrão AIR (Agent Identifier Registry):

urn:air:<publisher>:<namespace>:<agent-name>

Exemplos reais:

  • urn:air:hf.co:alice-dev:weather-agent — publicado no Hugging Face
  • urn:air:github.com:meu-user:minha-skill — publicado no GitHub
  • urn:air:meudominio.com:tools:api-converter — publicado em domínio próprio

Media types válidos

O campo type define que tipo de serviço a entry representa:

Media TypePara quê
application/mcp-server-card+jsonMCP servers
application/a2a-agent-card+jsonAgentes A2A (Agent-to-Agent)
application/ai-skillSkills de agentes
application/ai-catalog+jsonCatálogos nested (federação)
application/ai-registry+jsonRegistries dinâmicos

Passo 1: Criar o Manifesto

Vamos a três exemplos práticos cobrindo os casos mais comuns.

Exemplo 1: MCP server com dados inline

Se seu MCP server é simples e você quer que os metadados fiquem direto no catálogo, use o campo data:

{
  "specVersion": "1.0",
  "host": {
    "displayName": "Alice's AI Tools",
    "documentationUrl": "https://alice-dev.github.io/docs"
  },
  "entries": [
    {
      "identifier": "urn:air:hf.co:alice-dev:weather-agent",
      "displayName": "Weather Agent",
      "type": "application/mcp-server-card+json",
      "description": "Simple weather lookup using open data",
      "tags": ["weather", "open-data", "lookup"],
      "representativeQueries": [
        "What's the weather in São Paulo?",
        "Current temperature in Berlin",
        "Will it rain tomorrow in Tokyo?"
      ],
      "data": {
        "name": "Weather Agent",
        "description": "Simple weather lookup using open data",
        "tools": [
          {
            "name": "get_weather",
            "description": "Get current weather for a city",
            "inputSchema": {
              "type": "object",
              "properties": {
                "city": { "type": "string" }
              },
              "required": ["city"]
            }
          }
        ]
      }
    }
  ]
}

O campo data contém a estrutura completa do MCP server card — nome, descrição e lista de tools com seus schemas. Agentes que encontram esse catálogo já sabem exatamente como invocar seu server.

Exemplo 2: Skill referenciada por URL

Se sua skill vive em um repositório e você não quer duplicar metadados, use url:

{
  "specVersion": "1.0",
  "host": {
    "displayName": "Alice's AI Tools"
  },
  "entries": [
    {
      "identifier": "urn:air:github.com:alice-dev:pptx-creator",
      "displayName": "pptx-creator",
      "type": "application/ai-skill",
      "url": "https://github.com/alice-dev/pptx-creator",
      "description": "Create professional PowerPoint presentations following brand guidelines.",
      "tags": ["presentations", "pptx", "automation"],
      "representativeQueries": [
        "Create a slide deck for my quarterly review",
        "Generate a pitch presentation with 10 slides"
      ],
      "version": "2.1.0",
      "updatedAt": "2026-07-15T10:00:00Z"
    }
  ]
}

Com url, o agente sabe onde buscar os detalhes completos. Você mantém o catálogo leve e a fonte da verdade no repositório.

Exemplo 3: Agente A2A com endpoint

Para um agente que aceita comunicação Agent-to-Agent:

{
  "specVersion": "1.0",
  "host": {
    "displayName": "Minha Empresa IA",
    "identifier": "minhaempresa.com.br",
    "logoUrl": "https://minhaempresa.com.br/logo.png"
  },
  "entries": [
    {
      "identifier": "urn:air:minhaempresa.com.br:agents:tradutor-juridico",
      "displayName": "Tradutor Jurídico PT-EN",
      "type": "application/a2a-agent-card+json",
      "url": "https://api.minhaempresa.com.br/.well-known/agent.json",
      "description": "Traduz documentos jurídicos entre português e inglês com terminologia especializada.",
      "tags": ["translation", "legal", "pt-br", "en"],
      "capabilities": ["document-translation", "terminology-glossary"],
      "representativeQueries": [
        "Traduza este contrato para inglês",
        "Qual a tradução jurídica de 'cláusula resolutória'?",
        "Revise a tradução deste parecer"
      ],
      "version": "1.0.0",
      "updatedAt": "2026-07-20T14:30:00Z"
    }
  ]
}

Note que url e data são mutuamente exclusivos — use um ou outro, nunca os dois na mesma entry.

Catálogo com múltiplas entries

Na prática, você vai ter mais de um serviço. Basta adicionar ao array:

{
  "specVersion": "1.0",
  "host": {
    "displayName": "DevTools Corp",
    "identifier": "devtools.io",
    "documentationUrl": "https://docs.devtools.io"
  },
  "entries": [
    {
      "identifier": "urn:air:devtools.io:mcp:code-reviewer",
      "displayName": "Code Reviewer",
      "type": "application/mcp-server-card+json",
      "url": "https://devtools.io/.well-known/mcp/code-reviewer.json",
      "description": "Automated code review with security focus",
      "representativeQueries": [
        "Review this pull request for security issues",
        "Find potential SQL injection vulnerabilities"
      ]
    },
    {
      "identifier": "urn:air:devtools.io:skills:test-generator",
      "displayName": "Test Generator",
      "type": "application/ai-skill",
      "url": "https://github.com/devtools-io/test-generator",
      "description": "Generate comprehensive test suites from source code",
      "representativeQueries": [
        "Generate unit tests for this module",
        "Create integration tests for the auth flow"
      ]
    }
  ]
}

Passo 2: Hospedar em /.well-known/

O local canônico para o manifesto é /.well-known/ai-catalog.json. É o primeiro lugar que agentes vão procurar — a mesma convenção que .well-known/security.txt ou .well-known/openid-configuration.

O Content-Type deve ser application/json.

Nginx

Adicione ao bloco server do seu domínio:

location /.well-known/ai-catalog.json {
    alias /var/www/meusite/ai-catalog.json;
    default_type application/json;
    add_header Access-Control-Allow-Origin "*";
    add_header Cache-Control "public, max-age=3600";
}

O header CORS (Access-Control-Allow-Origin: *) é importante — agentes fazem requisições de domínios diferentes. O Cache-Control de 1 hora é um bom default; ajuste conforme a frequência de atualização do seu catálogo.

Cloudflare Pages

Se seu site está no Cloudflare Pages, crie o arquivo na pasta public/.well-known/:

public/
└── .well-known/
    └── ai-catalog.json

Adicione um _headers na raiz do public/ para configurar os headers:

/.well-known/ai-catalog.json
  Content-Type: application/json
  Access-Control-Allow-Origin: *
  Cache-Control: public, max-age=3600

O Cloudflare Pages serve arquivos estáticos da pasta public/ sem necessidade de configuração de rotas.

Vercel

No Vercel, coloque o arquivo em public/.well-known/ai-catalog.json e adicione ao vercel.json:

{
  "headers": [
    {
      "source": "/.well-known/ai-catalog.json",
      "headers": [
        { "key": "Content-Type", "value": "application/json" },
        { "key": "Access-Control-Allow-Origin", "value": "*" },
        { "key": "Cache-Control", "value": "public, max-age=3600" }
      ]
    }
  ]
}

Confirmação rápida

Depois do deploy, teste com curl:

curl -I https://seudominio.com/.well-known/ai-catalog.json

Você deve ver 200 OK com Content-Type: application/json. Se vir 404, o arquivo não está no path certo. Se vir 301/302, garanta que o redirect final chega no JSON.

Passo 3: Adicionar Sinais de Discovery

O well-known URI é o mecanismo primário, mas existem sinais complementares que aumentam a probabilidade de agentes encontrarem seu catálogo. Pense neles como redundância — cada um cobre um cenário diferente de discovery.

robots.txt

Adicione a diretiva Agentmap ao seu robots.txt:

User-agent: *
Allow: /

Agentmap: https://seudominio.com/.well-known/ai-catalog.json

Crawlers de agentes que respeitam o robots.txt vão encontrar seu catálogo mesmo sem acessar o well-known path diretamente. Funciona de forma análoga ao Sitemap: que você já usa para SEO.

No <head> das suas páginas (especialmente a homepage), adicione:

<link rel="ai-catalog" href="/.well-known/ai-catalog.json">

Agentes que renderizam ou parsam HTML vão detectar essa tag. É útil quando o agente está navegando seu site e precisa descobrir o catálogo a partir de qualquer página.

DNS Service Binding

Para discovery no nível de infraestrutura, sem depender de HTTP:

_catalog._agents.seudominio.com.  IN  SVCB  1 seudominio.com. (
    alpn="h2,h3" path="/.well-known/ai-catalog.json"
)

O record DNS _catalog._agents permite que agentes descubram seu catálogo via resolução DNS antes mesmo de fazer uma requisição HTTP. É o mecanismo mais robusto, mas também o mais avançado — implemente apenas se já tem familiaridade com records SRV/SVCB.

Qual usar?

Para a maioria dos casos, o well-known URI + robots.txt + HTML link cobrem 99% dos cenários. O DNS é para quem quer cobertura total em ambientes enterprise ou redes privadas.

Passo 4: Validar com Ferramentas Oficiais

Antes de considerar o deploy feito, valide a estrutura do seu manifesto.

Validação de schema com ajv-cli

O repositório do ai-catalog standard inclui schemas JSON Schema que você pode usar diretamente:

npx ajv-cli validate \
  -s spec/schemas/ai-catalog.schema.json \
  -d ./ai-catalog.json

Se o manifesto estiver correto, a saída será limpa. Se houver erros — campo obrigatório faltando, tipo errado, URL e data na mesma entry — o ajv mostra exatamente onde está o problema.

Teste de conformance completo

Para uma verificação mais rigorosa que vai além da estrutura (testa resolução de URLs, formatos de URN, media types válidos):

./conformance/bin/conformance-test manifest ./ai-catalog.json

Esse teste é mais pesado — faz requisições reais para URLs declaradas e valida que respondem corretamente. Use em staging antes de publicar.

Validação manual rápida

Se quiser apenas checar se o JSON é válido e tem a estrutura básica:

cat ai-catalog.json | python3 -m json.tool > /dev/null && echo "JSON válido"

E uma checagem rápida dos campos obrigatórios:

cat ai-catalog.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
assert 'specVersion' in data, 'Falta specVersion'
assert 'host' in data, 'Falta host'
assert 'displayName' in data['host'], 'Falta host.displayName'
assert 'entries' in data, 'Falta entries'
for i, e in enumerate(data['entries']):
    assert 'identifier' in e, f'Entry {i}: falta identifier'
    assert 'displayName' in e, f'Entry {i}: falta displayName'
    assert 'type' in e, f'Entry {i}: falta type'
    assert ('url' in e) != ('data' in e), f'Entry {i}: precisa de url OU data (não ambos, não nenhum)'
print(f'✓ Válido: {len(data[\"entries\"])} entries')
"

Passo 5: Testar com Hugging Face Discover

O Hugging Face Discover é um search engine que indexa ai-catalog.json de domínios públicos. É a forma mais rápida de verificar se seu catálogo está acessível e bem formatado “do lado de fora”.

Faça um POST para o endpoint de busca:

curl -X POST https://huggingface-hf-discover.hf.space/search \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "text": "nome-do-seu-servico"
    },
    "pageSize": 5
  }'

Se seu catálogo já foi indexado, a resposta vai incluir suas entries. Se não aparecer nada, o indexador ainda não passou pelo seu domínio — pode levar algumas horas após a primeira publicação.

Como funciona o discovery no HF

O Hugging Face mantém um crawler que periodicamente busca /.well-known/ai-catalog.json em domínios conhecidos. O próprio HF publica o seu em:

https://huggingface.co/.well-known/ai-catalog.json

O Discover também expõe um endpoint MCP para que outros agentes possam buscar programaticamente:

https://huggingface-hf-discover.hf.space/mcp

Isso significa que um agente conectado ao MCP do HF Discover pode buscar seus serviços em runtime — exatamente o cenário que o ai-catalog.json resolve.

Teste end-to-end

Para simular o que um agente faria ao encontrar seu catálogo:

# 1. Busca o catálogo
curl -s https://seudominio.com/.well-known/ai-catalog.json | jq '.entries[].displayName'

# 2. Verifica se o HF Discover encontra
curl -s -X POST https://huggingface-hf-discover.hf.space/search \
  -H "Content-Type: application/json" \
  -d '{"query": {"text": "weather agent"}, "pageSize": 3}' | jq '.results[:3]'

Se os dois passos funcionam, seu catálogo está operacional.

Boas Práticas para representativeQueries

O campo representativeQueries é opcional, mas é o que mais impacta a qualidade do discovery. Agentes usam essas queries para decidir se seu serviço é relevante para o que o usuário pediu. Pense nelas como os exemplos de invocação de uma skill — quanto mais realistas, melhor o matching.

Regras de ouro

2 a 5 queries por entry. Menos que 2 não dá contexto suficiente. Mais que 5 dilui a relevância.

Use linguagem natural, não comandos técnicos. O agente vai comparar a query do usuário com seus exemplos. Usuários não dizem “invoke get_weather with params city=SP” — dizem “como está o tempo em São Paulo?”.

// ❌ Ruim — linguagem de desenvolvedor
"representativeQueries": [
  "call get_weather API",
  "fetch weather data for location"
]

// ✓ Bom — linguagem de usuário
"representativeQueries": [
  "What's the weather in São Paulo?",
  "Will it rain tomorrow in New York?",
  "Current temperature in Berlin"
]

Cubra cenários diferentes, não variações da mesma pergunta. Cada query deve representar um caso de uso distinto.

// ❌ Ruim — três formas de dizer a mesma coisa
"representativeQueries": [
  "Translate this document",
  "Can you translate this?",
  "Please translate"
]

// ✓ Bom — três cenários diferentes
"representativeQueries": [
  "Translate this contract to English",
  "What's the legal term for 'cláusula resolutória' in English?",
  "Review the translation accuracy of this legal brief"
]

Inclua o idioma que seus usuários realmente usam. Se seu serviço atende brasileiros, queries em português. Se é global, misture.

Seja específico sobre o domínio. “Analyze this” não ajuda. “Analyze the SEO performance of this landing page” ajuda muito.

Próximos Passos: trustManifest para Enterprise

Para uso pessoal ou projetos open-source, o catálogo básico é suficiente. Mas em contextos enterprise — onde agentes precisam decidir se confiam no seu serviço — existe o trustManifest.

O trust manifest é um objeto que pode aparecer tanto no host (nível do publicador) quanto em cada entry (nível do serviço). Ele declara informações verificáveis sobre identidade e procedência.

Estrutura do trustManifest

CampoObrigatórioDescrição
identitySimIdentificador verificável (DID, domínio, org ID)
identityTypeNãoTipo do identificador (e.g., “domain”, “did:web”)
attestationsNãoArray de atestações de terceiros
provenanceNãoArray de informações de procedência
signatureNãoAssinatura criptográfica do manifesto

Exemplo prático

{
  "specVersion": "1.0",
  "host": {
    "displayName": "Empresa Verificada Ltda",
    "identifier": "did:web:empresa-verificada.com.br",
    "trustManifest": {
      "identity": "did:web:empresa-verificada.com.br",
      "identityType": "did:web",
      "attestations": [
        {
          "type": "domain-verification",
          "verifier": "cloudflare.com",
          "issuedAt": "2026-06-01T00:00:00Z"
        }
      ],
      "provenance": [
        {
          "type": "source-repository",
          "url": "https://github.com/empresa-verificada/ai-services"
        }
      ]
    }
  },
  "entries": []
}

O trust manifest permite que agentes enterprise apliquem políticas de confiança — “só conectar a serviços com domínio verificado”, “exigir assinatura criptográfica”, “aceitar apenas atestações de CAs conhecidas”. Na prática, é o que separa um ecossistema de “confia em tudo” de um que opera com critério.

Para começar, publique sem trust manifest. Quando seus consumidores forem organizações com políticas de segurança, adicione. O padrão é progressivo — funciona sem, melhora com.

Resumo do Fluxo

Cinco passos para tornar seus serviços descobríveis:

  1. Crie o ai-catalog.json com host + entries
  2. Hospede em /.well-known/ai-catalog.json com CORS habilitado
  3. Sinalize via robots.txt, HTML link tag e (opcionalmente) DNS
  4. Valide com ajv-cli ou conformance test
  5. Teste com Hugging Face Discover

O ai-catalog.json é a primitiva mais simples do ecossistema de interoperabilidade de agentes. Um arquivo JSON estático, sem auth, sem SDK, sem vendor lock-in. Publique o seu, teste com o HF Discover, e veja agentes encontrarem seus serviços sem que você precise fazer mais nada.