Lambë について
Lambëは、データの形を事前に把握せずに構造化データを調べて変換できるクエリ言語とCLIです。パイプラインの各段階の形状や出力形式との互換性を解析し、スキーマ検査と対処方法を示すエラーを備えています。
主な機能
- 01JSON、YAML、TOML、HCL、CSV、TSV、Markdownのクエリ
- 02各パイプライン段階の形状表示
- 03出力形式との互換性検査と変換候補
- 04JSON Schemaによるデータ形状の検査
- 05対話型REPLとDartライブラリ
活用シーン
- 01未知のAPIレスポンスの調査
- 02Helm chartやCI設定の監査
- 03データスキーマに基づくクエリ検査
- 04形状情報を使ったAIエージェントのフィールド抽出
MCP 対応
Lambëには、クエリ機能を実行時ツールとして提供するMCPサーバーが含まれます。Agent Skillとしても提供されます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
Lambë
扱っているデータの構造を示してくれる、構造化データ向けクエリ言語です。
lam は JSON、YAML、TOML、HCL、CSV、TSV、Markdown をクエリできます。他のクエリツールと異なり、クエリの実行前に、そのクエリが何をするかを教えてくれます。各パイプ段階での形状、結果をシリアライズできる出力形式、問題が起きる箇所がわかります。
データの構造をまだ把握していない場合に便利です。見慣れない API レスポンスの調査、Helm チャートの監査、CI パイプラインの前提条件の検証、または構造を推測せずに何かを抽出するよう AI エージェントに依頼する場合に使えます。
$ lam --to toml '.dependencies | keys' pubspec.yaml
Error: TOML output requires a map at the root, got list<string>.
Try appending one of:
| as(toml) # Wraps the list under a single-entry map (equivalent to `{items: .}`).
$ lam --to toml '.dependencies | keys | as(toml)' pubspec.yaml
items = ["rumil", "rumil_parsers", "rumil_expressions"]
クエリには制限があり、必ず終了します。再帰もラムダも def もありません。これはトレードオフです。Lambë はプログラミング言語を目指していないため、形状推論、--explain、--schema、エラーの修正案がすべて機能します。
Lambë(「ラムベー」と発音)は、クウェンヤ語(トールキンのエルフ語)で「言語」を意味します。パッケージ名は ASCII 互換性のため lambe です。
インストール
1 行のインストーラー(Linux と macOS 対応、sudo 不要、SHA256 チェックサムを検証):
curl -fsSL https://raw.githubusercontent.com/hakimjonas/lambe/main/install.sh | sh
最新の GitHub リリースから lam と lam-mcp をダウンロードし、~/.local/bin/ にインストールします。環境変数 LAMBE_VERSION(バージョンを固定)と LAMBE_PREFIX(インストール先を変更)に対応しています。詳細はスクリプトを参照してください。
その他の方法:
# From pub.dev (Dart users)
dart pub global activate lambe
# Dart library
dart pub add lambe
# Build from source
git clone https://github.com/hakimjonas/lambe.git && cd lambe
dart compile exe bin/lam.dart -o lam
すべてのインストール方法については、はじめにを参照してください。
形状を考慮した出力
Lambë は、クエリ結果の形状を、対象の形式でシリアライズ可能な形状と照合します。一致する場合は出力を生成します。一致しない場合は、必要な形状をエラーに示し、それを実現するクエリの断片を一覧表示します。対話型ターミナルでは、選択した断片を適用してその場で再試行するよう提案します。
$ lam --to toml '.name' pubspec.yaml
TOML output requires a map at the root, got string.
Try appending one of:
| as(toml) # Wraps the scalar under a single-entry map (equivalent to `{value: .}`).
Apply a bridge?
[1] | as(toml) # Wraps the scalar under a single-entry map (equivalent to `{value: .}`).
[q] cancel
> 1
value = "rumil"
同じ流れは CSV と TSV(ルートにレコードのリストが必要)および HCL(マップが必要)にも適用されます。
提案には、意図を表す as(<format>) 形式が表示されます。説明には、そのブリッジを構成する生の断片({value: .}、to_entries など)が示されるため、--explain と手動での組み合わせも引き続き利用できます。
CSV/TSV のスカラー以外のセル
デフォルトでは、CSV/TSV のセル内にネストされたリストやマップがあると拒否されます。これらを忠実に表現できる区切り形式がないためです。手早くエクスポートしたい場合や、情報の損失を許容できる場合は、--flatten-cells json(CLI)または :flatten-cells json(REPL)を渡して、インラインで JSON 文字列としてエンコードできます。生成されたファイルを Lambë に読み戻しても、元の構造は復元されません。忠実性が重要な場合は、クエリ側でデータの形状を変更してください。
as(fmt) — クエリ言語内でのブリッジ
対象形式の形状があらかじめわかっている場合、as(fmt) を使ってクエリ内でブリッジできます。入力がすでに対象の形状を満たしていれば、このコンビネーターは何もせず、適用できるブリッジが 1 つだけある場合はそれを適用し、複数ある場合は候補を一覧表示します。
$ lam --to toml '.dependencies | as(toml)' pubspec.yaml
rumil = "^0.6.0"
rumil_parsers = "^0.6.0"
rumil_expressions = "^0.6.0"
$ lam --to csv '.dependencies | as(csv)' pubspec.yaml
key,value
rumil,^0.6.0
rumil_parsers,^0.6.0
rumil_expressions,^0.6.0
as は json、yaml、toml、csv、tsv、hcl を受け付けます。
--explain — すべてのパイプ段階の形状を確認
--explain はクエリのパイプ部分をたどり、各段階の形状と、最後に最終形状をシリアライズできる出力形式の一覧を表示します。静的解析のみを行い、クエリは評価しません。実際の形状情報を初期値として使用するにはデータファイルを渡し、未知の入力を対象に追跡するには省略してください。
$ lam --explain '.dependencies | keys' pubspec.yaml
.dependencies : map<rumil: string, rumil_parsers: string, rumil_expressions: string>
| keys : list<string>
Writable as: json, yaml, csv, tsv
Not writable as: toml, hcl
--explain はデフォルトで、確実に空となるフィルター(既知の形状での filter(.missing))と、実行時に拒否される形状の不一致(リスト以外の入力に対する filter)を警告します。--explain-trivial を渡すと、引数が存在しないフィールドを参照する sort_by/group_by/map/unique_by についても警告します(誤字の場合もあれば、意図的な場合もあります)。エージェント向けツールやビルドパイプラインでは、--explain-json を指定すると、同じ情報を構造化された JSON ドキュメントとして出力します。
--schema — 形状を宣言して、Lambë に作業をチェックさせる
API コントラクト、OpenAPI 仕様、または手書きのドキュメントなど、データ用の JSON Schema がある場合は、--schema にそのパスを指定します。
$ lam --schema api.schema.json --explain '.users | map(.email)' response.json
.users : list<map<id: string, name: string, email: optional<string>>>
| map(.email) : list<optional<string>>
Writable as: json, yaml, csv, tsv
Not writable as: toml, hcl
スキーマを使うと、データだけでは表現できない情報を補えます。たとえば、オプションのフィールド(JSON Schema の required に基づく)、空のリストの要素の形状、サンプリングから shapeOf が推論できなかった型です。--explain でそれらを確認でき、評価器はその情報を信頼します。
データがある場合、Lambë はさらに検証も行います。たとえば、age: number とするスキーマに対し、データの age: "30" があると、読み込み時に JSON パス付きの診断を表示して終了コード 1 で終了します。気付かないうちにデータがずれたり、コントラクトに適合しないデータに対してクエリを実行したりすることはありません。
<datafile>.schema.json という名前の同階層のファイルも自動検出されるため、スキーマをデータの隣に配置するプロジェクトの規約であれば、明示的なフラグは不要です。
逆方向も同様です。lam --print-shape data.json を実行すると、推論された形状が JSON Schema ドキュメントとして出力されます。往復の例:
lam --print-shape data.json > data.schema.json # bootstrap a schema from data
lam --schema data.schema.json '.users' data.json # use it back
受け付ける JSON Schema キーワードは、type、properties、items、required です。値レベルの制約(minimum、pattern、enum など)、構造コンビネーター(allOf、oneOf)、$ref、条件付きスキーマは、キーワードごとのエラーを表示して拒否されます。Lambë は形状システムであり、検証エンジンではありません。より高度な検証には、ajv または check-jsonschema をお使いください。
クエリ構文
クエリは .(現在のデータ)から始まり、| で操作を連結します。
. the whole document
.name access a field
.users[0] index into a list
.users[0].address.city chain access
.users | filter(.age > 30) pipe into an operation
.users | map(.name) transform each element
パイプラインは左から右に読みます。各 | は、結果を次の操作に渡します。
.users | filter(.active) | sort_by(.name) | map(.name)
この例では、.users を取得し、アクティブなユーザーを残して名前で並べ替え、名前を抽出します。
式
.price * .qty arithmetic (+, -, *, /, %)
.age > 30 comparison (<, >, <=, >=, ==, !=)
.active && .verified logic (&&, ||, !)
if .age > 65 then "senior" else "active" conditional
{name, total: .price * .qty} construct a new object
"\(.name) is \(.age)" string interpolation
.[1:3] slice a list or string
操作
操作は | の後に続き、パイプで渡された値を変換します。
. | filter(.age > 30) keep matching elements
. | map(.name) transform each element
. | sort_by(.age) sort by a key
. | group_by(.dept) group into [{key, values}]
. | length count elements
. | first first element
. | sum sum numbers
. | keys map keys or list indices
. | has("field") check if a field exists
. | unique remove duplicates
. | flatten flatten one level of nesting
. | to_entries map to [{key, value}] pairs
. | filter_values(. > 5) filter a map's values
. | as(toml) bridge to an output format
全リストについては、以下のパイプライン操作を参照してください。
CLI
# Extract values
lam '.database.host' config.toml
lam '.spec.containers[0].image' deployment.yaml
# Filter and transform
lam '.users | filter(.age > 30) | map(.name)' data.json
# Aggregate
lam '.items | map(.price) | sum' data.json
# Sort and pick
lam '.items | sort_by(.price) | first' data.json
# Object construction
lam '.users | map({name, senior: .age > 65})' data.json
# String interpolation
lam '.users | map("\(.name) is \(.age)")' data.json
# Shape trace
lam --explain '.users | map(.name)' data.json
# Shape inspection (JSON Schema output)
lam --print-shape data.json
# Schema-checked queries: validate data against a schema as it runs
lam --schema api.schema.json '.users | map(.email)' response.json
# CI validation
lam --assert '.version != "0.0.0"' package.json
lam --assert '.replicas >= 2' deployment.yaml
# Format conversion
lam --to yaml '.config' data.json
lam --to csv '.users | map({name, age})' data.json
lam --to toml '.config | as(toml)' data.json
lam --to csv --flatten-cells json '.users' data.json # encode nested cells as JSON
# Line-delimited JSON (logs, event streams)
lam --ndjson '.user.id' events.ndjson
tail -f app.log | lam --ndjson '.level'
# Query any format (auto-detected from extension)
lam '. | filter(.status != "closed")' issues.csv
lam '.resource | map(._labels)' main.tf
lam '.children | filter(.type == "heading") | map(.children[0].text)' README.md
# Pipe from stdin
curl -s https://api.example.com/users | lam '.results | filter(.active)'
対話型 REPL
lam -i data.json
lambe v0.14.0 - type :help for commands, :q to quit
Data loaded: {3 fields, 42 users}
lambe> .users | filter(.age > 30) | map(.name)
["Bob", "Carol"]
lambe> .users[0]
{name: "Alice", age: 25, active: true}
lambe> :schema
{users: [{name: "string", age: "number", active: "boolean"}]}
lambe> :to yaml
Output format: yaml
クエリの結果を現在の出力形式でシリアライズできない場合、REPL は利用可能なブリッジをインラインで一覧表示します。候補の番号を押すと、そのブリッジが適用され、変換後の出力が表示されます。フィールド名(.us<TAB>)とパイプライン操作(| fil<TAB>)のタブ補完に対応しています。REPL は構文ハイライト、永続的な履歴(~/.lambe_history)、Ctrl+R による逆方向検索、\ による継続入力を使った複数行入力にも対応しています。
ライブラリ
import 'package:lambe/lambe.dart';
// Query pre-parsed data
final name = query('.users[0].name', data);
// Query a JSON string
final version = queryJson('.version', '{"version": "1.0.0"}');
// Query any format
final host = queryString('.database.host', tomlString, format: Format.toml);
// Parse once, evaluate many times
final ast = parseAst('.users | filter(.active) | map(.name)');
final result1 = evaluateAst(ast, dataset1);
final result2 = evaluateAst(ast, dataset2);
// Format conversion
final yaml = formatOutput(data, OutputFormat.yaml);
final csv = formatOutput(users, OutputFormat.csv);
// Shape inference and JSON Schema output
final shape = shapeOf(data); // Shape ADT
final schemaJson = renderJsonSchema(shape); // JSON Schema text
// Or parse a schema file and merge with observed data
final schema = parseJsonSchema(schemaSource);
final merged = mergeSchemaWithData(schema, shape); // throws on disagreement
形状とブリッジの API
// Infer the structural shape of a value
final shape = shapeOf(data);
// e.g. SMap({'users': SList(SMap({'name': SString(), 'age': SNum()}))})
// Check whether a value can be written in a given format
final report = canWriteAs(result, OutputFormat.toml);
switch (report) {
case Writable():
stdout.writeln(formatOutput(result, OutputFormat.toml));
case NotWritable(:final suggestions):
for (final r in suggestions) {
print('${r.label}: | ${r.display} — ${r.explanation}');
}
}
// Compose a user query with a bridge fragment
final bridges = synthesize(shape, OutputFormat.csv);
if (bridges.isNotEmpty) {
final composed = applyBridge(userAst, bridges.first);
final bridged = evaluateAst(composed, data);
}
// Static shape trace
final trace = explain(parseAst('.users | map(.name)'), shapeOf(data));
for (final stage in trace.stages) {
print('${stage.source}: ${renderShape(stage.shape)}');
}
対応形式
| 形式 | 入力 | 出力 | 適合性 |
|---|---|---|---|
| JSON | はい | はい | RFC 8259(318/318) |
| YAML | はい | はい | YAML 1.2.2(333/333) |
| TOML | はい | はい | TOML 1.1(681/681) |
| HCL/Terraform | はい | はい | HashiCorp 仕様(2760/2760) |
| CSV | はい | はい | RFC 4180 + 自動方言検出 |
| TSV | はい | はい | CSV のタブ区切り形式 |
| Markdown | はい | — | CommonMark 0.31.2(652/652) |
rumil_parsers のパーサーを使用し、公式仕様テストスイートでテストしています。
このリリースでは、Markdown は入力専用です。Markdown AST はデータ構造ではなくプレゼンテーションツリーであるため、任意のクエリ結果を Markdown テキストに戻す汎用的なマッピングはありません。Markdown ドキュメントの射影(見出しのリスト、件数、フィルタリングされたセクション)は、通常の --to フラグを使って JSON、YAML、CSV、または TSV として出力できます。
パイプライン操作
| 操作 | 例 | 説明 |
|---|---|---|
filter | .users | filter(.active) | 条件に一致する要素を保持します |
map | .users | map(.name) | 各要素を変換します |
sort | . | sort | 自然順で並べ替えます |
sort_by | .users | sort_by(.age) | キーで並べ替えます |
group_by | .users | group_by(.dept) | {key, values} にグループ化します |
unique | . | unique | 重複を削除します |
unique_by | .users | unique_by(.id) | キーに基づいて重複を削除します |
flatten | . | flatten | 1 階層を平坦化します |
reverse | . | reverse | 順序を逆にします |
keys | . | keys | マップのキーまたはリストのインデックスを取得します |
values | . | values | マップの値を取得します |
length | . | length | リスト、マップ、または文字列の長さを取得します |
first | . | first | 最初の要素を取得します |
last | . | last | 最後の要素を取得します |
sum | . | sum | 数値の合計を計算します |
avg | . | avg | 平均を計算します |
min | . | min | 最小値を取得します |
max | . | max | 最大値を取得します |
has | . | has("name") | フィールドの存在を確認します |
to_entries | . | to_entries | マップを [{key, value}] に変換します |
from_entries | . | from_entries | [{key, value}] をマップに変換します |
to_number | .price | to_number | 文字列を数値として解析します |
type | . | type | 実行時の型を文字列として取得します |
filter_values | . | filter_values(. > 5) | マップの値をフィルタリングします |
map_values | . | map_values(. * 2) | マップの値を変換します |
filter_keys | . | filter_keys(. != "secret") | マップのキーをフィルタリングします |
as | . | as(toml) | 出力形式の構造に合わせて変換します |
AI 統合
Lambë は、エージェントスキル(エージェントのセッションに専門知識として読み込まれます)と、MCP サーバー(実行時にツールとして呼び出せます)の両方として提供されています。
エージェントスキル
スキルフォルダーはこのリポジトリの .agents/skills/lambe/ にあり、Claude Code、OpenAI Codex、GitHub Copilot、Cursor、Microsoft Agent Framework がすべて読み込む、ベンダー横断の agent-skills 仕様に準拠しています。
別のプロジェクトのエージェントで Lambë を利用できるようにするには、フォルダーをエージェント標準の場所にコピーします。
# Personal (available across all your projects)
git clone https://github.com/hakimjonas/lambe /tmp/lambe-skill
mkdir -p ~/.agents/skills
cp -r /tmp/lambe-skill/.agents/skills/lambe ~/.agents/skills/
# Project-local
cp -r /tmp/lambe-skill/.agents/skills/lambe <your-project>/.agents/skills/
仕様に準拠したエージェントは、セッション開始時にスキルを自動検出します。
MCP サーバー
インストールしてから、プロジェクトに .mcp.json を追加します。
{
"mcpServers": {
"lambe": {
"command": "lam-mcp",
"args": []
}
}
}
これにより、フィードバックループ全体をカバーする 5 つのツールを AI アシスタントが利用できるようになります。
lambe_query— 抽出、フィルタリング、変換を行います。省略可能なschemaパラメーターを指定すると、クエリの実行前にデータの構造を検証します。lambe_print_shape— 不慣れなデータを調べます。JSON Schema サブセットのドキュメントを返します。lambe_check— JSON Schema に照らしてデータを検証します。{"ok": true}または{"ok": false, "error": "..."}を返し、不一致が生じたパスを示します。lambe_explain— クエリを静的にトレースします(データの有無は問いません)。各段階の形状、警告、書き込み可能性を含む構造化 JSON レポートを返します。lambe_assert— クエリ結果に対する真偽値のアサーションを行います。
lambe_query で要求された出力形式との形状の不一致が発生すると、エラーレスポンスには構造化された suggestions 配列が含まれます。各エントリーには、template_text、テンプレートを元の式に追加して作成した完全なクエリである apply_as、および 1 行の explanation が含まれます。エージェントは apply_as をそのまま使って、ツールを再度呼び出せます。
AI コーディングエージェント向け
プロジェクトのルートに AGENTS.md と .mcp.json を追加します。プロジェクトを開いた AI アシスタントは、データクエリに Lambë を検出して使用します。
CI での利用
# Validate config in GitHub Actions
- run: |
dart pub global activate lambe
lam --assert '.version != "0.0.0"' pubspec.yaml
lam --assert '.jobs | keys | length > 0' .github/workflows/ci.yml
テストマッチャー
lambe_test パッケージは、Dart 用のテストマッチャーを提供します。
import 'package:lambe_test/lambe_test.dart';
expect(response, lamWhere('.errors | length == 0'));
expect(config, lamEquals('.database.port', 5432));
expect(data, lamMatches('.name', startsWith('A')));
expect(data, lamHas('.users[0].address.city'));
ドキュメント
- はじめに - インストールと最初のクエリ
- 構文リファレンス - クエリ言語の全仕様
- REPL ガイド - 対話モード、コマンド、キーボードショートカット
- スキーマガイド - JSON Schema のサブセット、マージのセマンティクス、往復変換
- レシピ - Kubernetes、Terraform、CI、CSV の実例パターン
- マニュアルページ - Unix のマニュアルページ(
man -l doc/lam.1)
lambë ではないもの
Lambë は、JSON 形式のデータを対象とする、範囲を限定したツリー変換ツールです。
チューリング完全性、ユーザー定義関数、再帰下降(..)、try/catch、正規表現、ストリーミング、インプレース変更は意図的に含めていません。
範囲を限定することで、形状推論、--explain、as(fmt) によるブリッジが可能になります。
省略した機能の全リストと、それぞれに代わる lambë のイディオムについては、doc/non-goals.md をご覧ください。
設計
アーキテクチャと設計上の決定については、DESIGN.md をご覧ください。
Arda エコシステムの一部
左再帰文法をサポートするパーサーコンビネーターの Rumil を基盤としています。
- Rumil - 左再帰に対応したパーサーコンビネーター
- Rumil Parsers - JSON、YAML、TOML、XML、CSV、HCL、Proto3、Markdown 用のフォーマットパーサー
- Rumil Expressions - 共通の評価ヘルパー
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。