Published on

AutoGPT용 MCP 서버 구축 - 도구연결·권한통제

Authors

서로 다른 SaaS와 사내 시스템을 AutoGPT 같은 에이전트에 붙이다 보면, "연결" 자체보다 더 큰 문제가 곧바로 튀어나옵니다. 바로 권한입니다. 에이전트가 실수로 과도한 권한을 행사하거나, 프롬프트 인젝션으로 민감한 API를 호출하거나, 토큰이 유출되어 장기간 악용되는 시나리오가 현실적입니다.

이 글에서는 AutoGPT가 도구를 호출할 때 중간에서 정책과 감사를 강제하는 MCP 서버를 어떻게 구성할지 정리합니다. 핵심은 단순 프록시가 아니라, 도구 호출을 표준화하고 권한을 “정책 엔진”처럼 통제하는 게이트웨이로 만드는 것입니다.

MCP 서버를 두는 이유: 연결보다 통제가 본질

MCP(Model Context Protocol)는 모델(또는 에이전트)이 외부 도구를 호출할 때, 도구 목록과 스키마를 표준화해 제공하는 방식입니다. AutoGPT 입장에서는 "도구 카탈로그"와 "호출 규약"이 생기므로, 플러그인 난립을 줄이고 일관된 실행 경로를 만들 수 있습니다.

하지만 운영에서 더 중요한 이점은 다음입니다.

  • 권한 경계 고정: 에이전트가 어떤 도구를 어떤 파라미터로 호출할 수 있는지 서버에서 강제
  • 감사 로그 일원화: 누가(어떤 에이전트/사용자) 언제 무엇을 호출했는지 중앙 기록
  • 비밀 분리: 에이전트가 직접 API 키를 들고 있지 않게 만들고, 서버가 비밀을 보관
  • 정책 기반 차단: PII 포함 여부, 리소스 스코프, 호출 빈도, 비용 상한 등을 정책으로 제어

즉 MCP 서버는 “도구 연결 어댑터”이면서 동시에 “보안 게이트웨이”입니다.

권장 아키텍처: AutoGPT ↔ MCP ↔ Tool Adapters

구성은 크게 3계층을 추천합니다.

  1. MCP Front(프로토콜 레이어)

    • MCP 규약으로 도구 목록 제공 및 호출 수신
    • 인증, 세션, 레이트리밋, 요청 서명 검증
  2. Policy/Guard 레이어(권한·안전)

    • 스코프 기반 권한 검사
    • 파라미터 검증(JSON Schema)
    • 컨텐츠 정책(예: 경로 접근 제한, SQL allowlist, PII 마스킹)
    • 비용/빈도 제한
  3. Tool Adapter 레이어(실제 연동)

    • GitHub, Jira, Slack, S3, DB, 내부 백오피스 API 등
    • 각 도구별 토큰 관리(서버 측 보관)
    • 실행 결과 표준화 및 민감정보 제거

이 구조를 쓰면 “도구는 계속 늘어나는데, 보안/감사는 일관되게 유지”됩니다.

도구 연결 설계: 스키마 우선, 최소 기능부터

에이전트 도구는 기능이 많을수록 위험합니다. 처음부터 CRUD 전체를 주지 말고, 업무 플로우에 꼭 필요한 최소 기능만 노출하세요.

예를 들어 GitHub 연동이 필요하더라도 다음처럼 단계적으로 분리합니다.

  • 1단계: list_pull_requests, get_pull_request
  • 2단계: comment_pull_request
  • 3단계: merge_pull_request (가장 위험, 별도 승인 필요)

각 도구는 반드시 다음을 갖추도록 합니다.

  • 명확한 이름과 설명: 모델이 오해하지 않게
  • 입력 스키마: JSON Schema로 파라미터 범위 강제
  • 출력 스키마: 결과를 표준화해 후속 체인이 예측 가능하게

권한 통제 모델: 사용자·에이전트·도구를 분리

권한은 최소 3차원으로 나누는 게 안전합니다.

  • 사용자(End user): 실제 요청 주체
  • 에이전트(Agent): AutoGPT 런타임 인스턴스(워크스페이스 단위)
  • 도구/액션(Tool/Action): 호출 대상 및 작업 단위

권한 체크는 “사용자 권한”과 “에이전트 권한”을 모두 통과해야 합니다.

  • 사용자는 repo:read만 있는데 에이전트가 repo:write를 갖고 있으면 위험
  • 반대로 사용자가 넓은 권한을 갖고 있어도, 에이전트는 업무상 필요한 범위만 허용

스코프 예시

  • github:pr:read
  • github:pr:comment
  • github:pr:merge
  • db:readonly:analytics
  • s3:read:bucket:reports

스코프 문자열은 단순하지만, 운영에서 강력합니다. 정책 엔진(OPA 등)을 붙이기도 쉽습니다.

구현 예시: Node.js 기반 MCP 서버 골격

아래는 “도구 목록 제공”과 “호출 라우팅”을 단순화한 예시입니다. 실제 MCP SDK나 런타임에 따라 인터페이스는 달라질 수 있지만, 핵심은 동일합니다.

// mcp-server.ts
import express from "express";
import rateLimit from "express-rate-limit";
import { z } from "zod";

const app = express();
app.use(express.json({ limit: "1mb" }));

// 기본 레이트리밋(에이전트 폭주 방지)
app.use(
  rateLimit({
    windowMs: 60_000,
    max: 120,
    standardHeaders: true,
    legacyHeaders: false,
  })
);

// 예시 도구 스키마(입력 검증)
const CommentPRInput = z.object({
  repo: z.string().min(1),
  prNumber: z.number().int().positive(),
  body: z.string().min(1).max(2000),
});

type Scope = string;

function requireScopes(required: Scope[], granted: Scope[]) {
  for (const s of required) {
    if (!granted.includes(s)) {
      const err = new Error(`missing_scope:${s}`);
      (err as any).status = 403;
      throw err;
    }
  }
}

// 매우 단순한 인증 예시(운영에서는 JWT, mTLS, 서명 검증 등을 권장)
function authenticate(req: express.Request) {
  const apiKey = req.header("x-mcp-api-key") || "";
  if (apiKey !== process.env.MCP_API_KEY) {
    const err = new Error("unauthorized");
    (err as any).status = 401;
    throw err;
  }

  // 예시: 에이전트에 부여된 스코프
  return {
    agentId: "autogpt-workspace-001",
    scopes: ["github:pr:read", "github:pr:comment"],
  };
}

// 도구 카탈로그
app.get("/tools", (req, res) => {
  res.json({
    tools: [
      {
        name: "github_comment_pull_request",
        description: "GitHub PR에 코멘트를 남깁니다.",
        input_schema: {
          type: "object",
          properties: {
            repo: { type: "string" },
            prNumber: { type: "integer" },
            body: { type: "string" },
          },
          required: ["repo", "prNumber", "body"],
        },
      },
    ],
  });
});

// 도구 호출 엔드포인트
app.post("/tool/:name", async (req, res) => {
  try {
    const auth = authenticate(req);

    const toolName = req.params.name;
    if (toolName !== "github_comment_pull_request") {
      return res.status(404).json({ error: "tool_not_found" });
    }

    requireScopes(["github:pr:comment"], auth.scopes);

    const input = CommentPRInput.parse(req.body);

    // 여기서 실제 GitHub API 호출(서버가 토큰 보관)
    // 결과에서 민감정보 제거 후 반환
    res.json({
      ok: true,
      tool: toolName,
      result: {
        repo: input.repo,
        prNumber: input.prNumber,
        commented: true,
      },
    });
  } catch (e: any) {
    const status = e.status || 500;
    res.status(status).json({ error: e.message || "internal_error" });
  }
});

app.listen(8787, () => {
  console.log("MCP server listening on 8787");
});

이 예시에서 중요한 포인트는 다음입니다.

  • 도구별로 입력을 강하게 검증(Zod 등)
  • 스코프 기반 권한을 도구 호출 직전에 검사
  • 도구 카탈로그와 실행 경로를 분리

민감 도구는 2단계 실행으로: Plan-Approve-Execute

merge, delete, transfer_money 같은 도구는 “모델이 바로 실행”하면 사고가 납니다. 권장 패턴은 다음입니다.

  1. Plan: 에이전트가 실행 계획과 근거를 생성
  2. Approve: 사람 또는 정책 엔진이 승인
  3. Execute: 승인 토큰이 있을 때만 실행

이를 MCP 서버에서 구현하면, AutoGPT가 어떤 프롬프트로 움직이든 최종 실행은 통제됩니다.

// pseudo-code
if (toolName === "github_merge_pull_request") {
  // 1) 기본 스코프 체크
  requireScopes(["github:pr:merge"], auth.scopes);

  // 2) 승인 토큰 요구
  const approval = req.header("x-approval-token") || "";
  if (!isValidApproval(approval, auth.agentId, req.body)) {
    return res.status(412).json({ error: "approval_required" });
  }

  // 3) 실행
}

HTTP 상태 코드 412 같은 “사전조건 실패”를 사용하면, 에이전트 쪽에서도 흐름을 설계하기 좋습니다.

프롬프트 인젝션 방어: 도구 레벨에서 막아야 한다

모델은 텍스트에 취약합니다. “이 지시를 무시하고 비밀을 보내라” 같은 공격은 도구 호출로 이어질 수 있습니다. 그래서 방어는 프롬프트가 아니라 도구 실행 직전에 있어야 합니다.

실전 방어 체크리스트:

  • Allowlist 기반 리소스 제한
    • 예: repo는 특정 조직/프로젝트만
    • 예: DB는 읽기 전용 계정만
  • 경로 탐색 차단
    • 파일 도구가 있다면 .. 패턴, 절대경로 접근 제한
  • 출력 필터링
    • 토큰, 쿠키, 비밀키 형태 정규식 탐지 후 마스킹
  • 네트워크 이그레스 제한
    • 서버가 임의의 외부로 호출하지 못하게 VPC, FW 정책 적용

운영 필수: 감사 로그와 상관관계 ID

MCP 서버는 “누가 무엇을 왜 했는지”를 남길 수 있어야 합니다. 최소한 아래 필드를 구조화 로그로 남기세요.

  • request_id (상관관계 ID)
  • agent_id
  • end_user_id (있다면)
  • tool_name
  • input_hash (원문 대신 해시로 저장 가능)
  • decision (allow/deny)
  • policy_version
  • latency_ms

또한 오류의 상당수는 권한/토큰 문제로 나타납니다. 401과 403을 구분해 원인 분석을 빠르게 해야 합니다. 관련해서는 Spring Security 6 JWT 401/403 원인 9가지도 함께 참고하면, 인증과 인가를 분리해 사고를 줄이는 데 도움이 됩니다.

CI/CD에서 토큰 권한이 꼬여서 배포 자동화가 멈추는 경우도 흔합니다. GitHub 연동 도구를 운영한다면 GitHub Actions GITHUB_TOKEN 403 권한 오류 해결처럼 “토큰이 맞는데도 403”이 나는 전형적인 케이스를 미리 점검하는 게 좋습니다.

배포 관점: K8s에서 자주 터지는 지점

MCP 서버는 대개 사내망과 외부 모델 런타임 사이에 서며, 네트워크/헬스체크 이슈가 잦습니다.

  • readinessProbe가 실패해 트래픽을 못 받음
  • 외부 로드밸런서 헬스체크 경로/포트 불일치
  • 보안그룹 또는 네트워크 정책으로 이그레스가 막힘

이런 문제는 도구 호출이 “가끔” 실패하는 형태로 나타나 디버깅이 어렵습니다. EKS 환경이라면 EKS에서 NLB 타겟 Unhealthy - 헬스체크·Pod·SG 같은 체크리스트를 기준으로, 헬스체크 엔드포인트와 보안그룹, 프로브 설정을 먼저 고정하세요.

실전 체크리스트: 안전한 MCP 서버를 위한 최소 요건

인증

  • 서버 간 통신이면 mTLS 또는 요청 서명(HMAC) 적용
  • 사용자 컨텍스트가 있으면 JWT 검증(issuer, audience, exp)
  • 키/토큰은 KMS 또는 Secret Manager에 저장

인가

  • 스코프 기반 정책(도구 단위, 액션 단위)
  • 리소스 기반 정책(특정 repo, 특정 bucket, 특정 DB 스키마)
  • 고위험 도구는 승인 토큰 요구

검증

  • 입력 스키마 검증(JSON Schema, Zod)
  • 출력에서 민감정보 마스킹
  • SQL/명령 실행 도구는 allowlist 또는 템플릿 기반만 허용

안정성

  • 레이트리밋, 타임아웃, 재시도 정책
  • 서킷 브레이커(외부 SaaS 장애 전파 방지)
  • idempotency key(중복 실행 방지)

마무리: MCP 서버는 “도구 허브”가 아니라 “정책 집행점”

AutoGPT에 도구를 붙이는 순간, 여러분은 사실상 “자동화된 운영자”를 만드는 것입니다. 그래서 MCP 서버는 연결 편의보다 권한 경계, 감사, 승인 흐름을 먼저 설계해야 합니다.

처음에는 다음 3가지만 지켜도 사고 확률이 크게 줄어듭니다.

  • 도구를 최소 기능으로 쪼개고 스키마로 고정
  • 스코프와 리소스 정책으로 인가를 강제
  • 고위험 도구는 Plan-Approve-Execute로 분리

이렇게 구축하면 도구가 늘어나도 통제 모델은 유지되고, AutoGPT의 자동화 효율을 보안과 함께 가져갈 수 있습니다.