1. 项目概述:让AI助手成为你的Docker管家
如果你和我一样,日常开发离不开Docker,那你肯定对这套操作流程再熟悉不过了:写代码写到一半,需要确认某个服务是否启动,于是切到终端,敲下docker ps;发现日志不对劲,又得docker logs -f <container_id>盯着屏幕看滚动;想重启服务,还得复制粘贴容器ID,执行docker restart。一来二去,思路被打断,效率也下来了。更别提有时候容器多了,想快速查看哪个占用了大量CPU或内存,还得敲一堆docker stats命令去筛选。
最近在折腾AI编程助手(比如Cursor、Claude Desktop里的代码功能)时,我发现了Model Context Protocol(MCP)这个好东西。简单说,它就像给AI助手装上了一套“工具”,让AI不仅能和你聊天、写代码,还能直接操作你电脑上的其他工具和服务。于是我就想,为什么不做一个MCP服务器,让AI助手能直接管理我的Docker呢?这就是mcp-server-docker项目的由来。
这个工具本质上是一个运行在你本地的Node.js服务,它通过MCP协议,将Docker引擎的常用操作(如列出容器、查看日志、启停服务、执行命令、监控资源)暴露给你的AI助手。你不再需要离开编辑器或IDE,直接通过自然语言,比如“帮我看看有哪些容器在运行”或者“重启一下后端的API服务”,AI助手就能替你完成这些操作,并把结果清晰地呈现给你。整个过程无需任何API密钥,因为它直接连接到你本地的Docker守护进程套接字,安全又方便。
2. 核心原理与架构设计
2.1 MCP协议:AI的“瑞士军刀”接口
要理解这个项目,首先得搞懂MCP(Model Context Protocol)。你可以把它想象成AI模型的“USB接口”标准。在没有MCP之前,AI模型(比如Claude、GPT)的能力主要局限于其训练数据中的知识和基于文本的推理。它们知道“Docker”是什么,但无法直接操作你电脑上的Docker服务。
MCP定义了一套标准,允许开发者创建“服务器”(Server),这些服务器对外提供一系列定义好的“工具”(Tools)。而AI客户端(Client),如Claude Desktop、Cursor,可以动态发现并调用这些工具。当你在AI对话界面输入“列出所有容器”时,客户端会识别出你的意图,通过MCP协议调用mcp-server-docker提供的list_containers工具,获取结果后再格式化返回给你。这相当于扩展了AI的“手”和“眼”,让它能直接与真实世界交互。
2.2 项目架构与安全边界
mcp-server-docker采用了一个典型且安全的本地服务架构:
- 本地进程:它作为一个Node.js进程运行在你的开发机上,与你的AI助手客户端(如Cursor)处于同一安全上下文。
- Docker Socket连接:项目核心是使用官方的
dockerode库。这个库会直接连接到你的Docker守护进程监听的地方:- Linux/macOS:默认是Unix套接字
/var/run/docker.sock。 - Windows:默认是命名管道
//./pipe/docker_engine。 这意味着该工具拥有与你本地docker命令完全相同的权限。这是一个关键的安全设计:它没有在网络上暴露任何端口,也没有引入远程API密钥,所有操作都严格限定在你的本地机器上,避免了将Docker控制权暴露给外部网络的风险。
- Linux/macOS:默认是Unix套接字
- 工具封装:项目将复杂的Docker API调用封装成一个个简单的、语义化的MCP工具。例如,
container_logs工具背后,其实就是调用了dockerode的container.logs()方法,并处理了流式输出和参数(如tail,since,follow)。
注意:正因为该工具拥有与本地Docker命令同等的权限,所以在授权AI助手执行
remove_container或remove_image这类破坏性操作时需要格外谨慎。好在目前的实现中,这些操作通常需要明确的容器名或镜像名作为参数,AI不会主动执行删除操作,除非你明确指令它这么做。
2.3 为什么选择TypeScript和NPM分发?
从技术栈来看,项目使用TypeScript开发并发布到NPM,这带来了几个实实在在的好处:
- 类型安全:Docker API的对象结构复杂,TypeScript能在编译阶段就捕获许多潜在的类型错误,比如错误地访问一个可能为
null的容器状态字段。 - 开发体验:完善的类型提示让工具函数的封装和MCP协议接口的定义更加清晰可靠。
- 一键部署:通过
npx可以直接运行,用户无需克隆源码、安装依赖。npx会临时下载并执行最新版本的包,极大降低了使用门槛。这对于MCP服务器这种“即用即走”的工具来说非常合适。
3. 详细配置与客户端集成指南
虽然README给出了快速配置,但在实际接入不同客户端时,可能会遇到一些环境或路径问题。下面我展开讲讲各个客户端的配置细节和避坑点。
3.1 Cursor IDE配置详解
Cursor是目前对MCP支持最原生、体验最流畅的IDE之一。配置很简单,在你的用户目录或项目根目录下找到或创建.cursor/mcp.json文件。
{ "mcpServers": { "docker": { "command": "npx", "args": ["-y", "mcp-docker-server"] } } }实操心得:
-y参数是关键,它让npx在下载包时自动确认,避免出现交互式提示导致服务启动失败。- 配置完成后,必须完全重启Cursor(关闭所有窗口再重新打开),新的MCP服务器配置才会被加载。仅仅重载窗口有时是不够的。
- 如何验证是否成功?在Cursor的聊天框中,你可以尝试输入“你能操作Docker吗?”或者直接问“列出运行中的容器”。如果AI回复它无法执行该操作,说明配置未生效;如果它开始调用工具并返回结果,那就成功了。
3.2 Claude Desktop配置详解
Claude Desktop的配置逻辑类似,但配置文件的位置因操作系统而异:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
配置文件内容与Cursor一致。这里有个常见坑点:如果Claude Desktop正在运行,修改配置文件后它可能不会自动重载。最稳妥的方法是修改配置后,退出Claude Desktop应用再重新启动。
3.3 VS Code + Continue 扩展配置
VS Code本身不原生支持MCP,但可以通过强大的Continue扩展来实现。安装Continue扩展后,配置方式有两种:
方法一:全局用户设置在VS Code的设置(JSON模式)中,添加如下配置:
{ "continue.mcpServers": { "docker": { "command": "npx", "args": ["-y", "mcp-docker-server"] } } }方法二:项目级配置在项目根目录创建.vscode/mcp.json,内容同上。这种方式更适合团队项目,确保所有成员使用相同的MCP工具集。
注意事项:
- Continue扩展需要保持运行状态,它会在后台启动你配置的MCP服务器进程。
- 如果遇到连接问题,可以打开Continue扩展的输出面板(Output -> Continue),查看是否有错误日志。
3.4 权限问题排查(Linux/macOS重点)
在Linux或macOS上,最常见的启动失败原因是Docker Socket权限不足。默认情况下,/var/run/docker.sock属于root:docker用户组,普通用户需要加入docker组才能读写。
检查与解决步骤:
- 检查当前用户组:在终端运行
groups $USER,查看输出中是否包含docker。 - 检查Socket权限:运行
ls -l /var/run/docker.sock,应该看到类似srw-rw---- 1 root docker 0 ...的输出。重点是docker这个组有读写权限(rw-)。 - 添加用户到docker组(如果未加入):
重要:执行此命令后,你必须完全注销并重新登录,或者重启电脑,新的组权限才会生效。仅仅新开一个终端窗口是没用的。sudo usermod -aG docker $USER - 验证:重新登录后,运行
docker ps应该能正常执行,而不需要sudo。此时再通过AI助手调用MCP服务器,权限问题就应该解决了。
4. 核心工具使用场景与高级技巧
项目提供的工具看似简单,但在实际开发工作流中组合使用,能发挥巨大威力。下面我结合几个高频场景,拆解一下使用技巧。
4.1 场景一:日常开发状态监控与快速干预
典型对话流:
- 你:“我本地有哪些容器在跑?”
- AI(调用
list_containers):返回一个表格,包含容器名、状态、镜像、端口映射。你发现你的redis容器状态是Exited (1)。 - 你:“看看redis容器为什么挂了,显示最后50行日志。”
- AI(调用
container_logs,参数tail: 50):返回日志,显示“Failed to bind port 6379: address already in use”。 - 你:“哦,端口冲突了。重启一下我的
app-backend容器,它可能没完全释放端口。” - AI(调用
restart_container):重启成功。 - 你:“现在再启动redis容器。”
- AI(调用
start_container):启动成功,状态变为Running。
技巧:list_containers工具默认只显示运行中的容器。如果你想查看所有容器(包括已停止的),可以在提问时明确说“列出所有容器(包括已停止的)”。AI会传递相应参数给工具。
4.2 场景二:容器内问题诊断与调试
当应用在容器内行为异常时,快速进入容器排查是关键。
典型操作:
- 查看实时日志:“持续跟踪
my-app容器的日志输出。” AI会调用container_logs并设置follow: true,将日志流实时返回给你,就像在终端里执行docker logs -f一样。 - 执行诊断命令:“在
database容器里检查/var/lib/postgresql/data目录的磁盘使用情况。” AI会调用exec_command,执行df -h /var/lib/postgresql/data并将结果返回。 - 交互式Shell(间接实现):虽然工具没有直接提供
docker exec -it bash,但你可以通过连续执行命令来模拟。例如,先“在容器里执行ls -la看看文件列表”,再根据结果“进入某个目录并查看文件内容”。
注意事项:exec_command执行的是非交互式命令。对于需要TTY的复杂交互式程序(如vim,top),可能无法正常工作。它更适合执行cat,grep,ps,df这类能立即返回结果的诊断命令。
4.3 场景三:资源性能分析与瓶颈定位
微服务架构下,某个服务突然变慢,快速定位是CPU、内存还是IO问题至关重要。
操作流程:
- 快速概览:“显示所有容器的资源统计信息。” AI调用
container_stats,返回每个容器的CPU百分比、内存使用量/限制、网络IO等实时数据。你可以一眼看出哪个容器是“资源大户”。 - 持续监控:“持续监控
api-gateway容器的状态5秒钟。” AI可以循环调用container_stats或利用工具本身的流式能力(如果实现),观察资源使用的变化趋势,判断是否存在内存泄漏或CPU尖峰。 - 结合日志分析:发现某个容器CPU持续过高后,立刻让AI查看该容器的最近日志(
container_logs),结合时间点分析是否有异常请求或错误循环。
参数解读:container_stats返回的数据中,cpu_percent是相对于单个CPU核心的百分比。如果你的宿主机是4核,一个容器满载运行时,这个值可能达到400%。mem_usage和mem_limit能帮你快速判断容器是否接近内存限制,从而可能触发OOM(Out-Of-Memory)被杀。
4.4 镜像管理:清理磁盘空间
开发久了,本地会堆积大量中间镜像和悬空镜像,占用大量磁盘空间。
清理策略:
- 调查:“列出我所有的Docker镜像,按大小排序。” AI调用
list_images,你可以看到每个镜像的仓库标签、ID和虚拟大小。 - 识别无用镜像:重点关注那些
<none>:<none>的悬空镜像(dangling images),它们通常是构建过程中产生的中间层,已无用处。 - 安全删除:确认某个镜像确实无用后,指令AI:“删除镜像ID为
a1b2c3d4的镜像。” AI调用remove_image。对于有标签的镜像,使用仓库名和标签来指定更安全。
重要警告:通过AI删除镜像或容器是“一念之间”的事,没有二次确认弹窗。因此,对于删除操作,务必在指令中提供非常明确的标识(如完整的镜像ID或
repository:tag),避免AI误解你的意图而删错。例如,“删除所有名为<none>的镜像”这种模糊指令要避免使用。
5. 开发、扩展与自定义
如果你觉得现有的工具不够用,或者想学习MCP服务器的开发模式,这个项目本身也是一个很好的模板。
5.1 本地开发与测试
首先克隆项目并安装依赖:
git clone https://github.com/ofershap/mcp-server-docker.git cd mcp-server-docker npm install项目结构非常清晰:
src/index.ts:入口文件,定义了MCP服务器实例和所有工具。src/tools/:目录下应该包含了各个Docker操作的工具函数实现(如containerTools.ts,imageTools.ts)。src/types.ts:可能定义了MCP工具相关的类型接口。
运行测试确保一切正常:npm test构建TypeScript代码:npm run build
5.2 如何添加一个新工具
假设你想添加一个prune_system工具,用于一键清理Docker系统(类似docker system prune -a)。
在
src/tools/下创建或编辑工具文件,例如systemTools.ts:import { Docker } from 'dockerode'; import { z } from 'zod'; // 项目通常使用zod进行参数验证 export interface PruneSystemResult { containersDeleted: string[]; imagesDeleted: string[]; volumesDeleted: string[]; spaceReclaimed: number; // 字节数 } export async function pruneSystem(docker: Docker): Promise<PruneSystemResult> { // Dockerode的system.prune()返回Promise<PruneSystemInfo> const pruneInfo = await docker.system.prune({ filters: { dangling: { true: ['true'] } }, // 可以添加过滤器 }); // 将返回的数据格式化为更友好的结构 return { containersDeleted: pruneInfo.ContainersDeleted || [], imagesDeleted: pruneInfo.ImagesDeleted || [], volumesDeleted: pruneInfo.VolumesDeleted || [], spaceReclaimed: pruneInfo.SpaceReclaimed, }; }在
src/index.ts中注册这个新工具:import { pruneSystem } from './tools/systemTools'; // ... 其他导入 const server = new McpServer({ name: "docker-server", version: "1.0.0", }); // 注册现有工具... // 注册新的系统清理工具 server.tool( "prune_system", "清理Docker系统,删除所有未使用的容器、镜像、网络和卷。警告:此操作不可逆!", {}, async () => { const result = await pruneSystem(docker); return { content: [{ type: "text", text: `清理完成!\n` + `已删除容器: ${result.containersDeleted.length}个\n` + `已删除镜像: ${result.imagesDeleted.length}个\n` + `已删除卷: ${result.volumesDeleted.length}个\n` + `释放空间: ${(result.spaceReclaimed / 1024 / 1024).toFixed(2)} MB` }] }; } );更新类型定义和测试,然后重新构建、测试。
5.3 自定义连接与高级配置
默认连接本地Docker Socket很方便,但如果你需要连接远程Docker守护进程(通常不推荐,除非在安全内网),或者使用Docker Context,你可以修改初始化dockerode实例的方式。
例如,在src/index.ts中:
// 默认连接(本地socket) import Docker from 'dockerode'; const docker = new Docker(); // 如果你想通过TCP连接远程Docker(需配置TLS) // const docker = new Docker({ // host: '192.168.1.100', // port: 2376, // ca: fs.readFileSync('ca.pem'), // cert: fs.readFileSync('cert.pem'), // key: fs.readFileSync('key.pem'), // }); // 或者使用特定的Docker Context // const docker = new Docker({ socketPath: '/path/to/custom/docker.sock' });安全警告:将Docker守护进程暴露在TCP端口(即使有TLS)会显著增加攻击面。仅在绝对必要且网络环境可信的情况下才考虑这样做,并确保使用强证书和严格的网络防火墙规则。
6. 常见问题与故障排除实录
在实际使用和与社区交流中,我积累了一些典型问题的解决方法。
6.1 服务器启动失败或连接错误
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| AI助手报告“无法连接到Docker服务器”或“工具调用失败”。 | 1. Docker守护进程未运行。 2. 用户权限不足,无法访问Docker Socket。 3. MCP服务器进程启动失败(如Node.js版本不兼容)。 | 1. 运行docker version确认Docker正在运行。2. 按照上文【3.4 权限问题排查】步骤检查和修复权限。 3. 尝试在终端手动运行 npx -y mcp-docker-server,观察控制台是否有错误输出(如Node版本报错)。 |
手动运行MCP服务器时,报错Cannot find module 'dockerode'。 | npx在临时安装依赖时网络问题或缓存异常。 | 清除npm缓存:npm cache clean --force,然后重试。或者,在项目目录内本地安装并运行:npm install && npm start(如果项目提供了start脚本)。 |
| 在Windows上,AI助手无法找到Docker。 | Docker Desktop未运行,或命名管道路径问题。 | 确保Docker Desktop已启动并运行在Windows容器模式。MCP服务器默认使用Windows的Docker命名管道,这通常由Docker Desktop自动配置。 |
6.2 工具调用成功但返回空或意外结果
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
list_containers返回空数组,但你确定有容器在运行。 | 工具可能被配置为默认只返回“运行中”的容器,而你的容器可能处于created,paused或exited状态。 | 在向AI提问时更精确,例如:“列出所有容器,包括已停止的。” AI会传递all: true参数给工具。 |
container_logs返回“容器不存在”错误。 | 提供的容器名称或ID不正确。容器名称可能包含项目前缀(如在Compose中)。 | 先使用list_containers确认容器的准确名称或ID。注意Docker会自动生成名称,也可能是project_service_1这样的格式。 |
exec_command执行失败,返回exec failed。 | 目标容器未处于running状态。或者要执行的命令在容器内不存在。 | 先用list_containers确认容器状态。对于命令,尽量使用基础命令(sh,bash,cat,ls),并确认容器镜像包含这些二进制文件。 |
6.3 性能与稳定性考量
- 资源占用:该MCP服务器是一个轻量级Node.js进程,内存占用通常很小(几十MB)。但如果配置了
container_logs的follow: true来跟踪日志,并且日志量巨大,可能会增加一些CPU和内存开销。建议只在需要时开启跟踪,用完即停。 - 连接稳定性:MCP服务器是持久化进程。如果长时间不适用,客户端(如Cursor)可能会断开连接。大多数客户端具备重连机制,如果发现工具突然失效,尝试在客户端内重新触发一次对话或重启客户端。
- 与Docker CLI的差异:这个工具封装了最常用的操作,但并非100%覆盖
dockerCLI的所有命令和复杂参数。对于非常高级或特定的管理任务(如配置网络、Swarm集群管理),可能仍需回到终端。
6.4 安全最佳实践总结
- 最小权限原则:用于运行该MCP服务器的用户账户,只应拥有完成其功能所需的最小权限。通常,将其加入
docker组是必要的,但这就赋予了该账户在宿主机上近乎root的权限(因为Docker容器可以挂载宿主机目录)。因此,请确保该用户账户本身是安全的。 - 谨慎授权删除操作:意识到当你让AI执行
remove_container或remove_image时,它等同于你在终端敲下了docker rm -f或docker rmi。对于生产环境或存有关键数据的容器,操作前务必三思。 - 隔离开发环境:如果可能,在开发机上使用非root用户运行Docker Desktop,并利用Docker的用户命名空间映射功能,以进一步隔离容器与宿主机的权限。
- 网络隔离:该工具默认只连接本地Socket,这是安全的。切勿为了方便而将其配置为监听网络端口,除非你完全理解并接受了相应的安全风险。
这个项目把我从频繁切换终端的琐碎中解放了出来,让我能更专注地沉浸在代码逻辑里。它的价值不在于替代完整的Docker管理平台,而在于为日常高频、轻量的操作提供了一个无比流畅的“语音助手”。当你习惯了对着AI说“把那个出问题的服务日志给我看看”而不是去翻找终端窗口时,你会发现这种交互方式带来的心流体验提升是实实在在的。如果你也受困于类似的上下文切换,不妨花十分钟配置一下,它可能会成为你开发工具箱里又一个“用了就回不去”的利器。