Toggl Track の非公式 CLI ツール tgltrk を作りました

Toggl Track の非公式 CLI ツール tgltrk を作りました

tl;dr

  • Toggl Track をコマンドラインから操作できる Rust 製の CLI ツールを作りました
  • タイマー操作、タイムエントリ・プロジェクト・クライアント・タグの管理ができます
  • Claude Code のスキルとして登録すると、コーディング中の時間管理を雑に頼めます

https://crates.io/crates/tgltrk

github.com

Toggl Track とは

Toggl Track はタイムトラッキングサービスで、作業時間の記録・集計ができるやつです。 Web UI やモバイルアプリがあって、タイマーを開始/停止するだけで作業ログが残る。 プロジェクトやタグで分類できるので、何にどれくらい時間を使ったかを振り返れる系のあれ。

背景

Toggl Trackで主だった作業時間とかを記録しようと努めてるんだけど、まあうっかり開始し忘れたり、並行して複数のことをしたりするわけですよ。 あと、Claude Codeの作業時間とかもざっくり記録してるんだけど、なあなかなかうっかりしがち。 そういう時に、こんな感じで記録しておいて、ってAIの人にお願いして対応しておいて欲しい気持ちがあるんだけど、現状だとなかなか難しい。 Toggl TrackにはAPIがあるのでcurlベースでやってはくれるんだけど、わりと試行錯誤するし、APIトークンが思いっきりログに残りまくるのもなーという感じがありました。

Toggl Track の MCP サーバ(非公式)とかは存在するんだけど、MCP はちょっと仰々しいので、SKILLで簡単にできるといいなあという気持ちで、ひとまずCLIを作ろうと思ったのでした。

なにこれ

tgltrk は Toggl Track の非公式 CLI ツールです。 タイマー操作からプロジェクト管理まで、Web UI でできることの大部分をコマンドラインからできます。

$ tgltrk timer current
#12345678 Code review 01:23:45 [running]

$ tgltrk timer start -d "Code review" -p 12345 -t bug,review
✓ Timer started

$ tgltrk timer stop
✓ Timer stopped

できること

  • タイマー: 開始・停止・確認
  • タイムエントリ: 一覧・作成・編集・削除・続行
  • プロジェクト・クライアント・タグ: CRUD
  • ワークスペース: 一覧・取得
  • 全コマンドで --json 対応なので jq と組み合わせたりスクリプトに組み込める
  • レスポンスキャッシュ: プロジェクト一覧とかの変更頻度が低いデータはキャッシュから返すので API 呼び出しを節約できる

AI エージェント連携

--help-skill で SKILL.md を出力できるので、Claude Code のスキルとして登録可能。

mkdir -p ~/.claude/skills/tgltrk && tgltrk --help-skill > ~/.claude/skills/tgltrk/SKILL.md

登録すると「今日の作業時間出して」とか「さっきのタイマー止めて」みたいなのを雑に頼める、はず。 --json を適切に使い分けてくれるように SKILL.md を書いてあるので、割とうまくやってくれるはず。 ProjectとかClientも取得できるので、然るべきものを選んでエントリに設定してくれます(というか確認してくれる)。

なので、わりとざっくり依頼するだけでよしなにやってくれます。

認証

$ tgltrk auth login
API token: ********

トークンは OS のキーリング(macOS Keychain / Windows Credential Manager / Linux Secret Service)に保存されます。 macOS版のリリースバイナリは公証と署名が行われているので、多分初回にパスフレーズ聞かれたらあとは大丈夫だと思う。 Crates 版とかは自前ビルドになるので、バージョンアップデートごとにパスフレーズを聞かれるかもしれません。 それはKeychainの挙動なので、CLIとしては関知していませんし、取得もしていません。

代替手段として環境変数 TOGGL_API_TOKEN が設定されている場合はそちらが用いられます。

インストール

cargo install tgltrk

ビルド済みバイナリも GitHub Releases に置いてあります(Linux, Windows, macOS Apple Silicon(署名済み))。うまくビルドできないのでIntelMacは諦めました。

技術的なこと

  • Rust 製、MSRV 1.85(edition 2024)
  • Toggl Track API v9
  • テスト 175 個、行カバレッジ 92.75%
  • 独自のTTL付きローカルファイルキャッシュ / src/cache.rs

toggl-cli:

もともと、toggl-cliというRustベースのツールがあって、それを使えばいいかなあと思ってたんですけど、2025/11月が最終更新で、かついくつか問題がありました。

  1. API 呼び出しの非効率
  2. get_entities() が毎回5つのエンドポイントを並列呼び出し(time_entries, projects, tasks, clients, workspaces)
  3. toggl running(実行中タイマー確認)だけで5リクエスト消費。
  4. 無料プラン(30リクエスト/時)で4〜5回操作するだけで枯渇する可能性がある
  5. テストの薄さ
    • フォーク元の時点ではテストが auth コマンドの4件のみ
    • それ以外のAPIなどについては一切テストがない
  6. 非推奨・古い依存クレート
  7. structopt 0.3: 公式が非推奨とアナウンス。後継は clap v4
  8. lazy_static: std::sync::OnceLock(Rust 1.80+ で安定化)で代替可能
  9. エラーハンドリングの不備 1.一部の API エンドポイントが不正確

特に1とかは抜本的なレベルの問題なので、変に直すよりも作り直した方がいいなと判断しました。

かんそう

今回もClaude Code に大変にお世話になりました。ありがとう Claude Code。 所要時間は 2日間で大体17時間くらい。

Toggl Track の API は素直な REST なので、特に致命的な問題はなかったです。 強いて言えばレートリミットが 30req/h ってどういうことなん…という気持ち。しかも、有料にしてもそんなに使えるわけではないし。 なので、複雑になることを甘受してキャッシュ機構をつけたんだけど、管理画面では使用実績が増えないのだがどういうことなんだろう?

ともあれ、Claude Codeに雑に記録を頼むをちゃんと書いてくれるのでむっちゃ便利。 「コミットログのタイムスタンプ見て、大体の作業時間を推定して、15分単位くらいで記録しておいて」みたいな感じで頼んでいます。便利!

リンク

jetdb v0.3.1 でAccess のフォーム/レポートデザイン情報を読めるようにしました

tl;dr

  • jetdb v0.3.1 をリリースしました
  • form サブコマンドを追加して、フォーム/レポートのデザイン情報を読めるようになりました
    • コントロール一覧、プロパティ(RecordSource、イベントハンドラ等)の表示に対応しています
  • フォームのバイナリフォーマットを解析してドキュメント化しました

https://crates.io/crates/jetdb-cli github.com

新しい!

Access のフォームやレポートには、コントロールの配置やプロパティ、イベントハンドラの設定とかがバイナリ形式で保存されています。v0.3.1 ではその辺を読み取って表示できるようになりました。

サードパーティのOSSで、COM機能を使わず(≒Windows以外のプラットフォームで)この辺の値が取れるツールは今のところないと思います(需要もあんまりないけど)。

フォーム/レポートの一覧

$ jetdb form list mydb.accdb
F_メニュー    Form
F_顧客一覧    Form
R_売上レポート    Report

--forms-only / --reports-only でフィルタもできます。

コントロール一覧

$ jetdb form controls mydb.accdb F_メニュー
Btn_顧客一覧    CommandButton    0
Txt_検索    TextBox    1
Label_タイトル    Label    2

プロパティ表示

RecordSource、ControlSource、Filter、イベントハンドラ(OnClick 等)といったプロパティを表示できます。

$ jetdb form props mydb.accdb F_メニュー
Form: F_メニュー
Form Properties:
  RecordSource: T_メニュー
  Filter: [ID] > 0

Control: Btn_顧客一覧 (CommandButton)
  OnClick: [Event Procedure]
  Caption: 顧客一覧を開く

生バイナリのダンプ

form dump で Blob / TypeInfo / PropData の生バイナリを標準出力に出力できます。解析やデバッグ用です。 というか、バイナリの解析するために使っていた残骸です。

バイナリフォーマットのリバースエンジニアリング

Access のフォーム/レポートデザイン情報は MSysAccessStorage というシステムテーブルに格納されています。 各フォームは Blob、TypeInfo、PropData という3つのストリームを持っていて、Blob がデザイン本体のバイナリになっています。 (で、Jet3は構造が違うので読めません。)

でバイナリな上に、Undocumentedな仕様なので、公式な資料がないのは当然として非公式ドキュメントも見当たりませんでした(そもそも需要がなさそう)。

なので実際のデータを元に解析する必要がありました。

TypeInfo のヘッダ構造、Blob 内のプロパティエントリの型定義(Bool、Short、Long、Double、Color、Text、Binary、Guid)、コントロールセクションの境界検出ロジックなど、その辺がいろいろ複雑でしたがClaude Codeさんが頑張ってくれたのでまあなんとか。

資料については下記にまとめました(Claude Codeさんが)。

ざっくり調べた限りは、この仕様をまとめてドキュメントされてるのは見つけられなかったので、なんらかの役には立つだろう的な。

その他の変更

  • 一部のクエリが一覧に表示されなかった問題を修正しました
  • テストカバレッジを改善しました

かんそう

Access のBLOBな謎バイナリフォーマットを解釈するのは大変なんですが、まあ、Claude CodeさんがPythonのスクリプトを量産しまくりながらバイト単位の解析を進めてくれたので、なんかそれっぽく動きました。 VBA側で、オブジェクトの情報を取得して、それをSaveAsTextで保存することでAccessの構造情報が把握できるんですが、それと比べてもあんまり遜色ない情報だったので、まあ大丈夫かと。

未知のプロパティがあるとそこで処理を打ち切るとかしてるので、謎のコンポーネントとか謎のプロパティがあるとちょっと困るんですけど、まあレアなはずだからきっと大丈夫。

なお、そこそこ頻出する割には謎のままのTypeInfo#type_code: 0x1EFFみたいなエントリがあって、あんまり必要性のある要素じゃなさそうなのでとりあえず出力から除外するようにしたりしました。

ていうかそういうあたりで問題があったりしたので、すぐに0.3.0 -> 0.3.1 になったりしたんですよ。

macOS で動くのに Linux で落ちる

macOS のローカルテストでは普通に動いていて、CI の Linux でだけクラッシュするという状況が発生しました。 (CIではMac/Windows/Linuxでテストしています)

原因は「TypeInfo ヘッダのオフセットを1フィールド分(4バイト)間違えて読んでいた」みたいな感じなんですが、その値が0xFFFFFFFFなわけですよ。で、コントロール数だと解釈されて約42億個分のメモリ(約128GB)を確保しようとしたっぽい。

macOS はメモリのオーバーコミットに寛容なのか、仮想アドレス空間だけ確保して実際には物理メモリを消費らしい。 ので、まあ謎メカニズムで動いちゃってた。 一方で Linux のメモリ管理はちゃんと「いや、それは無理だし!」ってことでPanicですよ。

ので、わざわざDockerコンテナで再現してちまちまトラブルシュートとかが必要になりました。まあ、直ってよかった。

jetdb v0.2.2 で AI Agent Skillに対応しました

tl;dr

  • jetdb v0.2.2 をリリースしました
  • --help-skill フラグで AI エージェント向けのスキルファイルを出力できるようになりました
  • Claude Code / Codex CLI / Gemini CLI などに jetdb の使い方を教えられます

スキル(Skill)ファイルとは

Claude Code とかで特定のディレクトリに Markdown ファイルを置いておくと、エージェントがそれを読んでいい感じにツールの使ってくれる、というものです。

従前、外部部機能と連携する際にはMCPがよく使われていたのですが、トークンの消費が激しいよねとかちょっとした機能にたいいて重いのでは?とかいろんな意見があって、軽めのものならSkillでいいのでは的な雰囲気が強まっているという認識です(もちろんMCPにもメリットはあります)。

--help-skill の仕組み

スキルファイル(SKILL.md)は、単に特定のディレクトリに配置するだけで動きます。 npm なツールだとソースを持っているので単にコピーすれば良いと思います。

ただ、jetdbのようなシングルバイナリだとわざわざ別のファイルを準備したくないし、スキルインストールのための別のツールに頼るのも大袈裟かなという感じです。

ので、バイナリに埋め込みました。

Rust の include_str!() マクロでビルド時に SKILL.mdファイル をバイナリに組み込みます。 これにより--help-skill オプションで標準出力に吐き出す。

登録は1行でOK:

mkdir -p ~/.claude/skills/jetdb-cli && jetdb --help-skill > ~/.claude/skills/jetdb-cli/SKILL.md

スキルのバージョンがバイナリと一致するし、余計なファイルは増えない。 SKILL.mdを探す必要もない。

開発者は普通にSKILL.mdを書いて、普通にメンテナンスするだけ。 これはだいぶ良いと思うんですよ。

あと、名前が微妙だったので --export-skill というエイリアスも用意しています。

インストール・更新

cargo install jetdb-cli

リンク

circleci-logs v0.3.0 で 実行中のジョブログ取得に対応しました

tl;dr

  • circleci-logs v0.3.0 をリリースしました
  • 実行中(running)のジョブのログが取得できるようになりました
  • vt100 ターミナルエミュレーションでログの表示品質を改善しました
  • TUI モードでのライブストリーミング表示(実験的)を追加しました

前回からの変更

前回の記事 で「できないこと」として挙げていた2点がありました。

  • ログのストリーミング取得: tail -f みたいなことはできません
  • 実行中のジョブのログの参照: 今は実行済みのジョブしか取得できません

v0.3.0 でこの両方に対応しました。

実行中のジョブログ取得

CircleCI の公開 API (v1.1 / v2) は完了済みジョブのログしか返してくれないのですが、非公式APIを使うことによって実行中のログが取得できます。

実行中のジョブログ取得

CircleCI の公開 API (v1.1 / v2) は完了済みジョブのログしか返してくれません。実行中のジョブのログを取るには、Web UI が内部的に使っているプライベート API を叩く必要がありました。

v0.3.0 では、このプライベート API を使って実行中ステップの出力を取得できるようにしています。対話モードでもコマンドラインでも動きます。

デフォルトで有効になっています。無効にしたい場合は環境変数か設定ファイルで指定できます。

# 環境変数で無効化
export CIRCLECI_LOGS_PRIVATE_API=false

# または .circleci-logs.toml で無効化
use_private_api = false

vt100 ログレンダリング

ログの表示に vt100 ターミナルエミュレーションを入れました。エスケープシーケンスを解釈してちゃんと表示します。ターミナルにはカラー出力、ファイルやパイプにはプレーンテキストで出力されます。

ライブストリーミング(実験的)

TUI モード (-i) で実行中のステップを選ぶと、ポーリングによる疑似的なストリーミング表示ができるようになりました。 tail -f ぽいやつですね。

ただし、結構画面の制御が難しくていい感じに動いているとは言い難いところもあるので、実験的な機能として扱ってください。内容が壊れることはないですが、表示がわりと不安定です。Esc か q で抜けられます。

インストール

cargo install circleci-logs

かんそう

「できないこと」ができるようにあってよかった。 あと、ターミナルの表示制御むずい、何がどうなるべきかをちゃんと定義するのも大変だし、ターになる自体の特性に加えて端末エミュレータとか噛ませるとかほんと厳しい。

CircleCI のジョブログを取得する Rust製CLI ツール circleci-logs を作りました

tl;dr

  • CircleCI のジョブログ・ワークフロー情報・テスト結果をコマンドラインから取得できる Rust 製 CLI ツールを作りました
  • ジョブ番号や CircleCI の URL を渡すだけでログが取れます
  • Claude Code のスキルとして登録すると CI 失敗の調査を雑に頼めます
  • crates.io に公開済み、MIT ライセンス

https://crates.io/crates/circleci-logs

github.com

背景

諸事情でCircleCIがCIとして使われているレポジトリを使うことになりました。 で、結構CIが失敗してて、ログを見ながら調整するみたいなことが必要になったのでした。

意外なことにCircleCI CLIにもログを見る機能がなかったりします。 CircleCI CLIはローカル環境で動作確認などを行うためのものだし、JSON返すAPIはあるのでそっちを使えってことなんだとは思うのですが。実際CircleCI MCPはあって、こちらはログが取れる模様。

とはいえ、Webの画面で細かいログ見たくないし、JSONだと見づらいし、Bearer tokenと長いURL使ってcurl叩くのもなんか面倒だし…、みたいな感じ。

ひとまずcircleci-logsというnpmがあったのでそれを使っていたのですが、Claude Codeがいろいろ再試行したりするしちょっと微妙に気持ちではありました(実用はできたので直近の問題は解決しましたが)。

ので、もうちょっとぼくが使いやすいやつを作ってみました。

なにこれ

circleci-logs は、ジョブ番号やワークフロー UUID を渡すだけでログが取れる CLI ツールです。CircleCI の URL をそのまま貼り付けてもいけます。

前提として、CircleCIの実行ログはこんな感じの構造です。

Pipeline (123001)         1回のgit pushや定期実行で起動される単位
 └─ Workflow (uuid)    ジョブの実行順序・依存関係を定義
     └─ Job (456003)      個々の実行環境で動くステップの集まり
         └─ Step       実際のコマンド実行。ログはここに残る

それぞれを、ID(Number)を指定するだけで取得できます。

できること

  • 3階層対応: パイプライン → ワークフロー → ジョブ のドリルダウン
  • ログフィルタ: --errors-only で失敗ステップだけ、--grep で正規表現フィルタ
  • テスト結果: --testsstore_test_results のテスト結果を取得、--failed-only で失敗分のみ
  • JSON出力: --json で機械処理しやすい形式に
  • 対話モード: -i で TUI のドリルダウン。番号や UUID を調べなくても選ぶだけでログにたどり着けます
  • URL直接入力: CircleCI の Web UI の URL をそのまま引数に渡せます
  • プロジェクト自動検出: git remote から GitHub/Bitbucket のプロジェクトを自動判定

できないこと

  • ログのストリーミング取得: tail -f みたいなことはできません
  • 実行中のジョブのログの参照: 今は実行済みのジョブしか取得できません
    • 現状 v1.1 の output_url はジョブ完了後にしか生成されないので、実行中のジョブのログが見れません。後述の非公開 APIを使用すると上記2件に対応できるんだけど、今はやってません。

事前準備

CircleCI の API トークンは https://app.circleci.com/settings/user/tokens から発行できます。名前をつけて「Create New Token」するだけです。取得したトークンは export CIRCLE_TOKEN="xxx" で環境変数にセットしておけば、circleci-logs がそのまま使います。

つかいかた

詳しくは、--helpとかREADME.ja.mdを読んでください。

# ジョブログを取得
circleci-logs -j 4301

# ジョブログを取得(失敗ステップのみ)
circleci-logs -j 4504 --errors-only

# CircleCI の URL をそのまま貼れる
circleci-logs "https://app.circleci.com/pipelines/github/org/repo/123/workflows/UUID/jobs/456" --errors-only

# ワークフローのジョブ一覧
circleci-logs -w UUID

# パイプラインのワークフロー一覧
circleci-logs -p 142

git リポジトリ内で実行すれば remote URL からプロジェクトを自動検出するので、設定ファイルなしで使えます。 認証に必要となる CIRCLE_TOKEN 環境変数に設定したり、tomlファイルに書いたりできます。 なので、IDを引数に実行するだけ。 あと、コーディングエージェント向けにJSONで取得することもできます(出力や実行ログにトークンが記録されません)。

対話モード

circleci-logs -i で起動すると、パイプライン → ワークフロー → ジョブ → ノード → ステップ → ログ を TUI で階層的にたどれます。CircleCI の Web UI と同じ階層構造で、並列ジョブのノード選択にも対応しています。

? Select a job
  .. (back to workflows)
  #5678     rspec                success      2025-03-08 14:32:10
  #5679     lint                 success      2025-03-08 14:32:08
> #5680     e2e                  failed       2025-03-08 14:33:01
  .. (back to workflows)

番号や UUID を調べる手間なく、選ぶだけで目的のログにたどり着けるので、わざわざWebの管理画面に移動する必要はありません。

AI エージェント連携

せっかくなのでClaude Code のスキルとして登録できるようにしてあります。--help-skill で SKILL.md を出力できるので、以下のコマンドで登録可能です。

mkdir -p ~/.claude/skills/circleci-logs && circleci-logs --help-skill > ~/.claude/skills/circleci-logs/SKILL.md

登録すると、Claude Code に「CI 落ちてるから見て」と言えば勝手にログを取ってきて分析してくれると思います。--errors-only--json を適切に使い分けてくれるように SKILL.md を書いてあるので、コンテキストウィンドウを溢れさせることもない、はず。

ちなみに、細かい点としては「ソースファイル群からSKILL.mdをコピー」とかではなく、オプションで出力するのでシングルバイナリ単体でスキルの登録ができます。ちょっとした工夫をした気がする(npmなら不要なんだろうけど)。

「バイナリ自身が自身のドキュメントを持つ」というのは有用だと思ってて、人間がmanとか--helpとかを読んで使い方を理解する、みたいのと同じ簡易jにしたかったわけです。npm パッケージであれば npx でファイルを同梱・実行できるので別途気にする必要はないのですが、シングルバイナリの CLI でスキル登録まで完結させられるのでなかなか良いのではないかと思ってます。

インストール

cargo install circleci-logs

ビルド済みバイナリも GitHub Releases に置いてあります(Linux, macOS Intel/Apple Silicon, Windows)。

技術的なこと

  • Rust 製、MSRV 1.85(edition 2024)
  • CircleCI API v1.1(ジョブ詳細・ログ)と v2(ワークフロー・パイプライン)を使い分け
  • ログの並列取得に futures の buffer_unordered を使用
    • ステップログを並列フェッチしてレイテンシを削減
  • テスト 175 個、wiremock で API のモックテスト
    • 行カバレッジ83%(ロジック部分は96〜100%、TUI/CLI部分は60〜71%)
  • GitHub Actions で CI(fmt, clippy, audit, MSRV, test)とクロスプラットフォームリリース

CLIでCircleCIのログを見る方法についてちょっと調べた件

CircleCI の公式 CLI(circleci コマンド)は「ローカル CLI」という位置付けになっており、config のバリデーションやローカル実行が主な用途で、リモートのジョブログを取得する機能がありません。ターミナルからログを見る手段が公式には提供されていない状態です(MCP サーバはあります)。

API としても、ログ取得は v1.1 にしか存在せず、v2 API には移行されていません。 Discuss でも 2017 年頃から繰り返し要望が上がっていますが、対応される気配はなさそうです。

なお、Ruby 製の非公式 CLI(https://github.com/unhappychoice/circleci-cli)は watch コマンドで tailっぽいログストリーミングを実現しています。

ただしこれは api/private/output/raw という非公開エンドポイントに対して HTTP Rangeヘッダ付きで1秒間隔のポーリングをかける力技です。さらにビルド状態の変更通知には CircleCI 内部のPusher(WebSocket)チャンネルを使っていて、ログのポーリングと Pusher の通知を組み合わせることで擬似的なリアルタイム監視を実現しています。 どちらも非公開 APIを使っている感じですね。

なお、CircleCI の公式 MCPサーバーも、ソースを見ると同じ非公開 API (api/private/output/raw)を使ってログを取得している雰囲気です。この辺→ mcp-server-circleci/src/clients/circleci-private/jobsPrivate.ts at main · CircleCI-Public/mcp-server-circleci · GitHub

なので、 v2 APIにログ取得を入れる予定は当面なさそう。

標準的に提供してくれても良さそうなのになあ。(特に実行済みログの取得すらv2にないのが謎)

かんそう

今回も開発の大部分は Claude Code の支援で作成しました。ありがとう Claude Code。

そもそもCLIベースでログが取得できないのが謎なんですが、そういう方針なら仕方ない。 とはいえ、そのためにMCP サーバでトークン消費するのも嫌だなという感じでした。

npm 版を置き換えて自作ツールを使えるかと思うとちょっと気分がいい。

なお所要時間としては、だいたい 約5-6時間くらいで期間としては2日半くらいでした。


リンク

Microsoft Access のデータベースを Rust で読むライブラリとCLIツール jetdb を作りました

tl;dr

  • Microsoft Access の .mdb / .accdb ファイルを読み取れる Rust 製のライブラリと CLI ツールを作りました
  • pure Rust なので ODBC ドライバも C コンパイラも不要、macOS / Linux / Windows で動きます
  • Access 97 (Jet3) から Access 2019 (ACE17) まで対応
  • crates.io に公開済み、MIT / Apache-2.0 ライセンス

github.com

背景

諸事情で Access データベースの中身を扱う機会がありまして。

macOS や Linux で .mdb / .accdb ファイルを読む手段としては、C で書かれた mdbtools があります。なんと2000年3月が初版らしい、すごい歴史。

大抵のことはこれでどうにはなるんですが、今回やりたかったのは Tauri アプリからライブラリとして呼び出したい状況があって、せっかくのマルチプラットフォームなのだから、C の FFI を介さずに Rust からネイティブに使いたいよね的な感じが少しありました。

で、ついでなんでCLIつけるよね、既存のソフトウェアをRustで書く例もよく見かけるし、みたいな。

「手元に .mdb ファイルがあるけど中身を確認したい」というとき(滅多にないシチュエーション)、Access がなくても、Windows でなくてもこれだけでいけるはず的なやつです。

ちょっと困ったこと

mdbtoolsをそのまま移植すればいいかーと思ったんですけど、ライセンスがGPLv2なのでそれを継承するとTauriアプリに組み込むときに困るので、スクラッチで作ることになりました。

Java 製の Jackcess がApache Licenseなので、それを中心に扱い、mdbtools の HACKING.md も参照しつつ進める的な感じですかね。

できること

  • データベースエンジンのバージョン判定
  • テーブル一覧の表示
  • テーブルスキーマ(カラム、インデックス、リレーションシップ)の表示
  • テーブルデータの CSV エクスポート
  • DDL 生成(SQLite / PostgreSQL / MySQL / Access SQL)
  • 保存済みクエリの SQL 復元
  • オブジェクトプロパティの表示
  • VBA ソースコードの抽出
  • パスワード保護されたファイルの読み取り(RC4 / Agile Encryption)

これらの機能はAccess 97 から Access 2019 まで対応しているはずです、たぶん。

とくちょう

  • pure Rustなんで、ほとんどどこでも動くはずです。ビルドも簡単のはず!
    • MacOS / Linux / Windows のビルド済みバイナリも提供しています
  • DDLを生成するので他のDBに移植しやすい
    • SQLite / PostgreSQL / MySQL / Access 用に対応しています
  • VBA ソースコードの抽出ができます!
  • パスワード保護された .mdb / .accdb ファイルも読めます

VBA ソースコードの抽出について

すでにmdbtoolsとかJackcessという既存アプリケーションとの差別化要素として、両ツールができないVBAの抽出(標準モジュール、クラスモジュール、フォームモジュール)に対応しています。

とは言っても、OLE2なオブジェクトまでは取得できてるので、すでに十分な実績がある cfbovbaというクレートを普通に使ってCFB(Compound File Binary)コンテナをパースしただけなんですけどね。

それでも一応独自機能!

こういう感じ。

$ jetdb vba list vbaTest.mdb
Class1 Form_Form1 Module1

$ jetdb vba show vbaTest.mdb Module1
Attribute VB_Name = "Module1"
Option Compare Database
Option Explicit

Public Function Hello() As String
    Hello = "Hello, World!"
End Function

パスワード保護されたファイルの読み取りについて

こちらも既存ツールとの差別化ポイント。.accdb で使われている Agile Encryption(AES-CBC + SHA-512 鍵導出)によるパスワード保護に対応しています。mdbtools も Jackcess 本体もこの暗号化には対応しておらず、Jackcess は別プラグイン(Jackcess Encrypt)が必要です。

--password オプションを渡すだけ。

  $ jetdb tables --password secret encrypted.accdb
  Table1 Table2 Table3

パスワードなしでアクセスしようとするとちゃんと怒られます。

  $ jetdb tables encrypted.accdb
  jetdb: password required

ちなみに古い .mdb のパスワード保護は実質飾りで、データ自体は暗号化されていないので誰でも読めます。

CLI ツールとしての使い方

インストール

cargo install jetdb-cli

基本的な使い方

# エンジンバージョンを確認
$ jetdb ver database.mdb
JET4

みたいな感じのサブコマンド方式。 jetdbだけでhelpが表示されるし、README.ja.mdにも書いてあります!

ライブラリとしての使い方

CLI はライブラリの機能をそのまま公開しているので、CLI でできることは全部できます。

Rust のプロジェクトに組み込む場合:

[dependencies]
jetdb = "0.2"

詳しくはREADME.ja.mdlib.ja.mdをみていただけると!

英語ならdocs.rsにライブラリドキュメントみたいなやつが公開されています。

Tauri アプリに組み込む場合も Cargo.toml に依存を追加するだけなので、FFI 関係なく動きます。

対応バージョン

エンジン Access バージョン ファイル形式
Jet3 Access 97 .mdb
Jet4 Access 2000/2003 .mdb
ACE12 Access 2007 .accdb
ACE14 Access 2010 .accdb
ACE15 Access 2013 .accdb
ACE16 Access 2016 .accdb
ACE17 Access 2019 .accdb

たぶん、現状で流通してるものは大抵いけると思う。Jet3のVBA抽出はテストされてないかもしれないので、ファイル持ってるひといたらください。 あと、Microsoft Moneyで使われていたMSISAM形式というのがほぼJetぽいんですけど、ファイルもないし対応はしていません。 Jackcessかmdb-readerなら多分読めるはずですので、そちらに頼ってください。ほぼないシチュエーションだと思うけど。

制限事項

  • 読み取り専用(書き込みには対応していない)
  • テーブルの全行をメモリに読み込むため、行数が非常に多いテーブルではメモリ使用量に注意

かんそう

コードは約 18,000 行、テストは 720 超。テストデータとして Jackcess から引用した 95 個の実際の .mdb / .accdb ファイルを使っています。

今回も全部Claude Code CLIが書いてくれました。ありがとうClaude Code。ありがとうOpus 4.6。

全体的に素直に解釈できるような構成にしたつもりで、特別な工夫はあんまりないはず。 バイナリファイルを読んでパースするだけなんだし、先達の資料(HACKING.md)と、Jackcessの既存実装を参考にいろいろ頑張ってくれたはず、Claude Codeが。

なので、やろうと思うってからおそらく実稼働時間16-20時間くらい、期間としても5日くらいのはずです。すごいぞ、Claude Code。 全体のレビューとか詳細な分析とかはちょっとCodexを使いました。(その後暗号化ファイル対応とかしてのでもう少しかかりました。)

いちおうむちゃくちゃニッチなツールだけど、わりと大きめな名前取れたし、初めてCratesとして公開できたので、だいぶ良かったです。生成AI NGとかじゃなければ多分使えると思う。

リンク

機能比較表

Microsoft Access データベースツール比較表です。

調査日: 2026-02-19

jetdb MDB Tools Jackcess mdb-reader access-parser pyaccdb MMKiwi.MdbReader
基本情報
言語 Rust C Java JS/TS Python Python C#/.NET
バージョン 0.2.1 1.0.1 4.0.10 3.2.0 0.0.6 0.1.0β
ライセンス MIT/Apache-2.0 LGPL/GPL Apache 2.0 MIT Apache 2.0 AGPL v3 MIT
最終更新 2026-02 2025-01 2025-10 2026-02 2025-01 2025-11 2024-07
対応バージョン
Jet3 (Access 97) × ×
Jet4 (Access 2000) ×
Jet4 (Access 2003) ×
ACE12 (Access 2007) ×
ACE14 (Access 2010) ×
ACE16 (Access 2016) ×
ACE17 (Access 2019) ×
機能比較
テーブル読み取り
クエリ定義読み取り × × × ×
リレーションシップ × × × × ×
オブジェクトプロパティ × × × ×
VBA モジュール抽出 × × × × × ×
DDL 生成 × × × × ×
書き込み × × × × × ×
DateTimeExtended × × × ×
Complex Column ※2 × × × ×
暗号化対応
Jet (レガシー) △ ※1 × × ×
RC4 CryptoAPI × △ ※1 × × ×
Standard AES (ECB) × △ ※1 × × × ×
Agile AES × △ ※1 × ×
DDL 方言内訳
SQLite
PostgreSQL
MySQL
Access SQL
Oracle ×
Sybase ×

凡例: ○ 対応 △ 部分対応/条件付き × 非対応 — 該当なし

  • ※1 Jackcess 本体は暗号化非対応。Jackcess Encrypt 拡張ライブラリ(別途導入)で対応。
  • ※2 Attachment / Multi-Value / Versioned Memo。ACE 形式(Access 2007+)固有。jetdb は参照IDの取得のみ(実データ未展開)。Jackcess は読み取り対応(テーブル新規作成は不可)。

補足:

  • MDB Tools は Jet3/Jet4(.mdb)のみ対応。ACE 形式(.accdb)は非対応
  • Jackcess は Access 97(Jet3)非対応。Access 2000 以降の読み書きに対応
  • mdb-reader v3.2.0 で Attachment カラム対応を追加
  • pyaccdb は .accdb 形式のみ対応(.mdb 非対応)。暗号化は Agile のみ
  • access-parser は .accdb 対応を謳うがテスト範囲が限定的
  • MMKiwi.MdbReader はプレアルファ段階。本番利用は非推奨
  • jetdb v0.2.1 で RC4 CryptoAPI、Standard AES、NonStandard AES 暗号化と DateTimeExtended カラム型を追加

Claude Code で急に permission denied: claude-XXXX-cwd 的なエラーが出るようになった時の応急処置

Claude Code を使っていたら、急に毎回こんなエラーが出るようになった(2.1.1の時に気づいた)。

zsh:22: permission denied: /var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/claude-f341-cwd

コマンド自体は実行できるものの、毎回標準エラー出力が汚されて鬱陶しい。画面も赤くなるし、何より本当にエラーの場合に区別がつきにくい。 ので直したい。

推定される原因

Claude Code が /var/folders 配下へ一時ファイル claude-XXXX-cwd を作成しようとするが、何らかの理由(macOS アップデートや Claude Code のバージョンアップ)で書き込み権限が不足している。

GitHub Issue #8896 でも報告されてぽいけど、根本的な修正はまだリリースされていない雰囲気。

暫定対応

とりあえず一時ファイルが作れればいいはずなので、それ用の設定を追加する。

TMPDIR 環境変数を明示的に設定することで解決する。

bash# 一時ディレクトリを作成
mkdir -p ~/tmp
# シェル設定に追加(zsh の場合)
echo 'export TMPDIR="$HOME/tmp"' >> ~/.zshrc
source ~/.zshrc

これで Claude Code は /var/folders の代わりに ~/tmp を使うようになり、エラーが出なくなる。

細かい点として、~/tmp は自動クリーンアップされないので、たまに手動で掃除すると良い。

bash# 7日以上前のファイルを削除
find ~/tmp -type f -mtime +7 -delete

そのうち直るとは思うけど、特に致命的な問題があるわけではないのでTMPDIRは指定したままでも大丈夫。