sift について
siftは、コマンドを実行してログ、ビルド、テスト出力を絞り込む、開発者向けのMCPサーバー兼コマンドラインツールです。出力全体をディスクに保存し、選んだ行だけを表示しながら、省略部分も後から確認できます。
主な機能
- 01コマンドを実行して生出力を保存
- 02重要な行を選び省略箇所を表示
- 03保存内容の任意範囲を確認
- 04ログ、JSON、ソース構造を要約
- 05バックグラウンド出力を順次確認
- 06機密情報をマスクして行を選択
活用シーン
- 01大量のテスト出力から失敗を特定
- 02保存済みCIログやクラッシュダンプを確認
- 03長時間実行するビルドの出力を追跡
- 04関数本体を表示せずソースの宣言を確認
MCP 対応
MCPサーバーとして、クライアントからコマンドを受け取り、その出力を処理します。hookを使うと、クライアントが実行するシェルコマンドも経由させられます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
uvx sift-cliNVIDIA_API_KEYシークレットA free NVIDIA NIM key. Optional: without one every tool still runs the command, still keeps every byte, and still returns the exit code -- what is lost is the model's choice of lines, not the output. Also read from ~/.config/nvidia/api_key.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
7掲載時のツール一覧 · 3 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| cwd | any | いいえ | |
| keep | any | いいえ | |
| budget | any | いいえ | |
| command | string | はい | |
| timeout | any | いいえ | |
| background | boolean | いいえ |
詳細
sift
コマンドを実行し、重要な行だけをモデルに渡します。
テストスイートの出力は4,000行に及び、そのうち失敗を示すのは11行です。ビルドでは進捗バーが900回再描画されます。インストールでは、処理したすべてのパッケージが一覧表示されます。そのすべてがコンテキストウィンドウに入り、そしてここがコストのかかる部分ですが、その後のターンごとに全内容が再送信されます。
sift はこの問題に対応する MCP サーバー兼コマンドラインツールです。コマンドを自ら実行し、すべてのバイトをディスクに保存したうえで、失敗箇所、概要、読み手が実際に立ち止まるような行だけをモデルに渡します。残りは削除せず、印を付けて示します。
実測済みで、このリポジトリから再現できる数字が2つあります。このプロジェクト自身のテストスイートを実行すると、652行、モデル自身のトークナイザーで数えて21,392トークンが出力されます。返されるのは8行、208トークンです。99%削減となります(python test/kazanc.py)。ほとんどすべてを取り除くのは簡単なほうです。実際のビルドとテスト出力からなる22サンプルのコーパスでは、デフォルトの予算で、読み手にとって欠かせない140行のうち138行を保持します(python test/budget.py)。
$ sift run -- pytest
FAILED test/test_auth.py::test_expired_token - assert 401 == 200
...
─ 3,914 lines not shown · sift peek 9f2c41ab for any of them ─
=== 1 failed, 212 passed in 18.4s ===
自分で生成したものではないファイル、たとえばログ、保存済みの CI の記録、クラッシュダンプも、実行せずに同じように処理できます。
$ sift digest ci-run-8812.log
2026-09-02T04:11:07 Building 214 targets
─ 38,904 lines not shown · sift peek ci-run-8812.log for any of them ─
ERROR: //src/parser:parse_test failed in 4.1s
─ 1,022 lines not shown · sift peek ci-run-8812.log for any of them ─
FAILED: 1 of 214 targets
リストは行単位で読むものではありません。機械向けに書かれた JSON 配列には改行がまったくないこともあり、1行の中から選ぶ価値のあるものもありません。そのためレコードを単位とし、同じルールを適用します。
$ sift digest export.json
{"id": 3, "status": "failed", "error": "connection refused"}
─ 412 records not shown · sift peek export.json for the text they came from ─
{"id": 416, "status": "ok"}
同じ問いをソースコードについて尋ねると、目次が得られます。
$ sift outline src/parser.rs
pub struct Parser {
pub fn parse(input: &str) -> Result<Ast, Error> {
─ 34 lines not shown · sift peek src/parser.rs for any of them ─
impl Iterator for Tokens {
そして、終了しないコマンド、たとえば開発サーバー、ログの追跡、動作中も継続させたいビルドは、起動後に少しずつ読み取れます。
$ sift run --background -- cargo build --release
9f2c41ab
$ sift follow 9f2c41ab
warning: unused import: `std::fmt`
─ 212 lines not shown · sift peek 9f2c41ab for any of them ─
error[E0308]: mismatched types
sift 9f2c41ab · running · new lines 1-247 · 2 shown · nemotron
$ sift follow 9f2c41ab # only what has arrived since
$ sift follow --all --wait 30 # every run at once, holding for something new
$ sift stop 9f2c41ab # ends it, and everything it started
同じ内容が二度表示されることはありません。行番号は実行時のものです。キャプチャが存在する限り、247行目は sift peek でも247行目のままです。
何が判断するのか
判断するのは無料のモデルです。sift は番号付きの行をモデルに送り、ひとつだけ尋ねます。どの番号が重要ですか? モデルは番号だけを返し、それ以外の返答は使いません。上で読んだテキストは、ローカルのキャプチャからバイト単位でそのまま出力されます。
これが仕組みのすべてであり、だからこそこれは要約ツールではありません。要約ツールは、行の内容を誤って伝えることがあります。sift にはそれができません。行を書き換えることはなく、選ぶだけだからです。
つまり、対応言語の一覧も必要ありません。モデルはトルコ語、日本語、アラビア語、ヒンディー語を読み、Rust、COBOL、Mojo、そして先週リリースされた言語も読みます。ここでは言語を列挙しないため、対応漏れもありません。
3つのルール
表示される内容を捏造しません。 判定役が返すのは行番号です。テキストは常にローカルファイルから取得されます。
何も捨てません。 sift peek <handle> は、加工せずそのままのキャプチャを返します。表示の各空白箇所には、何行分を省略したかが示されます。
コマンドの実行を妨げません。 API キーがない、ネットワークがない、エンドポイントが過負荷である、返答が無意味である、抽出処理にバグがある、またはこのマシンの外に何も出してはならないと判断した場合でも、そのどの場合も、それらを必要としないルールにフォールバックします。コマンドは実行され、出力が得られ、終了コードも取得できます。
マシンの外に出るもの
ひとつだけです。質問のテキストです。送信する前に、認証情報らしきものはすべて置き換えられます。既知のプレフィックスを持つトークン、JWT、認証ヘッダー、接続文字列内のパスワード、PEM ブロックの本文が対象です。
確認する際に影響はありません。モデルに尋ねるのは行番号だけで、行はお使いのファイルから出力されるため、送信時にマスクされた行も、お使いの環境では完全な状態で表示されます。
スイッチは3つです。
SIFT_MAX_CAPTURE=0 # keep everything a command writes, however much that is
SIFT_BASE_URL=... # ask your own endpoint instead, and no key is wanted
SIFT_MODELS=a,b # which models to ask there, best first
SIFT_NO_MODEL=1 # never send anything; use the deterministic view
SIFT_MASK=0 # send unmasked
SIFT_CACHE=0 # ask again, even about text already answered
SIFT_EFFORT=low # let the model think less, and lose some of what matters
SIFT_PATIENCE=0 # one quick pass only; do not wait out a busy hour
マスキングは完全ではなく、完全だと主張するものでもありません。特に特徴のない、ただのシークレットはそのまま通過します。保証されるのは SIFT_NO_MODEL だけです。
4つ目は、実測に基づくトレードオフで、デフォルトではオフです。404行のビルドログで重要な行を尋ねると、モデルは12トークンの回答を出すために約900トークンの推論を書き、回答まで15秒かかります。SIFT_EFFORT=low なら同じ質問に2.4秒で答えます。その代わり、コーパス全体では、読者にとって不可欠な行を7.8%見落とします。まさにこのツールが必要とされる場面、つまりメインフレームのジョブのリターンコードや、クラッシュループの診断でです。速度を上げることはできますが、デフォルトではありません。その代償も明記されています。
3つ目は、同じものに二度支払わないためのものです。まったく同じバイト列に対するまったく同じ質問への回答がすでにあれば、もう一度購入する代わりにそれを再利用します。保存するのは行番号だけで、表示内容そのものではありません。そのため、テキストは引き続きお使いのファイルから出力され、区切りマーカーにもお使いのキャプチャの情報が示されます。リクエストなしで表示された場合、モデル名の代わりに (remembered) と表示されます。
キャプチャしたバイト列が $SIFT_HOME(デフォルトでは ~/.cache/sift)の外に出ることはありません。アップロードも、別の場所へのログ記録もありません。キャプチャディレクトリを削除すれば、そのキャプチャに関して保存されたものはすべて削除されます。
1つのキャプチャで保持されるのは最大1ギガバイトです。実際のビルドログや、コマンドがループにはまった数秒間の出力と比べても十分な量です。後者こそが、この制限が設けられた理由です。何も捨てず、他の人が必要とするディスク容量を使い切ることもありません。読み取りが止まることはありません。誰も読み取らないパイプがコマンドを止めてしまうからです。それだけは起こさないようにしています。そのため、コマンドは最後まで実行され、終了コードはコマンド自身のものになり、全実行分を保持したと思い込まないよう、末尾に kept the first 1,073,741,824 bytes of it と表示されます。ディスク容量を優先したい場合は、SIFT_MAX_CAPTURE=0 で上限を解除できます。
キャプチャが勝手に消えることもありません。バックグラウンドでスキャン、期限切れ、整理を行うことはありません。キャプチャを削除するのは sift gc [DAYS] だけで、実行したときに削除され、それ以前に削除されることはありません。残されるのはハンドルごとに1行ずつで、削除日時とサイズだけです。コマンドは記録されません。そのため、2週間後に区切りマーカーを確認しても、存在しないハンドルを指定したときの回答ではなく、"removed on the 8th" と表示されます。
コマンド
sift run [--timeout SECONDS] [--shell] [--background] [--cwd DIR]
[--budget LINES] [--keep PATTERN] [--] COMMAND...
sift follow [HANDLE] [--all] [--wait N]
what a background run has said since you last looked
sift stop [HANDLE] end it, and everything it started
sift outline PATH|- what a file declares, without its bodies
sift digest PATH...|- what is in files somebody else produced
sift peek HANDLE|PATH [FIRST] [LAST]
sift hook answer one shell-command event on stdin
sift mcp speak the protocol on stdin, for a client
sift tools which dense tools this machine has
sift tool NAME [ARGS...] run one of them, distilled
sift memory [TERM] what has been run here before, and how it went
sift list [COUNT] what is running, and what has been run
sift stats [COUNT] what the shortening cost, and what it saved
sift gc [DAYS] remove captures older than that, and say what went
- のパスを指定すると標準入力を読み込みます。これは、ほかの人が出力した内容を取り込むもう一つの方法です。
$ journalctl -u nginx --since yesterday | sift digest -
Sep 08 04:11:07 nginx[2114]: worker process 2119 exited on signal 11
─ 8,204 lines not shown · sift peek 7c1a04e9 for any of them ─
Sep 09 01:02:55 nginx[2114]: signal 15 (SIGTERM) received, exiting
パイプから届く内容には、誰かが再び入力できるパスがありません。そのため、独立したキャプチャとして保持し、ギャップマーカーにはそのキャプチャを示します。二つ目のルールがある理由はここにあります。行を省略したビューが一時ファイルを指していたとしても、誰かがそれを読む頃には、そのファイルはもう存在しないからです。
digest に複数のパスを渡すと、同時に処理します。また、--all を指定すると、実行中のすべてのコマンドを一度に追跡します。どちらも同じ考え方です。待つことがコストなので、一度で済ませます。--wait N は、何も起きていないという応答ではなく、実行結果が実際に出るまで待ちます。
sift hook を使うと、プロキシを介さずに、クライアントが独自に実行するシェルコマンドを捕捉できます。クライアントのツール実行前フックにこれを指定すれば、すべてのシェルコマンドが最初にここを通ります。すべてをルーティングします。捕捉する価値のあるコマンドを推測することはありません。そうしたコマンドのリストは、ツールを別の形で並べたものにすぎず、コマンドがどれほど出力するかは、実行前には分からないからです。すべてをルーティングしてもコストはかかりません。12 行のビューなら 12 行です。フェイルオープンなので、バグが起きてもシェルはそのまま動作します。SIFT_HOOK=0 で無効にできます。
これが最も大きな効果をもたらす部分であり、有効にする必要がある唯一の部分です。そのため、見つけられるのを待つのではなく、自ら有効化を提案します。
sift hook --install # says what it gives and what it costs, then asks
sift hook --uninstall # and takes it back out
クライアントの設定に 1 行を書き込み、元のファイルのコピーを保存し、そこにあるほかの内容には一切触れません。sift run がこの案内を表示するのは一度だけで、その後は表示しません。
sift tool は、「ファイルを開かずに」質問に答える 3 つのプログラムのうち 1 つを実行します。構造検索には sg(ast-grep)、インデントの変更と内容の変更を見分けられる差分表示には diff(difftastic)、ツリーのサイズ確認には loc(scc)を使います。これらは本質的に出力が大きく、そのためまさにここで使うのに適しています。このパッケージにはバイナリは含まれていません。sift tools を実行すると、このマシンにインストールされているものと、それぞれの名前が表示されます。インストールするかどうかは、引き続き自分で決められます。
sift memory はモデルをまったく使いません。質問の内容は、回数、結果、ここで一度も成功したことのないコマンドなど、数えれば分かることです。数えるようモデルに頼むと、時間がかかり、リクエストを消費し、間違えることもあります。計算できないことだけをモデルが判断し、それ以外は判断しません。
sift stats は、短縮によってどれだけ節約できたかと、そのコストを表示します。これらは同じ種類の数値ではないため、分けて扱います。節約率は、このツールが保持するバイト数を使った独自の計算なので正確です。コストはエンドポイントが数えたトークン数なので、測定値です。そして、その隣には本当に知りたい数値が表示されます。つまり、送信時点で同じトークナイザーを使って数えた出力の重さです。
$ sift stats
handle captured shown part asks weighed cost command
9f2c41ab 62,003 B 450 B 0.7% 1 21,392 5,120 pytest -v
1 run · 62,003 B captured · 450 B shown · 0.7% of it
cost 5,120 tokens, as the endpoint counted them
the output put to a model weighed 21,392 tokens -- numbering and question included
最後の行には、キャプチャ単体より少し多い情報が含まれています。行番号を付け、その前に質問を添えて送信したことを示しており、推測で差し引くことはありません。報告が不都合な内容もそのまま示せます。小さなコマンドでは、質問にかかるコストが出力の重さを上回ることがありますが、それもここで確認できます。エンドポイントが数えていない実行結果はダッシュで示し、両方の合計から除外します。バイト数を 4 で割って埋め合わせることはありません。
--keep PATTERN を指定すると、ほかに何を選んだか、予算がいくらかにかかわらず、パターンに一致するすべての行を表示します。指定するのはこのツールが推測したパターンではなく、ユーザー自身のパターンです。ここでパターンが何かを決めるのはこの場合だけであり、入力するまでは何も決まりません。--budget LINES は、1 つのビューに対する上限です。
エージェント内での利用
同じ回答を MCP 経由でも利用でき、そこで最も効果を発揮します。ツールの結果は、その後のターンごとに再送信されるため、トランスクリプトに含めずに済ませたビルドログは、その後もずっとトランスクリプトに残りません。
uv tool install "sift-cli[mcp]" # or pipx; see Installing
claude mcp add --scope user sift -- sift-mcp
キーは ~/.config/nvidia/api_key から読み込まれるため、環境変数に設定したり、どこかに貼り付けたりする必要はありません。--scope user を指定すると、今いるプロジェクトだけでなく、すべてのプロジェクトにサーバーが設定されます。
次に、クライアントを再起動してください。MCP サーバーはセッションの開始時に接続されるため、そのコマンドを実行したセッションからは、このサーバーは認識されません。
キーがなくてもサーバーは起動しますが、モデルが必要な各ツールは説明を返して処理を辞退します。これにより、エージェントは、良い回答と見分けがつかない粗悪な回答を渡されるのではなく、自身のシェルに切り替えます。
stdio に対応するクライアントならどれでも使えます。コマンドは sift-mcp で、sift mcp でも同じサーバーを起動できます。これは、パッケージを独自の名前で実行したいクライアント向けの別名です(uvx --from "sift-cli[mcp]" sift-cli mcp。MCP レジストリーのエントリーで指定されているコマンドです)。利用できるのは
run、follow、outline、digest、digest_many、tool、peek です。list と stats は意図的に提供していません。これらを提供すると、このマシンで最近実行されたすべてのコマンドがモデルに渡されます。モデルが尋ねていないコマンドも含まれます。ターミナルを操作する人はすでにそれらにアクセスできますが、ソケット経由で接続するモデルにはその権限がありません。
クライアントから stderr は見えないため、すべての結果の最終行には、対象のハンドル、コマンドの終了状態、そしてモデルが行を選んだのか、それとも到達可能なモデルがなかったのかが記されます。
インストール
uv tool install sift-cli # the command line, no dependencies at all
uv tool install "sift-cli[mcp]" # and the MCP server
pipx install でも同じことができます。どちらの方法でも、専用の環境で sift と sift-mcp が PATH に追加されます。コマンドラインツールにはこれが適しています。このパッケージのものを、ビルドに使う Python に入れる必要はありません。
すでに有効化している仮想環境内なら、通常の pip install sift-cli が使えます。ただし、Debian、Ubuntu、WSL のシステム Python に対しては使えません。これらには EXTERNALLY-MANAGED マーカーが付属しており、仕様上、pip はインストールを拒否します。
error: externally-managed-environment
× This environment is externally managed
その拒否は正しい動作です。解決策は --break-system-packages ではありません。uv tool または pipx を使ってください。
パッケージ名は sift-cli で、コマンドは sift と sift-mcp です。名前が異なるのは、PyPI の sift-mcp が他の人のプロジェクト名だからです。これはエージェントのアクションの認可を扱う、無関係な MCP サーバーです。ここにあるものは彼らのものではなく、あちらにあるものもこのプロジェクトのものではありません。
Python 3.12 以降が必要で、コマンドラインツールに依存関係はありません。
モデルが必要です — キーを使うか、自分のモデルを使うか
sift は、どの行が重要かをモデルに尋ねます。モデルを指定する方法は2つあり、どちらか一方だけが必要です。
無料のキー
同梱されておらず、共有もできません。 キーはご自身で用意する必要があります。無料で、1分ほどで取得できます。
- https://build.nvidia.com でキーを取得します
siftが参照できる場所にキーを保存します。
export SIFT_API_KEY=nvapi-... # or NVIDIA_API_KEY
# or, once and for good:
mkdir -p ~/.config/nvidia && echo 'nvapi-...' > ~/.config/nvidia/api_key
または、自分のモデルを使い、キーは使わない
sift の接続先を指定すると、代わりにそこへ問い合わせます。問い合わせの内容は変わりません。通常の OpenAI 形式でエンドポイントに問い合わせ、キーがない場合は Authorization ヘッダーを送信しません。
export SIFT_BASE_URL=http://localhost:11434/v1 # Ollama's own port
export SIFT_MODELS=the-model-you-pulled # whatever that server calls it
同じ2行で、llama.cpp(--api)、vLLM、LM Studio、LocalAI、社内ゲートウェイ、または POST /v1/chat/completions に対応するその他のエンドポイントを利用できます。SIFT_MODELS にはカンマ区切りで候補を並べ、順番に問い合わせます。
自分で入力したアドレスは、明示的な選択として扱われます。キーがないことを警告することはなく、MCP サーバーも処理を辞退しません。エンドポイントで必要な認証情報については、ご自身でそのエンドポイントと確認してください。
上記でモデル名を挙げていないのは意図的です。ここでは一覧ではなく、接続方式としてテストしています。テストスイートが確認するのは、自分のエンドポイントに問い合わせが行われ、キーなしで問い合わせられることです。どのローカルモデルが良い回答をするかは、ここでは測定していません。モデル名を挙げるのは、根拠のない推奨になってしまいます。実際に実行する出力を使い、ご自身の環境で確認するには、
test/budget.pyのコーパスをご利用ください。
どちらも使わない場合
2 種類の呼び出し元には、意図的に異なる応答をします。
- ターミナルでは、コマンド、バイト列、終了コード、3 つ目のルールも含めてすべて引き続き実行されます。大きく表示されるバナーで、これらの行を選んだモデルは存在しないことと、出力の末尾を見ていることが示されます。
- MCP 経由では、ツールは実行を断り、その理由を伝え、エージェントには代わりに自身のシェルを使うよう指示します。人間なら劣化した表示を見て判断できますが、モデルには、疑う理由のないフッターが付いた短いテキストが渡されます。確認できない読み手に対して、気付かれないまま悪化させることだけはしません。
モデルなしで実行したい場合は、SIFT_NO_MODEL=1 を指定してください。これは見落としではなく意思決定です。すべての機能が動作し、説教もありません。
コントリビュートと問題の報告
CONTRIBUTING.md に全体像があります。意図的に 2 つの言語を使うこと、曲げられない 3 つのルール、そして各ルールを破る変更について記載されています。SECURITY.md には、何が実行され、何がマシンの外部に送られるのか、問題を非公開でどこに報告すればよいのかが記されています。
どのように構築されたか
24 のフェーズを順に進め、各フェーズを完了してから次に着手しました。それぞれのフェーズで、決定した内容とその代償を notlar/ に記録しています。notlar/00-PLAN.md には全体の流れと、意図的に構築しなかったもの、その理由が記されています。
テストは test/ にあります。その隣には test/mutations.py があり、コードが従う各ルールを 1 つずつ、計 162 個の変更で破り、テストスイートがそれに気付くかどうかを確認します。テストスイートがすべて成功したということは、テストがこのバージョンのコードに異議を唱えなかったというだけであり、より悪いバージョンにも異議を唱えるとは限りません。
また、このテストバッテリーが中断された場合に何が起きるかについても記されています。実際に中断されたからです。ディスク上に残った破損によって通常のテスト実行をすべてすり抜け、誰かが気付くまでにマシンを 6 回停止させました。現在は test/conftest.py がそのうち 1 つを修復し、破られていたルールを二重に維持しています。そのため、どこであっても単一の編集によって sift stop が所有するすべてのものに対するシグナルに変わることはありません。これが notlar/19 です。
ライセンス
MIT。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。