본문 바로가기
기술

Cursor 생산성 200% 향상: MCP로 나만의 Colima 도우미 만들기

by 스타스토리. 2025. 6. 16.
반응형
핵심 요약: 본 문서는 AI 기반 코드 에디터 Cursor에서 Colima, Docker 등 로컬 셸 명령어를 직접 실행하기 위해 MCP(Model Context Protocol) 서버를 구축하는 과정을 단계별로 설명한다. Python 스크립트를 작성하여 JSON-RPC 2.0 프로토콜을 구현하고, 이를 Cursor 설정에 연동하여 개발 생산성을 향상시키는 방법을 다룬다.

AI 코드 에디터 Cursor는 개발 과정에서 매우 강력한 도구이지만, colima startdocker ps와 같은 로컬 환경의 명령어를 실행하려면 별도의 터미널 창을 열어야 하는 번거로움이 있다. Cursor의 MCP(Model Context Protocol) 기능을 활용하면 이러한 작업을 에디터 내에서 직접 처리할 수 있다. 하지만 단순 셸 스크립트는 MCP의 통신 방식인 JSON-RPC 2.0 프로토콜을 따르지 않아 직접 연동이 어렵다.

본 문서는 이러한 문제를 해결하기 위해, MCP 명세에 맞는 Python 서버를 구현하고 Cursor에 설정하여 Colima 관련 명령어를 원활하게 실행하는 상세한 절차를 제공하는 것을 목표로 한다.

0. 시작 전 준비 사항

본 튜토리얼을 진행하기에 앞서, 아래의 개발 환경이 구축되어 있는지 확인해야 한다.

  • Cursor: AI 코드 에디터 설치 완료
  • Colima & Docker: 설치 및 기본 설정 완료
  • Python: v3.6 이상 (python3 --version 명령어로 버전 확인)
  • 코드 에디터: VS Code 또는 기타 선호하는 편집기

모든 요구사항이 충족되었다면, MCP 서버 스크립트를 저장할 프로젝트 디렉터리를 생성하고 해당 경로로 이동한다.

mkdir mcp-server
cd mcp-server

1단계: MCP 서버 스크립트 작성

가장 먼저, Cursor와 통신할 MCP 서버 역할을 수행할 Python 스크립트를 작성한다. 이 스크립트는 표준 입출력(stdin/stdout)을 통해 JSON-RPC 형식의 메시지를 주고받는다.

📄 mcp_colima_server.py

생성된 mcp_colima_server.py 파일에 아래의 코드를 작성한다. 이 코드는 `initialize`, `tools/list`, `tools/call`이라는 세 가지 핵심 MCP 메서드를 처리한다.

#!/usr/bin/env python3
import json
import sys
import subprocess
from typing import Dict, Any

class MCPColimaServer:
    def __init__(self):
        # 보안을 위해 실행 가능한 명령어 목록을 명시적으로 제한한다.
        self.allowed_commands = [
            "colima", "docker", "docker-compose", "brew", 
            "ls", "cat", "pwd", "ps", "which", "echo"
        ]
    
    def handle_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
        method = request.get("method")
        
        # 1. 서버 초기화 요청 처리
        if method == "initialize":
            return {
                "jsonrpc": "2.0", "id": request.get("id"), "result": {
                    "protocolVersion": "2024-11-05", "capabilities": {"tools": {}},
                    "serverInfo": {"name": "colima-helper", "version": "1.0.0"}
                }
            }
        
        # 2. 사용 가능한 도구 목록 요청 처리
        elif method == "tools/list":
            return {
                "jsonrpc": "2.0", "id": request.get("id"), "result": {
                    "tools": [{
                        "name": "run_command",
                        "description": "Run allowed shell commands",
                        "inputSchema": {
                            "type": "object",
                            "properties": {
                                "command": {"type": "string", "description": "Command to execute"}
                            },
                            "required": ["command"]
                        }
                    }]
                }
            }
        
        # 3. 실제 명령어 실행 요청 처리
        elif method == "tools/call":
            params = request.get("params", {})
            if params.get("name") == "run_command":
                command = params.get("arguments", {}).get("command", "")
                result = self.execute_command(command)
                return {
                    "jsonrpc": "2.0", "id": request.get("id"), "result": {
                        "content": [{"type": "text", "text": result}]
                    }
                }
        
        # 지원하지 않는 메서드에 대한 오류 응답
        return {
            "jsonrpc": "2.0", "id": request.get("id"),
            "error": {"code": -32601, "message": f"Method not found: {method}"}
        }
    
    def execute_command(self, command: str) -> str:
        # 명령어 첫 부분이 허용 목록에 있는지 검증
        cmd_parts = command.split()
        if not cmd_parts or cmd_parts[0] not in self.allowed_commands:
            return f"Error: Command not allowed: {cmd_parts[0]}"
        
        try:
            result = subprocess.run(
                command, shell=True, capture_output=True, text=True, timeout=30
            )
            if result.returncode == 0:
                return result.stdout or "Command executed successfully."
            else:
                return f"Error: {result.stderr}"
        except Exception as e:
            return f"Error: {str(e)}"
    
    def run(self):
        # 표준 입력으로 들어오는 각 라인을 읽어 처리
        for line in sys.stdin:
            try:
                request = json.loads(line.strip())
                response = self.handle_request(request)
                # 처리 결과를 JSON 형태로 표준 출력
                print(json.dumps(response), flush=True)
            except (json.JSONDecodeError, Exception) as e:
                # 오류 발생 시 JSON-RPC 오류 메시지 출력
                error_response = {
                    "jsonrpc": "2.0", "id": None,
                    "error": {"code": -32603, "message": f"Internal error: {str(e)}"}
                }
                print(json.dumps(error_response), flush=True)

if __name__ == "__main__":
    server = MCPColimaServer()
    server.run()
📌 권장 사항
Python 프로젝트는 의존성 관리를 위해 가상 환경(virtual environment) 내에서 관리하는 것이 모범 사례이다. 아래 명령어를 통해 가상 환경을 생성하고 활성화할 수 있다.
python3 -m venv .venv
source .venv/bin/activate

2단계: 스크립트 실행 권한 부여 및 테스트

작성된 Python 스크립트를 Cursor가 호출할 수 있도록 실행 권한을 부여해야 한다.

chmod +x mcp_colima_server.py

Cursor에 설정하기 전, 스크립트가 MCP 프로토콜에 맞게 올바르게 응답하는지 터미널에서 직접 테스트한다. 아래는 colima status 명령을 테스트하는 예시이다.

echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "run_command", "arguments": {"command": "colima status"}}}' | ./mcp_colima_server.py

테스트 실행 시, 아래와 같이 colima status의 결과가 포함된 JSON 응답이 출력되어야 한다.

{"jsonrpc": "2.0", "id": 1, "result": {"content": [{"type": "text", "text": "INFO[0000] colima is running \nINFO[0000] runtime: docker \nINFO[0000] arch: aarch64 \nINFO[0000] socket: unix:///Users/username/.colima/default/docker.sock \n"}]}}
🧐 오류 해결 (Troubleshooting)
만약 `command not found` 오류가 발생하면, 스크립트의 경로를 정확히 지정해야 한다. (예: /path/to/mcp-server/mcp_colima_server.py) 또한, 스크립트 최상단의 #!/usr/bin/env python3 부분이 올바른 Python 인터프리터를 가리키는지 확인한다.

3단계: Cursor MCP 설정 및 결과 확인

스크립트가 정상적으로 작동하는 것을 확인했다면, Cursor의 MCP 설정 파일에 해당 스크립트를 등록한다. Cursor에서 Cmd+Shift+P를 눌러 "Configure MCP..."를 검색하고 mcp.json 파일을 연다.

열린 mcp.json 파일에 아래와 같이 `colima-helper` 서버 설정을 추가한다. 주의: `command`와 `args`의 경로는 사용자의 실제 프로젝트 경로로 수정해야 한다.

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/YOUR_USERNAME/Desktop",
        "/Users/YOUR_USERNAME/Documents"
      ]
    },
    "colima-helper": {
      // 가상 환경을 사용한다면 .venv 안의 python 경로를 지정한다.
      "command": "/Users/YOUR_USERNAME/path/to/mcp-server/.venv/bin/python",
      "args": [
        // 실제 스크립트 파일의 절대 경로를 지정한다.
        "/Users/YOUR_USERNAME/path/to/mcp-server/mcp_colima_server.py"
      ]
    }
  }
}

설정 파일을 저장한 후, Cursor를 완전히 재시작한다. 재시작 후 채팅 창에서 @를 입력했을 때 colima-helper가 보이고, MCP 설정 상태에 "1 tools enabled"라고 표시되면 성공적으로 연동된 것이다.

이제 Cursor 채팅을 통해 다음과 같이 자연어로 명령을 내릴 수 있다.

colima 상태를 확인해줘
 
도구 사용: @colima-helper
colima status

명령어 실행 결과입니다.

INFO[0000] colima is running
INFO[0000] runtime: docker
INFO[0000] arch: aarch64
INFO[0000] socket: unix:///Users/dongwoo0518.kim/.colima/default/docker.sock

결론

이상으로 Python 스크립트를 이용해 MCP 서버를 구축하고, 이를 Cursor와 연동하여 Colima 및 Docker 관련 명령어를 실행하는 전 과정을 검토하였다. 이 방법을 통해 터미널과 에디터를 오가는 불편함을 줄이고, AI와의 상호작용만으로 개발 환경을 제어하는 효율적인 워크플로우를 구축할 수 있다.

본 튜토리얼에서 다룬 내용을 바탕으로, `allowed_commands` 목록에 자주 사용하는 다른 명령어들을 추가하여 자신만의 개발 자동화 도구를 만들어보는 것을 권장한다.

반응형