Transforme seu server em um pacote Python profissional — instalável, distribuível e pronto para publicação.
Ter um MCP Server funcionando como script é ótimo para desenvolvimento, mas para distribuir, versionar e permitir que outros usem seu trabalho, você precisa transformá-lo em um pacote Python profissional. Empacotar significa criar uma estrutura que permite instalar com pip ou uv, declarar dependências, definir versões e compartilhar com a comunidade de forma confiável.
Empacotar seu MCP Server significa transformá-lo de um script solto em um projeto Python com estrutura formal: metadados declarados no pyproject.toml, dependências explícitas, versionamento semântico e um entry point que permite rodar o server com um único comando no terminal. Um pacote pode ser instalado com pip install ou uv pip install, distribuído via PyPI ou GitHub, e versionado com tags Git. A diferença entre um script solto e um pacote profissional é a diferença entre um protótipo e um produto.
uv pip install .
Pense no empacotamento como colocar seu server numa caixa profissional com manual de instruções. Sem a caixa, você tem peças soltas que só você sabe montar. Com a caixa, qualquer pessoa pode instalar e usar com um único comando. Comece com uv init para criar a estrutura básica automaticamente.
O pyproject.toml é o arquivo central de qualquer projeto Python moderno. Ele substitui os antigos setup.py e setup.cfg com uma sintaxe declarativa limpa, padronizada pela PEP 621. É aqui que você define o nome do pacote, versão, descrição, dependências e como o pacote deve ser construído.
Um pyproject.toml típico para um 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"
A seção [project] define metadados, [build-system] define como construir e [project.scripts] cria comandos executáveis no terminal.
Use hatchling como build backend — é moderno, rápido e funciona perfeitamente com uv. Se preferir, setuptools também funciona, mas o hatchling é a escolha recomendada para projetos novos. Nunca mais escreva um setup.py — o pyproject.toml é o padrão oficial desde a PEP 621.
A PEP 621 (2021) padronizou os metadados de projetos Python no pyproject.toml. Desde então, mais de 85% dos novos pacotes publicados no PyPI adotaram esse formato. O setup.py é considerado legado — funciona, mas não é recomendado para projetos novos. Ferramentas modernas como uv, hatch e rye trabalham nativamente com pyproject.toml.
A organização de diretórios de um pacote Python segue convenções que facilitam a construção, os testes e a distribuição. O padrão src layout é a abordagem recomendada para evitar problemas de importação e garantir que os testes rodem contra o pacote instalado, não contra o código-fonte local.
Estrutura recomendada para um MCP Server empacotado:
meu-mcp-server/ ├── pyproject.toml ├── README.md ├── src/ │ └── meu_server/ │ ├── __init__.py │ └── server.py └── tests/ └── test_server.py
O diretório src/ isola o código-fonte. O __init__.py marca o diretório como pacote Python. O server.py contém a lógica do MCP Server com a função main().
Criar o diretório raiz do projeto e inicializar com uv init meu-mcp-server.
Criar src/meu_server/ com __init__.py e mover o código do server para server.py.
Criar diretório tests/ e configurar o pyproject.toml com metadados, dependências e entry points.
Entry points são a mágica que permite transformar seu MCP Server em um comando executável no terminal. Em vez de rodar python -m meu_server.server, o usuário simplesmente digita meu-server e o pacote é executado. Isso é configurado na seção [project.scripts] do pyproject.toml.
A configuração de entry point no pyproject.toml:
[project.scripts] meu-server = "meu_server.server:main"
Isso diz ao Python: "quando alguém digitar meu-server no terminal, execute a função main() do arquivo server.py dentro do pacote meu_server." Após a instalação, o comando fica disponível globalmente no ambiente virtual ativo.
Certifique-se de que sua função main() no server.py chama mcp.run(). É essa função que será invocada quando o usuário rodar o comando no terminal. Teste sempre com uv pip install -e . (modo editável) durante o desenvolvimento — assim, alterações no código são refletidas imediatamente sem reinstalar.
O nome do comando (antes do =) usa hifens: meu-server. O caminho do módulo (depois do =) usa underscores: meu_server.server:main. Misturar os dois é um erro comum que resulta em ModuleNotFoundError. Lembre-se: hifens no terminal, underscores no Python.
Declarar dependências corretamente é essencial para que seu pacote funcione em qualquer máquina. No pyproject.toml, você lista todas as bibliotecas que seu server precisa, com restrições de versão que garantem compatibilidade sem travar atualizações. O uv lock gera um lockfile que fixa as versões exatas para reprodutibilidade.
Dependências são declaradas na seção [project] com versionamento 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
]
Semver (versionamento semântico) usa o formato MAJOR.MINOR.PATCH: MAJOR muda quando há breaking changes, MINOR para novas features, PATCH para correções de bugs. Use >= para mínimos e < para tetos de compatibilidade.
fastmcp==0.1.0httpxuv lock para gerar lockfile>=0.1.0,<1.0uv lock e commitar o uv.lock
Segundo análises do PyPI, 23% dos problemas de instalação de pacotes Python são causados por dependências mal especificadas — versões incompatíveis, dependências faltando ou restrições muito rígidas. O uso de lockfiles (uv.lock) reduz falhas de reprodutibilidade em mais de 90%, garantindo que todos os desenvolvedores e ambientes de produção usem exatamente as mesmas versões.
Antes de distribuir seu pacote, você precisa verificar que ele funciona quando instalado — não apenas quando rodado diretamente do código-fonte. Isso significa construir o pacote com uv build, instalar o arquivo .whl gerado e testar que o entry point funciona corretamente.
O processo de build gera arquivos distribuíveis na pasta 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...
O arquivo .whl (wheel) é o formato padrão de distribuição Python. Ele contém seu código, metadados e informações de entry points — tudo que o pip/uv precisa para instalar corretamente.
Rodar uv build e verificar que o .whl é gerado sem erros na pasta dist/.
Criar um ambiente virtual limpo e instalar o .whl com uv pip install — simula uma instalação real.
Executar o comando do entry point e verificar que o server inicia corretamente e responde a requisições MCP.
Sempre teste em um ambiente virtual limpo — não no mesmo onde você desenvolveu. Isso garante que todas as dependências estão corretamente declaradas no pyproject.toml. Se algo funciona no seu ambiente de dev mas falha no limpo, significa que você esqueceu de declarar uma dependência.
Próximo Módulo: 5.2 — Publicando no GitHub