概述
在 Docker 中运行 lark-mcp 作为 MCP 服务,供 opencode 调用 Lark API。核心需要解决两个问题:
- Token 持久化 —
lark-mcp通过 keytar → libsecret → D-Bus → gnome-keyring 存储 token,Docker 无桌面环境全链路不可用 - 端口映射 — OAuth 登录回调需要浏览器访问容器内的登录服务
相关文件
| 文件 | 作用 |
|---|---|
Dockerfile | 镜像构建,安装所有依赖 |
scripts/entrypoint.sh | 容器启动时初始化 dbus + keyring |
scripts/start.sh | 启动容器 + 执行 OAuth 登录 |
opencode.json | opencode 的 MCP 配置 |
一、Token 持久化
依赖链
1
lark-mcp → keytar(Node.js) → libsecret(C) → D-Bus IPC → org.freedesktop.secrets(gnome-keyring)
Docker slim 镜像缺了三个环节:machine-id、dbus-daemon、gnome-keyring。
Dockerfile 安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# keytar 编译依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
pkg-config libsecret-1-dev libsecret-1-0 socat \
&& rm -rf /var/lib/apt/lists/*
# D-Bus + 密钥存储服务
RUN apt-get update && apt-get install -y --no-install-recommends \
dbus-daemon dbus-session-bus-common gnome-keyring \
&& rm -rf /var/lib/apt/lists/*
# machine-id(dbus 启动必需)
RUN mkdir -p /var/lib/dbus && \
echo "b9e7a32c5d8e4a2f9e6d1c3a7b5f8e2a" > /etc/machine-id && \
ln -sf /etc/machine-id /var/lib/dbus/machine-id
安装
dbus-daemon时需先apt-get update,slim 镜像的 apt cache 中不包含这个包。
entrypoint.sh(容器启动时自动初始化)
1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
set -e
# 启动 D-Bus session bus
dbus-daemon --session --address=unix:path=/tmp/dbus-session --fork || true
export DBUS_SESSION_BUS_ADDRESS=unix:path=/tmp/dbus-session
# 启动 gnome-keyring,空密码 unlock 创建默认 "login" collection
export GNOME_KEYRING_CONTROL=/root/.cache/keyring
echo "" | gnome-keyring-daemon --unlock --components=secrets >/dev/null 2>&1 || true
exec "$@"
ENTRYPOINT ["/entrypoint.sh"] 确保容器启动时自动执行上述初始化,环境变量传递给主进程。
二、端口映射(OAuth 登录回调)
问题
lark-mcp login 默认监听 127.0.0.1:3000。Docker 的 -p 将流量转发到容器 IP(如 172.17.0.x),而非 127.0.0.1,所以外部连不上。
如果用 --host 0.0.0.0 会触发 MCP SDK OAuth 库的 HTTPS 强制检查报错:Error: Issuer URL must be HTTPS。
解决:不同端口 + socat 桥接
关键思路:宿主机和容器的端口号不同,避免冲突,同时让 lark-mcp login 保持在 127.0.0.1 上运行,不触发 HTTPS 检查。
1
2
3
4
5
6
7
8
9
用户浏览器 http://localhost:3000/authorize?...
│
│ Docker: -p 3000:3001(宿主机 3000 → 容器 3001)
▼
容器 0.0.0.0:3001
│
│ socat: TCP-LISTEN:3001 → TCP:127.0.0.1:3000
▼
容器 127.0.0.1:3000(lark-mcp login,默认端口,无 HTTPS 检查)
start.sh 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 暴露容器 3001 到宿主机 3000
docker run -d --restart unless-stopped \
--name lark-bot \
-p 3000:3001 \
# 2. socat 桥接:0.0.0.0:3001 → 127.0.0.1:3000
docker exec -d lark-bot socat TCP-LISTEN:3001,fork,reuseaddr TCP:127.0.0.1:3000
# 3. lark-mcp login 默认绑 127.0.0.1:3000(不需要 --port)
docker exec -e DBUS_SESSION_BUS_ADDRESS=unix:path=/tmp/dbus-session lark-bot \
npx -y @larksuiteoapi/lark-mcp login -a "$APP_ID" -s "$APP_SECRET"
# 4. 验证 token 是否成功持久化:
docker exec -e DBUS_SESSION_BUS_ADDRESS=unix:path=/tmp/dbus-session lark-bot \
sh -c 'npx -y @larksuiteoapi/lark-mcp whoami'
注意:
docker exec不会继承 entrypoint 中export的环境变量,所以所有docker exec lark-mcp命令都要显式传-e DBUS_SESSION_BUS_ADDRESS=unix:path=/tmp/dbus-session。
远程服务器的情况
如果 Docker 跑在远程服务器(如腾讯云)上,用户浏览器访问 http://localhost:3000 指向的是本地 Mac,不是远程服务器的 3000 端口。需要用 SSH 本地端口转发(-L)把远程的 3000 端口映射到本地:
1
ssh -L 3000:localhost:3000 -i /path/to/key.pem root@<server-ip>
完整路径如下:
| 方向 | 路径 |
|---|---|
用户浏览器 http://localhost:3000 | → 本地 Mac 3000 端口 |
SSH -L 3000:localhost:3000 | → 远程服务器 localhost:3000 |
Docker -p 3000:3001 | → 容器 0.0.0.0:3001 |
| socat 桥接 | → 容器 127.0.0.1:3000 |
| lark-mcp login | ← 收到 callback,登录完成 |
-L的含义:-L 本地端口:目标主机:目标端口,将本地 Mac 的 3000 端口流量通过 SSH 隧道转发到远程服务器的 localhost:3000。
OAuth 回调完整流程
| 步骤 | 说明 |
|---|---|
| 1 | 浏览器打开 http://localhost:3000/authorize?... |
| 2 | 重定向到 Lark 授权页,用户登录授权 |
| 3 | Lark 将浏览器重定向回 http://localhost:3000/callback?code=... |
| 4 | -p 3000:3001 → 容器内 0.0.0.0:3001 |
| 5 | socat → 127.0.0.1:3000 |
| 6 | lark-mcp login 收到 code,交换成 token,存入 gnome-keyring |
三、AI Agent MCP 配置(以 opencode 为例)
在 opencode.json 中配置 lark-mcp 作为 MCP 服务端:
1
2
3
4
5
6
{
"command": [
"sh", "-c",
"lark-mcp mcp -a \"$LARK_APP_ID\" -s \"$LARK_APP_SECRET\" --oauth --token-mode user_access_token --domain https://open.larksuite.com"
]
}
关键点:
- 通过
sh -c从环境变量读凭据,不硬编码到配置文件 --oauth自动使用已存储的 user_access_token,过期时自动刷新--token-mode user_access_token使用用户级别的 token,可以访问用户自己的文档- 环境变量在
docker run --env-file .env时注入
总结
在 Docker 中集成 lark-mcp 的核心挑战来自其 token 存储链路对桌面环境的依赖。通过安装 dbus-daemon + gnome-keyring + 手动注入 machine-id,结合 entrypoint 脚本自动初始化 session bus,可以在无桌面环境的容器中完整跑通 keytar → libsecret → D-Bus → gnome-keyring 链路。OAuth 登录回调则通过 socat 桥接不同端口绕过 HTTPS 检查限制。
效果图
