lifecarelog
백엔드

MCP 서버 env는 스킬 런타임에 안 간다: 3일 조용히 실패한 자동화

MCP 설정에 넣은 환경변수가 스킬 프로세스엔 전달되지 않아 3일간 조용히 실패한 사례와, 부팅 검사·로그·실표면 확인으로 막는 방법을 정리해요.

6분 읽기

밤마다 도는 무인 작업이 하나 있어요. 외부 문서 서비스에서 카드를 읽어 정리하고 결과를 다시 써 넣는 일이에요. 로그엔 매일 success가 찍혔는데, 실제로 써 넣은 행은 3일 동안 0건이었어요. 원인은 환경변수 이름 하나였어요.

무슨 일이 있었나

MCP 서버 설정에 토큰을 NOTION_TOKEN으로 넣어 뒀어요. 대화형으로 쓸 땐 잘 됐고요. 그런데 같은 서비스를 부르는 스킬 런타임은 NOTION_API_KEY라는 이름을 찾았어요. 토큰을 못 찾은 스킬은 예외 대신 빈 목록을 돌려줬고, 상위 잡은 "처리할 게 없네" 하고 정상 종료했어요.

직접 겪어 보니 핵심은 프로세스 경계예요. MCP 서버 정의의 env 블록은 그 서버 프로세스에만 주입돼요. 스킬은 별도 프로세스로 뜨니까 값을 상속받지 않고, 자기 코드에 적힌 이름으로 다시 조회해요. 이름이 1글자만 달라도 남남이에요.

MCP 설정에 env 주입MCP 서버 프로세스만 읽음스킬은 다른 이름 조회값 없음빈 결과를 성공으로 보고

왜 3일이나 몰랐나

두 겹으로 조용했어요. 하나는 무인 경로예요. launchd나 cron으로 뜨는 프로세스는 셸 프로필을 안 읽어요. 터미널에서 돌려 보니 되길래 배포했는데, 무인 경로에서만 값이 비어 있었던 거죠. 다른 하나는 성공 판정이 종료코드였다는 점이에요. 0으로 끝나면 성공이었으니 빈 결과도 성공이었어요.

고친 것 4가지

첫째, 부팅 검사예요. 필수 env 목록을 두고 하나라도 비면 즉시 종료해요. 직접 만들어 보니 7줄이에요. 적용해 봤더니 첫 실행에서 바로 missing env가 떴어요. 3일 걸릴 발견이 5분으로 줄었죠.

for (const name of ["NOTION_API_KEY"]) {
  if (!process.env[name]) {
    console.error(`[boot] missing env: ${name}`);
    process.exit(1);
  }
  console.log(`[boot] env loaded: ${name}`);
}

둘째, 읽는 이름을 로그에 남겨요. 값 말고 이름만요. 위 코드 마지막 1줄이 전부인데, 나중에 어떤 이름을 찾았는지 추적할 때 이만한 게 없어요.

셋째, 무인 경로엔 직접 주입해요. launchd면 plist의 EnvironmentVariables, cron이면 잡 정의 안에요. 값은 시크릿 저장소에서 읽고요.

넷째, 성공 판정을 실표면으로 바꿨어요. "0으로 끝났다"가 아니라 "오늘 생성된 행이 1건 이상이다"를 확인해요. 테스트해 보면 이 검사 하나가 종료코드보다 훨씬 많은 종류의 조용한 실패를 잡아요. 24시간 안에 통과 기록이 없으면 알림이 오게 해 뒀어요.

  • 필수 env 부팅 검사
  • 읽는 이름 로그 1줄
  • 무인 경로에 직접 주입
  • 실표면으로 성공 판정

백엔드 관점의 교훈

환경변수는 전역 설정처럼 보이지만 실제 스코프는 프로세스예요. 설정 파일 한 곳에 넣었다고 모든 실행 경로가 같은 값을 보진 않아요. MCP 서버, 스킬, 크론 잡은 각자 다른 프로세스고 각자 방식대로 env를 읽어요.

라이프케어로그의 플랜 시리즈를 혼자 운영하다 보면 이런 무인 작업이 꽤 쌓여요. 플랜엘처럼 외부 데이터 API 키에 기대는 서비스일수록, 키가 없을 때 조용히 빈 결과를 내는 코드가 제일 위험해요. 없으면 죽는 게 맞아요. 죽어야 보이거든요.

정리하면 세 줄이에요. env는 프로세스마다 따로 확인한다. 없으면 즉시 실패한다. 성공은 종료코드가 아니라 결과물로 판정한다.

#MCP#환경변수#자동화 운영

라이프케어로그 서비스가 궁금하신가요?

AI 기반 건강·일정·재활 관리 앱을 직접 써보세요.

서비스 살펴보기

관련 글

댓글

아직 댓글이 없어요. 첫 댓글을 남겨주세요.