# 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.

Source: https://agentify.ia.br/blog/como-publicar-ai-catalog-json/

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.

 Campo
 Obrigatório
 Descrição

 displayName
 Sim
 Nome legível do publicador

 identifier
 Não
 DID ou domínio do publicador

 documentationUrl
 Não
 URL da documentação geral

 logoUrl
 Não
 URL do logotipo

 trustManifest
 Não
 Manifesto 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:

 Campo
 Obrigatório
 Descrição

 identifier
 Sim
 URN único no formato urn:air:<publisher>:<namespace>:<agent-name>

 displayName
 Sim
 Nome legível para humanos e agentes

 type
 Sim
 Media type do serviço

 url ou data
 Sim (um dos dois)
 Referência externa OU dados inline — mutuamente exclusivos

Os campos opcionais adicionam contexto para melhorar o discovery:

 Campo
 Descrição

 description
 Texto livre sobre o que o serviço faz

 tags
 Array de tags para categorização

 capabilities
 Capacidades específicas do serviço

 representativeQueries
 2-5 exemplos de perguntas que o serviço responde

 version
 Versão semântica do serviço

 updatedAt
 Timestamp ISO 8601 da última atualização

 metadata
 Objeto livre para dados extras

 trustManifest
 Manifesto 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 Type
 Para quê

 application/mcp-server-card+json
 MCP servers

 application/a2a-agent-card+json
 Agentes A2A (Agent-to-Agent)

 application/ai-skill
 Skills de agentes

 application/ai-catalog+json
 Catálogos nested (federação)

 application/ai-registry+json
 Registries 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.

### HTML link tag

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”.

### Verificar se o HF já tem seu catálogo

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

 Campo
 Obrigatório
 Descrição

 identity
 Sim
 Identificador verificável (DID, domínio, org ID)

 identityType
 Não
 Tipo do identificador (e.g., “domain”, “did:web”)

 attestations
 Não
 Array de atestações de terceiros

 provenance
 Não
 Array de informações de procedência

 signature
 Não
 Assinatura 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:

- **Crie** o `ai-catalog.json` com host + entries

- **Hospede** em `/.well-known/ai-catalog.json` com CORS habilitado

- **Sinalize** via robots.txt, HTML link tag e (opcionalmente) DNS

- **Valide** com ajv-cli ou conformance test

- **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.

-->
