loading…
Search for a command to run...
loading…
Tracks token usage and records coding sessions for Claude Desktop users, supporting both local SQLite and cloud Cloudflare D1 databases.
Tracks token usage and records coding sessions for Claude Desktop users, supporting both local SQLite and cloud Cloudflare D1 databases.
Claude Desktop 사용자의 토큰 사용량 추적과 vibe 코딩 세션 기록을 위한 MCP 서버.
로컬(SQLite)과 클라우드(Cloudflare Workers + D1)를 동시에 지원하는 듀얼 모드 구조로, 여러 디바이스에서 동일한 통계를 공유할 수 있습니다.
| Tool | 설명 |
|---|---|
log_usage |
대화 종료 후 입·출력 토큰 수를 로컬 DB에 기록 |
get_my_stats |
날짜별 누적 통계를 마크다운 테이블로 조회 |
submit_daily_report |
오늘 집계를 Google Sheets Webhook으로 제출 |
log_session |
세션 작업 내용(제목·요약·변경 파일·결정사항)을 마크다운으로 기록 |
echo |
연결 테스트용 |
부가 기능
data/sessions/YYYY-MM-DD.md)UNIQUE(email, date)) — force: true로 우회token-estimator, session-summarizer, report-validator[로컬 Claude Desktop] ──stdio──► src/index.ts ──► SqliteStore
│
UsageStore (공통 인터페이스)
│
[다른 디바이스 Claude] ──HTTPS──► src/worker.ts ──► D1Store
(mcp-remote) API_KEY 인증 Cloudflare D1
src/
index.ts # stdio 서버 진입점
worker.ts # Cloudflare Workers 진입점 (wrangler 빌드 전용)
db.ts # UsageStore 인터페이스 + createStore/getStore 팩토리
db/
sqlite.ts # SqliteStore — better-sqlite3 동기 API → Promise 래핑
d1.ts # D1Store — Cloudflare D1 네이티브 async API
sheets.ts # Google Sheets Webhook (지수 백오프 재시도)
tools/
usage.ts # log_usage / get_my_stats / submit_daily_report
session.ts # log_session
scripts/
log-hook.mjs # Claude Code hook 자동 기록 스크립트
migrations/
0001_init.sql # D1 스키마 마이그레이션
tests/ # vitest 단위 테스트 22개
.claude/
agents/ # token-estimator / session-summarizer / report-validator
settings.json # PostToolUse/Stop hook 설정
log_usage 호출
│
├─── 로컬 stdio 서버 (src/index.ts) ───► SQLite (data/usage.db)
│ ↑ 로컬 전용
│
└─── 클라우드 서버 (src/worker.ts) ───► Cloudflare D1
↑ 대시보드·타 디바이스 공유
submit_daily_report
│
└──────────────────► Google Sheets
↑ 날짜별 집계 스냅샷
| 항목 | SQLite (로컬) | Cloudflare D1 (클라우드) | Google Sheets |
|---|---|---|---|
| 저장 시점 | log_usage 호출 즉시 |
log_usage 호출 즉시 |
submit_daily_report 호출 시 |
| 저장 단위 | 대화 1건씩 원시 데이터 | 대화 1건씩 원시 데이터 | 날짜+모델별 집계 스냅샷 |
| 접근 방법 | 로컬 PC에서만 | 대시보드·API·타 디바이스 | Google Sheets UI |
| 수정 가능 | 직접 SQL 가능 | wrangler CLI | 스프레드시트에서 직접 |
| 용도 | 로컬 백업·빠른 조회 | 멀티 디바이스 공유·대시보드 | 장기 보관·비교 분석·공유 |
| 중복 방지 | 없음 (호출마다 삽입) | 없음 (호출마다 삽입) | UNIQUE(email, date) 자동 차단 |
usage_logs 테이블에 대화별 원시 기록 저장. daily_stats 뷰가 날짜+모델별로 실시간 집계.submit_daily_report 호출 시점의 집계값을 1회성 스냅샷으로 전송. 이후 log_usage를 추가 호출해도 Sheets는 자동 갱신되지 않음.로컬 stdio 서버에서 log_usage를 호출하면 SQLite에만 저장됩니다.
대시보드와 타 디바이스에 반영하려면 클라우드 서버로 호출해야 합니다.
방법 1 — Claude Desktop에서 클라우드 서버 사용 (권장)
claude_desktop_config.json에 mcp-server-cloud를 등록하고 해당 서버로 log_usage 호출:
"오늘 대화 토큰을 mcp-server-cloud로 기록해줘"
방법 2 — curl로 직접 호출
curl -X POST https://mcp-server.kdkim2000.workers.dev \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"log_usage","arguments":{"model":"claude-sonnet-4-6","input_tokens":85000,"output_tokens":18000}}}'
방법 3 — 로컬 → D1 일괄 이관
# SQLite 덤프 후 D1에 적용
sqlite3 data/usage.db ".dump usage_logs" > dump.sql
npx wrangler d1 execute mcp-usage --remote --file=dump.sql
Claude Desktop에서 MCP tool을 통해 log_usage를 호출할 때, Claude는 대화의 실제 토큰 수를 API 응답에서 직접 읽을 수 없습니다. 따라서 추정값을 기록합니다.
| 방법 | 정확도 | 설명 |
|---|---|---|
| Claude 직접 추정 | ±30% | 대화 규모를 감각적으로 추정 |
token-estimator sub-agent |
±20% | 대화 내용을 분석해 규모별 기준값 적용 |
| Anthropic Console 확인 | 실제값 | console.anthropic.com → Usage 탭 |
API 응답 usage 필드 |
실제값 | 직접 API 호출 시 응답에 포함 |
1. Anthropic Console (가장 정확)
console.anthropic.com → Usage 탭에서 날짜별·모델별 실제 토큰 수를 확인할 수 있습니다.
2. token-estimator sub-agent 활용
대화 종료 시 정확도를 높이려면:
이번 대화 토큰 추정해줘
sub-agent가 대화 규모·파일 크기·도구 호출 수를 분석해 보정된 추정값을 제공합니다.
3. 추정 기준 (직접 추정 시)
| 대화 규모 | input_tokens | output_tokens |
|---|---|---|
| 짧은 Q&A (5턴 이하) | 500 ~ 2,000 | 200 ~ 800 |
| 보통 작업 (10턴) | 2,000 ~ 8,000 | 800 ~ 3,000 |
| 긴 작업 (코드·문서) | 8,000 ~ 30,000 | 3,000 ~ 10,000 |
| 매우 긴 대화 | 30,000 ~ 100,000+ | 10,000+ |
정확한 수치보다 일관된 추정이 중요합니다. 매번 같은 기준으로 추정하면 상대적 비교는 충분히 유효합니다.
Node.js 18+, better-sqlite3 네이티브 모듈 빌드를 위한 Visual C++ Build Tools (Windows):
winget install Microsoft.VisualStudio.2022.BuildTools
git clone <repo>
cd mcp
npm install
.env.example을 복사해 .env로 저장 후 수정:
SHEETS_WEBHOOK_URL=https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec
[email protected]
DB_PATH=./data/usage.db
| 변수 | 필수 | 설명 |
|---|---|---|
SHEETS_WEBHOOK_URL |
제출 시 필수 | Google Apps Script 웹 앱 URL |
DEFAULT_USER_EMAIL |
권장 | tool 호출 시 email 생략 가능 |
DB_PATH |
선택 | SQLite 경로 (기본: ./data/usage.db) |
MAX_TOKENS_PER_CALL |
선택 | 단일 호출 최대 토큰 (기본: 1,000,000) |
%APPDATA%\Claude\claude_desktop_config.json:
{
"mcpServers": {
"mcp-server": {
"command": "npx",
"args": ["tsx", "E:\\apps\\mcp\\src\\index.ts"]
}
}
}
Claude Desktop 재시작 후 채팅창 하단 망치 아이콘에서 tool 목록을 확인합니다.
Cloudflare 무료 티어: Workers 100k req/일, D1 5GB 저장소.
npm install # wrangler 포함
npx wrangler login # Cloudflare 계정 로그인 (브라우저 인증)
npx wrangler d1 create mcp-usage
출력된 database_id를 wrangler.toml에 입력:
[[d1_databases]]
binding = "DB"
database_name = "mcp-usage"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
npm run migrate:remote # D1에 테이블/뷰 생성
npm run deploy # Workers에 배포
npx wrangler secret put API_KEY # 임의의 긴 문자열
npx wrangler secret put DEFAULT_USER_EMAIL # [email protected]
npx wrangler secret put SHEETS_WEBHOOK_URL # Google Apps Script URL
curl https://mcp-server.<account>.workers.dev \
-H "Authorization: Bearer <API_KEY>"
# → {"status":"ok","server":"mcp-server"}
{
"mcpServers": {
"mcp-server-cloud": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://mcp-server.<account>.workers.dev",
"--header", "Authorization: Bearer <API_KEY>"
]
}
}
}
브라우저에서 토큰 사용량을 시각적으로 확인합니다.
URL: https://mcp-server.kdkim2000.workers.dev/dashboard
localStorage에 저장됨| 영역 | 내용 |
|---|---|
| 요약 카드 | 오늘 / 이번 달 / 전체 누적 토큰 |
| 일별 바 차트 | 최근 30일 날짜별 사용량 (Chart.js) |
| 모델별 도넛 차트 | 모델 점유율 |
| 상세 테이블 | 날짜 · 모델 · 입력 · 출력 · 합계 (최신순) |
주의: 대시보드는 D1(클라우드)의 데이터만 표시합니다. 로컬 SQLite 데이터는 반영되지 않습니다.
[작업 중]
Claude Desktop에서 코딩·질의응답
[대화 종료 시]
1. "이번 대화 토큰 기록해줘" (mcp-server-cloud로)
→ D1에 저장 + 대시보드 즉시 반영
2. 코드 작업이 있었으면: "이번 세션 기록해줘"
→ log_session으로 data/sessions/YYYY-MM-DD.md에 저장
[일과 종료 시]
3. "오늘 사용량 제출해줘"
→ submit_daily_report로 Google Sheets 전송
→ 당일 중복 제출 자동 차단
log_usage (cloud) ──► D1 즉시 반영 ──► 대시보드 즉시 표시
└──► get_my_stats 조회 가능
submit_daily_report ──► Google Sheets 1회 스냅샷 전송
└──► submissions 테이블에 제출 이력 기록
log_usage — 토큰 사용량 기록대화가 끝날 때마다 호출합니다. Claude에게 "오늘 대화 토큰 기록해줘"라고 요청하거나, 시스템 프롬프트에 자동 호출을 지시합니다.
사용자: 이 코드 리뷰해줘 [... 대화 진행 ...]
사용자: 고마워, 오늘 대화 토큰 기록해줘
AI: [log_usage 호출]
기록 완료 (id: 7)
모델: claude-sonnet-4-6
입력: 12,450 | 출력: 3,812 | 합계: 16,262
파라미터
| 파라미터 | 필수 | 설명 |
|---|---|---|
model |
✅ | 모델명 (예: claude-sonnet-4-6) |
input_tokens |
✅ | 입력 토큰 수 (0 이상) |
output_tokens |
✅ | 출력 토큰 수 (0 이상) |
email |
— | 생략 시 DEFAULT_USER_EMAIL |
note |
— | 메모 (예: "코드 리뷰") |
get_my_stats — 사용량 조회사용자: 오늘 사용량 보여줘
AI: [get_my_stats 호출]
## 토큰 사용량 ([email protected])
| 날짜 | 모델 | 입력 | 출력 | 합계 |
|------------|-------------------|--------:|--------:|--------:|
| 2026-05-12 | claude-sonnet-4-6 | 12,450 | 3,812 | 16,262 |
**누적 합계: 16,262 tokens**
파라미터
| 파라미터 | 설명 |
|---|---|
date |
YYYY-MM-DD (생략 시 전체) |
limit |
최대 행 수 (기본 30) |
submit_daily_report — Google Sheets 제출사용자: 오늘 사용량 제출해줘
AI: [get_my_stats → submit_daily_report 순서로 호출]
제출 성공: 1행 제출 완료 (시도: 1회)
force: true 파라미터 추가log_session — 세션 기록코드 작업 완료 후 세션 내용을 기록합니다. data/sessions/YYYY-MM-DD.md에 누적 저장됩니다.
사용자: 이번 세션 기록해줘
AI: [log_session 호출]
세션 기록 완료: data/sessions/2026-05-12.md
제목: 인증 모듈 리팩토링
파라미터
| 파라미터 | 필수 | 설명 |
|---|---|---|
title |
✅ | 세션 제목 (30자 이내) |
summary |
✅ | 무엇을 왜 했는지 2~4문장 |
files_changed |
— | 변경 파일 목록 |
key_decisions |
— | 주요 의사결정 |
tags |
— | 분류 태그 |
Claude Desktop Settings → Custom Instructions에 추가하면 대화 종료 시 자동 호출됩니다:
대화가 완전히 끝날 때마다 다음을 순서대로 호출하세요:
1. log_usage — 이 대화의 추정 토큰 수 기록
(model: 현재 모델명, input_tokens/output_tokens: 추정값)
2. log_session — 코드 작업이 있었을 때만 호출
(title, summary, files_changed, key_decisions, tags)
로컬 + 클라우드 동시 연결 예시:
{
"mcpServers": {
"mcp-server": {
"command": "npx",
"args": ["tsx", "E:\\apps\\mcp\\src\\index.ts"]
},
"mcp-server-cloud": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://mcp-server.<account>.workers.dev",
"--header", "Authorization: Bearer <API_KEY>"
]
}
}
}
data/usage.db에 저장Claude Code hook이 Edit/Write/Bash 호출을 data/sessions/YYYY-MM-DD.md에 자동 기록합니다.
.claude/settings.json){
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write|Bash",
"hooks": [{"type": "command", "command": "node E:/apps/mcp/scripts/log-hook.mjs"}]
}],
"Stop": [{
"matcher": "",
"hooks": [{"type": "command", "command": "node E:/apps/mcp/scripts/log-hook.mjs"}]
}]
}
}
data/sessions/2026-05-12.md)# Session Log — 2026-05-12
- `10:23:41` **Edit** `src/db.ts`
- `10:24:05` **Write** `src/db/sqlite.ts`
- `10:31:12` **Bash** `npm test`
---
> `10:35:00` 세션 종료 [end_turn] (sid: a1b2c3d4)
## 10:35:01 — UsageStore 인터페이스 추출
> `구현` `TypeScript` `리팩토링`
기존 db.ts의 동기 함수를 UsageStore 인터페이스로 추상화하여 D1 어댑터를 추가할 수 있도록 구조를 분리했다.
Plan(Write) / Plan(Edit) 레이블: C:\Users\..\.claude\plans\ 경로의 plan 파일 변경Usage로 변경function doPost(e) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Usage');
const rows = JSON.parse(e.postData.contents);
rows.forEach(function(row) {
sheet.appendRow([
row.date, row.email, row.model,
row.input_tokens, row.output_tokens, row.total_tokens,
new Date().toISOString()
]);
});
return ContentService
.createTextOutput(JSON.stringify({ success: true, rowsWritten: rows.length }))
.setMimeType(ContentService.MimeType.JSON);
}
.env의 SHEETS_WEBHOOK_URL에 URL 입력npm run dev # tsx watch — 로컬 서버 실시간 재시작
npm run build # tsc — dist/ 빌드
npm run start # 빌드된 서버 실행
npm test # vitest — 22개 단위 테스트
npm run inspect # MCP Inspector UI 테스트
npm run dev:worker # wrangler dev — 로컬 Workers 서버 (:8787)
npm run deploy # Cloudflare Workers 배포
npm run migrate:remote # D1 스키마 원격 적용
npm run tail # Workers 실시간 로그
Claude Desktop을 stdio 트랜스포트로 연결하고 5개의 MCP tool을 구현했습니다.
핵심 구현 사항
better-sqlite3 동기 API로 로컬 SQLite 연동 (data/usage.db)submissions 테이블 UNIQUE(email, date) 제약으로 중복 제출 방지log_usage 입력 검증 — 음수 거부, MAX_TOKENS_PER_CALL 초과 거부기술 결정
"type": "module" — MCP SDK가 ESM 전용import 'dotenv/config' — ESM 환경 dotenv 로딩 패턴fileURLToPath(import.meta.url) — ESM에서 __dirname 대체Claude Code의 PostToolUse/Stop hook을 활용해 코딩 세션을 자동 기록합니다.
구현 사항
scripts/log-hook.mjs — stdin JSON 수신 후 Edit/Write/Bash/Stop 이벤트를 마크다운으로 append.claude/settings.json — hook 등록 (matcher: Edit|Write|Bash)log_session MCP tool — Claude가 세션 요약을 수동으로 기록C:\Users\..\.claude\plans\) 변경 시 Plan(Write/Edit) 레이블로 구분 기록Sub-agent 설계
token-estimator — 대화 컨텍스트에서 토큰 수 추정 (±25%)session-summarizer — hook 기록 + plan 파일 → log_session 인자 자동 생성report-validator — 제출 전 통계 합리성 검증 (ok/warn/block)여러 디바이스에서 동일한 통계를 공유하기 위해 원격 인스턴스를 추가했습니다.
핵심 설계 결정
UsageStore 인터페이스 도입 — sqlite/d1 구현이 동일 비즈니스 로직을 공유SqliteStore — 기존 동기 API를 Promise.resolve()로 래핑D1Store — prepare().bind().run() D1 네이티브 async APIInProcessTransport 구현 — Workers 환경에서 Node.js 전용 StreamableHTTPServerTransport 우회, 단일 JSON-RPC 요청을 Server 핸들러에 직접 dispatchsrc/worker.ts, src/db/d1.ts는 tsc 대상 제외, wrangler가 자체 번들링배포 결과
https://mcp-server.kdkim2000.workers.dev브라우저 접근성 향상과 보안 강화를 위한 추가 개발.
핵심 구현
GET /dashboard (공개 HTML) + GET /api/stats (Bearer 인증 JSON) 라우트 추가 — 별도 Pages/CDN 없이 단일 Worker에서 서빙maskSensitive() 함수 — Bash hook 기록 시 API Key·Bearer 토큰·Webhook URL·sk-/cfat_/ghp_ 접두사 등 9개 패턴을 [MASKED]로 치환hook 경로 버그 수정
\a·\m·\s·\l 등을 escape sequence로 처리 → E:\apps\mcp\scripts\... → E:appsmcpscripts...로 변환node E:/apps/mcp/scripts/log-hook.mjs (forward slash) — bash가 /를 escape 처리하지 않음| 영역 | 기술 |
|---|---|
| 런타임 | Node.js 18+ (ESM) |
| MCP SDK | @modelcontextprotocol/sdk ^1.12.0 |
| 로컬 DB | better-sqlite3 (동기 네이티브 모듈) |
| 클라우드 DB | Cloudflare D1 (SQLite 호환) |
| 배포 | Cloudflare Workers (wrangler ^4.0) |
| 환경변수 | dotenv |
| 테스트 | vitest (인메모리 SQLite + fetch mock) |
| TypeScript | ^5.8, module: Node16 |
개인/팀 내부 사용 전용.
Выполни в терминале:
claude mcp add mcp-token-tracker -- npx