Índice5 secciones
MCP4 min de lectura
Publicar en el registro MCP, y las cuatro cosas que nos frenaron
El registro oficial de MCP es la capa de descubrimiento que Anthropic publicó para los servidores del Model Context Protocol. Aparecer en él nos costó cuatro intentos. Cada rechazo era correcto y ninguno era evidente.
Mikhail Savchenko
La respuesta corta
Se publica con la CLI mcp-publisher contra un manifiesto server.json. La autenticación demuestra que controlas el dominio que nombra tu namespace: generas un par de claves Ed25519, sirves la mitad pública desde /.well-known/mcp-registry-auth en ese dominio y luego firmas la publicación con la mitad privada. Lo que nos rechazó fue una descripción de más de 100 caracteres, un campo mcpName ausente en package.json, un namespace que no coincidía con el dominio que estábamos demostrando y una URL de repositorio que no apuntaba a ningún sitio público.
Anthropic publicó un registro oficial para los servidores del Model Context Protocol. Un agente ya podía conectarse a uno; hasta el registro no había forma estándar de que se enterara de que un servidor concreto existía.
Publicamos allí el servidor de INITE Club. El flujo de publicación es corto y está bien diseñado. Aun así nos costó cuatro intentos, y cada rechazo nos enseñó algo que no estaba escrito en ninguno de los sitios que habíamos mirado.
Qué es en realidad una ficha
Una entrada del registro es un manifiesto server.json validado contra un esquema publicado. Abreviada, la nuestra declara un nombre, una descripción y las dos formas de llegar al mismo servidor; el archivo real lleva además $schema, una version de primer nivel y una versión en la entrada del paquete, y todo ello es obligatorio:
{
"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" } }
]
}
Las dos entradas apuntan al mismo club. El remote es el servidor; el paquete es un adaptador para los clientes que hablan stdio y nada más, que siguen siendo la mayoría.
Demostrar que el nombre es tuyo
club.inite/inite-club es un namespace de DNS inverso, y es una afirmación: este servidor pertenece a quien lleva inite.club. El registro te obliga a demostrarlo.
Generas un par de claves Ed25519 y sirves la mitad pública desde el propio dominio:
GET https://inite.club/.well-known/mcp-registry-auth
v=MCPv1; k=ed25519; p=<base64 public key>
Luego firmas la publicación con la mitad privada.
mcp-publisher login http --domain inite.club --private-key <hex>
mcp-publisher publish
La contraseña de una cuenta demostraría que tienes una cuenta. Servir ese archivo demuestra que controlas el dominio que el namespace nombra, que es lo que de verdad se está afirmando. Hay una variante por DNS, mcp-publisher login dns, que demuestra lo mismo con un registro TXT; nosotros tomamos la de HTTP porque el sitio ya era nuestro para desplegar y un archivo se rota más rápido que un registro DNS.
Lo cual importa, porque tuvimos que rotarlo. Perdimos la clave privada entre una publicación y la siguiente, y recuperarse significó generar un par nuevo y sustituir la clave servida antes de poder entregar nada. Guárdala fuera del repositorio y en un sitio duradero: la nuestra vive ahora en ~/.config/inite/ con modo 0600.
Los cuatro rechazos
Una descripción de más de 100 caracteres. El campo tiene un tope, y el tope es más corto que la frase que casi todo el mundo escribe primero. Lo que volvió nombraba el campo: claro una vez leído, invisible si estás buscando un stack trace. La nuestra necesitó varios borradores hasta ser a la vez lo bastante corta y cierta.
Un mcpName ausente en package.json. Si tu ficha adjunta un paquete npm, ese paquete tiene que reclamar la asociación de vuelta. El registro no se fía de tu palabra de que @inite/club-mcp es el paquete de club.inite/inite-club; el paquete tiene que decirlo también:
{ "name": "@inite/club-mcp", "mcpName": "club.inite/inite-club" }
Sin eso, cualquiera podría adjuntar el paquete de cualquiera a su propia ficha.
Un namespace que no coincidía con el dominio que se estaba demostrando. El namespace, la clave servida y el dominio en el comando de login tienen que nombrar todos el mismo dominio. Nosotros los tuvimos en desacuerdo, y el error que vuelve se lee como un problema de credenciales cuando es de nombres.
Una URL de repositorio que no apuntaba a ningún sitio público. El manifiesto lleva un bloque repository, y el nuestro nombraba un repositorio que no se podía descargar. Publica el código primero, o deja el bloque fuera hasta que lo hayas hecho.
Qué haríamos distinto
Escribir el manifiesto primero, antes de terminar el código. Cada uno de esos cuatro rechazos era una restricción sobre una decisión que ya habíamos tomado —un nombre, un paquete, una descripción— y cada una salía más barata de satisfacer antes de quedar escrita en otros tres sitios.
Y adjuntar el paquete. Una ficha con solo un remote es honesta pero estrecha: sirve a los clientes que ya hablan streamable HTTP y deja a todos los demás con un archivo de configuración que escribir a mano. La entrada de npm es lo que convierte el descubrimiento en una instalación.
Aparecer en el registro no es lo mismo que ser encontrado
Una entrada en el registro hizo que nuestro servidor fuera descubrible para los clientes que leen el registro. No nos puso, por sí sola, delante de nadie que estuviera mirando. Los directorios que la gente lee de verdad —las awesome-lists, los marketplaces, los sitios índice— mantienen cada uno su propio catálogo, y no todos consumen el oficial.
El registro tiene las reglas más claras de todos ellos, y por eso merece la pena hacerlo primero. No es lo último que hay que hacer.
En cifras
- Una entrada del registro es un manifiesto server.json validado contra un esquema JSON publicado; los rechazos que recibimos nombraban el campo infractor en vez de fallar de forma opaca.
- El campo description tiene un tope de 100 caracteres, menos de lo que ocupa el primer intento de casi cualquiera.
- Un paquete npm adjunto a una ficha tiene que llevar un campo mcpName en su package.json que coincida con el nombre del servidor, o el registro no aceptará la asociación.
- La propiedad del namespace se demuestra con una firma Ed25519 contra una clave pública que sirve el propio dominio en /.well-known/mcp-registry-auth, no con la contraseña de una cuenta.
- Una ficha puede llevar a la vez un endpoint remoto y un paquete: la nuestra anuncia streamable HTTP en el endpoint y un paquete npm para los clientes que solo hablan stdio.
Preguntas
- ¿Necesito el registro si mi servidor ya está en GitHub?
- Responden a preguntas distintas. Un repositorio le dice a una persona dónde está el código. El registro le dice a un agente, y a los clientes que buscan por cuenta de un agente, que existe un servidor con este nombre, qué transporte habla y qué paquete lo instala. Varios directorios y flujos de instalación de clientes leen el registro directamente, así que la ficha es la forma de que te descubra algo que no es un humano leyendo un README.
- ¿Qué diferencia hay entre la entrada remota y la del paquete?
- El remote es el servidor en sí: nuestro club habla streamable HTTP en una URL, y un cliente que lo soporte puede conectarse sin instalar nada. El paquete es el adaptador para los clientes que solo hablan stdio, que siguen siendo la mayoría. Listar los dos significa que un cliente elige el que de verdad puede usar en vez de fallar con el que no.
- ¿Por qué demostrar el namespace contra el dominio y no con un login?
- Porque el namespace es una afirmación sobre un dominio. club.inite sostiene que este servidor pertenece a quien lleva inite.club, y la única parte que puede demostrarlo es quien puede publicar un archivo en él. La contraseña de una cuenta demostraría que tienes una cuenta, que es otra cosa. Guarda la clave privada en un sitio duradero: perderla significa servir una clave pública nueva antes de poder publicar otra vez, y lo sabemos porque nos pasó.
- ¿Cómo funcionan las actualizaciones?
- Publicas el manifiesto otra vez con una versión nueva. El registro conserva la entrada y el nombre, así que los clientes que fijaron el nombre siguen resolviendo. La versión en server.json y la versión del paquete publicado tienen que coincidir; una ficha que apunta a una versión de paquete que npm no tiene es una ficha que no instala nada.