mcpField
C
cds-db-nlquery-mcp

cds-db-nlquery-mcp

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

cds-db-nlquery-mcp について

SAP CAPのデータベースエンティティを自然言語で読み取り専用検索するMCPサーバーです。CDSモデルを自動検出し、関連を使ったCDSクエリを生成してデータベースで実行します。

主な機能

  1. 01CDSモデルの自動検出
  2. 02自然言語からクエリへの変換
  3. 03SQLで実行するCDS関連結合
  4. 04読み取り専用のSELECTクエリ
  5. 05エンティティと列のアクセス制限

活用シーン

  1. 01SQLを書かずに関連するCAPエンティティを検索
  2. 02複数の関連エンティティからレコードを取得
  3. 03許可したエンティティと列に検索を制限

MCP 対応

MCPクライアントは`natural_language_query`ツールに質問を渡します。サーバーはデータベースの行を返し、CDSのdbレイヤーエンティティを読み取り専用で検索できます。

設定

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

mcp.json · 共通設定
{
"mcpServers": {
"shahidla-cds-db-nlquery": {
"command": "npx",
"args": [
"-y",
"@shahid.la/cds-db-nlquery-mcp"
],
"env": {
"ANTHROPIC_API_KEY": "<ANTHROPIC_API_KEY>",
"OPENAI_API_KEY": "<OPENAI_API_KEY>",
"MCP_ALLOWED_ENTITIES": "<MCP_ALLOWED_ENTITIES>",
"MCP_BLOCKED_COLUMNS": "<MCP_BLOCKED_COLUMNS>"
}
}
}
}
起動コマンドnpx -y @shahid.la/cds-db-nlquery-mcp
設定が必要な環境変数
ANTHROPIC_API_KEYシークレットAnthropic API key (used if ANTHROPIC_API_KEY is set and OPENAI_API_KEY is not)
OPENAI_API_KEYシークレットOpenAI (or OpenAI-compatible) API key
MCP_ALLOWED_ENTITIESComma-separated entity allowlist, enforced on JOINs too
MCP_BLOCKED_COLUMNSComma-separated column blocklist, stripped before the query is built

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

パッケージ
npm@shahid.la/cds-db-nlquery-mcp0.7.3

ツール

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

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

詳細

@shahid.la/cds-db-nlquery-mcp

CDS db-layer エンティティ(db/schema.cds)に対する自然言語クエリ用の MCP サーバーです。

データベースについて、平易な英語で質問できます。サーバーがスキーマを自動的に検出し、CDS を使って実際の SQL JOIN を生成して、行を返します。クエリのハードコードも、SQL も、スキーマ設定も不要です。LLM はお好みのものをご利用ください。Anthropic や OpenAI のほか、OpenAI 互換のエンドポイント(Azure OpenAI、Groq、Ollama、ローカルモデルなど)に対応しています。

対象は db/ レイヤーであり、OData サービスではありません。 エンティティが OData サービスとして公開されている場合は、代わりにサービスレイヤーを対象とする MCP パッケージをご利用ください。

読み取り専用です。 このパッケージが実行するのは SELECT クエリのみです。INSERT、UPDATE、DELETE は実行しません。


前提条件

  • @sap/cds >= 7 を使用する CAP プロジェクト
  • CDS データベースが設定済みであること(HANA Cloud、SQLite、PostgreSQL)
  • MCP クライアント(Claude Code、Claude Desktop、または MCP 互換ホスト)
  • LLM プロバイダーの API キー(下記のLLM プロバイダーを参照)。質問をクエリに変換するために使用し、回答の生成には使用しません

クイックスタート

1. CAP プロジェクトにインストールします

npm install @shahid.la/cds-db-nlquery-mcp

2. CAP プロジェクトのルート(package.json と同じフォルダー)に .mcp.json を作成します

{
  "mcpServers": {
    "cds-db-nlquery": {
      "command": "npx",
      "args": ["-y", "@shahid.la/cds-db-nlquery-mcp"],
      "cwd": "/absolute/path/to/your/cap/project",
      "env": {
        "ANTHROPIC_API_KEY": "sk-ant-..."
      }
    }
  }
}

cwd には、db/schema.cds がある CAP プロジェクトのルートの絶対パスを設定してください。OpenAI、Gemini、または別のプロバイダーを使用する方法については、LLM プロバイダーを参照してください。

npx -y はサーバーを起動するたびにパッケージを再解決します(ダウンロード後は毎回ネットワークから取得するのではなく、npm のキャッシュから取得しますが、それでも追加の解決ステップが発生します)。すでに npm install @shahid.la/cds-db-nlquery-mcp を実行している場合は、代わりに "command" でインストール済みのバイナリー node_modules/.bin/cds-db-nlquery-mcp を直接指定すると、特に本番環境で、起動がより予測しやすくなります。

3. Claude Code でプロジェクトを開き、質問します

Which customers have a DTI ratio above 5?
Show me all open payments overdue by more than 30 days, include the borrower name.
List all loans in the MINING sector with the customer's current DTI ratio.

回答例: サーバーは、行を Markdown の表ではなく、フィールドと値を縦に並べたリストとして表示するようクライアントに指示します(実際の出力であり、稼働中のデプロイ環境で検証済みです)。

Found 3 customers with DTI above 5:

PARTNER    : 30100003
DTI_RATIO  : 7.20
BU_SORT1   : Domestic Customer AU 3

PARTNER    : 30100001
DTI_RATIO  : 5.80
BU_SORT1   : Domestic Customer AU 1

PARTNER    : 30100002
DTI_RATIO  : 5.40
BU_SORT1   : Domestic Customer AU 2

仕組み

質問すると、次のように動作します。

  1. MCP クライアント(例:Claude Code)が、質問を添えて natural_language_query ツールを呼び出します。
  2. MCP サーバーは起動時に CDS スキーマを読み込み済みです。エンティティ名、列、関連を含むこのスキーマと質問を、設定した LLM プロバイダー(Anthropic、OpenAI、または OpenAI 互換のエンドポイント)に送信し、構造化されたクエリ記述に変換します。
  3. サーバーは CDS クエリを 1 つ実行します。CDS の関連パス(customer.BU_SORT1)によって実際の SQL JOIN が生成され、JavaScript ではなくデータベース上で実行されます。本番環境のデータ量にも対応できます。
  4. 結果が MCP クライアントに返され、クライアントが回答を整形して表示します。

ステップ 2 の LLM 呼び出しは、計画を立てるための小規模で低コストな処理(質問を JSON に変換する処理)です。大規模または高価なモデルは必要ありません。高速で低コストなモデル階層をおすすめします。


設定

すべての設定は、.mcp.json の env ブロック内の環境変数で行います。

{
  "mcpServers": {
    "cds-db-nlquery": {
      "command": "npx",
      "args": ["-y", "@shahid.la/cds-db-nlquery-mcp"],
      "cwd": "/path/to/your/cap/project",
      "env": {
        "ANTHROPIC_API_KEY": "sk-ant-...",
        "MCP_ALLOWED_ENTITIES": "Customers,Orders,Products",
        "MCP_BLOCKED_COLUMNS": "PASSWORD,EMBEDDING,SSN",
        "MCP_MAX_ROWS": "100",
        "MCP_MODEL_PATH": "db"
      }
    }
  }
}
変数デフォルト説明
MCP_ALLOWED_ENTITIES(すべてのエンティティ)エンティティの短縮名(FQN の最後のドットより後の名前。例:my.app.Customers ではなく Customers)をカンマ区切りで指定します。開発中は未設定のままにし、本番環境では必ず設定してください。
MCP_BLOCKED_COLUMNS(なし)すべての結果から除外する列名をカンマ区切りで指定します。クエリの実行前に除外されます。EMBEDDING、PASSWORD、SSN のような列に便利です。
MCP_MAX_ROWS500クエリあたりの最大行数です。取得後のフィルターではなく、SQL の LIMIT として適用されます。
MCP_MODEL_PATHdbCDS モデルのフォルダーまたはファイルへのパスを cwd からの相対パスで指定します。スキーマが model/、srv/ などにある場合は変更してください。
MCP_DB_USER / MCP_DB_PASSWORD(利用側アプリ独自の DB ユーザー)アプリのランタイムユーザーとは異なる HANA ユーザーで接続します。ホスト、ポート、スキーマはそのまま再利用し、認証情報のみを上書きします。これが重要な理由については、セキュリティ → 本番環境を参照してください。
LLM プロバイダー

小規模な LLM 呼び出しによって、質問をクエリ記述子に変換します。プロバイダーはご自身で用意してください。env ブロックで以下のいずれかを設定します。

Anthropic(Claude):

"env": {
  "ANTHROPIC_API_KEY": "sk-ant-...",
  "ANTHROPIC_MODEL": "claude-haiku-4-5-20251001"
}

OpenAI、または OpenAI 互換プロバイダー:

"env": {
  "OPENAI_API_KEY": "sk-...",
  "OPENAI_MODEL": "gpt-4o-mini"
}

OPENAI_MODEL には任意のモデル名を指定できます。プロバイダーが要求する値を使用してください。別の OpenAI 互換エンドポイントを指定するには、OPENAI_BASE_URL を設定します。

プロバイダーOPENAI_BASE_URLOPENAI_MODEL の例
OpenAI(省略。デフォルトを使用)gpt-4o-mini
Google Geminihttps://generativelanguage.googleapis.com/v1beta/openai/gemini-2.0-flash
Groqhttps://api.groq.com/openai/v1llama-3.1-8b-instant
Mistralhttps://api.mistral.ai/v1mistral-small-latest
DeepSeekhttps://api.deepseek.comdeepseek-chat
xAI(Grok)https://api.x.ai/v1grok-2-latest
Azure OpenAIAzure のエンドポイントデプロイ名
Ollama(ローカル)http://localhost:11434/v1llama3.1(API キーには空でない任意の文字列を指定できます)
変数説明
LLM_PROVIDER"anthropic" または "openai"。設定されている API キーから自動検出されます。両方が設定されていて、いずれかを強制したい場合にのみ必要です。
ANTHROPIC_API_KEY / ANTHROPIC_MODELAnthropic ネイティブ API
OPENAI_API_KEY / OPENAI_MODEL / OPENAI_BASE_URLOpenAI または任意の OpenAI 互換エンドポイント

これは計画専用の呼び出し(質問から JSON 記述子への変換)です。高速で低コストな階層のモデルで十分であり、推奨されます。


セキュリティ

開発

サーバーは、default-env.json またはサービスバインディングで CDS に設定されている、プロジェクト既存のデータベース接続を使用します。追加の設定は不要です。

本番環境

重要: このパッケージは cds.run() を介してデータベースに直接クエリを実行します。CAP サービスレイヤーは経由しないため、CAP の @requires および @restrict アノテーションは適用されません。アクセス制御は、データベースと設定のレベルで全面的にお客様の責任となります。

ステップ 1:読み取り専用の専用データベースユーザーを作成する

-- Run as DBADMIN in SAP HANA Cloud Central
CREATE USER MCP_READER PASSWORD 'your-password';
GRANT SELECT ON SCHEMA YOUR_HDI_SCHEMA TO MCP_READER;

-- Revoke tables you do not want queryable
REVOKE SELECT ON "YOUR_HDI_SCHEMA"."your.AuditLog" FROM MCP_READER;
REVOKE SELECT ON "YOUR_HDI_SCHEMA"."your.RegulatoryDocuments" FROM MCP_READER;

このユーザーは読み取りのみ可能です。書き込みアクセス権はありません。このパッケージとは独立して、HANA 自体がこれを適用します。

ステップ 2:MCP_DB_USER/MCP_DB_PASSWORD を指定してサーバーの接続先を設定する

サーバーは、アプリ自身のデータベース接続を引き継ぐのではなく、これらの認証情報で接続します。ホスト、ポート、スキーマは自動的に再利用され、ユーザー名とパスワードのみが上書きされます。

"env": {
  "MCP_DB_USER": "MCP_READER",
  "MCP_DB_PASSWORD": "your-password"
}

これらを .mcp.json に直接記述するのではなく、別の Git の追跡対象外ファイルに保存しても問題ありません。サーバーは、ここにある他の変数と同様に process.env を読み取ります。

ステップ 3:MCP_ALLOWED_ENTITIES を設定する

"env": {
  "MCP_ALLOWED_ENTITIES": "Customers,Orders,Products"
}

これは、直接クエリするエンティティと、select/where のアソシエーション結合を介して到達するエンティティの両方に適用されます。たとえば、Customers が許可リストに含まれていない場合、Orders に対するクエリから customer.NAME の結合パスを通じて Customers のデータを読み取ることもできません。データベースユーザーは HANA レベルでアクセスを制限し、MCP_ALLOWED_ENTITIES はアプリケーションレベルで第 2 の層を追加します。両方を使用してください。


結合

サーバーはスキーマから CDS アソシエーションを読み取ります。LLM がクエリ内で customer.BU_SORT1 を参照すると、CDS は実際の SQL JOIN を生成し、データベースが実行します。

1 つのクエリで複数のアソシエーションを使用できます:

「MINING セクターのローンについて、借り手の名前と現在の DTI を表示してください」

1 回のデータベースアクセスで Loans → BusinessPartners → BCA_DTI を結合する、単一の SQL ステートメントが生成されます。

制約事項: 同じクエリ内で、異なる 2 つのエンティティから同じ列名を選択しないでください(例:メインエンティティと結合先エンティティの両方から LOAN_ID を選択する)。データベースが重複する列名を拒否します。Claude にはこれを避けるよう指示していますが、認識しておく価値があります。

2 つの列を相互に比較する

ほとんどのフィルターは列を固定値と比較します(DTI_RATIO > 5)。*「担保価値がローン金額を下回るローン」*のような質問では、代わりに 2 つの列を比較します。val の代わりに valCol を使用します:

{ "col": "collateral.VALUE", "op": "<", "valCol": "AMOUNT" }

どちらの側にもアソシエーションパスを指定できます。これは他のすべてと同じ結合メカニズムで処理されます。別のクエリや、JavaScript 側での比較は不要です。

コード値の人間向けの意味でフィルタリングする

列に @Common.Text の値ヘルプアソシエーションがある場合(コード値を参照)、基となるコードを推測するのではなく、テキストフィールドで直接フィルタリングします:

{ "col": "status.TEXT", "op": "like", "val": "Active" }

like は大文字と小文字を区別しません(データベースの照合順序に依存せず、両側に UPPER() を適用します)。そのため、値の大文字と小文字を正確に一致させる必要はありません。


列とエンティティのラベル

LLM に見えるのは CDS モデル内の情報のみです。つまり、列名、型、および付与したラベルです。通常のコードコメント(// customer name)は表示されません。ラベルがないと、モデルの能力にかかわらず、曖昧な列名から LLM が誤った推測をすることがあります。

@title:自動的に再利用され、追加作業は不要

スキーマに SAP 標準の @title アノテーションがすでにある場合(Fiori UI または OData サービスを備えたプロジェクトでは一般的です)、サーバーが自動的に取得します。何もする必要はありません:

entity Customers {
  key PARTNER  : String(10);
  @title: 'Customer Name'
  BU_SORT1     : String(40);
}
@NLP.label:曖昧さの解消に使用します。@titleでは対応できない場合に

UI に表示するラベルには書くべきでない内容を LLM に伝える必要がある場合に使用します。たとえば、「この列を X には使わないでください」などです。実際に発生した不具合として、BU_TYPEコード列(1=個人、2=組織)がありました。顧客の「名前」を尋ねる質問では、特に指示がなかったため、LLM がこの列を選んでいました。次のように修正しました。

entity Customers {
  key PARTNER  : String(10);
  @NLP.label: 'Partner type code: 1=person, 2=organisation. NOT a name, never use for name lookups'
  BU_TYPE      : String(2);
  @NLP.label: 'Customer / business partner full name, use this whenever a question asks for a name'
  BU_SORT1     : String(40);
}

この2つのラベルを追加したところ、最も安価なモデルのティア(claude-haiku-4-5)でも、毎回正しい列を選ぶようになりました。**不具合ごとにプロンプトを調整するのではなく、スキーマのレベルで曖昧さを解消してください。**この方法は永続的で、使用する LLM プロバイダーやモデルにかかわらず機能します。

**優先順位:**まず@NLP.labelを確認し、設定されていない場合は@titleにフォールバックし、どちらも設定されていない場合は列名またはエンティティ名をそのまま使用します。

その他の@NLPアノテーション
entity Customers @(NLP.label: 'Active borrowers, loan customers with income and sector data') {
  dti : Association to BCA_DTI on dti.PARTNER = PARTNER
        @NLP.joinType: 'LEFT';
}
アノテーション場所効果
@NLP.labelエンティティまたは列LLM に表示される説明です。@titleにフォールバックし、次に名前を使用します。
@NLP.joinTypeアソシエーション結合タイプを'LEFT'または'INNER'に上書きします。設定されていない場合は、カーディナリティから自動検出されます。
@NLP.aliasアソシエーションクエリで使用するアソシエーション名を上書きします

すべて任意です。いずれも設定しなくてもパッケージは動作します。ただし、名前だけでは誤解される可能性がある列(コード、略語、別の意味に見えるものなど)では、ラベルがあるかどうかで、LLM が推測するか、正しく理解するかが変わります。


コード値:STATUS = 'C'は何を意味するのか?

ラベルは「この列が何を意味するか」を解決します。これとは別に、「その列のコード値が何を意味するか」という問題があります。STATUS = 'C'。クローズ済み?キャンセル済み?確認済み?

LLM に伝える方法は2つあります。どちらを使うかは、コードをデプロイしなくても値のリストを変更できるかどうかによって決まります。

@Common.Text:業務で設定可能なコードに使用します(一般的なケース)

ほとんどのステータスコードやタイプコードは業務データです。運用担当者が次の四半期に新しい値を追加する可能性があり、そのたびにスキーマファイルを変更する必要はありません。SAP 標準の方法は@Common.Textを使い、アソシエーションを介して小さなルックアップ/チェックテーブルを参照することです。これは、Fiori elements が値選択ドロップダウンに使用するものと同じパターンです。

entity LoanStatusCodes {
  key CODE : String(1);
  TEXT     : String(20);
}

entity Loans {
  @Common.Text: status.TEXT
  STATUS : String(1);
  status : Association to LoanStatusCodes on status.CODE = STATUS;
}

{CODE: 'A', TEXT: 'Active'}、{CODE: 'C', TEXT: 'Closed'}を登録します。後から新しいステータスを追加する場合は、LoanStatusCodesにINSERTするだけで済みます。スキーマの変更も、再デプロイも不要です。サーバーは@Common.Textを検出し、アソシエーションパスを通じて読みやすい値を利用できることを LLM に伝えます。LLM は通常の結合処理を使って、その値をselectに含めます。こちらでコードを追加する必要はありません。このパッケージの他の箇所と同じ、アソシエーションパスを使ったJOINです。

CDS のenum:本当に将来にわたって固定される値のセットに限って使用します
STATUS : String(1) enum { active = 'A'; closed = 'C'; };

これはコンパイル時に決まるため、値を追加するにはschema.cdsを編集して再デプロイする必要があります。プログラムのロジックそのものに結びついた値(いずれにしてもコードの変更が必要な値)にのみ適しています。業務上の分類には適していません。最初は独自のデモのステータスフィールドにenumを使っていましたが、まさにこの理由から@Common.Textに戻しました。ここでは推奨ではなく、対比のために説明しています。

目安:デフォルトでは@Common.Textを選んでください。コード変更なしではリストが増えることは絶対にないと確信できる場合に限り、enumを使用してください。

列に@Common.Textが設定されている場合、クエリ結果には通常どおり未加工のコードが返されます。「C」の代わりに「Closed」を表示するなどの表示形式は、結合を通じて取得した読みやすい値を使い、最終的な回答を生成する LLM に委ねられます。


起動ログ

サーバーの起動時に、Claude Code の出力パネルを確認してください。

[cds-db-nlquery-mcp] Schema loaded — 12 entities
[cds-db-nlquery-mcp] WARNING: MCP_ALLOWED_ENTITIES not set — all entities are queryable. Set in .mcp.json for production.
[cds-db-nlquery-mcp] WARNING: MCP_DB_USER not set — using the project's default DB connection (likely the main app's full-access user). Set MCP_DB_USER/MCP_DB_PASSWORD to a restricted read-only user for production use.
[cds-db-nlquery-mcp] LLM provider: anthropic
[cds-db-nlquery-mcp] Ready (stdio)

起動時に 0 entities と表示されますか? .mcp.json の cwd が CAP プロジェクトのルートを指していることと、MCP_MODEL_PATH がスキーマフォルダー名と一致していることを確認してください。

WARNING: No LLM provider configured と表示されますか? .mcp.json の env ブロックで ANTHROPIC_API_KEY または OPENAI_API_KEY を設定してください。LLM プロバイダーを参照してください。

WARNING: MCP_DB_USER not set と表示されますか? 開発時には想定される動作で、サーバーは CAP アプリと同じデータベース接続を使用しています。本番環境で使用する前に、専用の読み取り専用ユーザーの作成についてセキュリティ → 本番環境を参照してください。


独自のデプロイ環境でテストする

npm install で得られるのは src/ と MCP サーバーだけです。公開されているパッケージはそれだけです。GitHub リポジトリには、実際の CDS スキーマ、シードデータ、期待される結果を検証済みの約 35 件のクエリ例、および独自のデータベースに対して実際にテストを実行するための 4 つのスクリプトを含む examples/capability-demo/ フォルダーもあります(説明を読むだけではありません)。1 つは内部関数に対してクエリ例を直接実行し、1 つは実際の LLM を介してエンドツーエンドで質問できるようにし、1 つはすべての質問例の自然言語テキストを実際の LLM に渡して結果を確認し、もう 1 つは src/mcp-server.js 自体を実際の子プロセスとして起動して、内部関数を直接呼び出すのではなく、Claude Code や Claude Desktop と同じ方法で実際の MCP stdio プロトコル経由で操作します。これらは npm install には含まれません。必要な場合はリポジトリをクローンしてください。詳細は、そのフォルダー内の README を参照してください。


リリースのテスト方法

npm test(ユニットテスト 126 件)はモックデータベースに対して実行されます。高速ですが、バックエンド固有の動作は検出できません(実際の HANA デプロイ環境では、モックが問題なく受け入れたものが何度も拒否されています)。リリースにタグを付ける前に、稼働中の HANA Cloud に接続された実際の CAP プロジェクトに対して npm run test:deployment を実行します。実際の認証情報と、デプロイ済みの examples/capability-demo/ スキーマが必要なため、npm test や CI の一部ではありませんが、任意ではなく必須の手動ステップです。examples/capability-demo/smoke-test-server.js も実行します。これ自体が src/mcp-server.js を起動し、内部関数を直接実行するのではなく、実際の LLM 呼び出しを伴う実際の MCP stdio プロトコルを介して操作します。

RELEASE_VERIFICATION.md は、その実際の記録であり、追記専用です。各リリースの前に取得してコミットされる、稼働中の BTP HANA に対する実行結果の実出力であり、単なる主張ではありません。


ライセンス

MIT

コメント

MCP は初めてですか?

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

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