# AI 에이전트

Token Savior를 붙이면 토큰이 80% 줄어든다는데, 과연 진짜일까?

@Hong2026-10-24

이 글에 담긴 코드와 결과는 직접 실행해 측정한 값을 바탕으로 작성했습니다. 실측은 Hong과 팀원들이 진행했고, 그 결과를 바탕으로 AI가 글을 쓰고 Hong이 검토합니다.

실습 코드 내려받기

질문
Token Savior MCP 서버를 붙이면 Claude Code가 코드를 찾을 때 쓰는 토큰이 줄어들까?
답
내장 도구와 같이 붙이면 Claude는 주로 Read와 Grep을 썼고 요청마다 도구 정의 3,335토큰이 더해져 입력 합계가 늘었다. Token Savior만 남기면 호출 경로 질문에서 탐색으로 늘어난 입력이 9,142에서 3,462토큰으로 줄었다.
조건
2026-10-23, Claude Code 2.1.309 claude -p, Opus 5.5, effort medium, token-savior-recall 4.24.0, 18회 호출

환경

측정일
2026년 10월 23일
Claude Code
2.1.309(claude --version), 구독 로그인, claude -p
모델
--model opus, 결과 JSON modelUsage의 모델 ID는 모든 실행에서 claude-opus-5-5
effort
--effort medium
Token Savior
token-savior-recall[mcp] 4.24.0, optimized 프로필(도구 15개), python:3.12-slim 기반 Docker 이미지에서 실행
코드베이스
고정 시드로 만든 Python 파일 59개, 18,753줄, 함수 571개
반복
질문 2개 × 조건 3개 × 3회, 미리 정한 순서로 섞어 18번
실행 환경
macOS 15.7, Docker 27.5.1
비용
결과 JSON의 total_cost_usd, 구독 호출을 API 단가로 환산한 추정치(실제 청구액 아님)
목차
  1. Token Savior가 줄이는 것과 공개 수치
  2. 실습 준비
  3. 실습 1: 세 조건을 섞어 18번 실행하기
  4. 실습 2: 집계하고 비교하기
  5. 정리

이번 시간에는 Token Savior라는 MCP 서버를 Claude Code에 붙였을 때 코드를 찾는 데 드는 토큰이 실제로 줄어드는지, claude -p(대화 없이 한 번 실행하고 끝나는 모드)로 측정하며 배워가는 시간입니다.

예를 들어서 우리가 "이 함수는 어느 파일에 있고 무엇을 돌려줘?"라고 묻는다고 해볼게요. Claude Code는 보통 Grep으로 이름을 찾고 Read로 파일을 엽니다. 필요한 건 함수 몇 줄인데 파일이 400줄이면, 나머지 줄도 전부 입력 토큰이 돼요.

그래서 정답이 정해진 코드 질문 두 개를 세 가지 조건으로 섞어서 보내 볼게요. 내장 도구만 쓰는 조건, 내장 도구에 Token Savior를 더한 조건, Token Savior만 남긴 조건입니다.

claude -p에는 temperature나 seed를 정하는 옵션이 없어서, 다시 돌리면 숫자가 달라질 수 있어요.

Token Savior가 줄이는 것과 공개 수치

Token Savior(PyPI 패키지 이름은 token-savior-recall)는 시작할 때 코드베이스를 색인해 두고, 함수나 클래스 같은 심볼 단위로 찾아 주는 MCP 서버예요. MCP는 Claude Code 같은 에이전트에 외부 도구를 붙이는 표준 방식입니다. 예를 들어 get_function_source 도구는 파일 전체 대신 함수 하나의 소스만 돌려줘요. 대신 Token Savior의 도구 정의가 요청마다 입력에 더해집니다.

이 글은 저자가 권하는 optimized 프로필을 써요. 도구 15개만 내보내는 프로필이고, README는 이 도구 정의를 약 1.5K토큰으로 적었어요.

Token Savior README는 tsbench(저자가 만든 벤치마크)에서 과제당 활성 토큰(active tokens)이 17,221에서 3,395로 80% 줄었다고 적었어요. 다만 측정용 실행 코드가 공개되지 않아 독립적으로 검증할 수 있는 수치가 아니라 보고된 수치로 받아들여 달라고 저자가 밝혔습니다.

저자는 2026년 8월 9일에 낸 재측정을 다음 날 철회하기도 했어요. 143세션 중 Token Savior 도구를 부른 세션이 하나뿐이었다는 이유예요. 그래서 이 글은 토큰만 비교하지 않고, Claude가 Token Savior 도구를 실제로 불렀는지를 결과 JSON에서 직접 세어 볼게요.

실습 준비

이 글의 명령은 모두 실습 코드의 token-savior-symbol-lookup/ 디렉터리에서 실행합니다.

  • gen_repo.py: 고정 시드로 측정용 코드베이스 repo/를 만드는 스크립트
  • Dockerfile: Token Savior 4.24.0을 설치한 이미지
  • mcp-savior.json: claude -p에 넘기는 MCP 설정(Token Savior 있음)
  • mcp-none.json: claude -p에 넘기는 MCP 설정(Token Savior 없음)
  • questions/ 디렉터리: 정답이 정해진 코드 질문 두 개(q1.txt, q2.txt)
  • run_measure.sh: 조건을 섞은 순서로 claude -p를 18번 부르는 측정 스크립트
  • summarize.py: 결과 JSON을 모아 조건별로 비교하는 집계 스크립트
  • README.md: 파일 목록

따라 하시려면 Claude Code 2.1.309(claude --version)와 Claude 구독 로그인, Docker가 필요해요. Python과 Token Savior는 Mac에 설치하지 않고 컨테이너 안에서만 써요.

py는 현재 디렉터리를 컨테이너의 /w에 연결하고 python:3.12-slim에서 Python 스크립트를 실행해요. 아래 블록은 파일로 만들지 말고 실습 디렉터리에서 연 터미널에 그대로 붙여 넣어 주세요. 이후 명령은 모두 이 터미널에서 실행하고, 터미널을 새로 열면 다시 붙여 넣으시면 됩니다.

py() { docker run --rm -v "$PWD":/w -w /w python:3.12-slim python "$@"; }

측정용 코드베이스 만들기

gen_repo.py는 repo/shop/ 아래에 도메인 디렉터리 10개를 만들고, 파일마다 정답과 상관없는 함수 12개를 채워요. 정답을 정해 두려고 심은 함수는 해당 파일의 한가운데에 들어갑니다.

  • 배송비: shipping_fee_for가 기본 요금을 계산하고, 다른 파일의 zone_surcharge가 지역 할증을 더해요. 제주 3kg이면 2,800 + 700 × 2 + 3,500 = 7,700입니다.
  • 쿠폰 할인: checkout에서 build_order_total, apply_promotions를 거쳐 shop/promotions/coupons.py의 coupon_discount까지 네 파일에 걸쳐 이어져요. 쿠폰 WELCOME, 소계 40,000이면 4,800입니다.
  • 함정: shop/legacy/promo_v1.py에 이름이 같은 coupon_discount가 하나 더 있어요. 값은 4,000이지만 checkout에서 이어지는 import 경로에는 없습니다.
py gen_repo.py
grep -rn "def coupon_discount" repo
wc -l repo/shop/api/checkout.py repo/shop/promotions/coupons.py
files=59 lines=18753 functions=571
repo/shop/legacy/promo_v1.py:178:def coupon_discount(code, subtotal):
repo/shop/promotions/coupons.py:210:def coupon_discount(code, subtotal):
     396 repo/shop/api/checkout.py
     423 repo/shop/promotions/coupons.py
     819 total

checkout 함수는 두 줄인데, 그 함수가 든 파일은 396줄이에요. 이 차이가 Token Savior가 줄이려는 부분입니다.

Token Savior 이미지 만들기

Token Savior는 Docker 이미지로 만들어서 띄울게요. Dockerfile은 이렇습니다.

FROM python:3.12-slim
RUN pip install --no-cache-dir "token-savior-recall[mcp]==4.24.0"
ENV WORKSPACE_ROOTS=/repo TOKEN_SAVIOR_CLIENT=claude-code TOKEN_SAVIOR_PROFILE=optimized
ENTRYPOINT ["token-savior"]

환경 변수는 README의 설정 예를 따랐어요. WORKSPACE_ROOTS=/repo는 색인할 디렉터리이고, TOKEN_SAVIOR_PROFILE=optimized는 도구 15개짜리 프로필이에요.

docker build -t token-savior:4.24.0 .
docker run --rm --entrypoint pip token-savior:4.24.0 show token-savior-recall | head -2
Name: token-savior-recall
Version: 4.24.0

MCP 설정과 질문

Claude Code에 넘길 MCP 설정 mcp-savior.json이에요.

{
  "mcpServers": {
    "savior": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "-v", ".:/repo:ro", "token-savior:4.24.0"]
    }
  }
}

Claude Code가 이 명령으로 컨테이너를 띄우고, 표준 입출력(-i)으로 MCP 메시지를 주고받아요. -v .:/repo:ro는 claude를 실행한 디렉터리를 컨테이너의 /repo에 읽기 전용으로 연결해요. 우리는 repo/ 안에서 claude를 실행할 거라 코드베이스가 그대로 연결됩니다. 컨테이너는 claude -p를 부를 때마다 새로 떠서 매번 다시 색인해요.

README는 이 설정에 "alwaysLoad": true를 넣으라고 권해요. tool search(도구 정의를 필요할 때 검색해서 불러오는 기능)가 켜져 있으면 도구가 검색 뒤에 숨어서 모델이 잘 부르지 않기 때문이에요. 이 글은 claude -p에 쓸 내장 도구 목록을 정하는 --tools에 tool search 도구(ToolSearch)를 넣지 않아서 tool search가 꺼지고, 도구 정의가 처음부터 모두 실립니다.

Token Savior가 없는 조건에는 빈 설정 mcp-none.json을 넘겨요.

{"mcpServers": {}}

질문은 두 개예요. questions/q1.txt는 이름을 알려 주지 않고 하는 일로 함수를 찾게 해요.

주문 배송비를 지역과 무게로 계산하는 함수를 찾아 줘. 그 함수는 어느 파일에 정의돼 있고, 지역 "jeju", 무게 3kg으로 부르면 무엇을 돌려주나? 코드는 고치지 마. 마지막 줄에 JSON 한 줄로만 답해: {"function": "함수 이름", "file": "저장소 루트 기준 경로", "value": 숫자}

questions/q2.txt는 진입점에서 호출 경로를 따라가게 해요. 이름이 같은 함정 함수를 피해야 맞힐 수 있습니다.

shop/api/checkout.py의 checkout()이 쿠폰 할인액을 계산할 때 실제로 호출하는 함수를 호출 경로를 따라가서 찾아 줘. 그 함수는 어느 파일에 정의돼 있고, 쿠폰 "WELCOME", 소계 40000이면 할인액은 얼마인가? 코드는 고치지 마. 마지막 줄에 JSON 한 줄로만 답해: {"function": "함수 이름", "file": "저장소 루트 기준 경로", "value": 숫자}

두 질문 모두 마지막 줄의 JSON을 스크립트가 정답과 그대로 비교해요. 함수 이름, 파일 경로, 값이 셋 다 맞아야 정답입니다.

실습 1: 세 조건을 섞어 18번 실행하기

측정 스크립트 run_measure.sh는 schedule에 적힌 순서대로 claude -p를 18번 부르고, 회차마다 결과 JSON을 out/q1-base/run-001/ 같은 디렉터리에 남겨요. 조건을 고르고 claude -p를 부르는 부분만 볼게요(발췌).

  case "$mode" in
    base) tools="Read,Grep,Glob"; mcp=../mcp-none.json ;;
    both) tools="Read,Grep,Glob"; mcp=../mcp-savior.json ;;
    ts) tools=""; mcp=../mcp-savior.json ;;
  esac

  (cd repo && claude -p --model opus --effort medium \
    --setting-sources project --strict-mcp-config \
    --mcp-config "$mcp" --allowedTools "mcp__savior" \
    --tools "$tools" --no-session-persistence \
    --output-format json --verbose \
    < "../questions/$question.txt" > "../$run_dir/claude.json" 2> "../$run_dir/stderr.log")

조건 이름은 <질문>-<조건>이고, 조건은 세 가지예요.

  • base: 내장 도구 Read, Grep, Glob만 써요. MCP 서버는 없습니다.
  • both: 같은 내장 도구에 Token Savior를 더해요. 평소에 MCP 서버를 설치해서 쓰는 모습이에요.
  • ts: --tools ""로 내장 도구를 모두 빼고 Token Savior만 남겨요. Token Savior 도구만으로 찾게 했을 때 얼마나 드는지 보려는 비교용 조건이에요.

claude -p에 준 옵션 중 이 둘만 짚을게요.

  • --setting-sources project --strict-mcp-config: 사용자 설정, 플러그인, 훅, 계정에 연결한 claude.ai 커넥터가 섞이지 않게 해요. MCP 서버는 --mcp-config로 넘긴 것만 붙습니다.
  • --output-format json --verbose: --verbose를 붙이면 결과 JSON이 마지막 결과 하나가 아니라 대화의 모든 기록을 담은 배열이 돼요. 요청마다의 도구 호출과 결과, 마지막의 토큰 사용량이 다 들어 있습니다.

실행해 볼게요. 맥이 잠들면 호출이 끊기니 caffeinate로 감싸서 돌립니다.

chmod +x run_measure.sh
caffeinate -dims ./run_measure.sh
q1-ts out/q1-ts/run-001
q1-base out/q1-base/run-001
q1-both out/q1-both/run-001
q2-both out/q2-both/run-001
q2-ts out/q2-ts/run-001
q2-base out/q2-base/run-001
q1-base out/q1-base/run-002
q1-both out/q1-both/run-002
q1-ts out/q1-ts/run-002
q2-ts out/q2-ts/run-002
q2-base out/q2-base/run-002
q2-both out/q2-both/run-002
q1-both out/q1-both/run-003
q1-ts out/q1-ts/run-003
q1-base out/q1-base/run-003
q2-base out/q2-base/run-003
q2-both out/q2-both/run-003
q2-ts out/q2-ts/run-003

실습 2: 집계하고 비교하기

summarize.py는 조건마다 결과 JSON 세 개를 읽어서 정답을 채점하고, 토큰과 도구 호출을 세요. 입력 토큰은 input_tokens, cache_creation_input_tokens, cache_read_input_tokens 세 필드에 나뉘어 있는데, 캐시에서 읽었든 새로 썼든 모델이 읽은 양은 같으니 셋을 더해서 봅니다.

출력 항목은 다음과 같아요. first부터 cost까지는 세 회차의 중앙값입니다.

  • correct: 맞힌 회차 수
  • used_ts: Token Savior 도구를 한 번이라도 부른 회차 수
  • first: 첫 요청의 입력 토큰. Claude Code의 고정 부분, 도구 정의, 질문을 합친 크기예요.
  • growth: 마지막 요청의 입력에서 첫 요청의 입력을 뺀 값. 탐색하는 동안 대화에 쌓인 도구 호출, 도구 결과, Claude가 중간에 쓴 내용의 크기예요.
  • total: 모든 요청의 입력 토큰 합계. 요청마다 앞의 대화를 다시 읽으니 요청이 많을수록 커져요.
  • output, turns, sec, cost: 출력 토큰, 턴 수, 걸린 시간(초), 추정 비용(달러)

그 아래 회차별 줄은 도구x호출 수:결과 글자 수예요. 예를 들어 Readx2:24542는 Read를 두 번 불렀고, 두 결과를 합쳐 24,542자가 Claude에게 갔다는 뜻입니다.

py summarize.py
q1-base correct=3/3 used_ts=0/3 first=5693 growth=7593 total=47625 output=1073 turns=6 sec=14.0 cost=0.0823
  run-001 Globx1:2018 Grepx2:493 Readx2:24542
  run-002 Globx1:2018 Grepx3:912 Readx1:12745
  run-003 Globx1:166 Grepx3:1443 Readx1:12745
q1-both correct=3/3 used_ts=3/3 first=9028 growth=7170 total=57645 output=1211 turns=5 sec=15.2 cost=0.0905
  run-001 Grepx1:275 Readx1:11797 ts:get_full_contextx1:529 ts:search_codebasex1:239
  run-002 Grepx1:415 Readx2:6001 ts:search_codebasex1:7338
  run-003 Grepx1:415 Readx3:12830 ts:search_codebasex1:14519
q1-ts correct=3/3 used_ts=3/3 first=6763 growth=7229 total=53561 output=994 turns=6 sec=16.2 cost=0.1015
  run-001 ts:get_full_contextx1:529 ts:get_function_sourcex1:61 ts:search_codebasex3:13342
  run-002 ts:get_full_contextx1:529 ts:get_function_sourcex1:61 ts:search_codebasex2:1115
  run-003 ts:get_function_sourcex1:127 ts:search_codebasex4:16304
q2-base correct=3/3 used_ts=0/3 first=5728 growth=9142 total=67904 output=1800 turns=7 sec=23.7 cost=0.1214
  run-001 Globx1:1347 Grepx5:5177 Readx2:12813
  run-002 Globx1:339 Grepx3:4224 Readx2:12813
  run-003 Globx1:1347 Grepx3:5113 Readx1:12201
q2-both correct=3/3 used_ts=1/3 first=9063 growth=8278 total=88143 output=1859 turns=8 sec=22.0 cost=0.0846
  run-001 Grepx4:1997 Readx2:12673
  run-002 Grepx4:4258 Readx2:12813 ts:get_function_sourcex1:83
  run-003 Globx1:1347 Grepx4:1702 Readx2:12813
q2-ts correct=3/3 used_ts=3/3 first=6798 growth=3462 total=52379 output=1192 turns=7 sec=23.5 cost=0.0588
  run-001 ts:find_symbolx1:1947 ts:get_function_sourcex3:907 ts:search_codebasex1:5120
  run-002 ts:get_function_sourcex4:628 ts:search_codebasex2:5843
  run-003 ts:get_function_sourcex4:745 ts:search_codebasex2:5843

18번 모두 정답이었어요. 정답률로는 조건을 가를 수 없으니 토큰과 도구 호출을 볼게요.

하나 밝혀 둘 게 있어요. 측정할 때 repo/에는 제가 정답을 확인하려고 Python으로 import해 본 흔적인 __pycache__/ 디렉터리가 남아 있었어요. 그래서 base의 Glob 결과 세 번에 .pyc 경로가 671자, 671자, 156자 섞였습니다. 이 글대로 만든 repo/에는 없으니 Glob 결과가 그만큼 작게 나올 거예요.

도구 정의가 요청마다 3,335토큰을 더했어요

first를 보면 base는 5,693, both는 9,028이에요. 같은 질문에 Token Savior만 더했으니, 차이 3,335토큰이 도구 15개의 정의입니다. 두 번째 질문에서도 5,728과 9,063으로 차이가 같았어요.

README의 약 1.5K토큰보다 두 배를 조금 넘는 값이에요. tool search가 켜져서 정의가 미뤄졌다면 이만큼 커지지 않았을 테니, 정의가 처음부터 모두 실렸다는 것도 이 숫자로 확인됩니다. 이 3,335토큰은 한 번만 드는 게 아니라 요청마다 실려요.

같이 붙이면 Claude는 주로 내장 도구를 썼어요

both 조건에서 Token Savior를 부른 회차는 첫 질문 3번 중 3번, 두 번째 질문 3번 중 1번이었어요. 그런데 회차별 줄을 보면 부른 횟수가 한두 번이고, 그 옆에서 Read가 여전히 6,001~12,830자를 가져왔습니다.

그래서 탐색으로 쌓인 양(growth)은 base와 거의 같았어요. 첫 질문이 7,593과 7,170, 두 번째 질문이 9,142와 8,278입니다.

반면 입력 합계(total)는 첫 질문이 47,625에서 57,645로 21.0%, 두 번째 질문이 67,904에서 88,143으로 29.8% 늘었어요. 탐색은 거의 그대로인데, 도구 정의가 요청마다 더해졌기 때문이에요.

Token Savior만 남기면 호출 경로 질문에서 줄었어요

두 번째 질문의 ts 조건은 growth가 3,462로 base의 9,142보다 62.1% 적었어요. 입력 합계도 67,904에서 52,379로 22.9% 줄었고, 출력 토큰 중앙값도 1,800에서 1,192로 줄었습니다.

어디서 줄었는지는 회차별 줄에 보여요. base는 Read가 매번 12,201~12,813자를 가져왔어요. 앞에서 본 것처럼 396줄짜리 checkout.py를 통째로 읽은 몫이 대부분입니다. ts의 get_function_source는 628~907자였어요.

다만 ts도 search_codebase가 5,120~5,843자를 가져왔어요. 검색은 Token Savior를 써도 Grep과 비슷하게 결과가 커질 수 있다는 뜻입니다.

설명으로 찾는 질문에서는 줄지 않았어요

첫 질문은 함수 이름을 알려 주지 않고 "배송비를 지역과 무게로 계산하는 함수"를 찾게 했어요. 여기서는 ts의 growth가 7,229로 base의 7,593과 거의 같았고, 입력 합계는 47,625에서 53,561로 오히려 12.5% 늘었습니다.

이름을 모르면 먼저 찾아야 하잖아요? 그 단계에서 Token Savior도 결국 search_codebase로 검색을 했고, 세 번째 회차의 search_codebase 네 번은 합쳐서 16,304자를 돌려줬어요. 이름을 알아낸 뒤의 get_function_source는 127자였습니다.

이 이미지는 README의 기본 설치([mcp])라 선택 설치인 벡터 검색([memory-vector])이 빠져 있어요. 함수 단위로 줄이는 효과는 찾을 대상을 이미 알 때 나타나고, 찾는 단계 자체는 이번 설치(벡터 검색 없음)에서는 줄여 주지 않았어요.

정리

범위: Claude Code 2.1.309의 claude -p, claude-opus-5-5, effort medium, Token Savior 4.24.0 optimized 프로필, 고정 시드로 만든 Python 파일 59개, 질문 2개 × 조건 3개 × 3회, 18번 호출.

  • 내장 도구 옆에 Token Savior를 붙여도 Claude는 주로 Read와 Grep을 썼어요. 6회차 중 4회차만 Token Savior를 한두 번 불렀으니, MCP 서버를 붙였다면 결과 JSON의 도구 호출로 실제로 쓰였는지부터 세어 보세요.
  • Token Savior의 도구 정의 15개는 요청마다 3,335토큰을 더했어요. README 프로필 표의 약 1.5K토큰보다 컸으니, 도구 정의 비용은 Claude의 usage로 직접 재세요.
  • 함께 붙인 조건은 탐색량이 거의 그대로인데 입력 합계가 21.0%, 29.8% 늘었어요. 거의 쓰이지 않는 MCP 서버는 토큰만 더한다고 보셔야 합니다.
  • Token Savior만 남기면 호출 경로를 따라가는 질문에서 탐색으로 쌓인 입력이 9,142에서 3,462토큰으로 62.1% 줄었어요. 파일을 통째로 읽는 대신 함수 소스만 받아 왔기 때문이에요.
  • 하는 일로 함수를 찾는 질문에서는 Token Savior도 넓은 검색을 해서 줄지 않았고, 입력 합계는 12.5% 늘었어요. 뜻으로 찾는 벡터 검색은 설치하지 않았으니, 기본 설치로는 찾는 단계가 줄지 않는다고 보세요.
내 링크 만들기