关于 Modal MCP
Modal MCP 是通过本地 Modal CLI 管理 Modal 应用、容器、卷和密钥的 MCP 服务,也可部署和运行应用。适用于希望从 Claude Code 等 MCP 客户端操作 Modal 的开发者。
主要功能
- 01查看资源与应用历史
- 02获取和搜索应用日志
- 03部署应用并运行任务
- 04管理应用状态与回滚
- 05检查成本与密钥
适用场景
- 01排查应用日志中的错误
- 02部署可共享的应用端点
- 03运行一次性或分离任务
MCP 相关
向 MCP 客户端提供 12 种 Modal 管理工具。工具通过本地 `modal` CLI 执行,并使用本机配置的 Modal 配置文件和凭据。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
12收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| env | any | 否 | |
| action | string | 是 | |
| version | any | 否 | |
| app_identifier | string | 是 |
详细介绍
MCP Modal 服务
一个用于管理 Modal 的 MCP 服务,涵盖应用、容器、卷和密钥;还可直接从 Claude Code 和其他 MCP 客户端部署和运行 Modal 应用。
每个工具都会调用本地的 modal CLI,因此会使用你机器上配置的 Modal 配置文件和凭据。无需管理额外令牌。
安装
此服务已作为 mcp-modal 发布到 PyPI。无需手动安装——推荐使用 uvx 运行,它会按需获取并启动服务。只需将 MCP 客户端指向以下命令即可(请参阅配置)。
每个版本也会打上标签并发布到
Releases 页面,附带发行说明以及
PyPI 上相同的 .whl / .tar.gz 文件,可用于固定版本、在隔离网络环境中
安装,或查看两个版本之间的更改。
登录 Modal
此服务使用你的本地 Modal 凭据。如果你还没有完成身份验证,请运行:
modal setup
这会打开浏览器供你登录,并将令牌存储在 ~/.modal.toml 中。如果你已在其他地方登录,可运行 modal profile current 检查。
配置
使用 claude mcp CLI 将服务添加到 Claude Code:
claude mcp add mcp-modal -- uvx mcp-modal@latest
或者将其添加到项目根目录下的 .mcp.json 文件中。这是更适合团队的方式
——任何打开此仓库的人都会使用相同的配置:
{
"mcpServers": {
"mcp-modal": {
"command": "uvx",
"args": ["mcp-modal@latest"]
}
}
}
为什么使用 @latest,以及何时固定版本
uvx 会缓存首次运行时构建的环境,之后不会再次检查 PyPI:
“uvx 会在首次调用时使用所请求工具的最新可用版本。 此后,除非指定其他版本、清理缓存或刷新缓存,否则 uvx 会使用缓存的工具版本。” — uv 文档
因此,直接运行 uvx mcp-modal 意味着安装时使用最新版本,此后永远固定不变——重启
客户端或重启电脑都不会改变版本,因为缓存保存在磁盘上。不同的人可能会因为首次运行时间不同而使用不同版本,而且不会收到任何提示。
mcp-modal@latest会在每次启动时重新解析版本,因此重启后即可获取新版本。 代价是启动时需要进行一次网络往返。工具功能仍在频繁变动时请使用此方式。[email protected](指定具体版本)可确保运行结果可复现,升级也会变成 有意为之的单行更改。需要稳定性或面向更多用户时请使用此方式。
对于已经被旧缓存版本困住的机器,只需切换为上述任一种形式即可——请求指定版本会使缓存失效。否则,运行
uv cache clean mcp-modal 可强制刷新缓存。
要求
- Python 3.11 或更高版本
uv(提供uvx)- Modal CLI 1.5 或更高版本,并已配置有效凭据(
modal setup)——1.5 引入了modal billing summary/rates,并将账单报告切换为 snake_case 列名;费用工具可读取两种写法,但这两个视图需要 1.5 版本 - 如需支持 Modal deploy 和 run:
- 要部署/运行的项目必须使用
uv管理依赖 - 必须在该项目的虚拟环境中安装
modal
- 要部署/运行的项目必须使用
安全
此服务会使用 ~/.modal.toml 中的凭据调用本地 modal CLI。一些工具本身具有强大的能力——如果驱动此服务的 MCP 客户端遭到提示词注入(例如,通过其获取的日志中的恶意文本),以下路径可能导致权限提升,因此应保留在客户端的工具审批提示之后,而非自动批准:
deploy_modal_app/run_modal_app— 在主机上执行任意本地 Python(modal deploy会导入应用文件;uv run会解析并安装目标项目的依赖项)。modal_volume_files,且action="put"— 可读取任意本地文件(例如~/.ssh/id_rsa、~/.modal.toml),并将其上传到云卷(可用于数据外传)。modal_volume_files,且action="get"和force=True— 可覆盖任意本地路径(例如~/.zshrc或 shell 配置文件,可用于持久化)。manage_modal_container,且action="exec"— 按设计会在容器内运行任意命令。
每个工具都声明了 MCP 工具注解,因此客户端可以区分 4 个只读工具(list_modal_resources、get_modal_logs、search_modal_logs、analyze_modal_costs——它们的 readOnlyHint: true)与会更改远程状态或启动计算的 8 个工具。其中 6 个标记为 destructiveHint: true;例外是 run_modal_app 和 inspect_modal_secret,它们会启动计算,但不会删除或覆盖任何内容。读取操作可自动批准;其余操作应保留在审批提示之后。
可选的本地路径允许列表
为限制两个会操作文件系统的卷工具,请将 MCP_MODAL_ALLOWED_LOCAL_PATHS 环境变量设为以 os.pathsep 分隔的目录列表(macOS/Linux 上为 :)。设置后,除非解析路径(展开 ~ 并折叠 .. 和符号链接后)位于其中某个根目录内,否则 modal_volume_files 会拒绝操作任何本地路径——action="put" 时的 local_path,以及 action="get" 时的目标路径。下载目标 "-"(返回内容而不是写入文件)不受此限制,因为不会向磁盘写入内容。
未设置此变量时(默认情况),不设任何限制,因此现有配置不受影响。请在 MCP 客户端中配置,例如:
{
"mcpServers": {
"mcp-modal": {
"command": "uvx",
"args": ["mcp-modal"],
"env": { "MCP_MODAL_ALLOWED_LOCAL_PATHS": "/Users/me/modal-workspace:/tmp/modal" }
}
}
}
所有工具还会在 -- 选项结束分隔符之后传递用户提供的名称/路径,因此以 - 开头的值始终会被视为数据,而不是 modal CLI 标志。传递给 manage_modal_secret 的密钥值会从回显的命令、日志和任何错误输出中删除。
支持的工具
共 12 个工具。相关操作通过 action/resource 参数分组,而不是按 CLI 子命令拆分为每个子命令一个工具:每个工具的架构在整个会话期间都会加载到模型的上下文中,因此较小的工具集合能为实际工作留出更多空间(也能减少模型需要选择的近似工具)。
用于操作环境范围资源的工具可以接收可选的 env 参数,以指定目标 Modal 环境;若省略,则使用配置文件的默认环境(或 MODAL_ENVIRONMENT)。例外是 manage_modal_container 和容器日志——容器 ID 全局唯一,且 CLI 不接受环境参数。
只读
-
列出 Modal 资源(
list_modal_resources)——一次查询即可获取整个账户的信息。- 参数:
resource(必填)、name、path(默认为/)、env resource的取值:value 返回内容 name表示apps已部署、正在运行或最近停止的应用 — app_history某个应用的部署版本(用于回滚) 应用名称/ID containers正在运行的容器( ta-...)用于筛选的应用 ID volumes命名卷 — volume_files卷中的文件(使用 path)卷名称 secrets密钥名称(不会公开密钥值) — environments此工作区有效的 env取值— profile当前配置及所有配置 — - 如果
volume_files确实没有列出任何内容,则会设置empty: true并附上说明,以便区分空目录和错误路径。 - 列表超过 200 项时会截断,并通过
omitted_items给出省略的项目数。
- 参数:
-
获取 Modal 日志(
get_modal_logs)——获取或流式传输应用或容器的日志。- 参数:
identifier(必填)、target(auto/app/container,默认为auto——以ta-开头的标识符均视为容器)、timeout_seconds(默认为 30)、env、since、until、tail、source(stdout/stderr/system)、timestamps、follow - 不带
tail的since会获取时间范围内的所有条目;同时传入until可限制日志量(最大 范围为 35 天,tail上限为 20,000),避免繁忙应用的输出过多。 - 设置
follow=True后,日志会持续流式传输,直到应用/容器停止或达到timeout_seconds,然后返回包含truncated: true的快照。 - 此工具仅涵盖 stdout/stderr/system 流;某些故障(例如报告为 “... exited with ...” 的崩溃)属于 Modal 控制面板事件,不是日志行,因此不会显示在此处。
- 参数:
-
搜索 Modal 日志(
search_modal_logs)——搜索日志,并获取每个命中项及其周围的日志行,专为“问题出在哪里?”这类调试场景设计。日志只获取一次,然后在本地搜索,因此可以提供上下文、正则表达式、大小写控制和精确的匹配数。- 参数:
identifier(必填)、pattern(必填)、target(默认为auto)、regex、case_sensitive、context_lines(默认为 3)、max_matches(默认为 50)、since、until、tail(默认为最近 1000 条)、source、exclude(搜索前排除噪声行,例如"queue put failed")、prefilter、timestamps(默认为true)、timeout_seconds、env - 繁忙应用请限制时间窗口。 单独使用
since会获取从该时间点到现在的所有日志——对于日志频繁的应用,每小时可能有数百 KB;30 秒的获取限制会导致日志被截断(logs_truncated: true),输出也会受到预算限制。解决方法是同时设置since和until,将时间范围限定在你关注的那一分钟,通常只需几 KB。 - 设置
prefilter=True会将pattern作为服务端子字符串筛选条件传给 Modal(modal app logs --search),因此不会获取不匹配的行——这是处理日志量大到无法完整拉取时的有效方法。此功能要求regex=False,此时上下文行也只会显示其他匹配项,因此可先用它定位时间窗口,再通过prefilter=False重新查询该窗口。 - 返回
match_count和matches:带时间戳、行号的上下文块,匹配行前缀为>,例如> 8: 2026-06-04T... ValueError: bad input。始终会搜索获取到的全部日志,因此即使返回的上下文块较少,match_count仍然精确。returned表示返回的匹配项数量(相邻匹配项会合并为一个块,并由returned_blocks统计)。使用exclude时会报告excluded_lines。 - 如果 CLI 拒绝某个时间窗口(时间范围颠倒、超过 35 天或
tail超过 20,000),工具会返回success: false和 Modal 提供的错误信息,而不是单独的退出码。 - 与
get_modal_logs一样,此工具仅涵盖 stdout/stderr/system 流。
- 参数:
部署与运行
-
部署 Modal 应用(
deploy_modal_app)- 部署 Modal 应用(
modal deploy)。已部署的 Web 端点会持续有效,因此输出中的链接均可访问并可分享(通过urls返回)。 - 参数:
absolute_path_to_app(必需)、env、name、tag、strategy(rolling/recreate)、stream_logs - 应用目录必须使用
uv,并在其虚拟环境中安装modal。
- 部署 Modal 应用(
-
运行 Modal 应用(
run_modal_app)- 一次性运行函数或本地入口点并收集其输出(
modal run)。 - 参数:
absolute_path_to_app(必需)、function_name、env、detach、timeout_seconds(默认值为 120) - 如果运行在超时时仍未结束,则返回包含
truncated: true的快照。 传入detach=True,即可让长时间运行的任务在 Modal 上超时后继续运行。
- 一次性运行函数或本地入口点并收集其输出(
为什么没有
modal serve工具?modal serve只会在阻塞进程运行期间保持其端点有效——返回结果的 MCP 工具会立即终止该进程,从而拆除端点,返回一个失效的 URL。请使用deploy_modal_app来创建持久且可分享的端点。
状态变更
-
管理 Modal 应用(
manage_modal_app)——action可设为stop(关闭应用并终止其容器)或rollback(重新部署先前版本)。- 参数:
action(必需)、app_identifier(必需)、version(仅用于回滚——默认为紧邻的上一版本)、env
- 参数:
-
管理 Modal 容器(
manage_modal_container)——action可设为exec(在运行中的容器内运行命令,使用modal container exec --no-pty)或stop(终止容器)。- 参数:
action(必需)、container_id(必需)、command(仅用于exec——参数列表,例如["python", "-c", "print('hi')"])、timeout_seconds(默认值为 60)
- 参数:
-
管理 Modal 卷(
manage_modal_volume)——action可设为create、delete(删除卷及其所有数据,不可撤销)或rename。- 参数:
action(必需)、volume_name(必需)、new_name(仅用于重命名)、env
- 参数:
-
Modal 卷文件(
modal_volume_files)——对卷内文件执行写入操作:action可设为put(上传)、get(下载)、cp(在卷内复制)或rm。- 参数:
action(必需)、volume_name(必需)、local_path、remote_path、paths(用于cp:源路径在前,目标路径在后)、recursive、force、env action="get"且local_path="-"时,会返回文件内容而不是写入文件。- 如需列出卷的内容,请使用
list_modal_resources(resource="volume_files")。
- 参数:
-
管理 Modal 密钥(
manage_modal_secret)——action可设为create或delete。- 参数:
action(必需)、secret_name(必需)、key_values(字典)、from_dotenv(路径)、from_json(路径)、force、env。创建时必须至少提供key_values、from_dotenv或from_json中的一项。 - 返回的所有字段(包括错误输出)都会对密钥值进行脱敏。
- 如需列出密钥名称,请使用
list_modal_resources(resource="secrets")。
- 参数:
成本
- 分析 Modal 成本(
analyze_modal_costs)——只读。只获取一次modal billing,然后在本地汇总,让你看到按金额排序的总额和 环比变化,而不是数百条原始记录。- 参数:
view(默认by_app)、period、start、end、resolution(d/h)、timezone、app、environment、top_n(默认 10)、tag_names view的取值:取值 可回答的问题 by_app“哪个应用的成本最高?”——按支出对应用排序,并显示占比 timeline“为什么周一这么贵?”——显示每个时间区间的成本,以及一个 explanation,用于对比峰值区间和前一个区间,并列出增长最多的应用by_environment资金流向了哪个环境 by_resourceCPU、GPU 类别、内存和存储的费用 summary月度周期的计费成本与计量成本,以及抵扣额/套餐调整 rates当前单价 total_cost始终涵盖范围内的所有记录,即使groups被截断为top_n条也是如此——请直接引用该值,不要累加可见记录。- 计费范围是整个工作区(CLI 不接受
-e),因此此处报告的是所有 环境的费用;environment会在之后筛选记录。 - Modal 只报告完整时间区间,所以未结束当天的费用会显得较低。
- 参数:
Secret——检查
- 检查 Modal Secret(
inspect_modal_secret)——列出 secret 中的键名, 绝不显示键值。- 参数:
secret_name(必填)、env、image、timeout_seconds(默认 300) - Modal 有意不提供用于此操作的 API:CLI、SDK 和 gRPC
层都不行。查看 secret 定义了哪些键的唯一方法,是将其挂载到容器中
并列出环境变量。因此,此工具会运行
modal shell --secret <name>,并使用compgen -e(bash 内置命令,只打印导出的变量名称——即使在容器内部也 绝不会打印任何值),然后减去镜像和 Modal 运行时本身设置的变量——23 个已知 名称,以及六个前缀(MODAL_、PYTHON、PIP_、NVIDIA_、CUDA_、LD_LIBRARY_PATH)下的所有变量;这也涵盖了每个容器中都有的MODAL_TOKEN_*凭据。 - 此调用会启动远程计算,因此会花费几美分并耗时数十秒(需要构建镜像时
会更久)。此服务中的其他读取操作都免费;使用
list_modal_resources(resource="secrets")查看有哪些 secret,仅在需要了解 某个 secret 内部内容时才使用此工具。 - 返回
keys,以及未经筛选的all_env_names,这样看起来像运行时变量的键 也会显示出来,而不会被悄悄剔除。 - 省略
image可使用 Modal 默认镜像(构建时会与服务的 Python 版本匹配,是 最可靠的选择)。如果工作区的镜像构建器不接受该 Python 版本,可以传入镜像, 例如python:3.12-slim。
- 参数:
提示词
该服务还提供四个 MCP 提示词——客户端可直接调用的多步骤工作流(在 Claude Code 中,它们显示为 /mcp__mcp-modal__<name>)。提示词按需获取,因此与工具不同,不会占用每个会话的上下文:
debug_modal_app(app_name,可选symptom)——按顺序执行的排查流程:检查应用是否正常运行,在日志中搜索包含上下文的回溯信息;如果获取的日志被截断,则缩小时间窗口,而不是扩大窗口;改为查看日志末尾;检查同一时间窗口内是否有相关应用受到影响;检查容器;然后对照部署历史并考虑回滚。deploy_and_verify(absolute_path_to_app,可选env)——确认目标工作区,执行部署,报告线上 URL,然后验证应用是否正常运行,而不是想当然地认为它正常。review_modal_account(可选env)——只读清单,会标记空闲应用、原因不明的运行中容器以及孤立的卷/密钥,并指出清理各项所需的具体调用,但不会执行这些调用。investigate_modal_costs(可选period、app)——从每日时间线一路追查支出增长,直到峰值时段、资源类别,以及导致支出的部署或仍在运行的容器。
输出上限
日志、运行和执行输出在返回前都会受到长度限制,因此某个输出量大的应用不会占满你的上下文窗口。默认上限为每个文本字段 40,000 个字符(约 10k 个 token);字段被截断时,结果会设置 output_capped: true,文本中也会标明丢弃了多少内容。截断后的字段会保留开头和结尾,因此启动横幅和末尾的回溯信息都能保留下来。
搜索结果事先不会被截断:search_modal_logs 会在整个已获取的日志中进行 grep,只限制返回的上下文块数量,因此 match_count 始终准确。
对于被截断的日志搜索,提高 timeout_seconds 或输出上限很少是正确的做法——减少获取量才是。使用 since 和 until 限定时间窗口,使用 source/exclude 进行筛选,或设置 prefilter=True,以便在 Modal 内丢弃不匹配的行。
设置 MCP_MODAL_MAX_OUTPUT_CHARS 可提高或降低输出上限,设置为 0 则可完全禁用限制:
{
"mcpServers": {
"mcp-modal": {
"command": "uvx",
"args": ["mcp-modal"],
"env": { "MCP_MODAL_MAX_OUTPUT_CHARS": "80000" }
}
}
}
响应格式
所有工具都以标准化格式返回响应,具体格式会根据操作类型略有不同:
# Lookups (list_modal_resources):
{
"success": True,
"apps": [...], # or "containers", "volumes", "contents", "secrets", ...
"omitted_items": 0 # present when the listing was capped at 200 entries
}
# Action operations (deploy, stop, rollback, create, delete, rename, cp, 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
"output_capped": False, # True when text was trimmed to fit MCP_MODAL_MAX_OUTPUT_CHARS
"command": "executed command string"
}
# Log search (search_modal_logs):
{
"success": True,
"match_count": 12, # exact: the whole fetched log is searched
"returned": 5, # matches actually shown
"returned_blocks": 2, # adjacent matches merge into one context block
"matches": ["> 8: ...", ...],
"logs_truncated": False, # True when the log fetch hit timeout_seconds
"output_capped": False,
"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
}
许可证
本项目采用 MIT 许可证授权,详情请参阅 LICENSE 文件。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。