크라우니AI 브라우저 MCP — claude-in-chrome 대체 1차 버전
개요
클로드 세션의 브라우저 자동화를 Chrome 확장 MCP(claude-in-chrome) 대신 크라우니 자산(CrownyAI, WKWebView 네이티브 앱) 으로 수행하도록 1차 버전을 완주했다.
기존에는 ~/.claude/scripts/크라우니AI검증.sh(원샷: 로드→dom/text/measure/console/shot 수집→종료)만 있어 click/type 을 계속 주고받는 지속 세션형 루프가 불가능한 갭이 있었다. 이번에 그 갭을 메웠다.
- 네이티브 로컬 제어서버(127.0.0.1 전용) + MCP 서버(
crowny-ai-browser) 2단 구조
- 기존 한선씨/검증 자산 재사용(스냅샷 로직 공유 함수 승격, console 후킹 재사용)
- 검증: 컴파일 → 앱 기동 → curl 전 명령 응답 → MCP stdio 종단 → 스크린샷 근거까지 실측
무엇을 했는지
1. 네이티브 로컬 제어서버 (native/crowny-ai-control.m, 신규)
- 바인딩 127.0.0.1(INADDR_LOOPBACK) 전용 — 외부 노출 0. 접속 주소 이중 판정(bind 제한 + accept 후 재확인).
- env 게이트:
CROWNY_CTL=1 또는 CROWNY_CTL_PORT 없으면 포트조차 열지 않음(일반 사용자 실행 회귀 0).
- 셸 주입 경로 0: 파일 전체에
system()/NSTask/체계() 호출 없음(크라우니메일·크라우니클라우드에서 반복 지적된 패턴 원천 차단). 토큰 옵션(CROWNY_CTL_TOKEN → X-Crowny-Token, 불일치=403).
- 포트:
crowny-ports.sh set browserctl 9841로 정식 등록(gateway.yaml SSOT). 점유 시 +1..+9 폴백, 실제 포트를 /tmp/crowny-ai-ctl.port에 기록(MCP가 읽는 계약).
- 명령(POST JSON):
/health /navigate /click /type /key /screenshot /read_page /console /tabs /select_tab
- accept 루프는 전용 시리얼 큐, 웹뷰 접근은 전부 메인큐 홉(세마포어, 상한 8초) — 서버 스레드 영구 블록 방지.
- 스크린샷은 검증 표면 로직을 공유 함수
caSnapshotWebViewPNG() 로 승격해 재사용(중복 구현 금지). console 은 window.__crownyVerifyConsole 후킹을 그대로 읽음(게이트만 caVerifyMode() || caCtlEnabled() 로 확장).
2. 주입 경로 정책 — 실측으로 확정
| 경로 | 결과(실측) |
|---|
| CGEventPostToPid 마우스 | ✗ 페이지 mousedown 리스너 미발화(앱 활성·번들 신원 무관) — 메모리 feedback_appkit_트래킹중_뷰재생성_이벤트단절 의 결론 재확증 |
| CGEventPostToPid 키보드(유니코드) | ○ 백그라운드에서도 문자 삽입 도달. 단 포커스 선점 전제 |
| CGEventPostToPid 기능키(Enter) | △ 백그라운드에서 유실 |
| JS 합성(elementFromPoint→pointer/mouse/click, value+input/change, KeyboardEvent+requestSubmit) | ✓ 전부 PASS, 포커스 무탈취 |
→
기본값(auto) = JS 합성,
via:"cgevent" 는 명시 opt-in. 사용자 포커스를 훔치지 않는 헤드리스 루프가 기본이 됐다.
3. MCP 서버 (mcp/mcp-server.js, 신규 — 외부 의존 0)
- 손수말이 stdio JSON-RPC 2.0(기존 크라우니 MCP 브리지
partner-tools/mcp-server.js 관용구 재사용 — SDK 의존 0).
- 도구 5종:
crowny_navigate · crowny_computer(click|type|key|screenshot) · crowny_read_page · crowny_read_console_messages · crowny_tabs(list|select).
- 자동 기동: 제어서버 죽어 있으면
CrownyAI.app/Contents/MacOS/CrownyBrowser 를 CROWNY_CTL=1 로 1회 spawn 후 0.5s×24회(12초) 폴링. 번들 실행파일을 쓰는 이유 = 번들 신원이 있어야 창 활성화가 된다.
- screenshot 은 PNG 를 읽어 MCP
image 컨텐츠(base64)로 반환, 나머지는 JSON 텍스트.
- 등록:
~/.claude.json top-level mcpServers["crowny-ai-browser"] (백업 /tmp/claude.json.bak-mcp-*).
4. 한선씨 동반(정본)
| 파일 | 자체시험 |
|---|
native/제어서버.한선 — 게이트·포트확정·토큰·경로정리·명령판정·주입경로·좌표변환·키매핑·응답코드·4상 실행판정 | 42/42 티 ALL_PASS |
mcp/브라우저MCP.한선 — 도구→경로 매핑·인자충족·기동판정(4상)·실행파일선택·응답종류·JSON-RPC 메서드분류 | 32/32 티 ALL_PASS |
학습 등록:
crownycode-learn.sh add 브라우저제어서버_MCP_명령라우팅_주입경로4상 /
크라우니AI브라우저MCP_도구경로매핑_기동보증 (둘 다 lookup HIT 확인).
5. 세션 배선
~/.claude/commands/크라우니코드.md — "🌐 브라우저 자동화 = crowny-ai-browser MCP(claude-in-chrome 대신)" 절 추가(도구표 + 주입경로 주의 + 원샷 검증.sh 와의 분기 기준).
~/.claude/scripts/크라우니갑옷.sh — B2 브라우저 자동화 감지층 추가(UserPromptSubmit 훅 경유). "웹페이지 …클릭/스크린샷/자동화", claude-in-chrome, "크롬 확장/헤드리스" 패턴에 결정형 매칭 → 이 MCP 사용 지시 주입. 오탐 시험: "브라우저 좋아하는 색깔"=미발동 확인.
검증 결과 (실측 근거)
| 항목 | 근거 |
|---|
| 컴파일 | make ai 에러 0(신규 경고 0, 기존 경고만) |
| 앱 기동 + 리슨 | 로그 [CrownyAI-CTL] 제어서버 리슨 127.0.0.1:9841 (pid=…, token=on/off) |
| curl 전 명령 | /health /navigate /read_page /screenshot /tabs /console /select_tab 전부 200 + 정상 JSON. 미지원 경로=400, 토큰 불일치=403 |
| 실제 네비게이션 | JS 클릭으로 example.com "Learn more" → https://www.iana.org/help/example-domains 이동 확증(health url 변화) |
| 실제 입력 루프 | click→type→Enter → 폼 submit 핸들러 발화, 스크린샷 /tmp/mcp-final.png 에 SUBMITTED:MCP최종시험 육안 확인 |
| MCP 종단 | stdio JSON-RPC 로 initialize/tools/list/5도구 호출 전부 성공, 앱 미기동 상태에서 자동 기동 후 동작(pid 신규) |
| 번들 반영 | install-ai-app.sh + 심볼가드 strings … | grep -cE "crowny-ai-ctl.port|org.crowny.ai.ctl" = 2 |
| 포트 등록 | crowny-ports.sh get browserctl → 9841 |
관련 파일 경로
/Users/ef/CrownyBrowser/native/crowny-ai-control.m (신규 — 제어서버)
/Users/ef/CrownyBrowser/native/제어서버.한선 (신규 — 로직 정본, 42/42)
/Users/ef/CrownyBrowser/native/crowny-ai.h (§8 제어 표면 계약 추가 + caSnapshotWebViewPNG 선언)
/Users/ef/CrownyBrowser/native/crowny-ai-verify.m (스냅샷 공유 함수 승격, console 후킹 게이트 확장)
/Users/ef/CrownyBrowser/native/crowny-ai-content.m (console 후킹 호출 게이트 1줄)
/Users/ef/CrownyBrowser/native/crowny-ai-main.m (didFinishLaunching 말미 caCtlRegister+caCtlStart)
/Users/ef/CrownyBrowser/native/Makefile (AI_SRC 에 control.m 추가)
/Users/ef/CrownyBrowser/mcp/mcp-server.js (신규 — MCP 글루)
/Users/ef/CrownyBrowser/mcp/브라우저MCP.한선 (신규 — 로직 정본, 32/32)
/Users/ef/.claude.json (mcpServers crowny-ai-browser 등록)
/Users/ef/.claude/commands/크라우니코드.md · /Users/ef/.claude/scripts/크라우니갑옷.sh (세션 배선)
- 검증 산출:
/tmp/mcp-final.png(폼 submit 육안), /tmp/mcp-clean.png(AI홈), /tmp/ctl-shot1.png
잔여 이슈
- MCP 도구 노출은 세션 재시작 필요 —
~/.claude.json 등록은 완료됐지만 현재 실행 중인 클로드 세션에는 mcp__crowny-ai-browser__* 도구가 로드되지 않는다. 새 세션에서 확인 필요.
- read_page 품질 1차 수준 — 선택자 기반 상호작용 요소만(a/button/input/select/textarea/role/onclick/contenteditable), 접근성 트리 미사용, iframe·shadow DOM 미탐색, 스크롤 밖 요소 좌표 보정 없음.
- 멀티탭/멀티윈도우 최소 —
/tabs·/select_tab 는 첫 창만 등록(단일 창 기준). 새 탭 생성·닫기·창 선택 API 없음. CrownyAI 는 이미 멀티윈도우라 caCtlRegister 를 창 목록 전체로 확장하면 열림.
- CGEvent 마우스 미도달 — 진짜 네이티브 마우스 이벤트가 필요한 케이스(드래그, 네이티브 크롬 UI 조작)는 아직 불가.
via:"cgevent" 는 좌표 변환까지만 검증됨(전역좌표 계산은 창이 화면 밖일 때 음수/화면초과 값이 나오는 미검증 구간 있음).
- 스크롤·대기(wait) 명령 없음 — 페이지 스크롤, "요소 나타날 때까지 대기"가 없어 긴 페이지는
read_page 좌표가 뷰포트 밖일 수 있다.
- 정리 사고(정직 기록): 검증 중
pkill 로 사용자가 쓰던 CrownyAI 인스턴스를 함께 종료했다(세션 복원은 정상 종료 시에만 저장되므로 그 창의 탭 목록은 유실 가능). 다음부터 pid 지정 종료로 좁혀야 한다.
- crowny.org 네비게이션 미확인 —
https://crowny.org 는 로드되지 않고 AI홈에 머물렀다(example.com·iana.org·file:// 은 정상). 기존 메모리 feedback_공개https000_헤어핀NAT_오진 과 같은 범주로 보이며 이번 트랙 코드 문제는 아니나 별도 확인 필요.
- RPN 정본(제어서버.한선·브라우저MCP.한선의
clike_to_rpn 변환) 미착수.