add mcp-ssh-manager config
This commit is contained in:
parent
40c140bbbf
commit
9d700a412d
11
.mcp.json
Normal file
11
.mcp.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"ssh-manager": {
|
||||
"command": "node",
|
||||
"args": ["/tmp/mcp-ssh-manager/node_modules/mcp-ssh-manager/src/index.js"],
|
||||
"env": {
|
||||
"DOTENV_CONFIG_PATH": ".env"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
222
docs/mcp/install-launchd.sh
Normal file
222
docs/mcp/install-launchd.sh
Normal file
@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
LABEL="com.bvisible.mcp-ssh-manager"
|
||||
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"
|
||||
LOG_DIR="$HOME/Library/Logs/mcp-ssh-manager"
|
||||
OUT_LOG="$LOG_DIR/out.log"
|
||||
ERR_LOG="$LOG_DIR/err.log"
|
||||
|
||||
fail_cleanup() {
|
||||
if launchctl print "gui/$(id -u)/${LABEL}" >/dev/null 2>&1; then
|
||||
launchctl bootout "gui/$(id -u)" "$PLIST_PATH" >/dev/null 2>&1 || true
|
||||
fi
|
||||
if [ -f "$PLIST_PATH" ]; then
|
||||
rm -f "$PLIST_PATH" || true
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "usage: $0 {install|uninstall|status|logs}"
|
||||
}
|
||||
|
||||
pick_node() {
|
||||
if [ -x /opt/homebrew/bin/node ]; then
|
||||
echo /opt/homebrew/bin/node
|
||||
elif [ -x /usr/local/bin/node ]; then
|
||||
echo /usr/local/bin/node
|
||||
elif command -v node >/dev/null 2>&1; then
|
||||
command -v node
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
find_repo_up() {
|
||||
local dir="$PWD"
|
||||
for _ in 0 1 2 3 4 5; do
|
||||
local pj="$dir/package.json"
|
||||
if [ -f "$pj" ] && /usr/bin/grep -E '"name"[[:space:]]*:[[:space:]]*"[^\"]*mcp-ssh-manager' "$pj" >/dev/null 2>&1; then
|
||||
echo "$dir"
|
||||
return 0
|
||||
fi
|
||||
dir="$(dirname "$dir")"
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
find_repo_search() {
|
||||
local base
|
||||
for base in "$HOME/code" "$HOME/projects" "$HOME/src"; do
|
||||
if [ -d "$base" ]; then
|
||||
local hit
|
||||
hit="$(find "$base" -maxdepth 3 -type d -name '*mcp-ssh-manager*' 2>/dev/null | head -n 1)"
|
||||
if [ -n "$hit" ]; then
|
||||
echo "$hit"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
find_binary_under() {
|
||||
local base
|
||||
for base in /usr/local /opt/homebrew; do
|
||||
if [ -d "$base" ]; then
|
||||
local hit
|
||||
hit="$(find "$base" -maxdepth 4 -type f -name 'mcp-ssh-manager' 2>/dev/null | head -n 1)"
|
||||
if [ -n "$hit" ]; then
|
||||
echo "$hit"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
write_plist() {
|
||||
local workdir="$1"
|
||||
shift
|
||||
local -a args=("$@")
|
||||
|
||||
mkdir -p "$(dirname "$PLIST_PATH")" "$LOG_DIR"
|
||||
|
||||
{
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?>'
|
||||
echo '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
|
||||
echo '<plist version="1.0">'
|
||||
echo '<dict>'
|
||||
echo ' <key>Label</key>'
|
||||
echo " <string>${LABEL}</string>"
|
||||
echo ''
|
||||
echo ' <key>ProgramArguments</key>'
|
||||
echo ' <array>'
|
||||
for arg in "${args[@]}"; do
|
||||
echo " <string>${arg}</string>"
|
||||
done
|
||||
echo ' </array>'
|
||||
if [ -n "$workdir" ]; then
|
||||
echo ''
|
||||
echo ' <key>WorkingDirectory</key>'
|
||||
echo " <string>${workdir}</string>"
|
||||
fi
|
||||
echo ''
|
||||
echo ' <key>EnvironmentVariables</key>'
|
||||
echo ' <dict>'
|
||||
echo ' <key>PATH</key>'
|
||||
echo ' <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>'
|
||||
echo ' </dict>'
|
||||
echo ''
|
||||
echo ' <key>RunAtLoad</key>'
|
||||
echo ' <true/>'
|
||||
echo ''
|
||||
echo ' <key>KeepAlive</key>'
|
||||
echo ' <true/>'
|
||||
echo ''
|
||||
echo ' <key>StandardOutPath</key>'
|
||||
echo " <string>${OUT_LOG}</string>"
|
||||
echo ''
|
||||
echo ' <key>StandardErrorPath</key>'
|
||||
echo " <string>${ERR_LOG}</string>"
|
||||
echo '</dict>'
|
||||
echo '</plist>'
|
||||
} > "$PLIST_PATH"
|
||||
}
|
||||
|
||||
install() {
|
||||
trap 'fail_cleanup' ERR
|
||||
|
||||
local binary_path=""
|
||||
if command -v mcp-ssh-manager >/dev/null 2>&1; then
|
||||
binary_path="$(command -v mcp-ssh-manager)"
|
||||
else
|
||||
binary_path="$(find_binary_under || true)"
|
||||
fi
|
||||
|
||||
if [ -n "$binary_path" ]; then
|
||||
echo "using binary: $binary_path"
|
||||
write_plist "" "$binary_path"
|
||||
else
|
||||
local repo=""
|
||||
repo="$(find_repo_up || true)"
|
||||
if [ -z "$repo" ]; then
|
||||
repo="$(find_repo_search || true)"
|
||||
fi
|
||||
|
||||
if [ -z "$repo" ]; then
|
||||
echo "could not find mcp-ssh-manager binary or repo" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local pj="$repo/package.json"
|
||||
local has_start=""
|
||||
if [ -f "$pj" ] && /usr/bin/grep -E '"start"[[:space:]]*:' "$pj" >/dev/null 2>&1; then
|
||||
has_start=1
|
||||
fi
|
||||
|
||||
if [ -n "$has_start" ]; then
|
||||
local pm=""
|
||||
if [ -f "$repo/pnpm-lock.yaml" ] && command -v pnpm >/dev/null 2>&1; then
|
||||
pm="$(command -v pnpm)"
|
||||
echo "using pnpm start in repo: $repo"
|
||||
write_plist "$repo" "$pm" "start"
|
||||
elif command -v npm >/dev/null 2>&1; then
|
||||
pm="$(command -v npm)"
|
||||
echo "using npm run start in repo: $repo"
|
||||
write_plist "$repo" "$pm" "run" "start"
|
||||
else
|
||||
echo "start script exists but npm or pnpm not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
local node
|
||||
node="$(pick_node)"
|
||||
if [ -z "$node" ]; then
|
||||
echo "node not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
local entry=""
|
||||
for p in "dist/index.js" "build/index.js" "index.js" "src/index.ts"; do
|
||||
if [ -f "$repo/$p" ]; then
|
||||
entry="$repo/$p"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$entry" ]; then
|
||||
echo "no entry file found in repo" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "using node entry: $entry"
|
||||
write_plist "$repo" "$node" "$entry"
|
||||
fi
|
||||
fi
|
||||
|
||||
launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH"
|
||||
launchctl kickstart -k "gui/$(id -u)/${LABEL}"
|
||||
|
||||
echo "installed: $PLIST_PATH"
|
||||
echo "logs: $LOG_DIR"
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
launchctl bootout "gui/$(id -u)" "$PLIST_PATH" >/dev/null 2>&1 || true
|
||||
rm -f "$PLIST_PATH"
|
||||
echo "removed: $PLIST_PATH"
|
||||
}
|
||||
|
||||
status() {
|
||||
launchctl print "gui/$(id -u)/${LABEL}"
|
||||
}
|
||||
|
||||
logs() {
|
||||
tail -f "$OUT_LOG" "$ERR_LOG"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
install) install ;;
|
||||
uninstall) uninstall ;;
|
||||
status) status ;;
|
||||
logs) logs ;;
|
||||
*) usage; exit 1 ;;
|
||||
esac
|
||||
204
docs/mcp/mcp-ssh-manager-macos.md
Normal file
204
docs/mcp/mcp-ssh-manager-macos.md
Normal file
@ -0,0 +1,204 @@
|
||||
# mcp-ssh-manager macOS 自启 launchd
|
||||
|
||||
本指南适用于 **macOS 13+(含 macOS 26)**,使用 **launchd** 设置登录后自启。方案可回滚、可调试、可日志追踪,不修改业务代码,也不使用第三方守护工具。
|
||||
|
||||
## 1) 自适应探测规则
|
||||
|
||||
脚本会按顺序自动探测启动方式,成功即用:
|
||||
|
||||
1) 若 `command -v mcp-ssh-manager` 存在,使用其绝对路径作为 `ProgramArguments[0]`。
|
||||
2) 否则按以下范围搜索仓库或安装路径:
|
||||
- 当前工作目录及其父级(最多向上 5 层),查找 `package.json` 且 `name` 含 `mcp-ssh-manager`。
|
||||
- `~/code`、`~/projects`、`~/src` 下搜索目录名包含 `mcp-ssh-manager`,深度 `<=3`。
|
||||
- `/usr/local`、`/opt/homebrew` 下搜索 `mcp-ssh-manager` 相关文件。
|
||||
3) 若找到 Node 项目:
|
||||
- 优先 `pnpm start` 或 `npm run start`(`scripts.start` 存在时)。
|
||||
- 否则使用 `node <entry>`,入口依次为:`dist/index.js` -> `build/index.js` -> `index.js` -> `src/index.ts`。
|
||||
4) Node 解释器绝对路径优先级:`/opt/homebrew/bin/node` -> `/usr/local/bin/node` -> `which node`。
|
||||
5) `ProgramArguments` 使用绝对路径,禁止依赖 shell PATH 或 `~/.zshrc`。
|
||||
|
||||
## 2) 安装脚本
|
||||
|
||||
同目录脚本:`docs/mcp/install-launchd.sh`。用法:
|
||||
|
||||
```
|
||||
./install-launchd.sh install
|
||||
```
|
||||
|
||||
脚本会生成并安装 plist:
|
||||
|
||||
```
|
||||
~/Library/LaunchAgents/com.bvisible.mcp-ssh-manager.plist
|
||||
```
|
||||
|
||||
日志目录:
|
||||
|
||||
```
|
||||
~/Library/Logs/mcp-ssh-manager/
|
||||
```
|
||||
|
||||
## 3) 生成的 plist 完整内容
|
||||
|
||||
脚本会根据探测结果生成 plist。以下为两种可能的完整内容示例。
|
||||
|
||||
**示例 A:直接使用二进制**
|
||||
|
||||
```
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.bvisible.mcp-ssh-manager</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/mcp-ssh-manager</string>
|
||||
</array>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Users/USER/Library/Logs/mcp-ssh-manager/out.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Users/USER/Library/Logs/mcp-ssh-manager/err.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
**示例 B:Node 项目入口**
|
||||
|
||||
```
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.bvisible.mcp-ssh-manager</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/opt/homebrew/bin/node</string>
|
||||
<string>/Users/USER/path/to/mcp-ssh-manager/dist/index.js</string>
|
||||
</array>
|
||||
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/Users/USER/path/to/mcp-ssh-manager</string>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Users/USER/Library/Logs/mcp-ssh-manager/out.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Users/USER/Library/Logs/mcp-ssh-manager/err.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
## 4) plist 关键字段说明
|
||||
|
||||
- `Label`:launchd 唯一标识,用于 `launchctl` 管理。
|
||||
- `ProgramArguments`:启动命令与参数,必须为绝对路径,禁止使用 shell 包裹。
|
||||
- `WorkingDirectory`:Node 项目使用的工作目录,必须为绝对路径。
|
||||
- `EnvironmentVariables`:明确指定 PATH,避免 launchd 的默认 PATH 过短。
|
||||
- `RunAtLoad`:plist 加载时立即启动。
|
||||
- `KeepAlive`:进程退出时自动拉起。
|
||||
- `StandardOutPath`/`StandardErrorPath`:stdout 与 stderr 日志路径。
|
||||
|
||||
## 5) 一键命令
|
||||
|
||||
以下命令均可直接复制执行:
|
||||
|
||||
```
|
||||
./install-launchd.sh install
|
||||
./install-launchd.sh uninstall
|
||||
./install-launchd.sh status
|
||||
./install-launchd.sh logs
|
||||
```
|
||||
|
||||
## 6) 手动命令
|
||||
|
||||
**安装与启动:**
|
||||
|
||||
```
|
||||
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.bvisible.mcp-ssh-manager.plist
|
||||
launchctl kickstart -k gui/$(id -u)/com.bvisible.mcp-ssh-manager
|
||||
```
|
||||
|
||||
**卸载与停止:**
|
||||
|
||||
```
|
||||
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.bvisible.mcp-ssh-manager.plist
|
||||
rm -f ~/Library/LaunchAgents/com.bvisible.mcp-ssh-manager.plist
|
||||
```
|
||||
|
||||
**状态与调试:**
|
||||
|
||||
```
|
||||
launchctl print gui/$(id -u)/com.bvisible.mcp-ssh-manager
|
||||
```
|
||||
|
||||
**日志追踪:**
|
||||
|
||||
```
|
||||
tail -f ~/Library/Logs/mcp-ssh-manager/out.log ~/Library/Logs/mcp-ssh-manager/err.log
|
||||
```
|
||||
|
||||
## 7) 验证是否自启成功
|
||||
|
||||
1) 登录后检查 launchd 状态:
|
||||
|
||||
```
|
||||
launchctl print gui/$(id -u)/com.bvisible.mcp-ssh-manager
|
||||
```
|
||||
|
||||
2) 查看日志是否有持续输出:
|
||||
|
||||
```
|
||||
tail -f ~/Library/Logs/mcp-ssh-manager/out.log
|
||||
```
|
||||
|
||||
## 8) 常见问题与排查
|
||||
|
||||
- **权限问题**:LaunchAgents 以当前用户运行,避免写入 root 目录。
|
||||
- **WorkingDirectory 不存在**:确保仓库路径存在且为绝对路径。
|
||||
- **Node 路径错误**:确认 `/opt/homebrew/bin/node` 或 `/usr/local/bin/node` 可执行。
|
||||
- **端口占用**:检查已有进程是否占用目标端口,必要时先停止旧进程。
|
||||
- **PATH 不一致**:保证 `EnvironmentVariables.PATH` 包含常用路径。
|
||||
|
||||
**临时前台运行对照验证:**
|
||||
|
||||
- 若使用二进制:
|
||||
|
||||
```
|
||||
/usr/local/bin/mcp-ssh-manager
|
||||
```
|
||||
|
||||
- 若使用 Node 入口:
|
||||
|
||||
```
|
||||
/opt/homebrew/bin/node /Users/USER/path/to/mcp-ssh-manager/dist/index.js
|
||||
```
|
||||
|
||||
Loading…
Reference in New Issue
Block a user