pyproject.toml·manifest.json·실제 도구 소스코드(src/windows_mcp/tools/)까지 뜯어, "MCP가 무엇이고 이 서버가 어떻게 윈도우를 제어하는가"를 구조적으로 해부한다.이 레포가 무엇을 하는 물건인가.
Windows-MCP는 LLM이 직접 다룰 수 없는 윈도우 운영체제를 다룰 수 있게 만드는 중개 서버다. AI가 "메모장을 열고 '안녕'이라고 타이핑해"라고 결정하면, 이 서버가 실제로 윈도우 API를 호출해 앱을 열고 글자를 입력한다. AI는 명령을 내리는 두뇌, Windows-MCP는 그 명령을 윈도우에서 실행하는 손발이다.
핵심은 "비전 모델 없이도 화면을 이해한다"는 점이다. 보통 컴퓨터 자동화는 화면을 이미지로 캡처해 픽셀을 분석(OCR·객체탐지)하지만, Windows-MCP는 윈도우가 내부적으로 들고 있는 접근성 트리(UI Automation tree)를 읽어 "여기 버튼이 있고, 이름은 저장, 좌표는 (820,540)"처럼 구조화된 텍스트로 화면을 파악한다. 그래서 가볍고, 어떤 LLM과도 붙는다.
왜 Claude Desktop 확장 2M+ 사용자를 모으고 트렌딩에 오르는가.
2024~2026년은 "에이전트가 실제로 컴퓨터를 조작한다"는 흐름(Computer Use)이 폭발한 시기다. 맥/리눅스용 컴퓨터 제어 도구는 여럿 나왔지만, 압도적 점유율의 윈도우를 정식으로, 그리고 가볍게 다루는 MCP 서버는 드물었다. 이 레포가 트렌딩에 오르는 이유는 다음과 같다.
OpenAI Operator나 Anthropic Computer Use 류는 스크린샷을 비전 모델에 넣어 픽셀로 판단한다 — 느리고, 비싸고, 비전 모델에 종속된다. Windows-MCP는 접근성 트리(UIA)를 텍스트로 읽어 요소 좌표를 얻으므로, 비전 없는 저렴한 LLM으로도 동작하고 지연도 0.2~0.5초로 짧다.
uvx windows-mcp serve 한 줄이면 끝. PyPI·MCP Registry에 등록돼 있고 Claude Desktop·Cursor·Gemini CLI·Codex CLI 등 거의 모든 MCP 클라이언트 설치 가이드를 제공한다. 윈도우 7~11을 폭넓게 지원해 "내가 매일 쓰는 그 PC"를 그대로 자동화 대상으로 삼을 수 있다.
단순 클릭·타이핑을 넘어 PowerShell·레지스트리·파일시스템·웹 스크래핑·다중 선택·클립보드·알림까지 노출한다. 특히 브라우저 자동화용 DOM 모드(use_dom=True)는 브라우저 UI를 걷어내고 웹페이지 콘텐츠만 추출해 더 깔끔한 웹 자동화를 가능케 한다(Chrome·Edge·Firefox 지원).
① 풀 시스템 권한. 레지스트리 편집·파일 삭제·PowerShell 실행이 가능해 되돌릴 수 없는 작업을 LLM이 수행할 수 있다(README도 "irreversible operations" 경고). ② 프롬프트 인젝션 위험. 화면에 떠 있는 악성 텍스트가 에이전트를 조종할 수 있다. ③ 접근성 트리 한계. 문단 내 부분 텍스트 선택, IDE 코딩, 게임 조작은 안 된다. 그래서 인증·IP 허용목록·도구 화이트리스트·SSRF 방어 같은 보안 기능을 같이 내장한다.
이 서버를 떠받치는 기술을 역할별로 분해했다.
Windows-MCP는 순수 Python 3.13+ 단일 패키지다. 별도 프론트엔드는 없고(클라이언트가 곧 UI), 대신 윈도우 네이티브 API를 파이썬에서 호출하는 얇은 레이어가 핵심이다.
| 계층 | 역할 | 사용 기술 / 라이브러리 |
|---|---|---|
| MCP 프레임워크 | 도구 등록·전송(transport) | fastmcp>=3.0 (stdio / SSE / streamable-http) |
| UI 자동화 | 접근성 트리 읽기·요소 제어 | Python-UIAutomation(comtypes), pywin32 |
| 화면 캡처 | 고속 스크린샷 | dxcam(DXGI) → mss → pillow 폴백 |
| 입력 시뮬레이션 | 마우스·키보드 주입 | pywin32 (Win32 SendInput 계열) |
| 요소 매칭 | 이름→요소 퍼지 검색 | thefuzz/fuzzywuzzy + python-levenshtein |
| 웹 스크래핑 | 페이지→마크다운 변환 | requests + markdownify |
| CLI / 설정 | serve·install·auth 명령 | click, platformdirs(config 경로) |
| 보안 | 인증·OAuth·SSRF 방어 | 자체 infrastructure/(auth·oauth·security) |
| 텔레메트리 | 익명 사용 통계(옵트아웃) | posthog, uuid7 |
| 출력 포맷 | 표·코드 하이라이트 | tabulate, pygments |
Windows-MCP의 스택은 "통역사 + 손" 조합이다. fastmcp는 AI의 말(MCP 프로토콜)을 받아주는 접수창구, UIAutomation은 화면을 글로 읽어주는 통역사(눈), pywin32는 실제로 클릭·타이핑하는 손, dxcam은 사진을 찍는 카메라다. 비전 모델 대신 통역사(접근성 트리)를 쓰는 게 이 프로젝트의 정체성이다.
@mcp.tool(...) 데코레이터를 붙이면 자동으로 LLM이 호출 가능한 도구가 된다. stdio(로컬 직결)·SSE·HTTP 스트리밍 전송을 모두 지원한다.comtypes로 호출해 "어떤 요소가 어디 있는지"를 텍스트로 얻는다 — 비전 없는 화면 이해의 핵심.mss, pillow 순으로 자동 폴백한다(WINDOWS_MCP_SCREENSHOT_BACKEND로 강제 지정 가능).AI의 명령이 어떤 길을 거쳐 실제 클릭이 되는가.
전체 흐름은 3단(클라이언트 → MCP 서버 → 윈도우 OS)으로 나뉜다. 클라이언트(예: Claude Desktop)는 LLM과 사용자를 들고 있고, 그 안의 LLM이 "어떤 도구를 어떤 인자로 호출할지"를 결정한다. 결정된 호출은 MCP 프로토콜로 Windows-MCP 서버에 전달되고, 서버가 윈도우 API로 실제 동작을 수행한 뒤 결과(텍스트/스크린샷)를 되돌려준다.
에이전트는 먼저 Snapshot으로 화면 상태를 받는다. 이때 각 상호작용 요소에 정수 label(0,1,2…)이 매겨진다. 이후 Click(label=3)처럼 번호로 요소를 지목하면 서버가 get_coordinates_from_label()로 실제 좌표를 역산해 클릭한다. 좌표가 분명하면 Click(loc=[x,y])로 직접 지정도 가능하다.
# tools/input.py — Click 도구의 핵심 (발췌·요약)
def click_tool(loc=None, label=None, button="left", clicks=1):
if label is not None:
loc = desktop.get_coordinates_from_label(label) # 번호→좌표
x, y = loc
desktop.click(loc=loc, button=button, clicks=clicks)
return f"{button} clicked at ({x},{y})."
화면 파악 도구를 둘로 나눴다. Screenshot은 UI 트리 추출을 건너뛰고 이미지+커서+창 목록만 빠르게 반환(시각 맥락이 필요할 때 첫 호출용). Snapshot은 상호작용 요소 id·스크롤 영역·DOM까지 완전 추출(클릭 대상이 필요할 때). 속도와 정보량을 호출자가 선택하게 한 것.
비동기 UI는 "버튼이 나타날 때까지" 기다려야 한다. WaitFor는 한 번의 도구 호출 안에서 text_exists·active_window·element_exists·element_enabled·focused_element 같은 조건을 폴링한다. 또 입력값을 강건하게 다루는데, 일부 클라이언트가 리스트를 '[100,200]' 문자열로 직렬화해 보내도 _as_loc()가 다시 파싱해 복원한다.
label은 직전 Snapshot의 화면 상태에 묶여 있다. Snapshot을 안 부르면 desktop_state is None이라 "Please call Snapshot first" 오류가 난다. 또 화면이 바뀌면 label도 무효가 되므로, 화면 전환 후에는 다시 Snapshot을 찍어 새 label을 받아야 한다.
기능별로 잘 쪼개진 패키지. 도구 정의와 실제 실행이 분리돼 있다.
핵심은 src/windows_mcp/ 아래다. tools/는 MCP 도구의 "정의·검증·설명"을, 각 기능 디렉토리(desktop·powershell·registry…)는 "실제 윈도우 작업"을 담당하는 정의/실행 분리 구조다.
읽는 순서 추천: ① pyproject.toml로 의존성·진입점 파악 → ② tools/input.py로 도구가 어떻게 정의되는지(데코레이터·인자 검증) → ③ desktop/service.py로 실제 클릭/타이핑 구현 → ④ tree/·uia/로 접근성 트리를 label로 바꾸는 마법을 본다.
| 알고 싶은 것 | 먼저 볼 파일 |
|---|---|
| 도구는 어떻게 등록되나 | tools/input.py (register + @mcp.tool) |
| 화면을 어떻게 텍스트로 읽나 | tree/ + uia/ |
| 실제 마우스/키보드 주입 | desktop/service.py |
| 인증·OAuth·SSRF 방어 | infrastructure/security.py·oauth.py·auth.py |
| 서버 실행/설치 명령 | __main__.py + config.py |
이 레포를 거치며 실제로 손에 남기면 좋은 것들.
Windows-MCP는 "잘 만든 FastMCP 서버"의 교과서다. 핵심은 함수 하나 = 도구 하나라는 패턴과, LLM이 잘 이해하도록 description을 꼼꼼히 적는 것이다. 아래는 이 레포가 도구를 등록하는 실제 형태다.
# tools/input.py — FastMCP 도구 등록 패턴 (요약)
@mcp.tool(
name="Click",
description=(
"Performs mouse clicks at coordinates [x, y] or a UI element's label. "
"button: 'left'/'right'/'middle'. clicks: 0=hover,1=single,2=double."
),
annotations=ToolAnnotations(
title="Click",
readOnlyHint=False, # 화면을 바꾸는 동작인가
destructiveHint=True, # 되돌릴 수 없을 수 있음
idempotentHint=False, # 여러 번 호출하면 결과가 달라짐
),
)
def click_tool(loc=None, label=None, button="left", clicks=1):
...
여기서 배울 핵심: ① description은 LLM을 위한 사용설명서(인자 의미를 자연어로), ② ToolAnnotations로 도구의 위험도/멱등성을 메타데이터로 표시, ③ register() 함수로 의존성(get_desktop 등)을 주입받는 깔끔한 구조.
"비전 없이 화면을 이해한다"의 정체가 바로 UIA다. 윈도우의 모든 창·컨트롤은 트리로 노출되며, 각 노드는 name·control_type·좌표·focus 여부 같은 메타데이터를 갖는다. Windows-MCP는 이 트리를 순회해 상호작용 가능한 노드만 추려 label을 붙인다. 이 개념을 익히면 어떤 데스크톱 자동화도 만들 수 있다.
앱을 실행할 때 "노트패드"라고만 해도 정확한 실행 파일을 찾아야 한다. 이를 위해 thefuzz(레벤슈타인 거리 기반)로 가장 비슷한 항목을 고른다. 사람의 모호한 표현을 정확한 대상으로 매핑하는 실전 기법이다.
이 레포의 진짜 학습법은 tools/ 에 새 파일을 만들어 도구 하나를 추가해 보는 것이다. 예: 현재 시각을 반환하는 Clock 도구. 기존 notification.py를 복사해 구조를 흉내 내면, MCP 서버 확장의 전 과정을 한 사이클 경험할 수 있다.
"이걸 돌리려면 뭐가 필요한가"에 대한 현실적 답.
무거운 모델을 로컬에서 돌리지 않으므로(LLM은 클라이언트/클라우드 몫) 요구사항은 가볍다. 단, 반드시 윈도우 본체에서 실행돼야 한다 — 윈도우 API를 직접 호출하기 때문이다(WSL에서 쓰려면 powershell.exe 브리지 필요).
| 항목 | 요구사항 | 비고 |
|---|---|---|
| 운영체제 | Windows 7 / 8 / 10 / 11 | UI Automation API가 있는 윈도우 전용 |
| Python | 3.13 이상 | pyproject가 requires-python=">=3.13" 지정 |
| 패키지 매니저 | uv (Astral) | pip install uv 또는 설치 스크립트 |
| 언어 설정 | 영어 권장 | 비영어 환경은 App-Tool 비활성화 권장 |
| 디스플레이 | 실제 화면(GUI 세션) | 스크린샷·클릭 대상이 필요. headless 부적합 |
| RAM/CPU | 일반 사무용 수준 | 지연 0.2~0.5초, 시스템 부하·LLM 속도에 좌우 |
로컬(stdio)만 쓰면 보안 설정이 없어도 되지만, --transport sse/streamable-http로 네트워크에 열면 반드시 --auth-key(Bearer 토큰)·--ip-allowlist(CIDR)·TLS(--ssl-certfile)를 켜야 한다. 안 켜면 누구나 당신 PC를 원격 조작할 수 있다. 고고도 위험 도구(PowerShell·Registry)는 --exclude-tools로 끄는 것도 방법이다.
"읽기"에서 "하기"로 바꾸는 난이도별 과제.
uvx windows-mcp serve로 서버를 켜고, Claude Desktop의 claude_desktop_config.json에 등록한 뒤 /mcp로 도구 목록이 뜨는지 확인한다. "AI가 내 PC 도구를 인식한다"를 눈으로 보는 단계.
AI에게 "메모장 열고 오늘 할 일 3가지를 적은 뒤 todo.txt로 저장해"라고 시킨다. 내부적으로 App→Snapshot→Type→Shortcut(Ctrl+S)→Type 흐름이 도는 것을 관찰한다. 도구 체이닝을 체득한다.
브라우저에서 위키 페이지를 연 뒤 Snapshot(use_dom=True)로 본문만 깔끔히 뽑고, 또는 Scrape 도구로 페이지를 마크다운으로 받아 요약시킨다. 비전 없이 웹을 읽는 방식을 비교한다.
windows-mcp auth --with-tls로 토큰·인증서를 만들고, --transport streamable-http --ip-allowlist로 특정 IP에서만 접속되게 한다. Authorization: Bearer 헤더가 없으면 거부되는 것을 직접 확인한다.
tools/에 새 파일을 만들고 register() + @mcp.tool로 도구(예: 활성 창 제목만 반환하는 ActiveTitle)를 추가해, 빌드 후 클라이언트에서 호출한다. MCP 서버 개발의 전 과정을 완주한다.
이 레포를 발판으로 "에이전트 + 데스크톱 자동화"를 4주 코스로.
| 주차 | 주제 | 도달 목표 |
|---|---|---|
| 1주차 | MCP 프로토콜 기본 | 도구/리소스/전송 개념, FastMCP로 "hello tool" 작성 |
| 2주차 | 윈도우 UI Automation | UIA 트리 순회, control_type·name 읽기, label 매핑 이해 |
| 3주차 | 입력 시뮬레이션 + 화면 캡처 | pywin32 SendInput, dxcam/mss 스크린샷, 좌표계 다루기 |
| 4주차 | 보안 · 전송 · 배포 | OAuth+PKCE, IP 허용목록·TLS, PyPI/Registry 배포 |
| 이후 | 에이전트 루프 설계 | Snapshot→판단→Action→재관찰 루프, 프롬프트 인젝션 방어 |
이 레포의 가치는 "윈도우 제어"보다 "잘 설계된 MCP 서버의 본보기"에 있다. 도구 description 작성법, 위험 도구의 annotation 표기, 입력값 강건화(_as_loc), 서비스/도구 분리 같은 패턴은 어떤 MCP 서버를 만들든 그대로 재사용된다. 윈도우가 없어도 코드를 읽는 것만으로 MCP 설계 감각을 크게 얻을 수 있다.
읽다 막히는 용어 빠른 참조.
| MCP | Model Context Protocol. LLM과 외부 도구를 잇는 표준. 서버가 도구를 노출하면 클라이언트가 호출한다. |
| MCP 서버 | 도구·리소스를 노출하는 쪽. Windows-MCP가 여기 해당(윈도우 제어 도구 제공). |
| MCP 클라이언트 | LLM을 들고 도구를 호출하는 쪽. Claude Desktop·Cursor·Codex CLI 등. |
| FastMCP | 파이썬으로 MCP 서버를 만드는 프레임워크. @mcp.tool 데코레이터가 핵심. |
| Tool(도구) | LLM이 호출할 수 있는 함수 단위 기능. Click·Type·Snapshot 등. |
| UI Automation(UIA) | 윈도우의 접근성 API. 화면 요소를 트리로 노출 → 비전 없이 화면 이해. |
| 접근성 트리 | 창·버튼·입력칸을 부모-자식 트리로 표현한 구조. 자동화의 데이터 소스. |
| label(요소 id) | Snapshot이 각 상호작용 요소에 매긴 정수 번호. Click(label=3)처럼 지목. |
| Snapshot / Screenshot | Snapshot=완전 상태(요소 id·DOM 포함), Screenshot=빠른 이미지+창 목록만. |
| DOM 모드 | use_dom=True. 브라우저 UI를 걷어내고 웹페이지 콘텐츠만 추출. |
| transport(전송) | 클라이언트-서버 통신 방식. stdio(로컬)·SSE·streamable-http(네트워크). |
| stdio | 표준 입출력으로 직접 연결. 로컬 클라이언트의 기본 전송. |
| dxcam / DXGI | GPU 출력 버퍼를 직접 복사하는 초고속 스크린샷 방식. |
| SSRF | Server-Side Request Forgery. Scrape가 내부망·사설IP 접근을 차단해 방어. |
| uv / uvx | Astral의 초고속 파이썬 패키지 매니저. uvx는 설치 없이 패키지 실행. |
| ToolAnnotations | 도구 메타데이터. readOnly·destructive·idempotent 힌트로 위험도 표시. |
uvx windows-mcp serve)