Publicar un servidor MCP en npm y PyPI paso a paso
Publica un mismo servidor MCP en ambos registros para que cualquier cliente lo inicie con npx o uvx. Configura package.json y pyproject.toml, comprueba el tarball, prueba en MCP Inspector, publica con trusted publishing en GitHub Actions y añade herramientas de imagen y video mediante una API.
Has creado un servidor MCP y funciona en tu equipo portátil. Ahora un compañero, o un desconocido en internet, quiere tenerlo funcionando con una sola línea de configuración: sin git clone, sin paso de compilación y sin el hilo de "¿qué versión de Node usas?". Eso es exactamente lo que te da un registro. Publica en npm y los clientes iniciarán tu servidor con npx. Publica en PyPI y lo iniciarán con uvx. Si haces ambas cosas, cualquier cliente MCP, desde Claude Desktop hasta Cursor o VS Code, puede lanzar tu servidor con solo un nombre de paquete.
Este recorrido sigue el camino en orden: estructura del repositorio, package.json, pyproject.toml, pruebas locales, la primera publicación manual y un flujo de trabajo de GitHub Actions que envía ambos paquetes desde una sola etiqueta. Todos los pasos parten de un servidor stdio que ya funciona en local.
💡 Antes de empezar: necesitas Node 18 o superior para npm, o Python 3.10 o superior para PyPI, además de cuentas gratuitas en npmjs.com y pypi.org. Activa la autenticación de dos factores en ambas, ya que los dos registros la exigen a quien publique.
Por qué publicar en ambos registros
La mayoría de los servidores MCP empiezan en un solo lenguaje, normalmente TypeScript o Python, y se quedan ahí. Eso funciona hasta que alguien con otra pila tecnológica quiere probar el tuyo. Un equipo de datos en Python no instalará Node para ejecutar una herramienta, y un equipo de front-end no montará un entorno virtual. Publicar en ambos registros elimina esa excusa.
Dos públicos, un solo servidor
Así se comparan las dos vías, una al lado de la otra:
npm
PyPI
Comando de ejecución
npx -y your-package
uvx your-package
SDK oficial
@modelcontextprotocol/sdk
mcp (incluye FastMCP)
Manifiesto
package.json
pyproject.toml
Qué se sube
Un tarball generado a partir de dist/
Un archivo de código fuente más una wheel
Comando de publicación
npm publish
uv publish o twine upload
Autenticación en CI
Publicación de confianza o token granular
Publicación de confianza o token de API
La configuración más limpia es una implementación por lenguaje con un único contrato de herramientas compartido. Los nombres de las herramientas, los esquemas de entrada y las descripciones son idénticos en ambos paquetes, así que un prompt que funciona con la versión de npm se comporta igual con la de PyPI. Guarda ese contrato en un pequeño archivo JSON dentro del repositorio y haz que el CI compare ambas compilaciones con él.
Resiste el atajo de un envoltorio fino en Python que invoca npx como proceso externo. Funciona hasta que el usuario no tiene Node instalado, y entonces falla con un error que nadie entiende de un vistazo.
Elige la estructura del paquete
Un único repositorio con dos carpetas mantiene sencilla la publicación:
Cada carpeta es un paquete independiente con su propio manifiesto. El flujo de publicación usa después working-directory para compilarlos por separado, así que nada se filtra de un lado al otro.
Ponle nombre una vez y compruébalo dos veces
Elige un nombre y úsalo en ambos registros. Los usuarios lo recordarán y los resultados de búsqueda coincidirán.
npm: en minúsculas, apto para URL y sin espacios. Un nombre con ámbito como @yourscope/my-mcp-server evita colisiones y funciona bien para servidores MCP.
PyPI: los nombres no distinguen entre mayúsculas y minúsculas, y tratan -, _ y . como el mismo carácter, así que My_MCP.Server y my-mcp-server colisionan.
Disponibilidad:npm view my-mcp-server devuelve un 404 cuando el nombre está libre. En PyPI, abre pypi.org/project/my-mcp-server/ y un 404 significa lo mismo.
💡 Comprueba ambos nombres antes de escribir el README alrededor de uno. Descubrir el día de la publicación que el nombre ya está tomado cuesta una tarde de renombrado.
Escribe un README que funcione como documentación
Ambos registros muestran tu README como página del paquete, y para muchos usuarios es la única documentación que leen. Incluye cuatro cosas, en este orden:
Una frase que explique qué hace el servidor
Una configuración de cliente para copiar y pegar para npx y otra para uvx
Una tabla de herramientas con una línea por cada una
Todas las variables de entorno que lee el servidor, marcadas como obligatorias u opcionales
Publica el paquete de npm
Configura package.json
Tres campos deciden si npx funciona en absoluto: bin, files y el shebang de tu archivo de entrada.
{
"name": "@yourscope/my-mcp-server",
"version": "0.1.0",
"description": "MCP server that does one useful thing",
"type": "module",
"bin": { "my-mcp-server": "dist/index.js" },
"files": ["dist", "README.md", "LICENSE"],
"engines": { "node": ">=18" },
"scripts": {
"build": "tsc",
"prepublishOnly": "npm run build"
},
"dependencies": { "@modelcontextprotocol/sdk": "^1.0.0" },
"license": "MIT"
}
bin asocia el nombre del comando con el archivo de entrada compilado. Sin él, npx no tiene nada que ejecutar.
files es una lista blanca. Solo dist/, el README y la licencia entran en el tarball.
prepublishOnly recompila justo antes de cada publicación, así que nunca envías salidas desactualizadas.
El shebang#!/usr/bin/env node debe ser la primera línea de src/index.ts. TypeScript lo conserva en el archivo compilado.
Revisa el tarball antes de enviarlo
Ejecuta npm pack --dry-run y lee la lista de archivos que muestra. Quieres ver dist/, el README, la licencia y package.json. No quieres .env, archivos de prueba, mapas de origen que no pensabas compartir ni un node_modules suelto.
💡 Un .env filtrado es el error más común en una primera publicación, y una versión publicada no se puede retirar. La lista blanca de files es tu red de seguridad, así que mantenla.
Haz la primera publicación
npm login
npm publish --access public
Los paquetes con ámbito son privados por defecto, por eso --access public importa en la primera publicación. Introduce tu código de dos factores cuando te lo pidan. Luego comprueba que funciona desde otra carpeta:
cd $(mktemp -d)
npx -y @yourscope/my-mcp-server
El proceso debería arrancar y quedarse esperando la entrada por stdin. Ese silencio es correcto, porque un servidor stdio solo habla cuando un cliente le escribe.
Publica el paquete de PyPI
Escribe pyproject.toml
El empaquetado en Python se define en un solo archivo. Esta versión usa hatchling como backend de compilación:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "my-mcp-server"
version = "0.1.0"
description = "MCP server that does one useful thing"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "MIT" }
dependencies = ["mcp>=1.0"]
[project.scripts]
my-mcp-server = "my_mcp_server.server:main"
La tabla [project.scripts] equivale a bin. Crea un comando al instalar. Si el nombre del script coincide con el del paquete, uvx my-mcp-server funciona sin más. Si es distinto, ejecuta uvx --from my-mcp-server script-name.
Un servidor mínimo que usa FastMCP del SDK oficial de Python:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-mcp-server")
@mcp.tool()
def ping() -> str:
"""Return pong so clients can confirm the server is alive."""
return "pong"
def main() -> None:
mcp.run() # stdio transport by default
Compila y revisa los archivos
cd python
uv build
uvx twine check dist/*
uv build escribe dos archivos en dist/: un archivo de código fuente (.tar.gz) y una wheel (.whl). Las wheels se instalan rápido porque no hay nada que compilar, por eso uvx y pip las prefieren. twine check confirma que el README se muestra bien como página del paquete, así detectas metadatos rotos antes de que lo haga PyPI.
Prueba en TestPyPI y luego publica
TestPyPI es un sitio aparte, con cuentas y tokens propios. Existe para que una primera subida fallida no te cueste nada.
El índice adicional es importante. Tus dependencias, incluido mcp, están en el PyPI real, y TestPyPI no las tiene. Cuando la instalación de prueba funcione, publica de verdad:
uv publish --token <pypi-token>
uvx my-mcp-server
💡 Las versiones son permanentes en ambos registros. PyPI nunca acepta el mismo nombre de archivo dos veces, ni siquiera después de un borrado, y npm no permite reutilizar un número de versión publicado. Si algo sale mal, sube la versión y vuelve a publicar.
Prueba antes de publicar
Ejecútalo en MCP Inspector
MCP Inspector abre una página web local donde puedes listar herramientas, rellenar argumentos y leer las respuestas sin procesar. Apúntalo primero a la salida compilada y después al paquete tal como lo ejecutaría un usuario:
El segundo comando es el más importante. Ejercita el paquete instalado en lugar de tu árbol de trabajo, así que los archivos que faltan y los puntos de entrada incorrectos aparecen aquí y no en un informe de error.
Mantén stdout limpio
En stdio, stdout transporta el protocolo. Un console.log() o un print() suelto inyecta texto en el flujo JSON-RPC y el cliente se desconecta con un error de análisis. Envía cada línea de registro a stderr: console.error() en Node, y print(..., file=sys.stderr) o el módulo logging en Python.
Prueba la configuración del cliente
Esta es la configuración que pegarán tus usuarios. Prueba ambas entradas en un cliente real:
Ejecuta la lista de comprobación previa a la publicación desde una terminal limpia en una carpeta temporal. Una instalación global o un node_modules cercano pueden ocultar un archivo que falta durante semanas.
npm pack --dry-run lista solo lo que pretendes enviar
twine check dist/* pasa sin advertencias
Inspector lista todas las herramientas a través de npx y de uvx
Nada escribe en stdout salvo mensajes del protocolo
Los bloques de configuración del README coinciden con lo que acabas de probar
Automatiza y versiona las publicaciones
Publicación de confianza, sin tokens almacenados
Ambos registros permiten que un flujo de trabajo de GitHub Actions publique mediante OpenID Connect. Registras una vez tu repositorio y el archivo del flujo en el lado del registro, y a partir de ahí el registro confía en ese flujo exacto. No hay ningún token de larga duración en los secretos de tu repositorio, así que no hay nada que filtrar ni rotar. El flujo solo necesita el permiso id-token: write.
En PyPI, añade un publicador de confianza en la configuración de publicación del proyecto. Un proyecto nuevo puede usar un publicador pendiente, así que la primera versión también puede salir desde CI. En npm, añade el publicador de confianza en la configuración del paquete. Las pantallas de configuración cambian de vez en cuando, así que sigue los pasos que muestren en ese momento.
Una etiqueta, dos publicaciones
Al subir una etiqueta como v0.1.0 se activan ambos trabajos en paralelo:
El paso npm install -g npm@latest asegura que la CLI de npm sea lo bastante reciente para la publicación de confianza. Añade un paso needs: con tu prueba si quieres que una compilación fallida bloquee la publicación.
Semver que los clientes respetan
Los clientes MCP y los agentes que los usan dependen de tus nombres de herramientas y esquemas de entrada, así que trátalos como tu API pública:
Cambio
Aumento de versión
Corregir un error, sin cambios en el esquema
Parche (0.1.1)
Añadir una herramienta nueva o un argumento opcional
Menor (0.2.0)
Renombrar o eliminar una herramienta, o añadir un argumento obligatorio
Mayor (1.0.0)
Actualiza ambos manifiestos al mismo número en un solo commit y luego etiquétalo. Un script sencillo que edite package.json y pyproject.toml a la vez evita el desajuste típico en el que npm está en 1.2.0 y PyPI en 1.1.0. Los usuarios que quieran estabilidad pueden fijar una versión mayor en su configuración, por ejemplo @yourscope/my-mcp-server@1.
Una vez que ambos paquetes estén publicados, también puedes registrar el servidor en el MCP Registry oficial. Verifica la propiedad leyendo un campo mcpName en package.json y una línea mcp-name: que coincida en el README de PyPI, y después publica los metadatos con el comando mcp-publisher. El registro sigue evolucionando, así que consulta su documentación actual antes de depender del formato exacto.
Añade herramientas de imagen y video
Un servidor publicado se vuelve más útil en cuanto puede crear algo. La generación de imágenes y de video son las herramientas más pedidas, y la API de PicassoIA las hace una adición corta. La URL base es https://api.picassoia.com/v1, la autenticación es un token Bearer que empieza por pia_sk_, y los trabajos son asíncronos: creas una predicción, la consultas repetidamente y luego lees el resultado.
Consulta la documentación de la API para los campos de entrada exactos de cada modelo, porque la forma de output y los parámetros aceptados cambian de un modelo a otro.
Pasa el token a través de la configuración del cliente. Nunca incrustes un token en el paquete. Léelo desde el entorno y deja que cada usuario lo configure en su configuración MCP:
💡 Revisa los requisitos de plan actuales en la página de la API de PicassoIA antes de prometer uso gratuito en tu README. La redacción de los precios puede cambiar, y tus usuarios te pedirán cuentas por lo que escribiste.
Redacta las notas de la versión con un modelo de lenguaje. Un modelo de lenguaje puede encargarse de la tarea que hace que la gente se salte los changelogs. Este es un flujo rápido con Claude Sonnet 5:
Ejecuta git log v0.1.0..HEAD --oneline y copia la salida.
Abre la página del modelo en PicassoIA y pega el registro con una instrucción de una línea: agrupa los cambios en Añadido, Modificado y Corregido, con lenguaje sencillo.
Indícale qué cambios afectan a los nombres de herramientas o a los esquemas, para que se marquen como cambios incompatibles.
Revisa el resultado contra el diff y pégalo en la release de GitHub.
Para una segunda opinión sobre tu pyproject.toml o tu archivo de flujo de trabajo, GPT 5.6 Sol es un buen revisor para tareas de programación.
Pruébalo tú mismo en Picasso IA
Tu página de paquete merece una imagen principal de verdad y un clip de demostración corto, no una captura de una terminal. Crea la imagen principal con Picasso IA Image, pule los detalles con Picasso IA Image Editor Pro y luego anima el último fotograma en un clip corto con Picasso IA Video.
Un primer experimento sencillo:
Escribe un prompt de 40 palabras que describa un escritorio de programador tranquilo con luz de mañana
Genera tres variaciones y quédate con la más nítida
Guárdala como imagen principal en tu README
Anímala para el anuncio de la versión
Explora todos los modelos disponibles en picassoia.com/en/all-models, elige el que mejor encaje con tu estilo y publica algo que merezca la pena abrir. Tu primera versión está a una etiqueta de distancia.