mcpField
W
Warehouse MCP

Warehouse MCP

ローカル
github.com/tantn89データベース01 週間前に更新

Warehouse MCP について

Warehouse MCP は、DuckDB、Postgres、BigQuery を Agent が読み取り専用で照会するための MCP サーバーです。業務指標の検索、ロール別の行ポリシー、データソース内での仮名化、SQL 制御、監査ログを備えます。

主な機能

  1. 01DuckDB、Postgres、BigQueryへの読み取り専用アクセス
  2. 02業務指標の検索と制御付きSQL
  3. 03ロール別の行フィルタリング
  4. 04データソース内での個人情報の仮名化
  5. 05スキャン上限と結果のマスキング
  6. 06値を記録しない監査ログ

活用シーン

  1. 01データウェアハウスへの業務質問
  2. 02パートナーへの行フィルター付きデータ提供
  3. 03氏名を露出せず個人データの指標を分析
  4. 04高コストなクエリの制限とアクセス履歴の確認

MCP 対応

データセットの一覧・説明、指標クエリ、制御付き SQL 実行を含む4つの読み取り専用ツールを MCP クライアントに公開します。stdio と、ロールに対応した bearer key による HTTP 接続をサポートします。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"tantn89-warehouse": {
"command": "uvx",
"args": [
"warehouse-mcp"
],
"env": {
"WAREHOUSE_MCP_POSTGRES_DSN": "<WAREHOUSE_MCP_POSTGRES_DSN>",
"WAREHOUSE_MCP_PII_SALT": "<WAREHOUSE_MCP_PII_SALT>",
"WAREHOUSE_MCP_AUDIT_LOG": "<WAREHOUSE_MCP_AUDIT_LOG>"
}
}
}
}
起動コマンドuvx warehouse-mcp
設定が必要な環境変数
WAREHOUSE_MCP_POSTGRES_DSNシークレットPostgres connection string of a read-only role (Postgres configs only).
WAREHOUSE_MCP_PII_SALTシークレットFixed salt for stable pseudonyms across restarts (16-128 letters or digits).
WAREHOUSE_MCP_AUDIT_LOGPath of the JSONL audit log; stderr when unset.

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
pypiwarehouse-mcp0.1.0

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

warehouse-mcp

ウェアハウスを渡さずに、AI エージェントにウェアハウスを使わせましょう。

Claude、Cursor、または任意の MCP クライアントが、ガバナンスの効いた読み取り専用レイヤーを通じて DuckDB、Postgres、BigQuery をクエリできる MCP サーバーです。

  • エージェントがビジネス用語で質問できるセマンティックモデル。
  • エージェントに書き込み、ファイルの読み取り、許可範囲外へのアクセスを言いくるめられない SQL ガード。
  • ロールごとの行ポリシー。
  • 個人データの仮名化。
  • 監査ログ。
  • コスト上限。

以下のすべての主張は、テストまたは実測結果によって裏付けられています。

記事: SQL ガードをすり抜けた別名:ウェアハウスアクセス向け MCP サーバーのレッドチームテスト

ci pypi mcp registry python mcp license

確認項目結果根拠
104 件の回避試行(データ持ち出し、行フィルターの回避、PII の探索、ファイルおよびシステムへのアクセス、ステートメントの偽装、リソースの悪用)DuckDB と Postgres の両方で漏えい 0 件tests/test_red_team.py、CI で実行
3 つのエンジンで同じ 15 件のクエリを実行DuckDB、Postgres、BigQuery で結果が一致同等性テスト、BigQuery の実行結果
SQL ガードを回避した書き込みエンジン自体が拒否:読み取り専用トランザクション、その後に権限チェックtest_postgres_parity.py
複数テラバイトのテーブルに対する不用意なエージェントのクエリ課金前に拒否:「4.65 GB をスキャンする見込みで、1.00 GB の上限を超えています」BigQuery の実行結果
MCP 経由で回答する Claude Haiku 5.5 エージェントを、SQL を正解データとして評価(26 ケース、各 3 試行)78/78 件が正解。ガバナンス関連の 6 ケースを含み、質問 1 件あたり $0.0016、p95 は 6.3 秒。regressgate によるゲート管理evals/cases.jsonl

1 行で試す

同梱のデモでは、realtime-lakehouse が取得した、約 10 万件のリアルタイム Wikipedia 編集データをローカルの DuckDB ファイルから提供します。 キーは不要です。

{
  "mcpServers": {
    "warehouse": { "command": "uvx", "args": ["warehouse-mcp", "--demo"] }
  }
}

これを Claude Desktop の設定に追加するか、claude mcp add warehouse -- uvx warehouse-mcp --demo を実行してください。 次に、*「ボットの活動が最も多かった Wiki はどこで、それぞれの編集者のユニーク数はいくつでしたか?」*と尋ねてください。

デモでの実際のツール呼び出しから取得した、エージェントに見える内容:

warehouse_query_metrics(dataset="edits", metrics=["edits","editors","bot_share"], dimensions=["wiki"])
  wiki          edits   editors  bot_share
  enwiktionary  41594   46       0.994
  commonswiki   16053   224      0.849
  enwiki         9576   1401     0.489

warehouse_run_sql("SELECT editor, count(*) AS edits FROM edits GROUP BY editor ORDER BY edits DESC")
  pii_6b19c4a15ccf  41335        <- a pseudonym: counts and joins are exact, the name never leaves
  pii_6a8d5fd6c44b   9638

warehouse_run_sql("SELECT * FROM edits; DROP TABLE edits")
  Error: Send exactly one statement; multiple statements are not allowed.
warehouse_run_sql("SELECT pg_read_file('/etc/passwd') FROM edits")
  Error: Function pg_read_file() is not allowed. Use standard SQL functions (...)

仕組み

flowchart LR
    A[Agent] -->|MCP: stdio, or HTTP + bearer key| T[4 read-only tools]
    T --> R[Role<br/>from the key]
    R --> G[SQL guard<br/>sqlglot, backend dialect]
    G -->|rewrite each dataset into| P[Policy subquery<br/>declared columns only<br/>salted pseudonyms<br/>role row filter]
    P --> B[(Backend safeguards<br/>DuckDB: read-only, no file access<br/>Postgres: read-only txn + grants<br/>BigQuery: dry run + byte cap)]
    B --> X[Cell cap + email/phone redaction]
    X --> L[Audit log<br/>no values, no salt]
    L --> A

出力ではなく、データソースで仮名化します。 ガードは、個人情報を含む列をソルト付きハッシュに置き換えるサブクエリに、すべてのデータセット参照を書き換えます。この処理はデータウェアハウス内で行われます。その結果:

  • count(DISTINCT editor) は正確なままです;
  • 仮名を使った結合はデータセット間で機能します;
  • WHERE editor = 'Alice' のようなプローブは、比較対象がハッシュであるため何も一致しません;
  • 名前がネットワーク経由で外部に送信されることはありません。

系譜分析の後に出力をマスキングする方法は、エイリアス、式、関数を使って回避できます。ここでは、元の列にアクセスすること自体ができません。

各レイヤーは、前のレイヤーが失敗しても機能するように設計されています。 最初のレイヤーはガードです:

  • 単一ステートメントのみ;
  • SELECT のみ;
  • 宣言済みデータセットのみ;
  • 関数の許可リスト;
  • CTE 参照はスコープごとに解決されるため、WITH t AS (SELECT * FROM t) で実テーブル t にアクセスすることはできません;
  • LIMIT に上限を設定。

その後は、各エンジンが個別にロックダウンされており、ガードを迂回した状態でテストを行い、それを検証しています。

ツール:

ツール用途
warehouse_list_datasets呼び出し元のロールがクエリできるデータセットと、各データセットの粒度およびメトリクス
warehouse_describe_dataset列、仮名化される列、行ポリシー、サンプル行 3 件
warehouse_query_metrics推奨される方法です。 ディメンションと時間バケットで指定する名前付きメトリクスです。SQL は構文木として構築されるため、フィルター値が SQL になることはありません
warehouse_run_sqlメトリクスでは表現できない質問に対する、ガード付き SELECT 1 件

レッドチームが見つけた問題

敵対的テストスイートにより、SQL ガード以外で実際に 3 件の問題が見つかりました。いずれも修正され、回帰テストとして維持されています:

  1. タイムアウト後の ReDoS。 メールアドレスのマスキングに使う正規表現が二次的にバックトラックし、repeat('x', 1000000) から生成された 1 MB の文字列によって、サーバーが約 12 分間処理を続ける可能性がありました。これはデータウェアハウスのタイムアウト後に実行されていたため、処理を止めるものがありませんでした。正規表現の繰り返しに上限を設け、マスキング前にセルを切り詰めることで修正しました。
  2. 巨大な行が 1 件だけの場合、結果サイズの上限を回避できました。 行が収まるまで、最も長いテキストセルを半分にすることで修正しました。
  3. 仮名から名前を特定できました。 自分の User: ページを編集すると、その仮名と、名前を含むタイトルが結び付いてしまいます。この方法により、問題が見つかったスナップショットでは編集者の 5.7% が再特定されました。ユーザーページのタイトルに条件付き PII(pii_when: "namespace IN (2, 3)")を追加し、編集要約を仮名化することで修正しました。

続いて、2 つ目のモデルによる独立レビューで、テストスイートが見逃していた問題がガード内に 1 件見つかりました:

  1. エイリアスによって実テーブルを CTE に見せかけることができました。 WITH x AS (...) SELECT * FROM edits_raw x, x では、ガードがエイリアスで CTE 参照を照合していたため、物理テーブルにポリシーの書き換えが適用されませんでした。現在は、CTE 参照を各スコープ内でテーブル名に基づいて解決し、実テーブルに表示中の CTE 名をエイリアスとして付けることを拒否します。
  2. エンジンのエラーにデータが含まれていました。 Could not convert string '<value>' のようなメッセージは、結果のマスキングを迂回し、エージェントと監査ログの両方に届いていました。エラーメッセージから引用符で囲まれた値を取り除き、マスキングして長さを制限するようになりました。

BigQuery でパリティケースを実行したところ、漏えいではなく正確性に関するバグがもう 1 件見つかりました。時間リテラルが TIMESTAMP 列に対して DATETIME にキャストされていました。これは修正済みです。

独自のデータウェアハウスを設定する

extends: demo:wikipedia            # or write datasets/roles from scratch
connection: {kind: postgres, dsn_env: WAREHOUSE_MCP_POSTGRES_DSN}

roles:
  - {name: analyst, datasets: "*", api_key_env: KEY_ANALYST}
  - name: enwiki_partner             # an external partner sees one wiki only
    datasets: [edits, edits_per_minute]
    row_filters: {edits: "wiki = 'enwiki'", edits_per_minute: "wiki = 'enwiki'"}
    api_key_env: KEY_PARTNER
  - {name: trust_and_safety, datasets: "*", pii_clearance: true, api_key_env: KEY_TS}

データセットでは、列、それらのうち pii または pii_when に該当する列、free_text に該当する列、および名前付きメトリクスを宣言します。宣言されていない列は、エージェントからは存在しないものとして扱われます。HTTP 経由(warehouse-mcp --config my.yaml --transport http)では、各ベアラーキーによってロールが選択されます。キーは環境変数から取得され、設定ファイルにシークレットは含まれません。

各バックエンドのセットアップ、Claude Desktop の設定例、評価ワークフローについては、docs/deployment-guide.md をご覧ください。会社で導入する際の手順(データの把握、アクセスモデル、段階的な展開、成功指標、インシデント対応手順書)については、docs/deployment-playbook.md をご覧ください。

開発

pip install -e ".[dev]"
pytest                                    # DuckDB; Postgres/BigQuery suites skip without a DSN
docker compose up -d && python scripts/load_snapshot.py --target postgres
WAREHOUSE_MCP_POSTGRES_DSN=postgresql://mcp_reader:[email protected]:55432/warehouse pytest

アーキテクチャと脅威モデル · コードベースの概要 · ロードマップ

制限事項

  • 電話番号のマスキングは、free_text タグが付いた列に適用されます。comment AS c のようなエイリアスを使うと電話番号のマスキングは適用されなくなりますが、メールアドレスのマスキングは全体に適用されるため、そのまま適用されます。
  • HTTP では静的なベアラーキーと平文の HTTP を使用します。前段に TLS を配置してください。OAuth リソースサーバーモードはロードマップにあります。
  • Snowflake はロードマップにあります。

データ

デモデータは Wikimedia の公開最近の変更ストリームに由来し、ライセンスは CC BY-SA 4.0 です。 コードは MIT ライセンスです。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。