Published on

AutoGPT에 MCP 서버 붙여 로컬툴 안전 실행하기

Authors

에이전트 기반 자동화(예: AutoGPT)는 “생각하고 실행한다”는 장점 때문에, 로컬 머신에서 곧바로 셸 명령을 때리거나 파일을 읽고 쓰는 순간부터 보안 사고 가능성이 급격히 커집니다. 특히 프롬프트 인젝션으로 의도치 않은 명령이 실행되거나, 토큰/SSH 키 같은 민감정보가 로그로 유출되는 케이스가 흔합니다.

이 글에서는 MCP(Model Context Protocol) 서버를 중간 게이트웨이로 두어, AutoGPT가 로컬 툴을 직접 실행하지 못하게 만들고, 허용된 기능만 정책적으로 노출하는 방식으로 “로컬툴 안전실행”을 구현하는 방법을 다룹니다. 목표는 다음 3가지입니다.

  • 에이전트가 실행할 수 있는 툴을 명시적으로 제한(allowlist)
  • 파일/네트워크/프로세스 권한을 샌드박스/격리
  • 모든 툴 호출을 감사 로그로 남기고 재현 가능하게 운영

왜 AutoGPT에 MCP가 필요한가

AutoGPT류 에이전트는 보통 아래 중 하나로 로컬 실행을 합니다.

  • shell 또는 subprocess 기반 커맨드 실행
  • 로컬 파일 시스템 직접 접근
  • 브라우저 자동화/HTTP 호출

문제는 “에이전트가 똑똑해질수록” 실행 범위도 넓어지고, 그만큼 사고 표면이 커진다는 점입니다.

대표적인 위험 시나리오

  1. 프롬프트 인젝션으로 인한 임의 명령 실행
    • 예: 웹페이지/README에 숨겨진 지시문을 따라 rm -rf 계열 실행
  2. 민감정보 유출
    • 예: 홈 디렉터리의 .env, SSH 키, 브라우저 쿠키를 읽어 외부로 전송
  3. 권한 상승/지속성(Persistence)
    • 예: 크론 등록, 로그인 스크립트 변조, 쉘 프로파일 수정

MCP를 붙이면, 에이전트는 “툴을 직접 실행”하는 대신 “MCP 서버가 제공하는 도구 목록”만 호출할 수 있습니다. 즉, 실행 지점을 한 군데로 모아 정책을 강제할 수 있습니다.

MCP 아키텍처: 에이전트와 로컬 실행 사이에 방화벽 세우기

구성은 단순합니다.

  • AutoGPT(클라이언트): MCP를 통해 tools 호출
  • MCP 서버(게이트웨이): 허용된 도구만 노출, 파라미터 검증, 로깅
  • 로컬 툴 실행기(샌드박스): 실제 명령/스크립트 실행, 권한 최소화

핵심은 MCP 서버가 “도구 카탈로그”와 “실행 정책”을 소유하도록 만드는 것입니다.

설계 원칙 6가지(안전실행 체크리스트)

1) Allowlist 기반 툴 노출

  • “무엇이든 실행하는 shell”을 제공하지 않습니다.
  • 반드시 git_status, run_tests, read_project_file 같은 업무 단위 도구로 쪼갭니다.

2) 파라미터 스키마 검증

  • 도구 입력은 JSON 스키마로 강제합니다.
  • 경로는 워크스페이스 루트 하위만 허용하고, .. 를 차단합니다.

3) 작업 디렉터리와 파일 접근 범위 제한

  • 워크스페이스 경로를 예: WORKSPACE=/repo 로 고정
  • 홈 디렉터리, /etc, 키체인, 클라우드 크레덴셜 경로는 접근 불가

4) 네트워크 egress 제한

  • 기본은 차단, 필요할 때만 도메인 allowlist
  • 테스트/빌드 같은 로컬 작업은 네트워크 없이도 돌아가게 설계

5) 실행 시간/리소스 제한

  • 타임아웃, CPU/메모리 제한
  • 장기 실행은 잡 큐로 분리하고 상태 조회 API 제공

6) 감사 로그와 재현성

  • 어떤 도구가 어떤 입력으로 호출됐는지 구조화 로그(JSON)
  • 결과(표준출력/표준에러/exit code)와 실행 환경(커밋 해시 등) 기록

운영 관점에서 이런 “실행 경계”를 두면, 나중에 성능/장애 분석도 쉬워집니다. 예를 들어 런타임 블로킹 문제는 비슷한 패턴으로 재현되는데, Rust 기반 실행기를 쓴다면 이벤트 루프에서 blocking 호출로 패닉이 나는 이슈도 자주 겪습니다. 관련해서는 Rust Tokio runtime panic - blocking 호출 해결법 같은 글의 접근(격리/스레드풀 분리)이 도움이 됩니다.

구현 예시: Node.js MCP 서버로 “안전한 로컬 툴” 만들기

아래 예시는 MCP 서버가 두 개의 도구만 노출한다고 가정합니다.

  • read_file: 워크스페이스 내부 파일만 읽기
  • run_cmd: 특정 커맨드만 실행(예: git status, npm test)

중요: 본문에서 부등호 문자가 그대로 나오면 MDX에서 JSX로 오인될 수 있으므로, 코드 블록/인라인 코드로만 표기합니다.

1) 환경 변수와 워크스페이스 고정

export WORKSPACE=/repo
export MCP_LOG_DIR=/repo/.mcp-logs

2) MCP 서버 코드(개념 예시)

// mcp-server.js
import fs from "node:fs/promises";
import path from "node:path";
import { spawn } from "node:child_process";

const WORKSPACE = process.env.WORKSPACE;

function assertWorkspacePath(p) {
  const resolved = path.resolve(WORKSPACE, p);
  if (!resolved.startsWith(path.resolve(WORKSPACE) + path.sep)) {
    throw new Error("path_outside_workspace");
  }
  return resolved;
}

const ALLOWED_COMMANDS = new Map([
  ["git_status", { cmd: "git", args: ["status", "--porcelain"] }],
  ["npm_test", { cmd: "npm", args: ["test"] }]
]);

async function readFileTool({ relative_path, max_bytes = 200_000 }) {
  const full = assertWorkspacePath(relative_path);
  const buf = await fs.readFile(full);
  if (buf.byteLength > max_bytes) throw new Error("file_too_large");
  return { content: buf.toString("utf-8") };
}

async function runCmdTool({ name, timeout_ms = 60_000 }) {
  const spec = ALLOWED_COMMANDS.get(name);
  if (!spec) throw new Error("command_not_allowed");

  return await new Promise((resolve, reject) => {
    const child = spawn(spec.cmd, spec.args, {
      cwd: WORKSPACE,
      env: {
        // 최소 환경만 전달
        PATH: process.env.PATH
      },
      stdio: ["ignore", "pipe", "pipe"]
    });

    let out = "";
    let err = "";
    child.stdout.on("data", (d) => (out += d.toString("utf-8")));
    child.stderr.on("data", (d) => (err += d.toString("utf-8")));

    const timer = setTimeout(() => {
      child.kill("SIGKILL");
      reject(new Error("timeout"));
    }, timeout_ms);

    child.on("close", (code) => {
      clearTimeout(timer);
      resolve({ exit_code: code, stdout: out, stderr: err });
    });
  });
}

// 아래는 실제 MCP SDK에 맞춰 tools 등록/핸들러 연결을 해야 합니다.
// 여기서는 핵심 로직(allowlist, path 검증, timeout)을 보여주는 예시입니다.
export const tools = {
  read_file: readFileTool,
  run_cmd: runCmdTool
};

이 예시의 포인트는 다음과 같습니다.

  • assertWorkspacePath 로 경로 탈출을 원천 차단
  • ALLOWED_COMMANDS 로 실행 가능한 명령을 “기능 이름”으로 제한
  • spawn 시 환경 변수 최소 전달(토큰/키 유출 위험 감소)
  • 타임아웃으로 무한 실행 차단

3) 도구 설계 팁: “run_cmd”를 더 쪼개라

실무에서 run_cmd 같은 범용 실행기는 결국 공격 표면이 됩니다. 가능하면 아래처럼 분해하세요.

  • git_status, git_diff, git_apply_patch
  • run_unit_tests, run_lint
  • build_frontend, build_backend

도구가 세분화될수록 파라미터 검증이 쉬워지고, 로그도 의미가 생깁니다.

격리 강화: 샌드박스(컨테이너)로 “실행기” 분리

MCP 서버가 정책을 강제하더라도, 결국 실제 프로세스를 실행하는 머신이 뚫리면 끝입니다. 그래서 실행기는 컨테이너로 격리하는 편이 안전합니다.

Docker로 실행기 격리 예시

docker run --rm \
  --read-only \
  --cap-drop=ALL \
  --network=none \
  -v "$WORKSPACE":/repo:rw \
  -w /repo \
  node:20 \
  node mcp-server.js
  • --read-only 로 컨테이너 루트 파일시스템 쓰기 차단
  • --cap-drop=ALL 로 리눅스 capability 제거
  • --network=none 으로 기본 egress 차단
  • 워크스페이스만 마운트

네트워크가 필요한 도구(예: 패키지 설치)가 있다면, --network=none 을 유지한 채로 사내 프록시나 특정 도메인만 허용하는 별도 실행 경로를 두는 것이 좋습니다.

운영에서 자주 터지는 문제와 해결 포인트

1) 로그에 비밀이 섞인다

  • 표준출력에 환경변수나 토큰이 찍히는 도구는 금지
  • 로그 수집 전 마스킹(정규식) 적용
  • 도구 실행 환경에서 AWS_*, GITHUB_TOKEN 같은 변수를 아예 전달하지 않기

2) 파일 잠금/동시성 이슈

에이전트가 동시에 여러 작업을 돌리면 같은 파일을 수정하거나, 빌드 캐시를 건드리면서 경합이 납니다.

  • 워크스페이스를 작업 단위로 복제(임시 디렉터리)하거나
  • 도구별 mutex(락 파일)로 직렬화

데이터 레이스를 구조적으로 막는 사고방식은 Rust의 소유권 모델이 좋은 참고가 됩니다. 개념적으로는 Rust 소유권·빌림으로 데이터 레이스 0% 패턴 같은 접근을 “툴 실행 설계”에도 적용할 수 있습니다.

3) 장기 실행 작업이 에이전트를 멈춘다

  • run_build 같은 도구는 비동기로 제출하고 job_id 를 반환
  • get_job_status, get_job_logs 도구로 조회

이렇게 하면 모델의 컨텍스트를 낭비하지 않고, 타임아웃 정책도 명확해집니다.

AutoGPT에 붙이는 연결 전략(개념)

AutoGPT 자체는 버전/포크에 따라 툴 인터페이스가 다르지만, 핵심은 동일합니다.

  • AutoGPT가 직접 bash 를 실행하지 못하게 설정
  • “툴 호출”을 MCP 클라이언트로 라우팅
  • MCP 서버가 제공하는 도구만 사용

즉, 에이전트의 권한을 “로컬 OS”가 아니라 “MCP 도구 목록”으로 축소합니다.

권장 정책 템플릿(바로 적용 가능한 형태)

  • 파일 접근
    • 허용: 워크스페이스 하위 src, tests, package.json
    • 금지: 홈 디렉터리, 숨김 파일, 키/인증서, .env
  • 명령 실행
    • 허용: git status, git diff, npm test, pytest -q 처럼 고정된 커맨드
    • 금지: 셸 해석이 필요한 문자열 실행(예: sh -c), 파이프/리다이렉션
  • 네트워크
    • 기본 차단
    • 예외: 패키지 레지스트리/사내 아티팩트 서버만 허용
  • 리소스
    • CPU/메모리 제한, 60초 타임아웃(도구별 조정)
  • 로깅
    • 요청/응답/exit code/실행시간/작업 디렉터리/커밋 해시 기록

마무리

AutoGPT에 MCP 서버를 붙이는 순간, “에이전트가 OS를 직접 만지는 구조”에서 “정책을 통과한 도구만 실행하는 구조”로 바뀝니다. 이 차이가 곧 안전성과 운영 가능성을 결정합니다.

정리하면 다음 순서로 접근하는 것이 가장 현실적입니다.

  1. 범용 셸 실행을 제거하고, 업무 단위 도구로 분해
  2. MCP 서버에서 allowlist, 파라미터 검증, 경로 고정, 타임아웃 강제
  3. 실행기는 컨테이너로 격리하고 네트워크/권한 최소화
  4. 감사 로그를 남겨 문제를 재현 가능하게 만들기

이 구성은 “로컬 자동화”뿐 아니라 CI 에이전트, 코드 리뷰 봇, 사내 운영 봇에도 그대로 확장할 수 있습니다.