MCP 서버를 Claude Desktop 에 붙이는 문제
MCP 서버 등록이 문제였습니다. ZPi Controller 가 MCP 서버를 노출하고, 사용자가 자기 Claude Desktop 에 연결하려면:
- MCP exe 다운로드 (101 MB 자체포함 .NET)
- 적절한 위치에 배치
claude_desktop_config.json에 JSON 항목 추가- Claude Desktop 재시작
이걸 “엔지니어가 아닌 사람”이 하게 만들어야 했습니다. 시스템 페이지 → [MCP 설치 (PC연동)] 버튼 한 번 → BAT 다운로드 → 더블클릭 → 끝. 이 한 줄 UX 를 위해 의외로 많이 삽질했습니다.

시도 1: 단순 BAT — 즉시 실패
@echo off
powershell -Command "Invoke-WebRequest http://192.168.0.x:5000/api/mcp/exe -OutFile '%LOCALAPPDATA%\Programs\ZpiController\ZPi.Controller.Mcp.exe'"
... (config 패치)
echo 완료
pause
실패 1: 한국어 메시지에 CP949 인코딩으로 BAT 가 cmd.exe 파서를
깨뜨립니다. .은(는) 예상되지 않았습니다 에러입니다.
→ BAT 전체를 영어 ASCII 로 바꿨습니다. 사용자에게 보일 문구만 한글입니다 (config 패치 완료 안내).
실패 2: Download failed: 파일이 다른 프로세스에서 사용 중. Claude
Desktop 이 이미 MCP exe 를 열고 있어서 덮어쓰기 거부합니다.
→ 다운로드 전에 Claude Desktop / MCP 프로세스 종료 단계 추가했습니다.
실패 3: taskkill /IM Claude.exe /F 가 Claude Code (CCD) 까지
같이 죽입니다. 사용자가 CCD 에서 BAT 실행했으면 본인 세션 종료됩니다.
→ Process Path 보고 분기합니다. claude-code\ 가 들어간 경로는 skip 합니다:
Get-Process claude -EA 0 |
Where-Object { $_.Path -and $_.Path -notmatch 'claude-code' } |
Stop-Process -Force
시도 2: PowerShell escape 지옥
PowerShell escape는 BAT 안에서 한 줄로 JSON 을 패치할 때 터집니다:
powershell -Command "$exe='%EXE%'; $piHost='{host}'; ...
$entry=[PSCustomObject]@{ command=$exe; args=@('--pi',$piHost) };
if(...) { ... } else { ... }"
PowerShell 안에서 \" 로 큰따옴표 escape, \\\" 로 BAT 안의 큰따옴표를 escape 합니다.
괄호 안의 명령 (if (...)) 이 BAT 의 for 블록 안에 있으면 괄호
매칭이 깨집니다.
증상: PowerShell 출력은 정상인데 그 후에 .은(는) 예상되지 않았습니다
같은 cmd 파서 에러가 뜹니다. 디버깅하다 보니 — BAT 의 if errorlevel 1 ( echo Pi (192.168.0.x) 에 접속... ). 에서 메시지의 괄호가 if (...)
블록 괄호와 매칭돼서 블록이 일찍 닫히고, 남은 . 가 명령으로 해석됩니다.
→ 모든 메시지의 괄호 (...) 를 대괄호 [...] 로 변경했습니다. if errorlevel 1 (...) 블록은 goto :err 로 치환했습니다:
powershell ... -Command "..."
if errorlevel 1 goto :err_download
goto :ok
:err_download
echo *** Download failed.
pause & exit /b 1
:ok
...
시도 3: MS Store Claude Desktop 런치
Claude Desktop 자동 재시작이 MCP 설치 후에 필요합니다. 정규 Claude.exe 가
있으면 start "" "%LOCALAPPDATA%\Programs\Claude\Claude.exe" 가 깔끔합니다.
근데 사용자는 MS Store 버전을 쓰고 있었습니다 (C:\Program Files\WindowsApps\Claude_<버전>_x64__pzs8sxrjxfjjc\app\Claude.exe).
MS Store 앱은 직접 exe 경로로 못 띄웁니다. AppX 매니페스트의 shell:
URI 가 필요합니다:
shell:AppsFolder\Claude_pzs8sxrjxfjjc!App
이걸 어떻게 BAT 에서 띄우나? 여러 방법이 있습니다.
| 시도 | 결과 |
|---|---|
Start-Process "shell:AppsFolder\..." | “지정된 파일을 찾을 수 없습니다” 에러 |
Start-Process explorer.exe "shell:AppsFolder\..." | 앱은 뜨는데 AppsFolder 창도 같이 뜸 |
start "" "shell:AppsFolder\..." | “찾을 수 없습니다” |
ProcessStartInfo.UseShellExecute=true | 잘 동작! 근데 한국어 escape 가 깨짐 |
너무 많이 변형해본 끝에 결론: 자동 launch 자체를 포기했습니다. 사용자한테 시작 메뉴에서 Claude 클릭하라고 알리는 게 가장 신뢰성 높았습니다:
echo [5/5] Finishing up...
echo Please restart Claude Desktop manually:
echo 1. Open Start Menu [Win key]
echo 2. Type Claude and press Enter
echo The MCP entry will be loaded on next startup.
가끔은 자동화 욕심을 버리는 게 정답입니다. 수동 한 클릭 < 5가지 환경에서 실패하는 자동화입니다.
MCP 토큰 자동 주입
MCP 토큰 인증은 MCP exe 가 ZPi API 를 호출할 때 필요합니다 (운영자 비번을
외울 순 없으니까). 각 Pi 는 첫 부팅 시 32바이트 랜덤 토큰을 자동 생성해
mcp-token.txt 에 저장합니다 (0600 권한). BAT 가 이 토큰을 받아서 Claude
config 의 args 에 박습니다:
{
"mcpServers": {
"going-zpi-XXX": {
"command": "C:\\Users\\<you>\\AppData\\Local\\Programs\\ZpiController\\ZPi.Controller.Mcp.exe",
"args": ["--pi", "going-zpi-XXX.local", "--token", "<your-token>"]
}
}
}
MCP exe 는 모든 HTTP 요청에 X-MCP-Token 헤더를 자동 추가하고, Pi 의
auth gate 가 이 토큰을 cookie 없이도 통과시킵니다.
SKIP_DOWNLOAD 최적화
재설치 다운로드 skip이 없으면 BAT 한 번 깔린 PC 에서 다시 실행할 때 101 MB 를 또 받습니다. 귀찮습니다. 그래서:
echo [3/5] Installing MCP server...
set "SKIP_DOWNLOAD=0"
if exist "%EXE%" (
for %%I in ("%EXE%") do (
if %%~zI GTR 52428800 set "SKIP_DOWNLOAD=1"
)
)
if "%SKIP_DOWNLOAD%" == "1" (
echo Already installed - skipping download.
goto :download_done
)
echo Downloading [~101 MB]...
... powershell Invoke-WebRequest ...
:download_done
50 MB 넘는 exe 가 이미 있으면 다운로드 skip 합니다. 두 번째 실행부터는 토큰 재발급 + config 패치만 하니까 1-2초 안에 끝납니다.
다중 디바이스 지원 — 호스트명 기반 MCP key
Claude config 의 MCP key는 원래 하드코딩 "zpi-controller" 였습니다. 그
결과 한 PC 에서 BAT 실행 → zpi-controller: --pi 192.168.0.x,
그 다음 같은 PC 에서 다른 ZPi 의 BAT 실행 → 같은 key 덮어쓰기 → 먼저 등록한 게
사라집니다.
해결: key 를 호스트명에서 유도합니다. 보드 A (going-zpi-AAA) 가 만든 BAT 은
going-zpi-AAA, 보드 B (going-zpi-BBB) 가 만든 BAT 은 going-zpi-BBB 으로
다른 key 를 씁니다. 둘 다 Claude 에 등록되고 도구 이름도 자동으로 prefix 됩니다
(mcp__going-zpi-AAA__plc_set_output, mcp__going-zpi-BBB__plc_set_output).
// Software/Program.cs - install.bat generator
var entryKey = host.EndsWith(".local")
? host[..^6] // "going-zpi-XXX.local" → "going-zpi-XXX"
: host;
var safeEntryKey = Regex.Replace(entryKey, @"[^a-zA-Z0-9._\-]", "_");
위 호스트명(
going-zpi-AAA/going-zpi-BBB)은 IP 끝자리에서 자동으로 유도되는 예시 패턴일 뿐입니다. 실제 보드는 각자 다른 번호를 갖습니다.
여러 ZPi 가 있어도 자동으로 모두 인식합니다. 청소도 자동입니다 — 옛 zpi-controller
key 가 있으면 BAT 가 삭제하고 새 호스트별 key 로 교체합니다.
정리 — 설치 UX 가 가장 어려운 부분
설치 UX가 가장 어려운 부분이었습니다. 기능 코드는 며칠이면 짭니다. 그런데 “엔지니어 아닌 사람도 한 번 더블클릭으로 설치” 가 되게 만드는 건 인내심 테스트입니다:
- 한국어 인코딩 (CP949 vs UTF-8 vs ASCII)
- cmd.exe 의 파서 함정 (괄호 매칭,
.명령) - PowerShell ↔ BAT escape 지옥
- Windows 의 다양한 Claude Desktop 설치 변종 (정규 vs MS Store)
- 사용자 vs Claude Code 프로세스 구분
- 토큰 라이프사이클, 호스트명 변경, 다중 디바이스
각 함정을 만났을 때 “이건 더 단순한 방법이 있을 것” 같았는데 결국 없었습니다. UX 는 누적된 작은 결정의 집합입니다.
한 번 더블클릭으로 끝나는 설치 UX 는 인코딩·파서·escape·설치 변종을 하나씩 걷어낸 작은 결정들의 합입니다.
ZPi Controller 시리즈 다음 편
문의
- Email : [email protected]
- Insta : https://www.instagram.com/going.sen/
- Website : https://intosen.com/kr/consult/
댓글
닉네임만 입력하면 바로 댓글을 남길 수 있어요. Google/GitHub 로그인도 가능합니다.