git clone git@github.com:steveyegge/efrit.git

Emacs-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 파일 구조 조회 및 수정

실전 예시: ```

  1. AI: “현재 버퍼의 `my-function` 정의를 보여줘” → elisp-dev-mcp: find-function → 이맥스: (defun my-function …)

  2. AI: “이 함수를 리팩토링해줄게” → 코드 생성

  3. AI: “eval해서 테스트해보자” → emacs-mcp-server: eval-elisp → 이맥스: 즉시 적용

  4. AI: “변수 `my-var` 값이 뭐야?” → elisp-dev-mcp: describe-variable → 이맥스: 42

  5. 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) ```

워크플로우: ```

  1. [Emacs] 함수 정의 커서에서 C-c a e “이 함수를 리팩토링해줘”

  2. [agent-shell] → OpenCode

    • emacs-elisp-dev MCP로 함수 정의 조회
    • git MCP로 변경 이력 확인
    • 컨텍스트 기반으로 리팩토링 제안
  3. [OpenCode] → agent-shell 리팩토링된 코드 반환

  4. [Emacs] C-c a E → 제안된 코드 즉시 eval → 테스트

  5. [Emacs] 결과 확인 후 다시 C-c a e “타입 힌트를 추가해줘”

  6. 반복…

```

구체적 구현 예시

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 문제 해결

실용적 시작점:

  1. gptel + mcp.el (가장 안정적)
  2. elisp-dev-mcp (안전한 이맥스 노출)
  3. agent-shell 통합 (점진적)

당신의 장점:

  • Doom Emacs (강력한 설정 프레임워크)
  • agent-shell (ACP 프로토콜)
  • OpenCode (MCP 생태계)

이 세 가지를 연결하면 **진정한 REPL 방식 AI 협업**이 가능합니다!

참고 자료

프로젝트

문서

관련 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 ```

장점 정리

  1. 완전한 양방향 통합

    • Emacs → OpenCode (컨텍스트 전달)
    • OpenCode → Emacs (MCP로 상태 조회)
  2. REPL 방식 개발

    • 코드 제안 → 즉시 eval → 테스트 → 수정
  3. 멀티 에이전트 활용

    • 작업별 최적화된 에이전트
    • 서브에이전트 간 협업
  4. MCP 생태계

    • Git, GitHub, Org-mode 통합
    • 브라우저 자동화 (필요시)
    • 확장 가능
  5. 점진적 도입

    • 기존 agent-shell 워크플로우 유지
    • 필요한 기능만 활성화

다음 단계

  1. elisp-dev-mcp 설치 및 테스트
  2. OpenCode 설정 업데이트
  3. Doom config에 agent-shell-opencode-mode 추가
  4. 실제 Elisp 개발로 검증
  5. 워크플로우 최적화 및 문서화

메타데이터

  • 작성일: 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 구체적 구현