CMUX+Browser API vs. TMUX+Playwright
시작은 Agent에게 프롬프트로만 WordPress 플러그인을 만들게 할 때, 맘에 드는 결과물이 나오게 하는 과정이 비싸고 비효율적인 것을 해결하기 위해서였다. WordPress는 OCI 기반 가상호스트에서 동작하고 있어서 초기에는 Local Agent를 WP 환경 바깥 호스트에 직접 ssh로 접속해서 개발하거나, WP가 동작하는 도커 컨테이너 안에서 Agent를 동작시키는 방법 두가지를 사용해보았다.
VibeProxy를 사용하면 가상호스트가 되었든 Docker Container안이든 상관없이 Agent를 끼고 개발할 수 있지만, 웹브라우저를 기동하는 Window 환경이 없는 그 조건에서는 나만 화면을 보면서 Agent에게 내 지시를 받아 처리하는 건 비효율적인 방법으로 회귀하는 셈이다. (Window 환경을 위한 VNC류는 일단 제외한다) 굳이 가상호스트에서 작업해야하는 이유는 호스트의 NGINX Reverse Proxy 설정까지 조절하면서 개발해야하기 때문. NGINX가 여러대 돌아가므로 문맥이란 걸 무시할 순 없다. 또한 보안설정을 빡쎄게 걸어뒀기 때문에 WP 플러그인 실행 단계에서 일일히 조건을 맞춰야 한다. 암튼, 화면 요소에 대해 아무리 명확하게 CSS selector를 사용해서 프롬프트를 지시해도 빡치게 만들기 일쑤고, 처리과정에서 토큰 소비량도 만만찮아서 방법을 찾은 것이 CMUX에서 Browser API로 연동하거나 TMUX+Playwright를 기동하는 방법이었다.
결과는 명확하다. 개발과정에서는 cmux-mcp를 통해 CMUX+Browser API를 사용하는 것이다.
개발과정이라서 자동화 요구보다 지시사항을 제대로 수행하는게 먼저라서 CMUX+Browser API를 택했는데, 의외로 날 고민하게 만드는 건 따로 있었다.
sshfs vs. mutagen
로컬 맥북에서 원격지 도커 컨테이너 쪽으로 ssh 통신은 하는 건 TailScale 덕분에 손쉽게 해결된다. 그러나, 아무리 TailScale 네트워크가 OpenVPN 보다 빨라도 Agent가 파일을 조작할 때 한번에 처리하지 못하는 경우가 빈번하게 생겼다. ssh + script 호출을 inline으로 할 땐 문법에 예민해야하는데, Codex 조차도 Turn 사이사이 계속해서 실패를 함으로써 신경쓰이게 했다. 그렇다고 sshfs를 쓰자니 openFUSE를 설치해야하고, 그건 도저히 방법이 될 수 없었다. 그거 하나 쓰자고 맥북 보안을 풀어버리고 싶진 않다. 그래서, 대체제로써 SMB3, NFS v4, WebDAV, Mutagen, Syncthing, VS Code Remote-SSH 등을 평가하다가 Mutagen으로 최종 결정했다. VS Code Remote-SSH는 원격지에 node 프로세스를 너무 많이 띄운다. 무겁다. 나머지는 다 철지난 기술들. 그러나 Docker가 인수한 Mutagen은 꽤 쓸만했다. 덕분에 프로젝트별로 원격지 폴더를 로컬에 마운트해서 사용할 수 있게 됨. (Permission 설정에 좀 신경써야 한다)
AGENTS.md vs. memory-mcp + serena-mcp + morph-plugin …
이제 내 요청을 찰떡같이 해석해주는 걸 도와줄 MCP를 설정하고 어느 정도 작업진척이 있었다. Morph Plugin은 꽤 쓸만했는데, 꽤 빨리 허용치 한계를 넘어서 제외했다.
xray-mcp는 작은 프로젝트에나 쓸만하고 조금만 커지면 출력이 잘린다. xray-mcp가 문제가 아니라 Agent와 LLM에서 자르는 것 같다.
serena 하나로는 매 Turn 마다 쌓이는 것들을 제때 제때 적용할 수가 없었다. serena-mcp의 메모리 기능은 단순 markdown 저장방법이다.
AGENTS.md 에 어명이니 반드시 따라야 한다고 협박해봐야 100% 실행보장은 안되고, 문맥 쌓이면 밀리는 것은 어쩔 수 없는 일.
xChuCx/agent-memory, basicmachines-co/basic-memory, riponcm/projectmem, bzdOS/hubd, besslframework-stack/project-tessera, tverney/mcp-agent-memory … 등등, 후보 MCP 서버는 좀 있긴한데 MCP 개발을 해보고 싶던 차라서 생각하는 기능의 MCP 서버를 만들기로 결정했다.

CMUX 안에서 TMUX를 사용하는 이유는 두가지 인데, 하나는 CMUX 세션에서 작업이 진행되다가 앱이 먹통되는 사태에 대비하기 위해서고, iTerm2 TMUX 세션으로도 attach 시켜서 다른 태스크를 진행시키기 위해서다. CMUX안의 TMUX 세션은 중첩될 수가 없고 CMUX_* 환경변수가 전달되지 않아서 CMUX Workspace 하나당 하나의 TMUX 세션을 붙이는 게 현재로써는 최선이다. 게다가 Option 키 조합은 제대로 전달하지 못하는 것 같다. 이 때문에 CMUX가 전단에 있으면 만들어둔 RetroStage/SpectraSearch 등등의 TMUX plugin을 제대로 써먹을 수 가 없다. 그래서 TMUX 세션으로 attach 하면, 뭐 그런 제한은 없어지니까.
나의 인지 구조적 특징을 활용하고 도와줄 context-storage-mcp 서버 개발
나의 인지 구조적 특징은, 전체를 보고 있으나 실행은 작은 단위로 잘라서 진행하고 반드시 그 결과를 확인하고 예측과 실제를 비교해야만 다음 스텝으로 진행이 끝없이 이어진다는 점이다. 결과를 비교하지 않으면 창의적인 결과물을 얻기 힘들고, 진행 에너지가 점점 줄어든다. 각각의 단위 실행마다 여러가지 실험을 할 수 있어야 하고, 그 결과를 반드시 어떤 식으로든 종료/매듭지어야 한다. postponed 또는 obsolete 등으로 태그만 적용해도 인지구조에 오버플로우는 줄어든다. 아무것도 남기지 않는 것이 문제다.
LLM을 사용해서 서버관리를 할 때, 호스트에서 처리하는 각각의 태스크는 태스크 상호간에 영향을 미친다. 즉, 독립적이지 않다. 하나의 결정과 처리는 반드시 다른 곳에 영향을 주는 것이 대부분이다. 각 태스크가 어떤 식으로 호스트에 영향을 주는지를 추적해야하는데, LLM으로 서버관리를 하던 초기에는 추적하는 것 자체를 Workspace/Task.list 라는 파일로 만들고, Workspace/T1, Workspace/T2 .. Workspace/T1.B 등과 같이 태스크 식별자로 구분된 폴더에 관련 문서와 사용한 스크립트 등을 쌓아두는 식으로 간편하게 처리했었다. WP plugin을 개발하는 내용에는 서버측 조정 사항이 포함되므로 더더욱 문맥 유지가 필요하다.
문제는 태스크가 쌓이면서 현재 태스크가 이전 태스크의 결정을 업데이트하거나 아예 불필요하게 만들기도 하므로 문서 업데이트가 수시로 일어난다는 점이다. 그것도 그때마다 LLM에게 적용하라고 하면 되지만, 여전히 토큰 비용문제 때문에 그렇게 만만하진 않다. 태스크가 쌓이면 쌓일수록 늘어간다. 그리고 그것을 명시적으로 파일에 기록하면 LLM과 통신하는 문맥에 쓸데없이 끼어들어 문맥이 거대해지는 것을 막을 방법이 없다. 이때, MCP서버간의 통신채널로 그런 정보를 교환하게 하면 /compact 등의 명령으로 문맥을 압축할 때, 대화만 남기고 불필요한 정보를 없애버림으로써 문맥 크기를 유지하는데 도움이 될 것이다. (뭐가 됐든 나는 어쩔 수 없이 비용을 항상 염두에 둬야 한다. 비용걱정없으면 뭐… 좋겠지)
예를 들어 CSV block 이라는 기능을 사용하는 여러 메뉴를 만들었는데, 어느 페이지에서 수정이 발생하면 그 기능을 사용하는 모든 메뉴에 적용해야할 때가 있다. 하나의 파일로 모든 메뉴를 처리하게 하는 건 기본적인 조치사항이지만 조금씩 다를때는 그 메뉴마다 규칙을 지켜야 한다. 그걸 기억해서 처리하라고 직접 명령하는 건 무리데스. 개발과정에서 작은 단위의 분기가 계속해서 발생하고, 그때마다 누적된 결과가 이전 규칙을 업데이트 해야한다. 그걸 단순히 파일로 기억시키는 건 기술적인 문제를 초래하는 것보다 토큰비용 문제를 초래한다. 암튼 내가 구상한 건 태스크를 진행하면서 이전 내용과 현재 내용을 계속해서 업데이트하고 내가 뭐라고 지껄이든 간에 내 요청에 찰떡같이 알아듣고 실행하도록 도와줄 MCP를 만드는 것이다.
ctxstore MCP 라고 개명, 꽤 쓸만하다
역시, 실전적으로 MCP 서버를 사용하면서 제작하니 꽤 빨리 어떻게 해야할지를 알게된다. 이미 Graph Network 방식의 데이터 저장과 이용방식을 적용했다. 설정이 거의 필요없는 LanceDB를 사용해서 RAG 방식으로 검색을 해볼까도 생각했으나, 그건 나중으로 미루었다. 현재는 MCP 자체에 LLM에게 사용방법을 강하게 어필하고 AGENTS.md 파일에 사용을 강제하는 규정을 넣고있으나, 가능한 AGENTS.md 파일엔 규정 자체를 넣지 않아도 동작시키는게 목표다. 그게 쉽지 않는게, ctxstore 가 제공하는 기능이 태스크/기억 관련이라 경쟁 상대가 너무 많고 드러나기엔 일반적인 문맥이다. 주요 특징은 아래와 같다:
– Agent가 기준 문서를 무분별하게 재작성하지 않도록 기록 영역과 제안 영역을 분리해 두었다.
– topic별 문맥을 사람 지정 순서대로 로드한다.
– `nginx`, `tls`, `firewall` 같은 자연스러운 주제어를 실제 bundle topic으로 해석하는 topic resolver를 제공한다.
– `T1`, `T8.A` 같은 TaskID 중심 색인을 만들어 과거 작업, 관련 태스크, 최신 작업 순서를 조회할 수 있다.
– topic 생성, 병합, keyword 보강, include 재배치 같은 topic 유지보수 도구를 제공한다.
– CLI importer로 기존 문서 폴더를 `.context_storage` 구조로 부트스트랩할 수 있다.
– 모든 문서는 매 도구 호출 시 새로 읽어 사람이 직접 수정한 내용이 다음 호출에 반영될 수 있다.
– vocabulary를 관리하여 같은 뜻, 다른 표현을 찰떡같이 알아듣는다.
이를 테면, 태스크 하나를 진행하면서 발생하는 루틴을 하나의 이름으로 정의하고, 그 루틴과 관련된 태스크를 topic으로 묶거나 keyword로 엮어두면, 다른 세션에서 아무런 언급이 없어도 해당 표현에 해당하는 decision을 모두 탐색하여 곧바로 문맥에 적용하므로 아주, 아주, 속이 시원했다. 게다가 Context 문서를 직접 보고 수정하면 곧 바로 적용되므로 동적 AGENTS.md 파일을 만든 것과 비슷한 효과가 있다.
몇일간 더 사용해보고 글을 이어가겠다.
