Claude Desktop MCP — '한 번 더블클릭' 설치자를 만들기까지의 삽질

문제

ZPi Controller 가 MCP 서버를 노출한다. 사용자가 자기 Claude Desktop 에 연결하려면:

  1. MCP exe 다운로드 (101 MB 자체포함 .NET)
  2. 적절한 위치에 배치
  3. claude_desktop_config.json 에 JSON 항목 추가
  4. Claude Desktop 재시작

이걸 “엔지니어가 아닌 사람”이 하게 만들어야 했다. 시스템 페이지 → [MCP 설치 (PC연동)] 버튼 한 번 → BAT 다운로드 → 더블클릭 → 끝. 이 한 줄 UX 를 위해 의외로 많이 삽질했다.

시스템 페이지 — MCP 설치 카드

시도 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 /FClaude 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 지옥

BAT 안에서 PowerShell 한 줄로 JSON 패치하는데:

powershell -Command "$exe='%EXE%'; $piHost='{host}'; ...
  $entry=[PSCustomObject]@{ command=$exe; args=@('--pi',$piHost) };
  if(...) { ... } else { ... }"

PowerShell 안에서 \" 로 큰따옴표 escape, \\\" 로 BAT 안의 큰따옴표. 괄호 안의 명령 (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 런치

MCP 설치 후 Claude Desktop 을 자동 재시작해야 한다. 정규 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 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 최적화

BAT 한 번 깔린 PC 에서 사용자가 다시 BAT 실행하면 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초 안에 끝난다.

다중 디바이스 지원

원래 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 는 누적된 작은 결정의 집합이다.

다음

댓글

로그인 없이 이름만 적고 남기실 수 있어요. 남긴 댓글은 바로 게시됩니다.

댓글을 불러오는 중…