Skip to main content
Glama
casualkre

VoltageInputMcp

by casualkre

VoltageInputMcp

프론티어 모델이 툴 호출 속도가 아닌 입력 속도로 컴퓨터를 구동할 수 있게 해주는 MCP 서버입니다.

문제

컴퓨터 사용(Computer-use) 도구는 모든 동작마다 원격 모델로 왕복합니다. 스크린샷 올리고, 결정 내리고, 클릭 한 번. 이 방식은 양식 작성에는 괜찮지만, 빠르게 전달되어야 하는 일련의 입력이 필요한 모든 작업 — 게임 플레이, 모달 대화 상자 조작, 타임라인 구동, 세 번째 입력이 첫 두 입력이 이미 도착했는지에 의존하는 모든 UI — 에는 쓸모가 없습니다. 병목 현상은 모델의 지능 때문이 아닙니다. 지능이 800ms 떨어져 있고 입력은 8ms 간격으로 전달되어야 하기 때문입니다.

해법의 개요

결정(deciding)과 실행(doing)을 분리하고, 실행을 키보드가 있는 같은 머신에 두십시오.

  ┌─────────────────────────────────────────────────────────────────┐
  │  Layer 1  —  the orchestrator (Claude, or any MCP client)       │
  │  Writes a Playbook: states, what to look for, what is allowed,  │
  │  when to move on. Thinks once, up front. Watches and corrects.  │
  └───────────────────────────┬─────────────────────────────────────┘
                              │  MCP
  ┌───────────────────────────▼─────────────────────────────────────┐
  │  Layer 2  —  two small local models, on your GPU                │
  │                                                                 │
  │   vision (Qwen2.5-VL-3B)     "of these specific things,         │
  │                               which are on screen, and where?"  │
  │   actuator (Qwen3-1.7B)      "given that, which inputs?"        │
  │                                                                 │
  │  Neither plans. Both answer one closed question per cycle.      │
  └───────────────────────────┬─────────────────────────────────────┘
                              │
  ┌───────────────────────────▼─────────────────────────────────────┐
  │  safety governor  →  /dev/uinput  →  the actual desktop         │
  └─────────────────────────────────────────────────────────────────┘

오케스트레이터는 두뇌입니다. 소형 모델들은 팔입니다. 팔은 똑똑하지 않으며 똑똑할 것을 요구받지도 않습니다.

속도의 실제 원천

소형 모델이 빠르기 때문이 아닙니다 — 3B VLM도 여전히 ~300 ms가 걸립니다. 속도의 원천은 영향도가 큰 순서대로 네 가지입니다:

버스트(Bursts). 액추에이터는 입력을 단일 발화하지 않습니다. 버스트를 발화합니다: 모델이 개입하지 않는 전용 실행기가 실행하는 시간 계획된 입력 프로그램입니다.

g:0;c:l;w:150;t:"README.md";k:enter;w:80;k:ctrl+s

이것은 결정 한 번과 ~400 ms에 걸친 입력 일곱 개이며, 밀리초 단위로 예약됩니다. 40개 동작 버스트도 여전히 결정 한 번입니다. 입력 속도는 모델이 아닌 버스트가 결정합니다.

반사(Reflexes). 결정 사이에 모델을 전혀 사용하지 않고 마이크로초 단위로 저렴한 화면 프로브(픽셀 하나, 영역 평균 하나)로 발화되는 규칙입니다.

{"id": "heal", "when": "probe('health') < 0.25", "do": "k:q;w:60", "cooldown_ms": 800}

지각 건너뛰기. 대부분의 주기는 변하지 않은 화면을 보는 데 필요합니다. 40 µs 프레임 차이 검사가 비전 모델에 300 ms를 쓸지, 아니면 마지막 관찰 결과를 재사용할지 결정합니다. 일반적인 데스크톱 작업에서 대부분의 주기가 VLM을 건너뜁니다.

프롬프트 캐시 지역성. 프롬프트를 순서상 정적 부분 우선으로 배치하여 llama.cpp가 KV 캐시를 재사용하고 변경된 꼬리 부분만 다시 프리필할 수 있게 합니다.

소형 모델이 작음에도 불구하고 신뢰할 수 있는 이유

신뢰성을 요구받지 않기 때문입니다 — 제약을 받기 때문입니다.

llama.cpp에서 두 모델 모두 현재 상태에서 매 주기마다 재생성되는 GBNF 문법에 따라 생성합니다. 문법은 권고 사항이 아닙니다. 유효한 파스를 계속하는 토큰만 도달할 수 있도록 로짓(logits)을 마스킹합니다. 구체적으로, 액추에이터는 정말로 할 수 없는 것들이 있습니다:

  • 구조 없는 버스트를 메서드를 생성할 수 없습니다

  • 정책이 거부하는 키를 지정할 수 없음 — 해당 키는 문법에 존지하지 않습니다

  • 관찰되지 않은 요소를 참조할 수 없음 — 인덱스 범위는 이번 주기의 원소수로 만들어집니다

  • 재생목록이 선언하지 않은 상태 전환을 들을 수 없음

그리고 비전 모델은 UI 요소 이름을 발명할 수 없습니다: 토큰 어휘는 여러분이 작성한 watch 목록에 작은 일반 집합을 더한 것입니다. 따라서 sees("address bar") 가드는 3B 모델이 만들어낸 명사가 아니라 폐쇄형 어휘를 비교합니다.

재시도 루프도 방어적 JSON 파싱도 없습니다. 잘못된 형식의 출력아 불가능하기 때문이 아니라 표현 불가능하기 때문입니다.

재생목록

소형 모델에게 목표를 주지습니다. 상태 머신을 줍니다. 상태 전환은 런타임, 실행 환경이 평가하는 가드 표현식이며, 모델이 아닙니다.

{
  "name": "open_downloads",
  "goal": "Open the file manager at ~/Downloads. Delete nothing, confirm nothing.",
  "initial": "launch",
  "policy": {
    "dry_run": true,
    "allow_verbs": ["g", "c", "k", "t", "w"],
    "deny_labels": ["delete", "trash", "confirm", "empty trash"]
  },
  "budget": { "max_cycles": 60, "max_seconds": 90 },
  "states": {
    "launch": {
      "brief": "Open the application launcher and start the file manager.",
      "watch": ["application launcher", "search field", "file manager icon"],
      "on_enter": "k:meta;w:400",
      "transitions": [
        { "when": "sees('search field')", "to": "type_name" },
        { "when": "cycles() > 6", "to": "@failure", "note": "launcher never opened" }
      ]
    },
    "navigate": {
      "brief": "Focus the location bar with ctrl+l, type the path, press Enter.",
      "watch": ["location bar", "file list", "error message"],
      "on_enter": "k:ctrl+l;w:200",
      "transitions": [
        { "when": "text('Downloads')", "to": "@success" },
        { "when": "sees('error message')", "to": "@failure" }
      ]
    }
  },
  "success_when": "text('Downloads') and not flag('loading')"
}

voltage_reference는 전체 DSL, JSON 스키마, 가드 함수 표를 반환하므로 오케스트레이터는 이 저장소를 읽지 않고도 방책을 작성할 수 있습니다.

성능 튜닝

아래 모든 수치는 참조 머신(RTX 3050 6 GB 노터치북, Qwen2.5-VL-3B + Qwen3-1.7B, llama.cpp)에서 측정된 값으로 유도된 값이 아닙니다.

두 모델 모두 디코드 바운드입니다. 출력 토큰만이 유일하게 중요한 향상점입니다.

이것은 의외였습니다 — 원래 설계는 비전이 프리필 바운드일 것으로 가정했지만 실제의 그렇지 않습니다. 실측 프리필 시간은 ~28 ms로 448×252에서 896×504까지 평평합니다. 디코드 속도는 ~22 ms/토큰입니다. 그렇다면:

항목

비용

출력 토큰 하나

~22 ms

요소 한 개 보고

~21토큰 ≈ 500 ms

비전, 요소 bug

~1.0 s

비전, 요소 bug

~2.2 s

액추에이터, 프롬프트 캐시됨

메모 길이에 따라 140–400 ms

각각 기본값을 바꾼 것입니다:

  • max_elements 가 비전 비용을 지배합니다. **기본값 3.**를 6으로 올리면 인식 주기 당 ~1.5 s가 추가됩니다. 가드가 실제로 검사하는 수치로 설정하세요.

  • downscale_to를 낮추서는 도움이 되지 않고 대개 해가 됩니다. 448×252는 896×504에서 *2.5× 느리**게 측정되었습니다 — 더 흐릿한 이미지는 모델의 불확실성을 키워 더 많은 토큰을 생성합니다. 사용할 수 있는 가장 큰 크기를 사용합니다.

  • 액추에이터의 note 필드레이턴시의 55%를 차지했습니다. 그것은 단지 진단 목적이며, 48글자에서는 측정값이 412 ms/주기였는데 12글자에서는 184 ms, 0자에서는 140 ms였습니다. 이제 기본값은 12입니다.

요소는 {"l":"주소 표시줄","b":[...],"c":0.9}도 같은 이유로 27–29% 토큰 생략과 32–41% 낮은 레이턴시를 측정한 [label_index, x1, y1, x2, y2]로 인코딩됩니다. 닫혀진 watch 어휘로의 인덱싱은 훨씬 안전합니다: 모델이 레이블을 전혀 철자할 수 없고, 오타는 말 그대로 불가능합니다.

GBNF 평가는 샘플링된 토끈마다 CPU에서 한 번씩 실행되므로, 액추에이터는 GP로 완전히 오프로드되었음에도 불구하고 비전 모델보다 더 많은 스레드를 받게 됩니다. allow_keys을 제한하는 것도 레이턴시 최적화이지 보안 최적화에만 있는 것이 아닙니다.

잘못 설정하면 조용히 실패하는 두 가지 설정:

  • 빌드 시 GGML_CUDA_FA_ALL_QUANTS=ON. q8_0 KV 캐시 그리고 플래시 어텐션을 함께 제공합니다. 이 플래그가 없으면 llama.cpp가 해당 KV 조합에 대한 FA 커널을 컴파일하지 못하고 느려진 경로로 점프합니다 — 에 발생 없고, 원인불명의 나쁜 성능 수치만 보입니다. scripts/build-llama.sh가 설정합니다.

  • 실행 시 GGML_CUDA_ENABLE_UNIFIED_MEMORY=0. 1이면 VRAM 오버플로시 실패 없이 조용히 PCIe로 넘겨져 흐릅니다. 모든 것이 동작하고 ~10× 느려집니다. serve.sh는 이것을 강제 끕니다.

측정으로 예측하지 마세요:

.venv/bin/voltage bench

두 백엔드를 루프가 사용하는 정확한 프롬프트 형태로 구동하고 coldvs. 프로프트-캐시된 레이턴시, 세 가지 입력 크기에서의 ms/비주얼 토큰, 그리고 그레 내포하는 주기 시간을 보고합니다. 프롬프트 캐시 속도 상승이 ~1.5× 미만이면 뭔가 동적 요소가 프롬프트 프리픽스에 섞여 있다는 뜻입니다.

모델 비교

가장 당연한 실험 — "어떤 모델이 더 좋은레스트를 작성하는가?"라는 것은 잘못된것을 측정한 것입니다. 문법이 이미 모든 버스트를 유효하게 만들므로 크더보 큰 모델이 문법에서 이길 수 없습니다. 실제로 설정을 사용할 수 있는지 판단하는 것:

  1. 접지 정확도(grounding accuracy). 200 ms 빠르지만 위치오차 40 px인 모델은 쓸모없습니다 — 클릭이 빗나가 않기 때문. 그 이유는 클릭이 중심에 착지하기 때문에 IoU가 아니라 화면 픽셀의 중심 거리로 측정됩니다.

  2. 제약 조건 아래에서의 결정 품질. 모니터 관찰을 보았을 때 올바른 합법적인 동작을 선택합니까, 그리고 전체 시퀀스를 한 버스트로 연결하지 않고 사이클마다 조심스런 한 동작을 내는 것이 아니라연결해 낼 수 있습니까?

  3. 레이턴시 — 1번과 2번이 유의미해진 후에만 중요합니다.

.venv/bin/voltage fixture desktop      # capture a real screen
.venv/bin/voltage compare              # score whatever is running now

포 grounding truth는 오케스트레이터 모델이 라벨링한 실제 스크린샷에서 얻습니다 — 이는 이 시스템이 실행 시 사용하는 것과 같은 참조입니다. 합성 UI는 함정입니다: 그려진 직사각형은 실제 인터페이스로 학습된 모델에게 버튼으로 이미지되지 않으므로 이를 기준으로 채점하면 잘못된 스킬을 하게 됩니다.

결과는 실행을 거쳐 누적되므로 작업 흐름은: 프로필A 제공 → compare → 프로필B 제공 → compare → 표를 읽고, voltage compare --list는 재실행 없이 표를 하니다.

픽스처는 당신 것이며 커밋되지 않습니다. 스크린샷에 개인 정보가 있다면 fixtures/`.gitignore에 추가하세요.

안전

입력을 만는 것은 1.7B 모델입니다. 거버너는 권고라는 것이 아닌 메커니즘입니다: 모든 버스트가 를 통과합니다 — reflex 버스트와 직접 작성한 버스트 포함합니다.

  • dry_run이 기본입니다. 새 플레이북은 아무것도 건드리지 않고 모든 버스트를 파싱하고, 검사하고, 저널링합니다.

  • 정체 버스트 거부. 계획된 시퀀스를자를 실행하는 것은 실행하지 않는 것보다 나쁩니다.

  • **deny_labels**는 Delete / Confirm / Purchase / Allow라고 불린 모든 요소에 대한 클릭을 거부합니다 — 다른 곳에 나타나는 대화상자를 잡는 것입니다.

  • 영역 격리, 키 허용 목록, 금지된 코드 (코드/ctrl+alt+delete, alt+f4), 금지된 텍스트 패턴 (rm -rf, sudo), 버스트 크기 및 초당 입력 수 상한.

  • 네 가지 독립적인 내장물: voltage stop (파일을 작성 — SSH를 통해 동작), 루프가 걸리면 독자 스레드에서 발동하는 deadman timer, 실제 입력 경쟁 (실제 마우스를 만지면 멈춥니다), 그리고 Playbook 예산.

  • 누른 키는 항상 해제됩니다 — 중단 시, 충돌 시, 타임아웃 시, d:shiftu:shift 사이에 중단된 시퀀스가 Shift가 누른 상태로 남고는 안됩니다.

설치

cd voltage-input-mcp && ./scripts/setup.sh

이 명령은 /dev/uinput 접근을 확인하고, 시스템 의존성을 설치하고, venv 생성하며, 무엇이 누락되었는지 출력합니다. 그런 다음:

./scripts/fetch-models.sh lean && ./scripts/serve.sh lean
.venv/bin/voltage doctor

MCP 클라이언트에서 운영

MCP 클라이언트는 격리된 환경으로 server를 시작합니다 — PATH, HOME 정도만. PATHHOME은 합리적인 기본값이며, 이는 스크린 캡쳐를 파괴합니다 — 활동가 DBUS_SESSION_BUS_ADDRESSWAYLAND_DISPLAY에 접근해야 하기 때문입니다. 입력 주입은 이들 없이도 계속 일합니다 (uinput는 세션 서비스가 아닌 디바이스 파일이므로), 따라서 실패는 혼란스럽게 부분적으로 나타납니다: 버스트는 실행되고 스크린샷은 아닙니다.

다음을 명시적으로 전달합니다:

claude mcp add voltage-input \
  -e WAYLAND_DISPLAY="$WAYLAND_DISPLAY" \
  -e DISPLAY="$DISPLAY" \
  -e DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \
  -e XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" \
  -- /absolute/path/to/voltage-input-mcp/.venv/bin/voltage-input-mcp

voltage_doctor는 정확히 어떤 것이따라 있는지 보고합니다. 캡쳐가 실패한다면 가장 먼저 확인할 곳입니다.

요구사항

  • /dev/uinput 있는 Linux (X11, Wayland 또는 콘솔 — 디스플레이 서버 아래에 주입)

  • Python 3.11+

  • lean 프로필을 위한 ~5 GB GPU 여유 메모리; voltage profiles가 어느 것이 맞는지 보여줍니다

  • 빠른 경로는 llama.cpp, 또는 더 느린 빌더 없는 경로는 Ollama

KDE Plasma 6 on Wayland (KWin), CUDA, Python 3.14에서 검증됨.

MCP 도구

도구

용도

voltage_reference

Playbook + burst DSL지. 먼저 이것을 호출

voltage_doctor

이 컴퓨터 사용 가능한지, 그리고 아니라면 정확한 해결 방법

voltage_capture

스크린샷을 당신에게 반환

voltage_observe

비전 패스 한 번 — 특정 watch 목록이 작동하는지 확인하고 의존

voltage_validate_playbook

정적 검사: 가드, 활성 단계, 그래프, 불능 상태 전환

voltage_run

실행 시작; run_id 반환

voltage_status

단계별 타이밍 포함 최신 평가

voltage_steer

활성 실행을 수정 — 힌트, 변수, 강제 상태, dry_run

voltage_stop / voltage_pause

중지 또는 일시중지; 중지 시 항상 입력 보호 해제

voltage_journal

주기별 기록; only_refused는 정책 충돌 확인

voltage_execute_burst

로컬 모델을 우회한 입력의 직접 실행

voltage_calibrate

주입이 컴포지터에 도달하는지 확인

문서화

  • ARCHITECTURE.md — 루프가 어떻게 작동하는지, 각 선택의 이유, 시간이 어디서 쓰는지

  • PLAYBOOK.md — 작성 가이드

상태

디스크에 가중치가 없는 상태에서 할 수 있는 한 빌드되고 검증되었습니다. 149개의 테스트가 버스트 DSL, 가드 샌드박스, 안전 거버넌스, 플레이북 컴파일, GBNF 생성, uinput 전기 배선, 그리고 실행 루프 자체를 포함합니다 (스턴드 스 모델로 구동 — 정적 화면에서 on_change 지각이 실제로 비전 모델을 스킵하는지도 확인).

MCP 서버는 실제 클라이언트가 stdio로 전 과정 테스트입니다: 13개의 도구, 스키마, execute_burst가 유효한 버스트를 허용하고 sudo rm -rf /를 일치하는 규칙 거부했습니다.

실행되지 않은 것은 라이브 모델입니다. 그걸 실행하려면 llama.cpp를 빌드하고 가중치를 내려받아야 하는데, 그 준비는 scripts/가 수행합니다. 빌드 중에 의도적으로 실행하지 않은 두 가지 일도 있었는데, 이는 포털 권한 대화상자와 실제 입력 주입입니다. 둘 다 사용자의 데스크톱에서 동작하기 때문입니다.

이때부터의 작업 순서는 다음과 같습니다.

./scripts/setup.sh          # reports what needs sudo, doesn't run it
./scripts/build-llama.sh    # ~15 min with CUDA
./scripts/fetch-models.sh lean
./scripts/serve.sh lean
.venv/bin/voltage doctor    # should now say READY

그다음 MCP 클라이언트에서: voltage_calibrate(커서가 실제로 움직이는지 확인), voltage_observe(비전 모델이 레이블을 찾는지 확인)를 실행하고, 그 후 dry_run Playbook을 실행한 뒤, dry_run=false로 설정하기 전에 voltage_journal을 읽으십시오.

저자

Claude Opus 5(Anthropic)가 한 세션에서 아키텍처, 구현, 테스트, 문서를 포함해 처음부터 끝까지 작성했습니다. 사람이 아이디어를 제시하고, 제약 조건(KDE Wayland, 6GB VRAM, "컴퓨터 사용보다 빠르게")을 설정하고, 결과를 검토했지만, 코드를 작성하지는 않았습니다.

이 저장소에 담긴 플랫폼에 대한 발견 사항은 추측이 아니라 빌드 과정에서 머신을 조사하여 얻은 것입니다. KWin이 허용 목록에 없는 실행 파일에 ScreenShot2를 거부한다는 점, grim이 KWin에서는 작동할 수 없다는 점, MCP 클라이언트가 세션 버스를 정리하여 제거한다는 점 등입니다. 각각은 결정을 강제한 해당 코드 위치에 문서화되어 있습니다.

LICENSE는 어떤 개인도 저작권자로 명시하지 않으며, 그 이유는 해당 파일에 기록되어 있습니다.

라이선스

MIT. LICENSE 참조.

-
license - not tested
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.

  • Let ChatGPT, Claude & Cursor use your Mac: email, calendar, iMessage, Teams, files. Local, free.

  • Adaptive plan/build/review cycles for AI coding assistants, persisted across sessions.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/casualkre/voltage-input-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server