git clone git@github.com:steveyegge/efrit.gitEmacs-AI 통합 패러다임
핵심 문제 인식
“이맥스에서 에이전트를 사용할 때 이맥스의 장점을 전혀 활용하지 못하고 있다”
이맥스의 진정한 힘:
- REPL 방식 개발: eval → 즉시 적용 → 테스트 → 다시 수정
- 현재 상태 공유: 버퍼, 변수, 함수 정의, 패키지 정보
- 라이브 프로그래밍: 실행 중인 환경을 직접 수정
기존 AI 통합의 한계:
- 단방향: AI → 이맥스 (명령만 실행)
- 비동기적: 결과 받고 → 다시 물어봄
- 컨텍스트 단절: 이맥스 내부 상태를 AI가 모름
세 가지 통합 패러다임
1. 네이티브 Elisp Eval 방식 (직접 통합)
TODO Efrit (Steve Yegge)
- 이맥스 프로세스 내부에서 실행
- `eval` 함수로 AI가 모든 기능에 직접 접근
- MCP 없이 순수 Elisp로 작동
장점:
- 완전한 이맥스 통합
- 외부 프로토콜 오버헤드 없음
- 모든 Elisp 기능 직접 사용
단점:
- 초기 개발 단계 (불안정)
- 작성자도 “terrible at multi-step tasks”라고 인정
- 보안 위험 (무제한 eval)
현실: 많은 사용자가 기본 설정조차 어려워하고 있음
DONT Emigo
2. MCP 기반 방식 (표준 프로토콜)
MCP(Model Context Protocol): Anthropic이 2024년 11월 발표
- “AI를 위한 USB-C”
- M×N 문제 해결 (각 AI가 각 도구에 개별 통합 필요 → 표준 프로토콜)
- 자율 에이전트를 위해 설계됨
A. 이맥스를 MCP 서버로 만들기
elisp-dev-mcp (Laurynas Biveinis)
Elisp 개발을 위한 MCP 서버
제공 도구:
- `describe-function`: 함수 문서 추출
- `describe-variable`: 변수 정보 조회
- `find-function`: 소스 코드 정의 찾기
- `info-lookup`: Info 문서 접근
특징:
- 읽기 전용 도구 (안전함)
- mcp-server-lib.el 위에 구축
- 실제로 Claude와 도그푸딩하며 개발 중
- 커밋 히스토리에 Claude가 공동 저자로 나타남
설치: ```elisp (use-package mcp-server-lib :ensure t)
;; elisp-dev-mcp를 stdio 모드로 시작 M-x mcp-server-lib-start ```
emacs-mcp-server (rhblind)
포괄적 제어를 목표로 하는 MCP 서버
제공 도구:
- `eval-elisp`: 임의의 Elisp 코드 실행 ⚠️
- 버퍼 수정
- 파일 작업
- 이맥스 환경 전체 제어
보안 모델:
- 권한 프롬프트
- 위험 함수 화이트리스트
- “paranoid mode” 옵션
- 백업 강력 권장
경고: eval 권한은 매우 위험! 신중하게 사용할 것
org-mcp (Laurynas Biveinis)
Org-mode를 위한 MCP 서버
제공 도구:
- Org 파일 읽기/쓰기
- TODO 항목 조작
- 태그 관리
- 일정 작업
특징:
- 구조화된 API로 Org 파일 상호작용
- 쓰기 권한 부여 (백업 필수!)
- 자동 버전 관리 강력 권장
B. 이맥스 내에서 MCP 클라이언트 사용
mcp.el (lizqwerscott)
이맥스 내부의 MCP 클라이언트
기능:
- stdio와 SSE를 통해 MCP 서버 연결
- filesystem, fetch 등 다양한 서버 통합
- mcp-hub를 통한 서버 관리
- roots 프로토콜로 파일시스템 접근 제어
API: ```elisp ;; 동기 API (mcp-call-tool server-name tool-name args) (mcp-read-resource server-name uri) (mcp-get-prompt server-name prompt-name args)
;; 비동기 API (mcp-call-tool-async server-name tool-name args callback) ```
설치: ```elisp (use-package mcp :ensure t :config (mcp-hub-start)) ; 서버 관리 인터페이스 시작 ```
gptel + MCP 통합
gptel은 MCP 통합을 공식 지원
설정: ```elisp (use-package gptel :ensure t :config ;; MCP 도구를 gptel에 등록 (require ‘mcp)
;; filesystem MCP 서버 연결 (mcp-add-server “filesystem” :command ’(“npx” “-y” “@modelcontextprotocol/server-filesystem” “/path/to/allowed/dir”))
;; gptel이 MCP 도구 사용 가능하도록 설정 (setq gptel-use-mcp-tools t)) ```
사용: ```elisp ;; gptel 버퍼에서 ;; AI가 filesystem MCP 서버의 도구를 사용하여 파일 읽기/쓰기 가능 ```
3. ACP 기반 방식 (Agent Client Protocol)
agent-shell + OpenCode
현재 우리 환경: ``` Emacs (agent-shell) └─ ACP Protocol └─ OpenCode Agent ├─ Model: Claude 3.7 Sonnet ├─ MCP Servers: GitHub, Google Workspace, Playwright 등 └─ 서브에이전트: PM, code-refactor, code-reviewer ```
장점:
- OpenCode의 모든 기능 활용
- 다중 모델 지원
- 서브에이전트 시스템
- MCP 서버 생태계
단점:
- 이맥스 내부 상태 접근 불가 (현재)
- REPL 방식 개발 어려움 (현재)
당신이 놓친 핵심
양방향 상호작용의 부재
기존 방식 (단방향): ``` AI → 명령 → 이맥스 (실행) 이맥스 → 결과 → AI (출력만 받음) ```
진정한 REPL 방식 (양방향): ``` AI → Elisp 코드 → 이맥스 (eval) 이맥스 → 결과 + 현재 상태 → AI AI → 수정된 코드 → 이맥스 (eval) …반복… ```
MCP가 해결하는 부분
이맥스 상태를 AI에게 노출:
- `elisp-dev-mcp`: 함수 정의, 변수 값, 문서 읽기
- `emacs-mcp-server`: eval-elisp로 직접 코드 실행
- `org-mcp`: Org 파일 구조 조회 및 수정
실전 예시: ```
-
AI: “현재 버퍼의 `my-function` 정의를 보여줘” → elisp-dev-mcp: find-function → 이맥스: (defun my-function …)
-
AI: “이 함수를 리팩토링해줄게” → 코드 생성
-
AI: “eval해서 테스트해보자” → emacs-mcp-server: eval-elisp → 이맥스: 즉시 적용
-
AI: “변수 `my-var` 값이 뭐야?” → elisp-dev-mcp: describe-variable → 이맥스: 42
-
AI: “값을 100으로 바꿔보자” → emacs-mcp-server: eval-elisp (setq my-var 100) → 이맥스: 즉시 반영
```
실용적 선택지
지금 당장 시도할 만한 것
1. gptel + mcp.el (추천 ⭐⭐⭐⭐⭐)
장점:
- 가장 안정적
- 이맥스 네이티브 경험
- 기존 gptel 워크플로우에 MCP 도구 추가
- 점진적 도입 가능
설치: ```elisp ;; ~/.doom.d/packages.el (package! gptel) (package! mcp)
;; /.doom.d/config.el (use-package mcp :ensure t :config (mcp-add-server “filesystem” :command ’(“npx” “-y” “@modelcontextprotocol/server-filesystem” (expand-file-name ”/”)))
(mcp-add-server “git” :command ’(“uvx” “mcp-server-git” “—repository” (expand-file-name ”~/repos”))))
(use-package gptel :ensure t :config (setq gptel-use-mcp-tools t) (setq gptel-model ‘claude-3-7-sonnet gptel-backend (gptel-make-anthropic “Claude” :stream t :key (auth-source-pick-first-password :host “anthropic.com”)))) ```
사용: ```elisp M-x gptel
;; AI가 MCP 도구 사용: ”~/repos/my-project의 파일 목록을 보여줘” → filesystem MCP 서버 사용
“이 저장소의 최근 커밋을 확인해줘” → git MCP 서버 사용 ```
2. elisp-dev-mcp (추천 ⭐⭐⭐⭐)
장점:
- 안전한 읽기 전용
- 패키지 개발에 특화
- 이맥스 인트로스펙션
설치: ```bash git clone https://github.com/laurynas-biveinis/elisp-dev-mcp.git cd elisp-dev-mcp npm install ```
이맥스 설정: ```elisp (use-package mcp-server-lib :ensure t) ```
사용 (외부 AI에서): ```json // Claude Desktop 설정 { “mcpServers”: { “elisp-dev”: { “command”: “node”, “args”: [“/path/to/elisp-dev-mcp/build/index.js”], “env”: { “EMACS_SERVER_FILE”: “/tmp/emacs1000/server” } } } } ```
Claude에서: ``` “Emacs에서 `use-package` 함수의 정의를 보여줘” → elisp-dev-mcp: find-function
“gptel 패키지의 주요 변수들을 설명해줘” → elisp-dev-mcp: describe-variable ```
3. emacs-mcp-server (주의해서 ⚠️)
언제 사용:
- eval 권한이 반드시 필요할 때
- 철저한 백업이 준비되었을 때
- 위험을 이해하고 있을 때
설치: ```bash git clone https://github.com/rhblind/emacs-mcp-server.git cd emacs-mcp-server npm install ```
보안 설정: ```elisp ;; paranoid mode 활성화 (setq mcp-server-paranoid-mode t)
;; 허용할 함수만 화이트리스트 (setq mcp-server-allowed-functions ‘(setq defun defvar let let* progn)) ```
사용: ``` “이 버퍼의 내용을 소문자로 변환해줘” → emacs-mcp-server: eval-elisp → (downcase-region (point-min) (point-max)) ```
당신의 환경에 맞는 전략
현재 상황
- Emacs (Doom)
- agent-shell (ACP 프로토콜)
- OpenCode (MCP 서버 통합됨)
3단계 통합 계획
Phase 1: MCP 클라이언트 (Emacs 내부)
목표: 이맥스 안에서 MCP 도구 사용
```elisp ;; ~/.doom.d/packages.el (package! mcp)
;; /.doom.d/config.el (use-package mcp :config ;; OpenCode가 이미 사용하는 MCP 서버들을 이맥스에서도 사용 (mcp-add-server “git” :command ’(“uvx” “mcp-server-git” “—repository” (expand-file-name “/repos”)))
(mcp-add-server “github” :command ’(“/home/goqual/sync/emacs/claude-config/opencode/github-mcp-with-env.sh”))
;; 이맥스 전용 MCP 서버 (mcp-add-server “org” :command ’(“npx” “-y” “org-mcp” “—org-directory” (expand-file-name ”~/org”))))
;; Elisp 버퍼에서 MCP 도구 사용 (defun my/mcp-git-log () (interactive) (let ((result (mcp-call-tool “git” “git-log” ‘((limit . 10))))) (message “%s” result))) ```
효과:
- 이맥스에서 직접 MCP 도구 호출 가능
- OpenCode와 같은 도구 공유
Phase 2: MCP 서버 (Emacs를 노출)
목표: OpenCode가 이맥스 상태 접근
```elisp ;; elisp-dev-mcp 서버 시작 (use-package mcp-server-lib :config (mcp-server-lib-start)) ; stdio 모드로 시작
;; 또는 수동으로 M-x mcp-server-lib-start ```
OpenCode 설정: ```jsonc / ~.config/opencode/opencode.jsonc { “mcp”: { “emacs-elisp-dev”: { “type”: “local”, “command”: [“node”, “/path/to/elisp-dev-mcp/build/index.js”], “environment”: { “EMACS_SERVER_FILE”: “/tmp/emacs1000/server” }, “enabled”: true } } } ```
효과:
- OpenCode 에이전트가 이맥스 함수 정의 조회 가능
- 변수 값 확인 가능
- Info 문서 접근 가능
Phase 3: 양방향 통합 (REPL 방식)
목표: agent-shell ↔ OpenCode ↔ Emacs MCP
```elisp ;; agent-shell에서 이맥스 컨텍스트 전달 (defun agent-shell-send-with-emacs-context (prompt) “현재 이맥스 상태와 함께 프롬프트 전송” (interactive “sPrompt: ”) (let* ((buffer-info (buffer-name)) (major-mode major-mode) (point-info (point)) (region (when (use-region-p) (buffer-substring-no-properties (region-beginning) (region-end)))) ;; MCP로 추가 컨텍스트 수집 (git-status (mcp-call-tool “git” “git-status” nil)) (context (format ” <emacs-context> Buffer: %s Mode: %s Point: %d Region: %s Git Status: %s </emacs-context>
%s” buffer-info major-mode point-info (or region “none”) git-status prompt))) (agent-shell-send-prompt context)))
;; OpenCode가 제안한 코드를 이맥스에서 eval (defun agent-shell-eval-response () “OpenCode의 응답에서 Elisp 코드를 추출하여 eval” (interactive) (let* ((response (agent-shell-last-response)) (code (extract-elisp-code response))) ; 코드 블록 파싱 (when code (eval (read code)) (message “Evaluated: %s” code))))
;; 키바인딩 (map! :map agent-shell-mode-map :leader :desc “Send with Emacs context” “a e” #‘agent-shell-send-with-emacs-context :desc “Eval last response” “a E” #‘agent-shell-eval-response) ```
워크플로우: ```
-
[Emacs] 함수 정의 커서에서 C-c a e “이 함수를 리팩토링해줘”
-
[agent-shell] → OpenCode
- emacs-elisp-dev MCP로 함수 정의 조회
- git MCP로 변경 이력 확인
- 컨텍스트 기반으로 리팩토링 제안
-
[OpenCode] → agent-shell 리팩토링된 코드 반환
-
[Emacs] C-c a E → 제안된 코드 즉시 eval → 테스트
-
[Emacs] 결과 확인 후 다시 C-c a e “타입 힌트를 추가해줘”
-
반복…
```
구체적 구현 예시
1. Elisp 개발 워크플로우
```elisp ;; 1. 함수 개발 시작 (defun my-new-function (arg) “TODO: 문서 작성” arg)
;; 2. agent-shell에 요청 C-c a e “이 함수에 에러 핸들링과 문서를 추가해줘”
;; 3. OpenCode가 emacs-elisp-dev MCP로: ;; - describe-function으로 유사 함수 찾기 ;; - info-lookup으로 Elisp 매뉴얼 참조 ;; - 개선된 코드 제안
;; 4. 제안 받음: (defun my-new-function (arg) “Process ARG with error handling. Returns processed result or nil on error.” (condition-case err (progn (unless arg (error “ARG must not be nil”)) (process-arg arg)) (error (message “Error in my-new-function: %s” (error-message-string err)) nil)))
;; 5. 즉시 eval C-c a E
;; 6. 테스트 (my-new-function nil) ; → 에러 처리 확인 (my-new-function “test”) ; → 정상 작동 확인
;; 7. 추가 개선 요청 C-c a e “타입 체크를 더 강화해줘” ```
2. 패키지 설정 최적화
```elisp ;; 현재 설정 (use-package gptel :config (setq gptel-model ‘claude-3-7-sonnet))
;; agent-shell에 요청 C-c a e “이 gptel 설정을 최적화해줘. 키바인딩과 커스텀 백엔드 추가”
;; OpenCode가 elisp-dev-mcp로: ;; - describe-variable gptel-* 변수들 조회 ;; - find-function gptel 관련 함수들 확인 ;; - 최적화된 설정 제안
;; 제안 받음: (use-package gptel :bind ((“C-c g g” . gptel) (“C-c g s” . gptel-send) (“C-c g r” . gptel-rewrite)) :config (setq gptel-model ‘claude-3-7-sonnet gptel-default-mode ‘org-mode gptel-use-mcp-tools t)
;; 커스텀 백엔드 (gptel-make-anthropic “Claude-Fast” :stream t :key (auth-source-pick-first-password :host “anthropic.com”) :models ‘(claude-haiku-4-5)))
;; 즉시 eval C-c a E
;; 테스트 C-c g g ; gptel 버퍼 열기 ```
3. Org-mode 워크플로우
```elisp ;; org-mcp 서버 설정 (mcp-add-server “org” :command ’(“npx” “-y” “org-mcp” “—org-directory” (expand-file-name ”~/org”)))
;; OpenCode에서 Org 파일 조작 C-c a e ”~/org/tasks.org에서 DONE 상태인 TODO를 ARCHIVE로 옮겨줘”
;; OpenCode가 org-mcp로: ;; - Org 파일 파싱 ;; - DONE TODO 찾기 ;; - ARCHIVE 처리 ;; - 결과 보고
;; 결과 확인 C-x C-f ~/org/tasks.org ```
보안 고려사항
1. 읽기 전용 우선
```elisp ;; 안전: elisp-dev-mcp (읽기만) (mcp-server-lib-start)
;; 위험: emacs-mcp-server (eval 가능) ;; → paranoid mode 필수 (setq mcp-server-paranoid-mode t) ```
2. 화이트리스트 기반
```elisp ;; eval 허용 함수 제한 (setq mcp-server-allowed-functions ’(;; 변수 조작 setq setq-default ;; 함수 정의 defun defvar defcustom ;; 안전한 제어 구조 let let* progn if when unless ;; 메시지만 message)) ```
3. 백업 자동화
```elisp ;; eval 전 자동 백업 (defun my/backup-before-eval () (when (buffer-file-name) (let ((backup-file (concat (buffer-file-name) “.mcp-backup”))) (copy-file (buffer-file-name) backup-file t))))
(add-hook ‘mcp-server-before-eval-hook #‘my/backup-before-eval) ```
4. Git 통합
```elisp ;; eval 후 자동 커밋 (defun my/git-commit-after-eval () (when (and (buffer-file-name) (magit-git-repo-p)) (magit-stage-file (buffer-file-name)) (magit-commit-create ’(“-m” “MCP eval: auto-commit”))))
(add-hook ‘mcp-server-after-eval-hook #‘my/git-commit-after-eval) ```
성능 최적화
1. 컨텍스트 크기 제한
```elisp (defun agent-shell-send-with-emacs-context (prompt) (let* ((max-region-size 1000) ; 최대 1000자 (region (when (use-region-p) (let ((text (buffer-substring-no-properties (region-beginning) (region-end)))) (if (> (length text) max-region-size) (concat (substring text 0 max-region-size) ”…”) text))))) ;; … )) ```
2. MCP 서버 선택적 활성화
```elisp ;; 프로젝트별로 필요한 서버만 시작 (defun my/start-project-mcp-servers () (interactive) (cond ((projectile-project-type) ;; Elisp 프로젝트 ((eq (projectile-project-type) ‘emacs-lisp) (mcp-start-server “elisp-dev”)) ;; Web 프로젝트 ((member (projectile-project-type) ‘(npm yarn)) (mcp-start-server “playwright”)))) (t ;; 기본: git만 (mcp-start-server “git”)))) ```
3. 캐싱
```elisp ;; 자주 조회하는 정보는 캐시 (defvar my/mcp-cache (make-hash-table :test ‘equal))
(defun my/mcp-call-tool-cached (server tool args) (let ((key (format “%s:%s:%s” server tool args))) (or (gethash key my/mcp-cache) (puthash key (mcp-call-tool server tool args) my/mcp-cache)))) ```
디버깅
1. MCP 트래픽 로깅
```elisp (setq mcp-debug t) ; MCP 통신 로그 활성화
;; 로그 확인 M-x mcp-show-log ```
2. agent-shell 연동 확인
```elisp (defun my/test-agent-shell-mcp () “agent-shell과 MCP 연동 테스트” (interactive) (let ((test-cases ’((“git” “git-status” nil) (“elisp-dev” “describe-function” ((“function” . “use-package”))) (“org” “list-todos” ((“file” . ”~/org/tasks.org”)))))) (dolist (case test-cases) (let ((server (nth 0 case)) (tool (nth 1 case)) (args (nth 2 case))) (condition-case err (progn (message “Testing %s:%s…” server tool) (mcp-call-tool server tool args) (message ”✓ %s:%s OK” server tool)) (error (message ”✗ %s:%s FAILED: %s” server tool (error-message-string err)))))))) ```
로드맵
Phase 1: 기초 (1주)
- gptel + mcp.el 설치
- elisp-dev-mcp 설정
- agent-shell에서 기본 MCP 호출 테스트
Phase 2: 통합 (2주)
- agent-shell ↔ OpenCode ↔ Emacs MCP 연결
- 컨텍스트 전달 함수 구현
- eval 워크플로우 구축
Phase 3: 최적화 (2주)
- 성능 튜닝 (캐싱, 선택적 로딩)
- 보안 강화 (화이트리스트, 백업)
- 프로젝트별 프로필
Phase 4: 고급 (진행 중)
- LSP ↔ MCP 브릿지
- 멀티 에이전트 협업
- 자동화된 테스트 및 검증
결론
핵심 인사이트: 진정한 이맥스-AI 통합은 “에디터 안에서 채팅”이 아니라 “에디터를 에이전트에게 주는 것”
MCP의 가치:
- 프로토콜 우선 (vendor-neutral)
- 자율 에이전트를 위해 설계됨
- M×N 문제 해결
실용적 시작점:
- gptel + mcp.el (가장 안정적)
- elisp-dev-mcp (안전한 이맥스 노출)
- agent-shell 통합 (점진적)
당신의 장점:
- Doom Emacs (강력한 설정 프레임워크)
- agent-shell (ACP 프로토콜)
- OpenCode (MCP 생태계)
이 세 가지를 연결하면 **진정한 REPL 방식 AI 협업**이 가능합니다!
참고 자료
프로젝트
- elisp-dev-mcp: https://github.com/laurynas-biveinis/elisp-dev-mcp
- emacs-mcp-server: https://github.com/rhblind/emacs-mcp-server
- org-mcp: https://github.com/laurynas-biveinis/org-mcp
- mcp.el: https://github.com/lizqwerscott/mcp.el
- gptel: https://github.com/karthink/gptel
문서
- MCP 공식: https://modelcontextprotocol.io
- Anthropic MCP 발표: https://www.anthropic.com/news/model-context-protocol
- MCP 명세: https://spec.modelcontextprotocol.io
관련 llmlog
APPENDIX: OpenCode agent-shell-mode 구현 제안
개요
agent-shell에서 OpenCode를 사용할 때 MCP 기반 Emacs 통합을 최대한 활용하는 방법입니다. 기존 agent-shell-mode를 확장하여 OpenCode의 장점과 Emacs의 REPL 특성을 결합합니다.
핵심 아키텍처
``` ┌──────────────────────────────────────────────────────────┐ │ Emacs (Doom) │ │ ┌────────────────────────────────────────────────────┐ │ │ │ agent-shell-opencode-mode │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ MCP Client │◄────────┤ Context │ │ │ │ │ │ (mcp.el) │ │ Collector │ │ │ │ │ └──────┬───────┘ └──────────────┘ │ │ │ │ │ │ │ │ │ │ ACP Protocol │ │ │ │ ▼ │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ │ │ OpenCode Agent │ │ │ │ │ │ ┌─────────────┐ ┌────────────────┐│ │ │ │ │ │ │ MCP Servers │ │ Sub-agents ││ │ │ │ │ │ │ - github │ │ - code-refactor││ │ │ │ │ │ │ - git │ │ - PM ││ │ │ │ │ │ │ - emacs-dev │ │ - reviewer ││ │ │ │ │ │ └─────────────┘ └────────────────┘│ │ │ │ │ └──────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ MCP Server (Emacs 노출) │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ elisp-dev │ │ org-mcp │ │ │ │ │ │ (read-only) │ │ (org files) │ │ │ │ │ └──────────────┘ └──────────────┘ │ │ │ └────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘ ```
패키지 구조
```elisp ;; ~/.doom.d/packages.el (package! agent-shell) ; 기본 agent-shell (package! mcp) ; MCP 클라이언트 (package! gptel) ; 선택적 (기존 gptel 사용자)
;; MCP 서버 (외부 설치 필요) ;; npm install -g elisp-dev-mcp ;; npm install -g org-mcp ```
설정 파일
~/.doom.d/config.el
```elisp ;;; agent-shell + OpenCode 통합
;; 1. MCP 클라이언트 설정 (use-package! mcp :config ;; Emacs가 MCP 클라이언트로서 OpenCode의 서버들 사용 (mcp-add-server “git” :command ’(“uvx” “mcp-server-git” “—repository” (expand-file-name ”~/repos”)))
(mcp-add-server “github” :command ’(“bash” (expand-file-name ”~/sync/emacs/claude-config/opencode/github-mcp-with-env.sh”)))
;; 프로젝트별 활성화 (defun my/mcp-enable-project-servers () “프로젝트 타입에 따라 MCP 서버 활성화” (when (projectile-project-p) (pcase (projectile-project-type) (‘emacs-lisp ;; Elisp 프로젝트 nil) ; elisp-dev는 MCP 서버로 이미 실행 중 (‘npm ;; JavaScript 프로젝트 (mcp-add-server “playwright” :command ’(“npx” “@playwright/mcp@latest” “—extension”))) (‘python ;; Python 프로젝트 nil)))) ; TODO: Python MCP 서버 추가
(add-hook ‘projectile-after-switch-project-hook #‘my/mcp-enable-project-servers))
;; 2. MCP 서버 설정 (Emacs를 노출) (use-package! mcp-server-lib :defer t :config ;; Emacs를 MCP 서버로 시작 (daemon 모드) ;; OpenCode가 연결할 수 있도록 stdio 모드 (defun my/start-emacs-mcp-server () “Emacs를 MCP 서버로 시작” (interactive) (mcp-server-lib-start) (message “Emacs MCP server started on stdio”)))
;; 3. agent-shell-opencode-mode 정의 (define-minor-mode agent-shell-opencode-mode “OpenCode 전용 agent-shell 강화 모드” :lighter ” OC” :keymap (let ((map (make-sparse-keymap))) ;; 컨텍스트 전달 (define-key map (kbd “C-c a e”) #‘agent-shell-send-with-context) (define-key map (kbd “C-c a r”) #‘agent-shell-send-region-with-context)
;; 에이전트 제어 (define-key map (kbd “C-c a a”) #‘agent-shell-switch-opencode-agent) (define-key map (kbd “C-c a m”) #‘agent-shell-switch-opencode-model)
;; MCP 제어 (define-key map (kbd “C-c a M”) #‘agent-shell-enable-mcp-server)
;; 응답 처리 (define-key map (kbd “C-c a E”) #‘agent-shell-eval-last-response) (define-key map (kbd “C-c a A”) #‘agent-shell-apply-last-diff)
map))
;; 4. 컨텍스트 수집기 (defun agent-shell—collect-emacs-context () “현재 Emacs 상태를 수집하여 컨텍스트 생성” (let* ((buffer-info (format “Buffer: %s\nMode: %s\nPoint: %d” (buffer-name) major-mode (point)))
(region-info (when (use-region-p) (let ((text (buffer-substring-no-properties (region-beginning) (region-end)))) (format “\n<region>\n%s\n</region>” (if (> (length text) 1000) (concat (substring text 0 1000) “\n…”) text)))))
(git-info (when (and (fboundp ‘mcp-call-tool) (mcp-server-active-p “git”)) (condition-case nil (format “\n<git-status>\n%s\n</git-status>” (mcp-call-tool “git” “git-status” nil)) (error ""))))
(project-info (when (projectile-project-p) (format “\nProject: %s\nType: %s” (projectile-project-name) (projectile-project-type))))
(lsp-info (when (bound-and-true-p lsp-mode) (let ((symbol (thing-at-point ‘symbol))) (when symbol (condition-case nil (format “\n<lsp-context symbol=\“%s\”>\n%s\n</lsp-context>” symbol (lsp-hover-info-at-point)) (error "")))))))
(concat buffer-info region-info git-info project-info lsp-info)))
;; 5. 컨텍스트와 함께 프롬프트 전송 (defun agent-shell-send-with-context (prompt) “Emacs 컨텍스트와 함께 프롬프트를 OpenCode에 전송” (interactive “sPrompt: ”) (let* ((context (agent-shell—collect-emacs-context)) (full-prompt (format “%s\n\n<emacs-context>\n%s\n</emacs-context>” prompt context))) (agent-shell-send-prompt full-prompt)))
(defun agent-shell-send-region-with-context () “선택 영역과 컨텍스트를 함께 전송” (interactive) (unless (use-region-p) (user-error “No region selected”)) (let* ((prompt (read-string “What to do with this code: ”)) (context (agent-shell—collect-emacs-context))) (agent-shell-send-prompt (format “%s\n\n<emacs-context>\n%s\n</emacs-context>” prompt context))))
;; 6. OpenCode 에이전트 전환 (defun agent-shell-switch-opencode-agent (agent) “OpenCode 에이전트를 전환” (interactive (list (completing-read “Agent: ” ’(“build” “plan” “PM” “code-refactor” “code-reviewer” “security-forensics”) nil t))) (agent-shell-send-command (format “/mode %s” agent)) (message “Switched to %s agent” agent))
;; 7. OpenCode 모델 전환 (defun agent-shell-switch-opencode-model (model) “OpenCode 모델을 전환” (interactive (list (completing-read “Model: ” ’(“anthropic/claude-sonnet-4-5” “anthropic/claude-haiku-4-5” “openrouter/openai/gpt-5.1-chat” “openrouter/openai/gpt-5.1-codex” “deepseek/deepseek-coder”) nil t))) ;; ACP 프로토콜로 모델 변경 (구현 필요) ;; 현재는 설정 파일에서 agent별 모델 지정 (message “Model switching via ACP not yet implemented. Use agent config.“))
;; 8. MCP 서버 동적 활성화 (defun agent-shell-enable-mcp-server (server) “MCP 서버를 동적으로 활성화” (interactive (list (completing-read “MCP Server: ” ’(“playwright-extension” “browserbase” “n8n-mcp” “supabase”) nil t))) (pcase server (“playwright-extension” (mcp-add-server “playwright” :command ’(“npx” “@playwright/mcp@latest” “—extension”))) (“browserbase” (mcp-add-server “browserbase” :command ’(“bash” (expand-file-name “/sync/emacs/claude-config/mcp-wrappers/browserbase-mcp-wrapper.sh”)))) (“n8n-mcp” (mcp-add-server “n8n” :command ’(“bash” (expand-file-name “/sync/emacs/claude-config/mcp-wrappers/n8n-mcp-wrapper.sh”)))) (“supabase” (mcp-add-server “supabase” :command ’(“bash” (expand-file-name ”~/sync/emacs/claude-config/mcp-wrappers/supabase-mcp-wrapper.sh”))))) (message “Enabled MCP server: %s” server))
;; 9. 응답 처리 - Elisp eval (defun agent-shell-eval-last-response () “마지막 응답에서 Elisp 코드를 추출하여 eval” (interactive) (let* ((response (agent-shell—get-last-response)) (code (agent-shell—extract-elisp-code response))) (if code (progn (when (y-or-n-p (format “Eval this code?\n\n%s” code)) (condition-case err (progn (eval (read code)) (message ”✓ Code evaluated successfully”)) (error (message ”✗ Eval error: %s” (error-message-string err)))))) (message “No Elisp code found in response”))))
(defun agent-shell—extract-elisp-code (text) “응답에서 Elisp 코드 블록 추출” (when (string-match ”```\?:elisp\\\|emacs-lisp\\n\\$$?:.\\\|\n\*?\$$```” text) (match-string 1 text)))
(defun agent-shell—get-last-response () “마지막 agent 응답 가져오기” ;; agent-shell 구현에 따라 다를 수 있음 ;; 버퍼에서 마지막 응답 추출 (save-excursion (goto-char (point-max)) (let ((end (point))) (when (re-search-backward ”^▼\\|^Agent>” nil t) (buffer-substring-no-properties (point) end)))))
;; 10. 응답 처리 - Diff 적용 (defun agent-shell-apply-last-diff () “마지막 응답의 diff를 현재 버퍼에 적용” (interactive) (let* ((response (agent-shell—get-last-response)) (diff (agent-shell—extract-diff response))) (if diff (when (y-or-n-p “Apply this diff?“) (agent-shell—apply-diff diff)) (message “No diff found in response”))))
(defun agent-shell—extract-diff (text) “응답에서 unified diff 추출” (when (string-match “```diff\n\\$$?:.\\\|\n\*?\$$```” text) (match-string 1 text)))
(defun agent-shell—apply-diff (diff) “Unified diff를 현재 버퍼에 적용” ;; diff-mode 사용 (let ((diff-buffer (generate-new-buffer “agent-diff”))) (with-current-buffer diff-buffer (insert diff) (diff-mode) (goto-char (point-min))) (pop-to-buffer diff-buffer) (message “Review diff and use ‘C-c C-a’ to apply”)))
;; 11. agent-shell 후크 (add-hook ‘agent-shell-mode-hook (lambda () (agent-shell-opencode-mode 1)))
;; 12. hydra 메뉴 (선택적) (when (featurep ‘hydra) (defhydra hydra-agent-shell-opencode (:color blue :hint nil) ” ^Send^ ^Agent^ ^MCP^ ^Response^ ^^^^^^^^----------------------------------------------------------------- e: with context a: switch M: enable server E: eval code r: region m: model ^ ^ A: apply diff p: plain ^ ^ ^ ^ ^ ^ ” (“e” agent-shell-send-with-context) (“r” agent-shell-send-region-with-context) (“p” agent-shell-send-prompt) (“a” agent-shell-switch-opencode-agent) (“m” agent-shell-switch-opencode-model) (“M” agent-shell-enable-mcp-server) (“E” agent-shell-eval-last-response) (“A” agent-shell-apply-last-diff) (“q” nil “quit”))
;; Hydra 키바인딩 (map! :map agent-shell-mode-map :leader :prefix (“o” . “opencode”) :desc “OpenCode menu” “o” #‘hydra-agent-shell-opencode/body)) ```
OpenCode 설정 업데이트
```jsonc / ~.config/opencode/opencode.jsonc
{ “mcp”: { // 기존 서버들…
// Emacs MCP 서버 추가 “emacs-elisp-dev”: { “type”: “local”, “command”: [ “node”, “/path/to/elisp-dev-mcp/build/index.js” ], “environment”: { “EMACS_SERVER_FILE”: “/tmp/emacs1000/server” }, “enabled”: true },
“emacs-org”: { “type”: “local”, “command”: [ “npx”, “-y”, “org-mcp”, “—org-directory”, “/home/goqual/org” ], “enabled”: true } },
“agent”: { // Elisp 개발 전용 에이전트 “elisp-dev”: { “mode”: “subagent”, “description”: “Elisp development and Emacs configuration expert”, “model”: “anthropic/claude-sonnet-4-5”, “prompt”: “You are an Emacs Lisp expert. Use emacs-elisp-dev MCP server to:\n- Query function definitions with find-function\n- Check variable values with describe-variable\n- Look up documentation with info-lookup\n\nAlways check existing implementations before suggesting new code.”, “tools”: { “write”: true, “edit”: true, “bash”: false, “mcp_emacs-elisp-dev”: true }, “permission”: { “write”: “ask”, “edit”: “ask” } } } } ```
사용 워크플로우
1. 시작 (한 번만)
```bash
emacs —daemon
emacsclient -c
M-x my/start-emacs-mcp-server ```
2. Elisp 개발
```elisp ;; 1. 함수 작성 (defun my-new-function (arg) “TODO” arg)
;; 2. 커서를 함수 위에 두고 C-c a e ; agent-shell-send-with-context “이 함수에 에러 핸들링과 docstring을 추가해줘”
;; 3. OpenCode가 emacs-elisp-dev MCP로: ;; - 유사 함수 찾기 ;; - Elisp 매뉴얼 참조 ;; - 개선된 코드 제안
;; 4. 응답 확인 후 C-c a E ; agent-shell-eval-last-response ;; → 제안된 코드 즉시 eval
;; 5. 테스트 (my-new-function nil) (my-new-function “test”)
;; 6. 추가 개선 C-c a e “타입 체크 강화” ```
3. 프로젝트 작업
```elisp ;; 1. 프로젝트 오픈 SPC p p ; projectile-switch-project
;; 2. MCP 서버 자동 활성화 (프로젝트 타입 기반) ;; → my/mcp-enable-project-servers 실행됨
;; 3. 파일 수정 C-c a r ; agent-shell-send-region-with-context ;; 선택한 코드 영역을 OpenCode에 전송 ;; Git 상태, LSP 정보 포함
;; 4. OpenCode가 컨텍스트 기반 제안 ;; - git MCP로 변경 이력 확인 ;; - emacs-elisp-dev로 함수 정의 조회 ;; - LSP 정보로 타입 확인
;; 5. Diff 적용 C-c a A ; agent-shell-apply-last-diff ```
4. Org-mode 작업
```elisp ;; 1. Org 파일 열기 C-x C-f ~/org/tasks.org
;; 2. OpenCode에 요청 C-c a e “DONE 상태인 TODO들을 ARCHIVE로 옮겨줘”
;; 3. OpenCode가 emacs-org MCP로: ;; - Org 파일 파싱 ;; - TODO 상태 확인 ;; - ARCHIVE 처리
;; 4. 결과 확인 C-x C-s ; 저장 ```
고급 사용법
멀티 에이전트 협업
```elisp ;; 1. code-reviewer로 현재 코드 분석 C-c a a code-reviewer C-c a e “이 모듈의 보안 취약점을 찾아줘”
;; 2. 결과 확인 후 code-refactor로 전환 C-c a a code-refactor C-c a e “방금 찾은 취약점을 수정해줘”
;; 3. 수정 적용 C-c a E
;; 4. 다시 reviewer로 검증 C-c a a code-reviewer C-c a e “수정이 제대로 되었는지 확인해줘” ```
브라우저 자동화 통합
```elisp ;; 1. Playwright MCP 활성화 C-c a M playwright-extension
;; 2. 웹 스크래핑 요청 C-c a e “https://example.com에서 데이터를 수집해서 Org 파일로 저장해줘”
;; 3. OpenCode가: ;; - playwright MCP로 브라우저 제어 ;; - 데이터 추출 ;; - emacs-org MCP로 Org 파일 생성
;; 4. 결과 확인 C-x C-f ~/org/scraped-data.org ```
트러블슈팅
MCP 서버 연결 실패
```elisp ;; 1. MCP 로그 확인 (setq mcp-debug t) M-x mcp-show-log
;; 2. Emacs 서버 파일 확인 M-: server-socket-dir ;; → tmp/emacs1000
;; 3. elisp-dev-mcp 환경 변수 확인 ;; OpenCode 설정에서: “environment”: { “EMACS_SERVER_FILE”: “/tmp/emacs1000/server” ; 경로 일치 확인 } ```
ACP 프로토콜 디버깅
```elisp ;; agent-shell 디버그 모드 (setq agent-shell-debug t)
;; 로그 버퍼 확인 Messages agent-shell-debug ```
성능 문제
```elisp ;; 컨텍스트 크기 제한 (setq agent-shell-max-context-size 1000) ; 1000자로 제한
;; MCP 캐싱 활성화 (setq mcp-enable-cache t)
;; 불필요한 MCP 서버 비활성화 M-x mcp-stop-server RET playwright RET ```
장점 정리
-
완전한 양방향 통합
- Emacs → OpenCode (컨텍스트 전달)
- OpenCode → Emacs (MCP로 상태 조회)
-
REPL 방식 개발
- 코드 제안 → 즉시 eval → 테스트 → 수정
-
멀티 에이전트 활용
- 작업별 최적화된 에이전트
- 서브에이전트 간 협업
-
MCP 생태계
- Git, GitHub, Org-mode 통합
- 브라우저 자동화 (필요시)
- 확장 가능
-
점진적 도입
- 기존 agent-shell 워크플로우 유지
- 필요한 기능만 활성화
다음 단계
- elisp-dev-mcp 설치 및 테스트
- OpenCode 설정 업데이트
- Doom config에 agent-shell-opencode-mode 추가
- 실제 Elisp 개발로 검증
- 워크플로우 최적화 및 문서화
메타데이터
- 작성일: 2025-11-21 금요일 13:23
- 업데이트: 2025-11-21 금요일 13:30 (APPENDIX 추가)
- 작성자: AI Assistant (OpenCode via ACP)
- 컨텍스트: Emacs-AI 통합, MCP, REPL 방식 개발
- 디바이스: LAPTOP
- 핵심 개념: “에디터를 에이전트에게 주는 것”
- 추가 내용: agent-shell-opencode-mode 구체적 구현
Comments