Modal MCP

Um servidor MCP para gerenciar aplicativos, containers, volumes e segredos do Modal. Também ajuda a implantar e executar aplicativos Modal diretamente do Claude Code e de outros clientes MCP.

Documentação

Servidor MCP Modal

mcp-modal MCP server

PyPI

Um servidor MCP para gerenciar Modal — apps, contêineres, volumes e segredos — e para implantar e executar apps Modal diretamente do Claude Code e de outros clientes MCP.

Cada ferramenta executa comandos na sua CLI modal local, então opera com o perfil e as credenciais Modal configurados na sua máquina. Não há tokens extras para gerenciar.

Instalação

O servidor é publicado no PyPI como mcp-modal. Não é necessária instalação manual — a forma recomendada de executá-lo é com uvx, que o busca e inicia sob demanda. Basta apontar seu cliente MCP para o comando abaixo (veja Configuração).

Fazendo login no Modal

Este servidor usa suas credenciais Modal locais. Se você ainda não se autenticou, execute:

modal setup

Isso abre um navegador para login e armazena um token em ~/.modal.toml. Já está logado em outro lugar? Verifique com modal profile current.

Configuração

Adicione o servidor ao Claude Code com a CLI claude mcp:

claude mcp add mcp-modal -- uvx mcp-modal

Ou adicione-o a um arquivo .mcp.json na raiz do seu projeto:

{
  "mcpServers": {
    "mcp-modal": {
      "command": "uvx",
      "args": ["mcp-modal"]
    }
  }
}

Para fixar uma versão específica, use uvx mcp-modal@0.2.0.

Requisitos

  • Python 3.11 ou superior
  • uv (fornece uvx)
  • CLI Modal 1.x configurada com credenciais válidas (modal setup)
  • Para suporte a deploy e run do Modal:
    • O projeto sendo implantado/executado deve usar uv para gerenciamento de dependências
    • modal deve estar instalado no ambiente virtual desse projeto

Segurança

Este servidor executa comandos na sua CLI modal local usando as credenciais presentes em ~/.modal.toml. Algumas ferramentas são poderosas por design — se o cliente MCP que controla o servidor for alvo de injeção de prompt (por exemplo, por texto malicioso dentro de logs que ele buscou), estes são os caminhos de escalonamento e devem permanecer atrás dos prompts de aprovação de ferramentas do seu cliente, em vez de serem aprovados automaticamente:

  • deploy_modal_app / run_modal_app — executam Python local arbitrário no host (modal deploy importa o arquivo do app; uv run resolve e instala as dependências do projeto alvo).
  • put_modal_volume_file — pode ler qualquer arquivo local (por exemplo, ~/.ssh/id_rsa, ~/.modal.toml) e enviá-lo para um volume na nuvem (uma primitiva de exfiltração de dados).
  • get_modal_volume_file com force=True — pode sobrescrever qualquer caminho local (por exemplo, ~/.zshrc ou um perfil de shell, uma primitiva de persistência).
  • exec_modal_container — executa comandos arbitrários dentro de um contêiner, por design.

Lista de permissões opcional de caminhos locais

Para conter as duas ferramentas de volume que acessam o sistema de arquivos, defina a variável de ambiente MCP_MODAL_ALLOWED_LOCAL_PATHS como uma lista os.pathsep-separada de diretórios (: no macOS/Linux). Quando definida, put_modal_volume_file (seu local_path) e get_modal_volume_file (seu local_destination) são recusados, a menos que o caminho resolvido — após expandir ~ e colapsar ../links simbólicos — esteja dentro de uma dessas raízes. O destino de download "-" (stream para stdout) é isento porque nada é gravado em disco.

Quando a variável está não definida (o padrão), não há restrição, então configurações existentes não são afetadas. Configure-a no seu cliente MCP, por exemplo:

{
  "mcpServers": {
    "mcp-modal": {
      "command": "uvx",
      "args": ["mcp-modal"],
      "env": { "MCP_MODAL_ALLOWED_LOCAL_PATHS": "/Users/me/modal-workspace:/tmp/modal" }
    }
  }
}

Todas as ferramentas também passam nomes/caminhos fornecidos pelo usuário após um separador de fim de opções --, então um valor que começa com - é sempre tratado como dado, nunca como um flag da CLI modal. Valores de segredos passados para create_modal_secret são ocultados do comando ecoado, dos logs e de qualquer saída de erro.

Ferramentas Suportadas

26 ferramentas, agrupadas por área. Ferramentas com escopo de conta aceitam um argumento opcional env para segmentar um ambiente Modal específico; se omitido, elas usam o padrão do perfil (ou MODAL_ENVIRONMENT).

Deploy & Run

  1. Implantar App Modal (deploy_modal_app)

    • Implanta um app Modal (modal deploy). Endpoints web implantados persistem, então qualquer link na saída está ativo e compartilhável (retornado em urls).
    • Parâmetros: absolute_path_to_app (obrigatório), env, name, tag, strategy (rolling/recreate), stream_logs
    • O diretório do app deve usar uv com modal instalado em seu virtualenv.
  2. Executar App Modal (run_modal_app)

    • Executa uma função ou entrypoint local uma vez e transmite sua saída (modal run).
    • Parâmetros: absolute_path_to_app (obrigatório), function_name, env, detach, timeout_seconds (padrão 120)
    • Retorna um snapshot com truncated: true se a execução ainda estiver em andamento no timeout. Passe detach=True para manter jobs longos ativos no Modal além do timeout.

Por que não há ferramenta modal serve? modal serve só mantém seus endpoints ativos enquanto o processo bloqueante está em execução — uma ferramenta MCP que retorna os derrubaria imediatamente, entregando uma URL morta. Use deploy_modal_app para um endpoint persistente e compartilhável.

Apps

  1. Listar Apps Modal (list_modal_apps)

    • Lista apps atualmente implantados/em execução ou parados recentemente. Use isso para encontrar o nome/ID do app para as outras ferramentas de app.
    • Parâmetros: env
  2. Obter Logs do App Modal (get_modal_app_logs)

    • Busca ou transmite logs de um app por nome ou ID (modal app logs).
    • Parâmetros: app_identifier (obrigatório), timeout_seconds (padrão 30), env, since, until, tail, search, source (stdout/stderr/system), timestamps (prefixa cada linha com seu horário de parede), follow
    • Com follow=True, os logs são transmitidos até o app parar ou timeout_seconds ser atingido, retornando um snapshot com truncated: true.
    • Cobre apenas os streams stdout/stderr/sistema; algumas falhas (por exemplo, uma falha relatada como "... exited with ...") são eventos do dashboard Modal, não linhas de log, e não aparecerão aqui.
  3. Parar App Modal (stop_modal_app)

    • Para permanentemente um app e encerra seus contêineres (modal app stop).
    • Parâmetros: app_identifier (obrigatório), env
  4. Reverter App Modal (rollback_modal_app)

    • Reimplanta uma versão anterior de um app (modal app rollback).
    • Parâmetros: app_identifier (obrigatório), version (opcional — padrão para a versão anterior), env
  5. Obter Histórico do App Modal (get_modal_app_history)

    • Retorna o histórico de implantação de um app (modal app history). Use-o para encontrar um version para reversão.
    • Parâmetros: app_identifier (obrigatório), env

Contêineres

  1. Listar Contêineres Modal (list_modal_containers)

    • Lista contêineres atualmente em execução (modal container list).
    • Parâmetros: app_id (filtro opcional), env
  2. Obter Logs do Contêiner Modal (get_modal_container_logs)

    • Busca ou transmite logs de um ID de contêiner (modal container logs).
    • Parâmetros: container_id (obrigatório), timeout_seconds (padrão 30), since, until, tail, search, source, timestamps, follow
    • Mesma ressalva de stdout/stderr/sistema da ferramenta de logs de app acima.
  3. Executar no Contêiner Modal (exec_modal_container)

    • Executa um comando dentro de um contêiner em execução (modal container exec --no-pty).
    • Parâmetros: container_id (obrigatório), command (lista de argumentos, por exemplo, ["python", "-c", "print('hi')"]), timeout_seconds (padrão 60)
  4. Parar Contêiner Modal (stop_modal_container)

    • Encerra um contêiner em execução (modal container stop).
    • Parâmetros: container_id (obrigatório)

Pesquisa de Logs

  1. Pesquisar Logs Modal (search_modal_logs)
    • Busca nos logs de um app ou contêiner por um padrão e retorna cada ocorrência com as linhas ao redor — feito para depuração de "onde deu errado?". Os logs são buscados uma vez e pesquisados localmente, então (diferente do argumento search nas ferramentas de log) você obtém contexto, regex, controle de maiúsculas/minúsculas e contagens de correspondências, não apenas a linha correspondente nua.
    • Parâmetros: identifier (obrigatório — nome/ID do app ou ID do contêiner), pattern (obrigatório), target (app/container, padrão app), regex, case_sensitive, context_lines (padrão 3), max_matches (padrão 50), since, tail (padrão para as últimas 1000 entradas), source (stdout/stderr/system), exclude (remove linhas de ruído antes de pesquisar, por exemplo, "queue put failed"), timestamps (padrão true — carrega o horário de parede de cada linha para o resultado), timeout_seconds, env
    • Retorna match_count e matches: blocos de contexto com timestamp e número de linha onde linhas correspondentes são prefixadas com >, por exemplo, > 8: 2026-06-04T... ValueError: bad input. Relata excluded_lines quando exclude é usado.
    • Pesquisa apenas os streams stdout/stderr/sistema; falhas emitidas como eventos do dashboard Modal (por exemplo, "... exited with ...") retornam 0 correspondências mesmo quando a falha é real.

Volumes — Arquivos

  1. Listar Volumes Modal (list_modal_volumes) — lista todos os volumes. Parâmetros: nenhum.
  2. Listar Conteúdo do Volume (list_modal_volume_contents) — volume_name, path (padrão /). Define empty: true com uma mensagem quando a listagem genuinamente não retorna nada, para que um diretório vazio seja distinguível de um erro ou caminho errado.
  3. Copiar Arquivos (copy_modal_volume_files) — volume_name, paths (o último é o destino).
  4. Remover Arquivo (remove_modal_volume_file) — volume_name, remote_path, recursive.
  5. Enviar Arquivo (put_modal_volume_file) — volume_name, local_path, remote_path, force.
  6. Baixar Arquivo (get_modal_volume_file) — volume_name, remote_path, local_destination, force. Use - como destino para transmitir o conteúdo para stdout.

Volumes — Ciclo de Vida

  1. Criar Volume (create_modal_volume) — cria um volume persistente nomeado. Parâmetros: volume_name, env.
  2. Excluir Volume (delete_modal_volume) — exclui um volume e todos os seus dados (irreversível). Parâmetros: volume_name, env.
  3. Renomear Volume (rename_modal_volume) — Parâmetros: old_name, new_name, env.

Segredos

  1. Listar Segredos (list_modal_secrets)

    • Lista segredos publicados (apenas nomes e timestamps — valores nunca são expostos).
    • Parâmetros: env
  2. Criar Segredo (create_modal_secret)

    • Cria um segredo a partir de chaves/valores inline ou de um arquivo local (modal secret create). Valores de segredo são ocultados do command retornado.
    • Parâmetros: secret_name (obrigatório), key_values (dict), from_dotenv (caminho), from_json (caminho), force, env. Forneça pelo menos um de key_values, from_dotenv ou from_json.
  3. Excluir Segredo (delete_modal_secret) — Parâmetros: secret_name, env.

Descoberta

  1. Obter Perfil Modal (get_modal_profile)

    • Mostra o perfil ativo e todos os perfis configurados. Use-o para confirmar em qual workspace/conta o servidor está autenticado. Parâmetros: nenhum.
  2. Listar Ambientes Modal (list_modal_environments)

    • Lista os ambientes no workspace atual; os nomes são argumentos válidos de env para as outras ferramentas. Parâmetros: nenhum.

Formato de Resposta

Todas as ferramentas retornam respostas em um formato padronizado, com pequenas variações dependendo do tipo de operação:

# JSON / list operations (apps, containers, volumes, secrets, history, ...):
{
    "success": True,
    "apps": [...]   # or "containers", "volumes", "secrets", "history", "environments"
}

# Action operations (deploy, stop, create, delete, rename, copy, put, get, rm):
{
    "success": True,
    "message": "Operation successful message",
    "command": "executed command string",
    "stdout": "command output",  # if any
    "stderr": "error output"     # if any
}

# Log / run / exec operations (snapshot-based):
{
    "success": True,
    "logs": "...",          # or "output" for run/exec
    "truncated": False,     # True when cut off at timeout_seconds
    "command": "executed command string"
}

# Error case (all operations):
{
    "success": False,
    "error": "Error message describing what went wrong",
    "command": "executed command string",
    "stdout": "command output",  # if available
    "stderr": "error output"     # if available
}

Licença

Este projeto é licenciado sob a Licença MIT - consulte o arquivo LICENSE para obter detalhes.