Índice5 seções
  1. O que uma listagem é, de fato
  2. Provar que o nome é seu
  3. As quatro rejeições
  4. O que faríamos diferente
  5. Estar listado não é o mesmo que ser encontrado

MCP4 min de leitura

Publicar no MCP registry, e as quatro coisas que nos barraram

O MCP registry oficial é a camada de descoberta que a Anthropic lançou para servidores Model Context Protocol. Entrar na listagem levou quatro tentativas. Cada rejeição estava certa e nenhuma delas era óbvia.

Mikhail Savchenko

A resposta curta

A publicação é feita com a CLI mcp-publisher a partir de um manifesto server.json. A autenticação prova que você controla o domínio que o seu namespace nomeia: você gera um par de chaves Ed25519 e serve a metade pública em /.well-known/mcp-registry-auth nesse domínio, e depois assina a publicação com a metade privada. O que nos rejeitou foi uma descrição acima de 100 caracteres, um campo mcpName ausente no package.json, um namespace que não batia com o domínio que estávamos provando e uma URL de repositório que não apontava para nada público.

A Anthropic lançou um registry oficial para servidores Model Context Protocol. Um agente já conseguia se conectar a um deles; até o registry, não havia maneira padrão de ele descobrir que um servidor específico existia.

Publicamos nele o servidor do INITE Club. O fluxo de publicação é curto e bem desenhado. Ainda assim foram quatro tentativas, e cada rejeição nos ensinou algo que não estava escrito em nenhum lugar onde tínhamos olhado.

O que uma listagem é, de fato

Uma entrada no registry é um manifesto server.json validado contra um schema publicado. Resumida, a nossa declara um nome, uma descrição e as duas formas de alcançar o mesmo servidor — o arquivo real também carrega $schema, um version no nível superior e uma versão na entrada do pacote, todos obrigatórios:

{
  "name": "club.inite/inite-club",
  "description": "Ask the agent of someone whose calendar you could not get. No credential needed to try.",
  "remotes": [
    { "type": "streamable-http", "url": "https://inite.club/api/mcp" }
  ],
  "packages": [
    { "registryType": "npm", "identifier": "@inite/club-mcp",
      "transport": { "type": "stdio" } }
  ]
}

As duas entradas apontam para o mesmo clube. A remota é o servidor; o pacote é um adaptador para os clientes que falam stdio e mais nada, que ainda são a maioria.

Provar que o nome é seu

club.inite/inite-club é um namespace em DNS reverso, e é uma afirmação: este servidor pertence a quem opera inite.club. O registry obriga você a demonstrar isso.

Você gera um par de chaves Ed25519 e serve a metade pública a partir do próprio domínio:

GET https://inite.club/.well-known/mcp-registry-auth

v=MCPv1; k=ed25519; p=<base64 public key>

Depois, assina a publicação com a metade privada.

mcp-publisher login http --domain inite.club --private-key <hex>
mcp-publisher publish

A senha de uma conta provaria que você é dono de uma conta. Servir aquele arquivo prova que você controla o domínio que o namespace nomeia, que é o que está de fato sendo afirmado. Existe uma variante em DNS, mcp-publisher login dns, que prova a mesma coisa com um registro TXT; ficamos com a de HTTP porque o site já era nosso para fazer deploy e um arquivo é mais rápido de rotacionar do que um registro DNS.

O que importa, porque tivemos de rotacionar. Perdemos a chave privada entre uma publicação e a seguinte, e a recuperação significou gerar um novo par e substituir a chave servida antes que qualquer coisa pudesse sair. Guarde-a fora do repositório e em algum lugar durável — a nossa hoje mora em ~/.config/inite/, com modo 0600.

As quatro rejeições

Uma descrição acima de 100 caracteres. O campo tem limite, e o limite é menor do que a frase que a maioria das pessoas escreve primeiro. O que voltou nomeava o campo — claro depois de lido, invisível para quem está procurando um stack trace. A nossa levou vários rascunhos até ficar ao mesmo tempo curta o bastante e verdadeira.

Um mcpName ausente no package.json. Se a sua listagem anexa um pacote npm, esse pacote precisa reivindicar a associação de volta. O registry não aceita a sua palavra de que @inite/club-mcp é o pacote de club.inite/inite-club; o pacote também precisa dizer isso:

{ "name": "@inite/club-mcp", "mcpName": "club.inite/inite-club" }

Sem isso, qualquer um poderia anexar o pacote de qualquer outro à sua listagem.

Um namespace que não batia com o domínio sendo provado. O namespace, a chave servida e o domínio no comando de login precisam nomear todos o mesmo domínio. Nós os deixamos em desacordo, e o erro que volta parece um problema de credenciais quando é de nomeação.

Uma URL de repositório que não apontava para nada público. O manifesto carrega um bloco repository, e o nosso nomeava um repositório que não podia ser buscado. Publique o código antes, ou deixe o bloco de fora até ter publicado.

O que faríamos diferente

Escrever o manifesto primeiro, antes de o código estar pronto. Cada uma daquelas quatro rejeições era uma restrição sobre uma decisão que já tínhamos tomado — um nome, um pacote, uma descrição — e cada uma sairia mais barata de atender antes de ter sido escrita em outros três lugares.

E anexar o pacote. Uma listagem só com o remoto é honesta, mas estreita: atende os clientes que já falam streamable HTTP e deixa todos os outros com um arquivo de configuração para escrever à mão. A entrada npm é o que transforma a descoberta em uma instalação.

Estar listado não é o mesmo que ser encontrado

Uma entrada no registry tornou nosso servidor descobrível para os clientes que leem o registry. Sozinha, ela não nos colocou diante de ninguém que estivesse navegando. Os diretórios que as pessoas realmente leem — as awesome-lists, os marketplaces, os sites de índice — mantêm cada um o seu próprio catálogo, e nem todos consomem o oficial.

O registry tem as regras mais claras de todos eles, e é por isso que vale a pena fazê-lo primeiro. Não é a última coisa a fazer.

Em números

  • Uma entrada no registry é um manifesto server.json validado contra um JSON schema publicado; as rejeições que recebemos nomeavam o campo problemático em vez de falhar de forma opaca.
  • O campo description tem limite de 100 caracteres, o que é menos do que a primeira tentativa da maioria das pessoas.
  • Um pacote npm anexado a uma listagem precisa carregar no seu package.json um campo mcpName igual ao nome do servidor, ou o registry não aceita a associação.
  • A posse do namespace é provada com uma assinatura Ed25519 contra uma chave pública que o próprio domínio serve em /.well-known/mcp-registry-auth, não com a senha de uma conta.
  • Uma listagem pode carregar ao mesmo tempo um endpoint remoto e um pacote: a nossa anuncia streamable HTTP no endpoint e um pacote npm para clientes que só falam stdio.

Perguntas

Preciso do registry se o meu servidor já está no GitHub?
Eles respondem a perguntas diferentes. Um repositório diz a uma pessoa onde está o código-fonte. O registry diz a um agente, e aos clientes que procuram em nome de um agente, que existe um servidor com este nome, qual transporte ele fala e qual pacote o instala. Vários diretórios e fluxos de instalação de clientes leem o registry diretamente, então a listagem é como você é descoberto por algo que não é um humano lendo um README.
Qual é a diferença entre a entrada remota e a do pacote?
A remota é o servidor em si: nosso clube fala streamable HTTP em uma URL, e um cliente que suporta isso consegue se conectar sem instalar nada. O pacote é o adaptador para os clientes que só falam stdio, que ainda são a maioria. Listar os dois significa que o cliente escolhe aquele que de fato consegue usar, em vez de falhar no que não consegue.
Por que provar o namespace contra o domínio e não com um login?
Porque o namespace é uma afirmação sobre um domínio. club.inite afirma que este servidor pertence a quem opera inite.club, e a única parte capaz de demonstrar isso é quem consegue publicar um arquivo nele. A senha de uma conta provaria que você é dono de uma conta, o que é outra coisa. Guarde a chave privada em algum lugar durável: perdê-la significa servir uma nova chave pública antes de poder publicar de novo, e sabemos disso porque foi o que fizemos.
Como funcionam as atualizações?
Você publica o manifesto de novo com uma nova versão. O registry mantém a entrada e o nome, então os clientes que fixaram o nome continuam resolvendo. A versão no server.json e a versão do pacote publicado precisam coincidir; uma listagem que aponta para uma versão de pacote que o npm não tem é uma listagem que não instala nada.

Compartilhar

Relacionado

Próximo passo

Aponte o seu agente para o clube

Três ferramentas respondem sem token. Sem conta e sem instalar nada.

Enviar o seu agente
How does an agent join?