yeznable

[ MCP ] ChatGPT + AWS + Terraform 본문

하는 일/ai

[ MCP ] ChatGPT + AWS + Terraform

yeznable 2025. 6. 26. 13:39
728x90

 

지난 포스팅에서 Render를 활용해 간단하게 무료로 MCP 서버를 만들고 Custom GPT에 연결하는 실습을 했다.

 

[ MCP ] ChatGPT + MCP 서버

지난번에 아래 글에서 Claude with MCPs에 대해 소개했었다. [ 따라잡기 ] Claude with MCPs (0) - 소개AI를 활용하는 데에 뒤쳐지지 말아야겠다는 생각에 호기롭게 [ 따라잡기 ]라는 말머리도 달아 윈드서프

yeznable-blog.tistory.com

 

이게 가능하다면 AWS 서버도 ChatGPT랑 자연어로 관리할 수 있는게 아닌가 싶어서 테스트 해보았고 Terraform까지 활용하여 다음과 같이 동작시킬 수 있었다.

 

Render에서도 테스트 했었던 서버 파일구조 관리 기능은 다음과 같이 동작한다.

GPT한테 물어봐서 작성된 코드를 GPT가 직접 파일로 작성하고 실행하도록 요청할 수 있다.

파일구조 관리 기능 활용 캡쳐

 

GPT에게 자연어로 요청해서 Terraform 설치, 필요한 파일 작성까지 먼저 준비했다.

이후 Terraform 명령어를 활용해 새로운 EC2 인스턴스를 생성하고 삭제하도록 할 수 있었다.

Terraform 명령어 실행으로 AWS EC2 인스턴스를 관리
terraform apply 명령을 커스텀 GPT에게 시키기 전의 인스턴스
terraform apply 명령을 커스텀 GPT에게 시킨 후의 인스턴스
terraform destroy 명령을 커스텀 GPT에게 시킨 후의 인스턴스


이런 MCP + AWS 구조를 갖추기 위해서는 다음 조건을 만족해야 한다.

이 조건들에는 어느정도 과금이 들어가는 조건들도 있다.

  • 도메인 구매
  • 탄력적 IP(Elastic IP)를 통해 MCP 서버로 활용할 EC2 인스턴스의 IP 고정
  • Route 53을 통한 MCP 서버의 Elastic IP에 도메인 연결
  • 도메인에 HTTPS 접속이 가능하도록 설정(nginx+certbot)
  • FastAPI로 커스텀 GPT와 MCP 서버가 연결될 수 있도록 설정
  • MCP 서버의 인바운드 규칙 설정(FastAPI를 위한 8000번 포트)

나는 FastAPI를 아래와 같이 설정해서 활용한다.

 

GitHub - yejoonlee/YEZNABLE_MCP_SERVER

Contribute to yejoonlee/YEZNABLE_MCP_SERVER development by creating an account on GitHub.

github.com


이 저장소의 소스를 clone 한다고 바로 활용할 수 있는건 아니고 .env 파일을 다음과 같이 작성해두어야 한다.

SERVER_URL=https://<구매한 도메인 주소>
SECRET_TOKEN=<FastAPI에 요청할 때 쓸 토큰>
GRAPH_FILE_PATH=<그래프 파일을 저장할 위치>

 


Custom GPT 생성 설정은 다음과 같이 했다.

 

지침 설정

당신은 서버 관리 보조 역할을 수행합니다. FastAPI로 구성된 원격 MCP 서버와 연동되어 있으며, 다음과 같은 기능을 수행할 수 있습니다:

1. 명령어 실행:
   - 사용자가 요청한 명령어를 MCP 서버에 전송하여 백그라운드로 실행하거나 결과를 수집해 출력합니다.

2. 파일/디렉터리 관리:
   - 사용자의 요청에 따라 파일을 생성하거나, 삭제, 복사, 이름 변경할 수 있습니다.

3. 사용 시 주의사항:
   - MCP 서버는 리눅스 기반 Bash 쉘을 사용합니다.
   - 경로를 지정할 때는 항상 전체 경로 또는 홈 디렉토리 기준으로 상대 경로를 명확히 전달해야 합니다.
   - 중요한 시스템 디렉토리를 조작하지 않도록 주의합니다 (`/etc`, `/bin`, `/usr` 등).

요청이 가능하면 적절한 API를 호출해 실제로 수행하세요. 그렇지 않은 경우에는 설명과 안내만 제공하세요.

 

작업의 인증 설정은 API 키로 설정하고 내가 FastAPI에 활용한 방법인 Bearer를 선택한 뒤 위의 .env에도 적어놓은 토큰을 입력해서 저장한다.

 

스키마는 다음과 같다.

{
  "openapi": "3.1.0",
  "info": {
    "title": "MCP Control Server",
    "version": "1.0.0",
    "description": "A FastAPI server that executes shell commands and manages files remotely"
  },
  "servers": [
    {
      "url": "https://<구매하고 설정을 마친 도메인 주소>"
    }
  ],
  "paths": {
    "/run": {
      "post": {
        "operationId": "run_command",
        "summary": "Run a shell command without returning output",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "command": {
                    "type": "string",
                    "description": "Shell command to execute"
                  }
                },
                "required": [
                  "command"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Command sent"
          }
        }
      }
    },
    "/run-and-capture": {
      "post": {
        "operationId": "run_command_and_capture",
        "summary": "Run a shell command and return the output",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "command": {
                    "type": "string",
                    "description": "Shell command to execute"
                  }
                },
                "required": [
                  "command"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Command output returned"
          }
        }
      }
    },
    "/file/create": {
      "post": {
        "operationId": "create_file",
        "summary": "Create a file with optional content",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string",
                    "description": "Path where the file will be created"
                  },
                  "content": {
                    "type": "string",
                    "description": "Content to write in the file"
                  }
                },
                "required": [
                  "path"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "File created"
          }
        }
      }
    },
    "/file/delete": {
      "post": {
        "operationId": "delete_file",
        "summary": "Delete a file or directory",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string",
                    "description": "Path to the file or directory"
                  }
                },
                "required": [
                  "path"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "File or directory deleted"
          }
        }
      }
    },
    "/file/rename": {
      "post": {
        "operationId": "rename_file",
        "summary": "Rename or move a file or directory",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string",
                    "description": "Original file or directory path"
                  },
                  "new_path": {
                    "type": "string",
                    "description": "New file or directory path"
                  }
                },
                "required": [
                  "path",
                  "new_path"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Renamed or moved successfully"
          }
        }
      }
    },
    "/file/copy": {
      "post": {
        "operationId": "copy_file",
        "summary": "Copy a file or directory",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string",
                    "description": "Source path"
                  },
                  "new_path": {
                    "type": "string",
                    "description": "Destination path"
                  }
                },
                "required": [
                  "path",
                  "new_path"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "File or directory copied"
          }
        }
      }
    }
  }
}

원래는 MCP 서버만 제어하도록 생각하고 있었다.

그래서 인스턴스를 무료티어로 만들어서 커스텀 GPT와 연결해 제어가 되는지 테스트 해봤다.

테스트 완료 후 스펙업을 해서 활용할 생각이었는데 Terraform을 활용하면 무료 티어 인스턴스로 MCP 서버를 운영하고 필요에 따라서 Terraform으로 새로운 인스턴스를 만들어 쓸 수 있겠다는 생각이다.

728x90