Transforma tu server en un paquete profesional de Python: instalable, distribuible y listo para publicar.
Tener un MCP Server funcionando como script es excelente para el desarrollo, pero para distribuirlo, versionarlo y permitir que otros usen tu trabajo, necesitas transformarlo en un paquete profesional de Python. Empaquetar significa crear una estructura que permita instalar con pip o uv, declarar dependencias, definir versiones y compartir con la comunidad de forma confiable.
Empaquetar tu MCP Server significa transformarlo de un script suelto en un proyecto Python con una estructura formal: metadatos declarados en el pyproject.toml, dependencias explícitas, versionado semántico y un entry point que permite ejecutar el server con un solo comando en la terminal. Un paquete se puede instalar con pip install o uv pip install, distribuido a través de PyPI o GitHub y versionado con tags Git. La diferencia entre un script suelto y un paquete profesional es la diferencia entre un prototipo y un producto.
uv pip install .
Piensa en el empaquetado como poner tu server en una caja profesional con manual de instrucciones. Sin la caja, tienes piezas sueltas que solo tú sabes cómo ensamblar. Con la caja, cualquiera puede instalar y usar todo con un solo comando. Empieza con uv init para crear la estructura básica automáticamente.
O pyproject.toml es el archivo central de cualquier proyecto moderno de Python. Reemplaza los antiguos setup.py e setup.cfg con una sintaxis declarativa clara, estandarizada por PEP 621. Aquí defines el nombre del paquete, la versión, la descripción, las dependencias y cómo debe construirse el paquete.
Un pyproject.toml típico para un MCP Server:
[project] name = "meu-mcp-server" version = "0.1.0" description = "MCP Server para consulta de dados" requires-python = ">=3.10" dependencies = [ "fastmcp>=0.1.0", "httpx>=0.25", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project.scripts] meu-server = "meu_server.server:main"
La sección [project] define metadatos, [build-system] define cómo construir y [project.scripts] crea comandos ejecutables en la terminal.
Usa hatchling como build backend: es moderno, rápido y funciona perfectamente con uv. Si prefieres, setuptools también funciona, pero hatchling es la opción recomendada para proyectos nuevos. Nunca más escribas un setup.py — o pyproject.toml es el estándar oficial desde PEP 621.
La PEP 621 (2021) estandarizó los metadatos de proyectos Python en pyproject.toml. Desde entonces, más de 85% de los paquetes nuevos publicados en PyPI adoptaron este formato. El setup.py se considera legado: funciona, pero no se recomienda para proyectos nuevos. Herramientas modernas como uv, hatch e rye trabajan de forma nativa con pyproject.toml.
La organización de directorios de un paquete de Python sigue convenciones que facilitan la creación, las pruebas y la distribución. El patrón src layout es el enfoque recomendado para evitar problemas de importación y garantizar que las pruebas se ejecuten contra el paquete instalado, no contra el código fuente local.
Estructura recomendada para un MCP Server empaquetado:
meu-mcp-server/ ├── pyproject.toml ├── README.md ├── src/ │ └── meu_server/ │ ├── __init__.py │ └── server.py └── tests/ └── test_server.py
El directorio src/ aísla el código fuente. El __init__.py marca el directorio como paquete de Python. El server.py contiene la lógica del servidor MCP con la función main().
Crear el directorio raíz del proyecto e inicializarlo con uv init meu-mcp-server.
Crear src/meu_server/ con __init__.py y mover el código del servidor a server.py.
Crear directorio tests/ y configurar el pyproject.toml con metadatos, dependencias y entry points.
Los entry points son la magia que permite transformar tu MCP Server en un comando ejecutable en la terminal. En lugar de ejecutar python -m meu_server.server, el usuario simplemente escribe meu-server y se ejecuta el paquete. Esto se configura en la sección [project.scripts] del pyproject.toml.
La configuración del entry point en pyproject.toml:
[project.scripts] meu-server = "meu_server.server:main"
Esto le dice a Python: "cuando alguien escriba meu-server en la terminal, ejecuta la función main() del archivo server.py dentro del paquete meu_server." Después de la instalación, el comando queda disponible globalmente en el entorno virtual activo.
Asegúrate de que tu función main() en el server.py llama mcp.run(). Esta es la función que se invocará cuando el usuario ejecute el comando en la terminal. Prueba siempre con uv pip install -e . (modo editable) durante el desarrollo, así los cambios en el código se reflejan de inmediato sin necesidad de reinstalar.
El nombre del comando (antes de =) usa guiones: meu-server. La ruta del módulo (después de =) usa underscores: meu_server.server:main. Mezclar ambos es un error común que resulta en ModuleNotFoundError. Recuerda: guiones en la terminal, underscores en Python.
Declarar correctamente las dependencias es esencial para que tu paquete funcione en cualquier máquina. En pyproject.toml, enumeras todas las bibliotecas que necesita tu server, con restricciones de versión que garantizan la compatibilidad sin bloquear las actualizaciones. El uv lock genera un lockfile que fija las versiones exactas para garantizar la reproducibilidad.
Las dependencias se declaran en la sección [project] con versionado semántico (semver):
dependencies = [
"fastmcp>=0.1.0", # mínimo 0.1.0
"httpx>=0.25,<1.0", # entre 0.25 e 1.0
"pydantic>=2.0", # mínimo 2.0
]
Versionado semántico (versionado semántico) usa el formato MAJOR.MINOR.PATCH: MAJOR cambia cuando hay breaking changes, MINOR para nuevas features, PATCH para correcciones de errores. Usa >= para valores mínimos y < para límites de compatibilidad.
fastmcp==0.1.0httpxuv lock para generar un lockfile>=0.1.0,<1.0uv lock y confirmar con un commit el uv.lock
Según los análisis de PyPI, 23% de los problemas de instalación de paquetes de Python se deben a dependencias mal especificadas: versiones incompatibles, dependencias faltantes o restricciones demasiado estrictas. El uso de archivos de bloqueo (uv.lock) reduce los fallos de reproducibilidad en más de 90%, lo que garantiza que todos los desarrolladores y entornos de producción usen exactamente las mismas versiones.
Antes de distribuir tu paquete, necesitas verificar que funcione cuando esté instalado — no solo cuando se ejecuta directamente desde el código fuente. Esto significa construir el paquete con uv build, instalar el archivo .whl generado y probar que el entry point funcione correctamente.
El proceso de build genera archivos distribuibles en la carpeta dist/:
# Construir o pacote $ uv build Successfully built meu_server-0.1.0-py3-none-any.whl # Instalar o .whl gerado $ uv pip install dist/meu_server-0.1.0-py3-none-any.whl # Testar o entry point $ meu-server MCP Server rodando...
El archivo .whl (wheel) es el formato estándar de distribución de Python. Contiene tu código, metadatos e información de entry points — todo lo que pip/uv necesita para instalarlo correctamente.
Ejecutar uv build y verificar que el .whl se genere sin errores en la carpeta dist/.
Crear un entorno virtual limpio e instalar el .whl con uv pip install — simula una instalación real.
Ejecutar el comando del entry point y verificar que el server se inicie correctamente y responda a solicitudes MCP.
Siempre prueba en un entorno virtual limpio — no en el mismo entorno donde desarrollaste. Esto garantiza que todas las dependencias estén declaradas correctamente en el pyproject.toml. Si algo funciona en tu entorno de desarrollo, pero falla en uno limpio, significa que olvidaste declarar una dependencia.
Siguiente Módulo: 5.2 — Publicando en GitHub