Publicar um servidor MCP no npm e no PyPI passo a passo
Publique um servidor MCP nos dois registros para que qualquer cliente o inicie com npx ou uvx. Configure package.json e pyproject.toml, verifique o tarball, teste no MCP Inspector, publique com publicação confiável no GitHub Actions e adicione ferramentas de imagem e vídeo por meio de uma API.
Você criou um servidor MCP e ele roda no seu notebook. Agora um colega, ou um desconhecido na internet, quer que ele funcione com uma única linha de configuração: sem git clone, sem etapa de build, sem a conversa do tipo "qual versão do Node você está usando?". Isso é exatamente o que um registro oferece. Publique no npm e os clientes iniciam seu servidor com npx. Publique no PyPI e eles o iniciam com uvx. Faça as duas coisas e qualquer cliente MCP, do Claude Desktop ao Cursor e ao VS Code, consegue executar seu servidor apenas com um nome de pacote.
Este tutorial segue o caminho em ordem: estrutura do repositório, package.json, pyproject.toml, testes locais, a primeira publicação manual e um workflow do GitHub Actions que envia os dois pacotes a partir de uma única tag. Todos os passos partem do princípio de que você tem um servidor stdio que já roda localmente.
💡 Antes de começar: você precisa do Node 18+ para o npm ou do Python 3.10+ para o PyPI, além de contas gratuitas no npmjs.com e no pypi.org. Ative a autenticação em duas etapas nas duas, já que ambos os registros a exigem de quem publica.
Por que publicar nos dois registros
A maioria dos servidores MCP começa em uma linguagem, geralmente TypeScript ou Python, e permanece nela. Isso funciona até que alguém de outro ambiente queira testar o seu. Uma equipe de dados em Python não vai instalar Node para rodar uma ferramenta, e uma equipe de front-end não vai montar um virtualenv. Publicar nos dois registros elimina essa desculpa.
Dois públicos, um servidor
Veja como os dois caminhos se comparam lado a lado:
npm
PyPI
Comando de execução
npx -y your-package
uvx your-package
SDK oficial
@modelcontextprotocol/sdk
mcp (inclui FastMCP)
Manifesto
package.json
pyproject.toml
O que é enviado
Um tarball gerado a partir de dist/
Um arquivo de código-fonte mais uma wheel
Comando de publicação
npm publish
uv publish ou twine upload
Autenticação no CI
Publicação confiável ou token granular
Publicação confiável ou token de API
A configuração mais limpa é uma implementação por linguagem com um único contrato de ferramentas compartilhado. Nomes de ferramentas, schemas de entrada e descrições ficam idênticos nos dois pacotes, então um prompt que funciona com a versão do npm se comporta da mesma forma com a versão do PyPI. Mantenha esse contrato em um pequeno arquivo JSON no repositório e faça o CI comparar as duas builds com ele.
Resista ao atalho de um wrapper Python simples que chama npx por linha de comando. Ele funciona até que o usuário não tenha o Node instalado, e então falha com um erro que ninguém consegue entender de relance.
Escolha a estrutura do pacote
Um único repositório com duas pastas mantém o processo de lançamento simples:
Cada pasta é um pacote próprio, com seu próprio manifesto. O workflow de lançamento usa depois working-directory para gerar cada um separadamente, para que nada vaze de um lado para o outro.
Defina o nome uma vez, confira duas vezes
Escolha um nome e use-o nos dois registros. Os usuários se lembram dele, e os resultados de busca ficam alinhados.
npm: minúsculas, seguro para URL, sem espaços. Um nome com escopo como @yourscope/my-mcp-server evita colisões e funciona bem para servidores MCP.
PyPI: os nomes não diferenciam maiúsculas de minúsculas e tratam -, _ e . como o mesmo caractere, então My_MCP.Server e my-mcp-server entram em conflito.
Disponibilidade:npm view my-mcp-server retorna um 404 quando o nome está livre. No PyPI, abra pypi.org/project/my-mcp-server/ e um 404 significa o mesmo.
💡 Verifique os dois nomes antes de escrever o README em torno de um deles. Descobrir no dia da publicação que o nome já está em uso custa uma tarde de renomeação.
Escreva um README que funcione como documentação
Os dois registros exibem seu README como página do pacote, e para muitos usuários ele é a única documentação que leem. Coloque quatro coisas nele, nesta ordem:
Uma frase sobre o que o servidor faz
Uma configuração de cliente para copiar e colar para npx e outra para uvx
Uma tabela de ferramentas com uma linha para cada uma
Todas as variáveis de ambiente que o servidor lê, marcadas como obrigatórias ou opcionais
Publique o pacote npm
Configure o package.json
Três campos determinam se npx funciona de fato: bin, files e o shebang no arquivo 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 associa o nome do comando ao arquivo de entrada compilado. Sem ele, npx não tem nada para executar.
files é uma lista de permissões. Apenas dist/, o README e a licença entram no tarball.
prepublishOnly recompila logo antes de cada publicação, então você nunca envia uma saída desatualizada.
O shebang#!/usr/bin/env node precisa ser a primeira linha de src/index.ts. O TypeScript mantém essa linha no arquivo compilado.
Confira o tarball antes de enviar
Execute npm pack --dry-run e leia a lista de arquivos que ele imprime. Você quer ver dist/, o README, a licença e package.json. Você não quer ver .env, fixtures de teste, source maps que não pretendia compartilhar, nem um node_modules perdido.
💡 Um .env vazado é o erro mais comum em uma primeira publicação, e uma versão publicada não pode ser retirada. A lista de permissões files é sua rede de segurança, então mantenha-a.
Faça a primeira publicação
npm login
npm publish --access public
Pacotes com escopo são privados por padrão, por isso --access public é importante na primeira publicação. Digite o código da autenticação em duas etapas quando for solicitado. Depois, comprove que funciona a partir de outra pasta:
cd $(mktemp -d)
npx -y @yourscope/my-mcp-server
O processo deve iniciar e aguardar entrada no stdin. Esse silêncio é o comportamento correto, porque um servidor stdio só fala quando um cliente se comunica com ele.
Publique o pacote PyPI
Escreva o pyproject.toml
O empacotamento em Python é feito em um único arquivo. Esta versão usa hatchling como backend de build:
[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"
A tabela [project.scripts] equivale a bin. Ela cria um comando na instalação. Se o nome do script for igual ao nome do pacote, uvx my-mcp-server funciona direto. Se for diferente, execute uvx --from my-mcp-server script-name.
Um servidor mínimo usando FastMCP do 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
Gere e verifique os arquivos
cd python
uv build
uvx twine check dist/*
uv build grava dois arquivos em dist/: um arquivo de código-fonte (.tar.gz) e uma wheel (.whl). As wheels instalam rápido porque nada precisa ser compilado, e por isso uvx e pip as preferem. twine check confirma que o README é exibido corretamente como página do pacote, assim você descobre metadados quebrados antes que o PyPI os descubra.
Teste no TestPyPI e depois publique
O TestPyPI é um site separado, com contas e tokens separados. Ele existe para que um primeiro envio com falha não custe nada.
O índice extra é importante. Suas dependências, incluindo mcp, ficam no PyPI real, e o TestPyPI não as tem. Quando a instalação de teste funcionar, publique de verdade:
uv publish --token <pypi-token>
uvx my-mcp-server
💡 As versões são permanentes nos dois registros. O PyPI nunca aceita o mesmo nome de arquivo duas vezes, mesmo depois de uma exclusão, e o npm recusa reutilizar um número de versão já publicado. Quando algo estiver errado, aumente a versão e publique de novo.
Teste antes de enviar
Execute no MCP Inspector
O MCP Inspector abre uma página web local onde você lista ferramentas, preenche argumentos e lê as respostas brutas. Aponte-o para a saída gerada e depois para o pacote exatamente como um usuário o executaria:
O segundo comando é o mais importante. Ele testa o pacote instalado, e não a sua pasta de trabalho, então arquivos ausentes e pontos de entrada errados aparecem aqui e não em um relatório de bug.
Mantenha o stdout limpo
No stdio, o stdout transporta o protocolo. Um console.log() ou print() perdido injeta texto no fluxo JSON-RPC, e o cliente se desconecta com um erro de parse. Envie cada linha de log para o stderr: console.error() no Node, e print(..., file=sys.stderr) ou o módulo logging em Python.
Teste a configuração do cliente
Esta é a configuração que seus usuários vão colar. Teste as duas entradas em um cliente real:
Execute a checklist pré-publicação a partir de um shell limpo em uma pasta temporária. Uma instalação global ou um node_modules próximo pode esconder um arquivo ausente por semanas.
npm pack --dry-run lista apenas o que você pretende enviar
twine check dist/* passa sem avisos
O Inspector lista todas as ferramentas por meio de npx e por meio de uvx
Nada escreve no stdout além de mensagens do protocolo
Os blocos de configuração do README correspondem ao que você acabou de testar
Automatize e versione os lançamentos
Publicação confiável, sem tokens armazenados
Os dois registros permitem que um workflow do GitHub Actions publique por meio de OpenID Connect. Você registra o repositório e o arquivo do workflow no lado do registro uma única vez, e a partir daí o registro confia naquele workflow exato. Nenhum token de longa duração fica nos segredos do repositório, então não há nada para vazar ou rotacionar. O workflow só precisa da permissão id-token: write.
No PyPI, adicione um publicador confiável nas configurações de publicação do projeto. Um projeto novo pode usar um publicador pendente, para que a primeira versão também possa vir do CI. No npm, adicione o publicador confiável nas configurações do pacote. As telas de configuração mudam de tempos em tempos, então siga os passos atuais.
Uma tag, duas publicações
Enviar uma tag como v0.1.0 dispara os dois jobs em paralelo:
A etapa npm install -g npm@latest garante que a CLI do npm seja recente o bastante para a publicação confiável. Adicione uma etapa needs: com seu job de testes se quiser que uma build com falha bloqueie o lançamento.
Semver que os clientes respeitam
Os clientes MCP e os agentes por trás deles dependem dos nomes das ferramentas e dos schemas de entrada, então trate-os como sua API pública:
Mudança
Incremento de versão
Corrigir um bug, sem alterar o schema
Patch (0.1.1)
Adicionar uma nova ferramenta ou um argumento opcional
Minor (0.2.0)
Renomear ou remover uma ferramenta, ou adicionar um argumento obrigatório
Major (1.0.0)
Atualize os dois manifestos para o mesmo número em um único commit e, depois, crie a tag. Um pequeno script que edita package.json e pyproject.toml juntos evita o desencontro clássico em que o npm está em 1.2.0 e o PyPI em 1.1.0. Quem quer estabilidade pode fixar a versão major na configuração, por exemplo @yourscope/my-mcp-server@1.
Depois que os dois pacotes estiverem no ar, você também pode listar o servidor no MCP Registry oficial. Ele verifica a propriedade lendo um campo mcpName em package.json e uma linha mcp-name: correspondente no README do PyPI, depois publica os metadados com o comando mcp-publisher. O registro ainda está evoluindo, então leia a documentação atual antes de depender do formato exato.
Adicione ferramentas de imagem e vídeo
Um servidor publicado fica mais útil no momento em que consegue gerar algo. Geração de imagens e vídeos são as ferramentas mais pedidas, e a API do PicassoIA torna a inclusão rápida. A URL base é https://api.picassoia.com/v1, a autenticação é um token Bearer que começa com pia_sk_, e os trabalhos são assíncronos: você cria uma predição, consulta o status e depois lê o resultado.
Consulte a documentação da API para os campos de entrada exatos de cada modelo, porque o formato de output e os parâmetros aceitos mudam de um modelo para outro.
Passe o token pela configuração do cliente. Nunca embuta um token no pacote. Leia-o do ambiente e deixe cada usuário defini-lo na própria configuração MCP:
💡 Leia os requisitos atuais de planos na página da API do PicassoIA antes de prometer uso gratuito no seu README. A redação sobre preços pode mudar, e seus usuários vão cobrar o que você escreveu.
Rascunhe as notas de lançamento com um LLM. Um modelo de linguagem pode cuidar da tarefa que faz as pessoas pularem os changelogs. Aqui está um fluxo rápido com o Claude Sonnet 5:
Execute git log v0.1.0..HEAD --oneline e copie a saída.
Abra a página do modelo no PicassoIA e cole o log com uma instrução de uma linha: agrupe as mudanças em Added, Changed e Fixed, em linguagem simples.
Diga a ele quais mudanças afetam nomes de ferramentas ou schemas, para que sejam marcadas como incompatíveis.
Compare o resultado com o diff e cole-o na release do GitHub.
Para uma segunda opinião sobre o seu pyproject.toml ou o arquivo de workflow, o GPT 5.6 Sol é um bom revisor para tarefas de programação.
Experimente você mesmo no Picasso IA
A página do seu pacote merece uma imagem de destaque de verdade e um clipe de demonstração curto, não uma captura de tela de um terminal. Crie a imagem de destaque com o Picasso IA Image, refine os detalhes com o Picasso IA Image Editor Pro e depois anime o quadro final em um clipe curto com o Picasso IA Video.
Um primeiro experimento simples:
Escreva um prompt de 40 palavras descrevendo uma mesa de desenvolvedor calma sob luz da manhã
Gere três variações e fique com a mais nítida
Salve-a como imagem de destaque no seu README
Anime-a para o anúncio do lançamento
Veja todos os modelos disponíveis em picassoia.com/en/all-models, escolha um que combine com o seu estilo e publique algo que valha a pena abrir. Seu primeiro lançamento está a uma tag de distância.