概要
Claude Codeを使って自身の使用量をモニタリングするmacOSメニューバーアプリを作った経験を共有する。
背景
Claude Code Maxプランを使っていると、現在のセッションでトークンをどれだけ消費したか、メッセージを何通送ったかが気になることが多かった。ブラウザで使用量ページを毎回開くのは面倒だった。
Claude Codeは~/.claude/stats-cache.jsonに日別使用量データをローカルに保存している。このファイルを読み取ってメニューバーに常時表示すれば便利だと思い、プロジェクトを始めた。
そしてこのアプリ自体をClaude Codeで作った。Claude Codeが自分自身の使用量をモニタリングするアプリを作るという構図だ。
データソース
Claude Codeがローカルに保存する2つのJSONファイルを使用する。
| ファイル | 用途 |
|---|---|
~/.claude/stats-cache.json |
日別メッセージ/トークン/セッション/ツール呼び出し数、モデル別累積トークン |
~/.claude.json |
アカウント名、プラン種別(Pro/Max) |
stats-cache.jsonの構造は以下の通り。
{
"dailyActivity": [
{
"date": "2026-02-14",
"messageCount": 2511,
"sessionCount": 9,
"toolCallCount": 537
}
],
"dailyModelTokens": [
{
"date": "2026-02-14",
"tokensByModel": {
"claude-opus-4-6": 65342,
"claude-sonnet-4-5-20250929": 11726
}
}
],
"modelUsage": {
"claude-opus-4-6": {
"inputTokens": 15389,
"outputTokens": 282180,
"cacheReadInputTokens": 251600374,
"cacheCreationInputTokens": 5266604
}
},
"totalSessions": 28,
"totalMessages": 8789,
"longestSession": { "duration": 27382247, "messageCount": 85 },
"firstSessionDate": "2026-02-08T14:38:23.545Z"
}
技術スタック
Xcodeプロジェクトなしで、Swift Package Managerのみで構成した。
- SwiftUI + MenuBarExtra(macOS 14+、
.windowスタイル) - @Observableマクロ + @MainActor
- DispatchSourceファイル監視
- 外部依存なし
// swift-tools-version: 5.10
import PackageDescription
let package = Package(
name: "ClaudeUsageMeter",
platforms: [.macOS(.v14)],
targets: [
.executableTarget(
name: "ClaudeUsageMeter",
path: "Sources"
)
]
)
主要な実装
1. MenuBarExtra
macOS 14からSwiftUIのMenuBarExtraでネイティブメニューバーアプリを作れるようになった。.windowスタイルを適用するとクリック時にポップオーバーウィンドウが表示される。
@main
struct ClaudeUsageMeterApp: App {
@State private var viewModel = UsageViewModel()
var body: some Scene {
MenuBarExtra {
PopoverContentView(viewModel: viewModel)
} label: {
MenuBarLabel()
}
.menuBarExtraStyle(.window)
}
}
2. DispatchSourceファイル監視
stats-cache.jsonファイルが変更されたら即座にUIを更新するため、DispatchSourceでファイルシステムイベントを監視する。フォールバックとして60秒周期タイマーも併用する。
final class FileWatcher {
private var source: DispatchSourceFileSystemObject?
func start() {
let fd = open(path, O_EVTONLY)
guard fd >= 0 else { return }
let source = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fd,
eventMask: [.write, .rename, .delete],
queue: .main
)
source.setEventHandler { [weak self] in
self?.onChange()
}
source.setCancelHandler { close(fd) }
self.source = source
source.resume()
}
}
3. .appバンドルの作成
SPMでビルドしたバイナリをmacOSアプリバンドルとしてパッケージングするには、Info.plistを含むディレクトリ構造を手動で作成する必要がある。LSUIElementをtrueに設定するとDockに表示されないメニューバー専用アプリになる。
ClaudeUsageMeter.app/
├── Contents/
│ ├── Info.plist
│ ├── MacOS/
│ │ └── ClaudeUsageMeter
│ └── Resources/
│ └── AppIcon.icns
<key>LSUIElement</key>
<true/>
試行錯誤
1. サーバー側の使用量を取得できない
当初はプログレスバーで日次使用量と上限を可視化する予定だった。しかしWebで表示される使用率(「現在のセッション5%」「週間10%」など)はサーバー側で計算される値で、取得できる公開APIがなかった。
AnthropicのUsage & Cost APIはあるが、組織用Admin APIキー(sk-ant-admin...)が必要で、個人プランのセッション別/週間使用率とは異なるデータだった。
結局プログレスバーを削除し、stats-cache.jsonで確実に表示できるデータ(メッセージ数、トークン数、セッション数、モデル別使用量)に集中する方向に転換した。
2. macOSアイコンキャッシュ
Swiftスクリプトでアプリアイコンをプログラム的に生成したが、アイコンを変更してもmacOSのアイコンキャッシュのせいで反映されない問題があった。lsregisterでアプリを再登録し、FinderとDockを再起動する必要があった。
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f ClaudeUsageMeter.app
killall Finder Dock
実装結果
プラン策定から実装、アイコン生成、GitHubへのプッシュまで1セッションで完了した。最終的なプロジェクト構造は以下の通り。
Sources/
├── App/
│ └── ClaudeUsageMeterApp.swift
├── Models/
│ ├── StatsCache.swift
│ ├── ClaudeConfig.swift
│ └── UsageSnapshot.swift
├── Services/
│ ├── StatsLoader.swift
│ └── FileWatcher.swift
├── ViewModels/
│ └── UsageViewModel.swift
└── Views/
├── MenuBarLabel.swift
├── PopoverContentView.swift
├── HeaderView.swift
├── TodayStatsSection.swift
├── DailyTrendSection.swift
├── ModelBreakdownSection.swift
└── CumulativeStatsSection.swift
メニューバーアイコンをクリックすると、アカウント情報、今日のアクティビティ、7日間トレンドチャート、モデル別トークン使用量、累計統計を一目で確認できる。

ちなみにこのプロジェクトで人間がやったことは「これ作って」と言っただけだ。アプリ開発、GitHubリポジトリ作成、アイコン生成まで全部Claude Codeが勝手にやった。本人は横で見物しながらたまに「それ違うよ」とツッコミを入れただけである。
コメントする