In this article
Como conteinerizar servidores MCP em JavaScript
Os servidores MCP são extensões de fácil integração para LLMs, mas às vezes são difíceis de configurar porque exigem um ambiente de desenvolvimento. Vamos contornar esse problema fornecendo uma imagem Docker para nossos servidores MCP.
Em uma publicação anterior, expliquei como executar servidores MCP com Docker para que você, como usuário que quer se conectar a servidores MCP, não precise se preocupar em instalar a versão certa do Python nem descobrir como instalar npm ou npx para servidores MCP baseados em JavaScript.
Nesta publicação, vamos nos concentrar em você como desenvolvedor de servidores MCP e mostrar como é simples oferecer aos consumidores de MCP uma imagem Docker que eles possam usar. Vamos configurar o ambiente de desenvolvimento MCP da seguinte forma:
Configure um
Dockerfilepara seu servidor MCPCrie um workflow do GitHub Actions que compile a imagem Docker
Crie um workflow do GitHub Actions que envie a imagem Docker para o GitHub Packages
Vamos partir do princípio de que o projeto do servidor MCP foi desenvolvido em JavaScript (ou TypeScript) e usa um ambiente de desenvolvimento Node.js.
Como pré-requisito, você precisará de um ambiente de desenvolvimento Node.js, do projeto MCP que quer conteinerizar e do aplicativo Docker Desktop instalado localmente.
Configure um Dockerfile para seu servidor MCP
Um Dockerfile é o componente básico das imagens Docker. Ele define como uma imagem é criada e como será executada quando um contêiner for criado.
Crie o seguinte Dockerfile:
FROM node:22-slim AS builder
WORKDIR /app
COPY . /app
RUN --mount=type=cache,target=/root/.npm npm install
RUN --mount=type=cache,target=/root/.npm npm run build
RUN --mount=type=cache,target=/root/.npm-production npm ci --ignore-scripts --omit-dev
FROM node:22-slim AS release
WORKDIR /app
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/package.json /app/package.json
COPY --from=builder /app/package-lock.json /app/package-lock.json
ENV NODE_ENV=production
RUN npm ci --ignore-scripts --omit-dev
ENTRYPOINT ["node", "/app/dist/bin/cli.cjs"]O que está acontecendo neste Dockerfile?
Este é um workflow de criação de imagem Docker em várias etapas, que segue a prática recomendada de separar a imagem usada para compilar o projeto daquela usada para iniciar um contêiner Docker. Ele segue as 10 práticas recomendadas de Node.js para conteinerizar aplicações Node.js com Docker.
Ele define um diretório
/app, onde ficarão os arquivos do projeto do servidor MCP dentro do contêiner.Quando o contêiner Docker for executado, ele usará o arquivo
/app/dist/bin/cli.cjscomo argumento para o runtimenode. Provavelmente, você vai querer alterar esse caminho para corresponder ao ponto de entrada do seu servidor MCP, comoindex.jsouserver.js.
E agora? Com o Dockerfile pronto, podemos compilá-lo localmente e executá-lo para confirmar que funciona. Veja como criar esta imagem Docker (você pode substituir my-mcp-server pelo nome que quiser usar como tag da imagem Docker):
docker build -t my-mcp-server .Execute o comando acima no diretório raiz do projeto, onde também deve estar o Dockerfile. Uma nova imagem Docker será criada.
Como executar o servidor MCP a partir de uma imagem Docker? Use este comando:
docker run -i --rm --init -e DOCKER_CONTAINER=true my-mcp-serverEstamos executando um novo contêiner a partir da imagem Docker my-mcp-server que criamos anteriormente. Em seguida, usamos os argumentos -i e --rm para informar ao Docker que este é um processo interativo conectado ao STDIO e que, quando o contêiner for encerrado, ele também será removido automaticamente.
Como os usuários podem consumir um servidor MCP via Docker?
Agora que temos um servidor MCP, como os clientes MCP e as aplicações host vão consumi-lo? Como configurar o comando a ser executado se não for o ux do Python, o pip ou o npx do Node.js?
Eles precisam ter o Docker disponível, e muitos desenvolvedores provavelmente já terão o Docker ou uma alternativa compatível, como o Podman. Com o Docker instalado, podem configurar o servidor MCP da seguinte forma:
{
"mcpServers": {
"my-mcp-server": {
"command": "docker",
"env": {},
"args": [
"run",
"-i",
"--rm",
"my-mcp-server"
]
}
}Quase! Este exemplo está incompleto e não funcionará para usuários de MCP. Talvez você tenha notado que usamos my-mcp-server no comando Docker, mas como o Docker sabe onde encontrar essa imagem? E será que chegamos a publicá-la?
Isso mesmo: é essa parte que está faltando. Criamos um Dockerfile e compilamos uma imagem Docker localmente, mas não a publicamos em lugar nenhum. Para facilitar a vida de quem desenvolve software no GitHub, vamos publicar a imagem no registro GitHub Packages, que pode hospedar imagens Docker. Na verdade, é exatamente isso que o servidor MCP oficial do GitHub também faz.
Crie um workflow do GitHub Actions que compile a imagem Docker e a publique no GitHub Packages
Vamos começar criando um workflow do GitHub Actions para gerar uma imagem Docker, com as seguintes etapas no workflow de CI:
Compile a imagem Docker
Publique a imagem Docker no GitHub Packages
Assine a imagem Docker
A seguir está o arquivo completo do workflow, que você deve criar no seguinte diretório na raiz do projeto: .github/workflows/docker-publish-to-github.yml.
Antes de executar e salvar este arquivo, altere a variável de ambiente IMAGE_NAME para usar o nome da sua própria imagem Docker, em vez do exemplo my-mcp-server:
name: "Docker: GitHub Packages"
on:
push:
branches:
- main
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository_owner }}/my-mcp-server
jobs:
build_and_publish:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
id-token: write
attestations: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=ref,event=pr
flavor: |
prefix=
suffix=
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v2
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: trueVocê também vai notar que o arquivo de workflow do GitHub Actions acima, usado para compilar a imagem Docker, pressupõe que o nome da branch principal seja main.
Em seguida, você precisará conceder permissão de gravação aos executores do GitHub Actions. Para isso, acesse o repositório e vá até Configurações -> Actions -> Geral. Depois, ative as permissões de leitura e gravação, conforme mostrado a seguir:

Quer criar ainda mais com MCPs?
Quer saber como continuar? Separei alguns recursos úteis para ajudar você a entender melhor os servidores MCP e protegê-los:
Da injeção de prompt a outras vulnerabilidades de segurança, Segurança de MCP: o que é antigo volta com cara nova
22 de outubro de 2025
DevSecCon2025 — O Summit de Segurança de IA
Garanta sua vaga e proteja a transição para a IA nativa