d365fo-connector について
d365fo-connectorは、AIアシスタントがMicrosoft Dynamics 365 Finance & Operationsのメタデータ探索やデータ照会を行うためのMCPサーバーです。複数環境に接続でき、本番環境は読み取り専用に制限されます。
主な機能
- 01複数のD365環境への接続
- 02エンティティとスキーマの探索
- 03データの照会・集計・分析
- 04クエリのエクスポートと一括実行
- 05非本番環境でのレコード操作
- 06本番環境の読み取り専用化
活用シーン
- 01エンティティの項目や関係の確認
- 02売上や顧客データの分析
- 03環境間のスキーマ比較
- 04開発環境やUATでのレコード管理
MCP 対応
MCPクライアントにD365のメタデータリソースとデータアクセス用ツールを提供します。照会と分析は各環境で可能で、書き込み操作は非本番環境に限られます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
npx -y @zhound/d365fo-mcp-serverD365_TENANT_IDAzure AD tenant IDD365_CLIENT_IDApp registration client IDD365_CLIENT_SECRETシークレットApp registration client secretD365_ENVIRONMENT_URLD365 F&O environment URL, e.g. https://contoso.operations.dynamics.comローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
22掲載時のツール一覧 · 2 週間前に更新
このツールにパラメーターはありません。
詳細
D365 Finance & Operations MCP サーバー
Microsoft Dynamics 365 Finance & Operations 環境へのアクセスを提供する MCP(Model Context Protocol)サーバーです。Claude などの AI アシスタントが D365 のメタデータを調査し、データをクエリし、非本番環境で書き込み操作を実行できます。
特徴
- 複数環境のサポート - 複数の D365 環境(本番、UAT、開発環境)に接続
- 読み取り/書き込み操作 - すべての環境でデータをクエリ可能。非本番環境のみ作成、更新、削除が可能
- 本番環境の安全性 - 本番環境は設計上、常に読み取り専用です
- スキーマの検出とメタデータの調査に利用できる MCP リソース
- 柔軟なデータアクセス、集計、バッチ操作、分析に対応する 22 種類の専用ツール
- 環境ダッシュボード - ヘルスモニタリング、API 統計、操作の追跡
- Azure AD クライアント資格情報による セキュアな認証
- メタデータの自動キャッシュ(TTL は 24 時間、環境ごと)
アーキテクチャ
リソース
| リソース | URI | 用途 |
|---|---|---|
| エンティティ一覧 | d365://entities?filter=<pattern> | オプションのワイルドカードフィルターを使ってすべてのエンティティを一覧表示 |
| エンティティスキーマ | d365://entity/{entityName} | 任意のエンティティの完全なスキーマ(フィールド、キー、ナビゲーションプロパティ) |
| ナビゲーションプロパティ | d365://navigation/{entityName} | エンティティのリレーションシップとナビゲーションプロパティ |
| 列挙型の定義 | d365://enums | 値を含むすべての列挙型 |
| 保存済みクエリ | d365://queries | 保存済みクエリテンプレートの一覧 |
| ダッシュボード | d365://dashboard | すべての環境の JSON メトリクス(ヘルス、API 統計、最近の操作) |
ツール
すべてのツールで、対象とする D365 環境を指定するオプションの environment パラメーターを使用できます。
| ツール | 用途 |
|---|---|
list_environments | 接続状態とともに、設定済みのすべての D365 環境を一覧表示 |
set_environment | 現在のセッションで作業環境を設定 |
describe_entity | エンティティのスキーマをすばやく検索 |
execute_odata | 生の OData パスを実行(クエリ、単一レコード、件数) |
aggregate | エンティティデータに対して集計(SUM、AVG、COUNT、MIN、MAX、COUNTDISTINCT、パーセンタイル)を実行 |
get_related | エンティティのリレーションシップをたどって関連レコードを取得 |
export | クエリ結果を CSV、JSON、TSV 形式でエクスポート |
compare_periods | 変化量を計算する YoY、QoQ、MoM 期間比較 |
trending | 成長率と移動平均を用いた時系列分析 |
save_query | パラメーターに対応した再利用可能なクエリテンプレートを保存 |
execute_saved_query | パラメーターを置換して保存済みクエリテンプレートを実行 |
delete_saved_query | 保存済みクエリテンプレートを削除 |
join_entities | $expand またはクライアント側の結合を使用した、エンティティ間の結合 |
batch_query | 複数のクエリを並列実行 |
search_entity | 自動フォールバック戦略を備えた、堅牢なエンティティ検索 |
analyze_customer | 1 回の呼び出しで包括的な顧客分析を実行 |
create_record | 新しいレコードを作成(非本番環境のみ) |
update_record | 既存のレコードを更新(非本番環境のみ) |
delete_record | レコードを削除(非本番環境のみ) |
batch_crud | 1 つのバッチリクエストで複数の作成/更新/削除操作を実行(非本番環境のみ) |
compare_schemas | 2 つの環境間でエンティティスキーマを比較し、スキーマのずれを検出 |
dashboard | ヘルスステータス、API 統計、最近の操作を含む環境ダッシュボードを表示 |
インストール
npm から(推奨)
npx @zhound/d365fo-mcp-server
またはグローバルにインストールします。
npm install -g @zhound/d365fo-mcp-server
d365fo-mcp
ソースから
git clone https://github.com/zhound420/D365FO-claude-connector.git
cd D365FO-claude-connector
npm install
npm run build
クイックスタート(推奨)
対話型セットアップウィザードを実行します。
npm run setup
ウィザードでは、次の操作を行います。
- 前提条件(Node.js 18 以降、依存関係)を確認します
- D365 環境の設定を案内します
- D365 環境への接続をテストします
- 設定ファイルを生成します
- Claude Desktop および/または Claude Code を設定します
セットアップ後、Claude Desktop を再起動します(macOS では Cmd+Q を押してから再度開き、Windows では Ctrl+Q を押します)。または、Claude Code で新しいセッションを開始します。
設定
複数環境の設定(推奨)
プロジェクトのルートまたは作業ディレクトリに d365-environments.json ファイルを作成します。
{
"environments": [
{
"name": "production",
"displayName": "Production",
"type": "production",
"tenantId": "your-tenant-id",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"environmentUrl": "https://your-company.operations.dynamics.com",
"default": true
},
{
"name": "uat",
"displayName": "UAT (Tier 2)",
"type": "non-production",
"tenantId": "your-tenant-id",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"environmentUrl": "https://your-company-uat.sandbox.operations.dynamics.com"
},
{
"name": "dev",
"displayName": "Dev Sandbox",
"type": "non-production",
"tenantId": "your-tenant-id",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"environmentUrl": "https://your-company-dev.sandbox.operations.dynamics.com"
}
]
}
環境の種類:
type: "production"- 読み取り専用アクセス(すべての書き込み操作がブロックされます)type: "non-production"- 読み取り/書き込みのフルアクセス(作成、更新、削除が有効になります)
d365-environments.example.json をコピーして、設定のベースとして使用します。
単一環境(従来方式)
JSON 設定がない場合の代替手段として、次の環境変数もサーバーでサポートされています。
| 変数 | 説明 |
|---|---|
D365_TENANT_ID | Azure AD テナント ID |
D365_CLIENT_ID | Azure AD アプリケーション(クライアント)ID |
D365_CLIENT_SECRET | Azure AD クライアントシークレット |
D365_ENVIRONMENT_URL | D365 F&O 環境の URL(例:https://contoso.operations.dynamics.com) |
D365_ENVIRONMENT_TYPE | オプション:「production」または「non-production」(安全のため、既定値は「production」です) |
オプション:
| 変数 | 既定値 | 説明 |
|---|---|---|
D365_TRANSPORT | stdio | トランスポートモード(stdio または http) |
D365_HTTP_PORT | 3000 | HTTP ポート(http トランスポート使用時) |
D365_LOG_LEVEL | info | ログレベル |
D365_PAGINATION_TIMEOUT_MS | 60000 | 大規模なデータセットに対するページネーション付きリクエストのタイムアウト(ms) |
D365_CONFIG_FILE | 既定の場所にない場合の設定ファイルのパス |
Azure AD アプリの登録
手順 1:Azure AD アプリを作成する
- Azure Portal > Azure Active Directory > App registrations に移動します
- 「New registration」をクリックします
- 名前を入力します(例:「D365 MCP Server」)
- 「Accounts in this organizational directory only」を選択します
- 「Register」をクリックします
手順 2:API のアクセス許可を設定する
- 「API permissions」>「Add a permission」に移動します
- 「Dynamics 365 Finance and Operations」を選択します
- 「Application permissions」>
CustomService.ReadWrite.Allを選択します - 「Grant admin consent for [your organization]」をクリックします
手順 3:クライアントシークレットを作成する
- 「Certificates & secrets」>「New client secret」に移動します
- 説明と有効期限を設定します
- シークレットの値をすぐにコピーします(表示されるのは一度だけです)
- 次の情報を控えます。
- Tenant ID:Overview ページに記載されています
- Client ID:Overview ページに記載されたアプリケーション(クライアント)ID
- Client Secret:コピーした値
手順 4:D365 環境にアプリを登録する
重要: 接続する各 D365 環境(本番、UAT、開発)で、この手順を実施する必要があります。
-
D365 F&O で、次の場所に移動します。 システム管理 > 設定 > Azure Active Directory アプリケーション
-
「New」をクリックしてレコードを追加します。
フィールド 値 Client ID Azure AD のアプリケーション(クライアント)ID Name わかりやすい名前(例:「MCP Server Integration」) User ID アプリの実行に使用する D365 ユーザーアカウント -
User ID によって、アプリがアクセスできるデータが決まります。
- 適切なセキュリティロールを持つサービスアカウントを使用します
- 読み取り専用アクセスの場合:「View all data」などのロールを割り当てます
- 非本番環境で書き込みアクセスを許可する場合:作成/更新/削除を許可するロールを割り当てます
-
接続する環境ごとに、この手順を繰り返します
注: この手順を省略すると、Azure AD 認証に成功していても、API 呼び出しは 401 Unauthorized または 403 Forbidden エラーで失敗します。
セットアップ
Claude Desktop
Claude Desktop の設定ファイルに追加します。
macOS:~/Library/Application Support/Claude/claude_desktop_config.json
Windows:%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"Microsoft D365": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_TENANT_ID": "your-tenant-id",
"D365_CLIENT_ID": "your-client-id",
"D365_CLIENT_SECRET": "your-client-secret",
"D365_ENVIRONMENT_URL": "https://your-env.operations.dynamics.com"
}
}
}
}
Claude Code(CLI)
~/.claude/settings.json に追加します。
{
"mcpServers": {
"Microsoft D365": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_TENANT_ID": "your-tenant-id",
"D365_CLIENT_ID": "your-client-id",
"D365_CLIENT_SECRET": "your-client-secret",
"D365_ENVIRONMENT_URL": "https://your-env.operations.dynamics.com"
}
}
}
}
設定を追加したら、Claude Desktop または Claude Code を再起動します。
環境の表示設定
複数の D365 環境を使用する場合、Claude での表示方法を設定できます。
オプション A:環境ごとに個別のサーバーを設定(推奨)
このオプションでは、Claude のサイドバーに各環境が個別の MCP サーバーとして表示されます。
{
"mcpServers": {
"D365-production": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_CONFIG_FILE": "/path/to/d365fo-mcp-server/d365-environments.json",
"D365_SINGLE_ENV": "production"
}
},
"D365-uat": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_CONFIG_FILE": "/path/to/d365fo-mcp-server/d365-environments.json",
"D365_SINGLE_ENV": "uat"
}
},
"D365-dev": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_CONFIG_FILE": "/path/to/d365fo-mcp-server/d365-environments.json",
"D365_SINGLE_ENV": "dev"
}
}
}
}
メリット:
- 環境が Claude のサイドバーにすぐ表示されます
- クエリの対象環境が明確です
- すべてのプラットフォームで安定して動作します
仕組み: D365_SINGLE_ENV 環境変数により、サーバーは d365-environments.json から指定された環境のみを読み込みます。D365_CONFIG_FILE により、作業ディレクトリにかかわらず設定ファイルが見つかります。
オプション B:複数環境を扱う単一のサーバー
各クエリに environment パラメーターを指定して、単一のサーバーを使用します。
{
"mcpServers": {
"d365": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_CONFIG_FILE": "/path/to/d365fo-mcp-server/d365-environments.json"
}
}
}
}
クエリで環境を指定します。
{ "entity": "CustomersV3", "top": 10, "environment": "uat" }
メリット:
- サーバープロセスが 1 つで済みます
- 1 つのセッションで任意の環境にクエリを実行できます
対話型セットアップスクリプト(node setup.js)を使うと、どちらの設定も生成できます。
Claude への質問例
設定が完了したら、D365 環境について自然言語で Claude に質問できます。機能別の例を以下に示します。
エンティティの検索
あなた: D365 で顧客に関連するエンティティには何がありますか?
Claude は d365://entities?filter=*Cust* リソースを使用して、一致するエンティティを検索します。
あなた: CustomersV3 エンティティのスキーマを表示してください
Claude は describe_entity または d365://entity/CustomersV3 リソースを使用します。
データのクエリ
あなた: 最初の 10 件の顧客について、口座番号と名前を取得してください
Claude はパス CustomersV3?$top=10&$select=CustomerAccount,CustomerName を指定して execute_odata を使用します。
あなた: システムには販売注文がいくつありますか?
Claude はパス SalesOrderHeaders/$count を指定して execute_odata を使用します。
あなた: 顧客グループが「US」で、与信限度額が 50000 を超える顧客をすべて検索してください
Claude は OData フィルタークエリを自動的に構築します。
集計と分析
あなた: 合計購入額が多い上位 20 社の顧客を教えてください
Claude は groupBy、orderBy、top を指定して aggregate を使用します。
{
"entity": "SalesOrderLinesV2",
"aggregations": [{"function": "SUM", "field": "LineAmount"}],
"groupBy": ["OrderingCustomerAccountNumber"],
"orderBy": "sum_LineAmount desc",
"top": 20
}
あなた: 注文額の中央値はいくらですか? 90 パーセンタイルと 95 パーセンタイルも表示してください
Claude はパーセンタイル関数を指定して aggregate を使用します。
{
"entity": "SalesOrderLinesV2",
"aggregations": [
{"function": "P50", "field": "LineAmount", "alias": "median"},
{"function": "P90", "field": "LineAmount"},
{"function": "P95", "field": "LineAmount"}
],
"accurate": true
}
あなた: 商品カテゴリ別に総売上を分類してください
Claude は groupBy を指定して aggregate を使用します。
{
"entity": "SalesOrderLinesV2",
"aggregations": [{"function": "SUM", "field": "LineAmount"}],
"groupBy": ["ItemGroup"]
}
時系列分析
あなた: 過去 12 か月の月別売上推移と成長率を表示してください
Claude は trending を使用します。
{
"entity": "SalesOrderLinesV2",
"dateField": "CreatedDateTime",
"valueField": "LineAmount",
"granularity": "month",
"periods": 12,
"includeGrowthRate": true
}
あなた: 今年の売上を昨年と比較してください
Claude は前年比比較に compare_periods を使用します。
{
"entity": "SalesOrderLinesV2",
"dateField": "CreatedDateTime",
"comparisonType": "YoY",
"aggregations": [{"function": "SUM", "field": "LineAmount"}]
}
あなた: 第 4 四半期の売上は第 3 四半期と比べてどうでしたか?
Claude は前四半期比比較に compare_periods を使用します。
{
"entity": "SalesOrderLinesV2",
"dateField": "CreatedDateTime",
"comparisonType": "QoQ",
"aggregations": [{"function": "SUM", "field": "LineAmount"}]
}
顧客インテリジェンス
あなた: 顧客 US-001 について、プロフィール、注文、購入額、傾向を含む包括的な分析をしてください
Claude は 1 回の呼び出しで包括的な分析を行う analyze_customer を使用します。
{
"customerAccount": "US-001",
"includeOrders": true,
"includeSpend": true,
"includeTrending": true
}
あなた: 「S&S Industries」という名前の顧客を検索してください
Claude は、標準の OData では問題が発生する特殊文字を処理できる search_entity を使用します。
{
"entity": "CustomersV3",
"searchTerm": "S&S Industries",
"searchField": "CustomerName"
}
複数クエリと結合
あなた: ダッシュボード形式で、顧客総数、今月の注文総数、売上上位 5 件の商品を表示してください
Claude は 3 つのクエリを並列実行するために batch_query を使用します。
{
"queries": [
{"name": "total_customers", "entity": "CustomersV3", "top": 1},
{"name": "orders_this_month", "entity": "SalesOrderHeadersV2", "filter": "OrderCreatedDateTime ge 2024-01-01"},
{"name": "top_products", "entity": "SalesOrderLinesV2", "top": 5, "orderby": "LineAmount desc"}
]
}
あなた: 顧客名と顧客グループを含む最近の注文を表示してください
Claude は注文と顧客の詳細を関連付けるために join_entities を使用します。
{
"primaryEntity": "SalesOrderHeadersV2",
"primaryKey": "OrderingCustomerAccountNumber",
"secondaryEntity": "CustomersV3",
"secondaryKey": "CustomerAccount",
"primarySelect": ["SalesOrderNumber", "OrderCreatedDateTime"],
"secondarySelect": ["CustomerName", "CustomerGroup"]
}
データのエクスポート
あなた: 与信限度額が 100,000 ドルを超える顧客をすべて CSV にエクスポートしてください
Claude は形式とフィルターを指定して export を使用します。
{
"entity": "CustomersV3",
"format": "csv",
"filter": "CreditLimit gt 100000",
"select": ["CustomerAccount", "CustomerName", "CreditLimit"]
}
列挙型の確認
あなた: 販売注文のステータスにはどのような値がありますか?
Claude は d365://enums リソースを調べて、列挙型の定義を確認します。
最良の結果を得るためのヒント
-
業務に関する質問を直接する - MCP ツールが複雑な処理を代行します。「支出額が多い上位 20 社の顧客は?」や「第 4 四半期は第 3 四半期と比べてどうでしたか?」と尋ねるだけです。
-
自然な日付形式を使う - Claude は「先月」、「2024 年第 4 四半期」、「過去 12 か月」、または「2024 年 1 月 1 日」のような具体的な日付を理解します。
-
特殊文字を気にしない - 「S&S Industries」や「O'Brien Corp」の検索は自動的に処理されます。標準の OData では問題が起きる文字に対して、ツールが代替手段を用意しています。
-
傾向や比較をリクエストする - 組み込みの時間分析機能が複雑な処理を行います。「成長率を含む月次売上の推移を表示して」や「今年の売上を昨年と比較して」と尋ねてください。
-
複数の質問を組み合わせる - ダッシュボード形式の表示をリクエストできます。「顧客総数、今月の注文数、売上上位 5 商品を教えてください」などと尋ねると、クエリが並行して実行されます。
-
必要に応じてデータをエクスポートする - CSV、JSON、または TSV 形式でのエクスポートを直接リクエストできます。「与信限度額が 10 万ドルを超える顧客をすべて CSV にエクスポートして」と依頼してください。
-
説明を求める - OData 構文を学びたい場合は、Claude にクエリの説明を求めてください。「グループが US の顧客を表示して、OData クエリを説明して」と尋ねます。
API リファレンス
リソース
d365://entities
任意のフィルタリング条件を指定して、利用可能な D365 エンティティを一覧表示します。
クエリパラメーター:
filter(任意):ワイルドカードパターン(任意の文字には*、1 文字には?)
例:
d365://entities # List all entities
d365://entities?filter=Cust* # Entities starting with "Cust"
d365://entities?filter=*Header* # Entities containing "Header"
d365://entity/{entityName}
エンティティの完全なスキーマを取得します。
例:
d365://entity/CustomersV3
d365://entity/SalesOrderHeaders
レスポンスに含まれる内容:
- エンティティ名と説明
- 主キーのフィールド
- 型、制約、列挙型への参照を含むすべてのフィールド
- ナビゲーションプロパティ(リレーションシップ)
d365://navigation/{entityName}
エンティティのナビゲーションプロパティ(リレーションシップ)を取得します。
例:
d365://navigation/SalesOrderHeadersV2
d365://navigation/CustomersV3
レスポンスに含まれる内容:
- ナビゲーションプロパティ名
- 対象エンティティの型
- リレーションシップの多重度(一対多、多対一)
d365://enums
すべての列挙型定義を一覧表示します。
レスポンスに含まれる内容:
- 列挙型名と完全な名前空間
- 数値コードを含むすべてのメンバー値
ツール
list_environments
接続状態と権限を含む、設定済みの D365 環境をすべて一覧表示します。
パラメーター:
- 必須のパラメーターはありません
例:
{}
レスポンスに含まれる内容:
- 環境名と表示名
- 種類(本番/非本番)
- 接続状態
- 読み取り/書き込み権限
set_environment
現在のセッションで使用する環境を設定します。以降のツール呼び出しでは、この環境がデフォルトで使用されます。
パラメーター:
environment(文字列、必須):アクティブに設定する環境の名前
例:
{
"environment": "uat"
}
describe_entity
人が読みやすい形式でエンティティのスキーマを取得します。
パラメーター:
entity(文字列、必須):エンティティ名
例:
{
"entity": "CustomersV3"
}
execute_odata
D365 に対して生の OData パスを実行します。
パラメーター:
path(文字列、必須):/data/に追加する OData パス
例:
// Query with parameters
{ "path": "CustomersV3?$top=5&$select=CustomerAccount,CustomerName" }
// Single record by key
{ "path": "CustomersV3('US-001')" }
// Compound key
{ "path": "CustomersV3(DataAreaId='usmf',CustomerAccount='US-001')" }
// Count
{ "path": "CustomersV3/$count" }
// Filtered count
{ "path": "CustomersV3/$count?$filter=CustomerGroup eq 'US'" }
// With expansion
{ "path": "SalesOrderHeaders?$expand=SalesOrderLines&$top=3" }
aggregate
D365 エンティティデータの集計を実行します。単純な COUNT 操作には高速な /$count を使用し、それ以外の場合はクライアント側で集計します。
パラメーター:
entity(string、必須):集計するエンティティ名aggregations(array、必須):集計仕様の配列:function:「SUM」 | 「AVG」 | 「COUNT」 | 「MIN」 | 「MAX」 | 「COUNTDISTINCT」 | 「P50」 | 「P90」 | 「P95」 | 「P99」field:集計するフィールド(COUNT には「*」を使用)alias(任意):カスタム結果名
filter(string、任意):OData の $filter 式groupBy(array、任意):グループ化するフィールドaccurate(boolean、任意):正確な合計を得るため、すべてのレコードを取得します(デフォルト:false)sampling(boolean、任意):非常に大規模なデータセットの高速な推定に統計的サンプリングを使用します(デフォルト:false)orderBy(string、任意):集計エイリアスで結果を並べ替えます(例:「sum_LineAmount desc」)top(number、任意):並べ替え後、上位 N 件の結果のみを返します
パーセンタイル関数:
P50- 中央値(50 パーセンタイル)P90- 90 パーセンタイルP95- 95 パーセンタイルP99- 99 パーセンタイル
パフォーマンスに関する注意:
- デフォルトモードでは、迅速な推定のためレコード数を 5K 件に制限します
accurate=trueを指定すると、ページごとに 60 秒のタイムアウトを設定してすべてのレコードを取得し、自動再試行します(指数バックオフで 2 回再試行)sampling=trueを指定すると、非常に大規模なデータセット(100K 件以上)の統計的推定に約 10K 件のレコードサンプルを使用します
例:
// Count all customers
{ "entity": "CustomersV3", "aggregations": [{"function": "COUNT", "field": "*"}] }
// Sum with filter
{ "entity": "SalesOrderLines", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "filter": "SalesOrderNumber eq 'SO-001'" }
// Accurate mode for exact totals
{ "entity": "SalesOrderLines", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "accurate": true }
// Group by
{ "entity": "SalesOrderLines", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "groupBy": ["ItemNumber"] }
// Median order value (requires accurate=true for percentiles)
{ "entity": "SalesOrderLines", "aggregations": [{"function": "P50", "field": "LineAmount"}], "accurate": true }
// Fast estimate on very large dataset (100K+ records)
{ "entity": "BatchJobs", "aggregations": [{"function": "COUNT", "field": "*"}], "sampling": true }
// Top 20 customers by spend
{ "entity": "SalesOrderLines", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "groupBy": ["CustomerAccount"], "orderBy": "sum_LineAmount desc", "top": 20 }
get_related
エンティティのリレーションシップをたどり、関連レコードを 1 回の呼び出しで取得します。
パラメーター:
entity(string、必須):ソースエンティティ名key(string | object、必須):ソースレコードの主キーrelationship(string、必須):たどるナビゲーションプロパティ名select(string[]、任意):関連エンティティから含めるフィールドfilter(string、任意):関連レコードに適用するフィルターtop(number、任意):関連レコードの最大数(デフォルト:1000)
例:
// Get order lines for an order
{ "entity": "SalesOrderHeaders", "key": "SO-001", "relationship": "SalesOrderLines" }
// With compound key
{ "entity": "SalesOrderHeaders", "key": {"DataAreaId": "usmf", "SalesOrderNumber": "SO-001"}, "relationship": "SalesOrderLines" }
// With field selection and filter
{ "entity": "SalesOrderHeaders", "key": "SO-001", "relationship": "SalesOrderLines", "select": ["ItemNumber", "LineAmount"], "filter": "LineAmount gt 1000" }
export
D365 エンティティデータを CSV、JSON、または TSV 形式でエクスポートします。
パラメーター:
entity(string、必須):エクスポートするエンティティformat("json" | "csv" | "tsv"、任意):出力形式(デフォルト:"json")select(string[]、任意):含めるフィールドfilter(string、任意):OData の $filter 式orderBy(string、任意):OData の $orderby 式maxRecords(number、任意):レコードの最大数(デフォルト:10000)includeHeaders(boolean、任意):CSV/TSV のヘッダー行を含めます(デフォルト:true)
例:
// JSON export with field selection
{ "entity": "CustomersV3", "format": "json", "select": ["CustomerAccount", "CustomerName"] }
// CSV export with filter
{ "entity": "SalesOrderLines", "format": "csv", "filter": "SalesOrderNumber eq 'SO-001'" }
// TSV with ordering and limit
{ "entity": "Products", "format": "tsv", "orderBy": "ProductName asc", "maxRecords": 500 }
compare_periods
2 つの期間の集計を比較します(YoY、QoQ、MoM、またはカスタム範囲)。
パラメーター:
entity(string、必須):分析するエンティティdateField(string、必須):フィルタリングに使用する日付/日時フィールドaggregations(array、必須):aggregate ツールと同じcomparisonType("YoY" | "QoQ" | "MoM" | "custom"、必須):比較の種類referenceDate(string、任意):計算の基準日(デフォルト:今日)period1、period2(objects、任意):カスタム期間の範囲filter(string、任意):追加の OData フィルターgroupBy(string[]、任意):グループ化するフィールド
例:
// Year-over-Year comparison
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "comparisonType": "YoY", "aggregations": [{"function": "SUM", "field": "LineAmount"}] }
// Month-over-Month with grouping
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "comparisonType": "MoM", "aggregations": [{"function": "COUNT", "field": "*"}], "groupBy": ["ItemGroup"] }
// Custom date ranges
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "comparisonType": "custom", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "period1": {"start": "2024-01-01", "end": "2024-03-31"}, "period2": {"start": "2023-01-01", "end": "2023-03-31"} }
trending
集計、成長率、移動平均を用いた時系列分析です。
パラメーター:
entity(string, required):分析対象のエンティティdateField(string, required):グループ化に使用する日付/日時フィールドvalueField(string, required):集計する数値フィールドaggregation("SUM" | "AVG" | "COUNT" | "MIN" | "MAX", optional):デフォルト:"SUM"granularity("day" | "week" | "month" | "quarter" | "year", optional):デフォルト:"month"periods(number, optional):分析する期間数(デフォルト:12)endDate(string, optional):分析の終了日(デフォルト:今日)filter(string, optional):追加の OData フィルターmovingAverageWindow(number, optional):移動平均の計算に使用するウィンドウサイズincludeGrowthRate(boolean, optional):成長率を含めるかどうか(デフォルト:true)
例:
// Monthly revenue trend
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "valueField": "LineAmount", "granularity": "month", "periods": 12 }
// Weekly order count with moving average
{ "entity": "SalesOrderHeaders", "dateField": "OrderDate", "valueField": "*", "aggregation": "COUNT", "granularity": "week", "movingAverageWindow": 4 }
// Quarterly with filter
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "valueField": "LineAmount", "granularity": "quarter", "filter": "ItemGroup eq 'Electronics'" }
save_query
後で実行できるよう、再利用可能なクエリテンプレートを保存します。置換可能なパラメーターには {{paramName}} を使用します。
パラメーター:
name(string, required):クエリの一意な名前description(string, optional):クエリの説明entity(string, required):クエリ対象のエンティティselect(string[], optional):選択するフィールドfilter(string, optional):OData $filter(パラメーターには{{paramName}}を使用)orderBy(string, optional):OData $orderby 式top(number, optional):最大レコード数expand(string, optional):OData $expand 式
例:
// Basic query
{ "name": "active_customers", "entity": "CustomersV3", "filter": "IsActive eq true" }
// With parameters
{ "name": "customer_orders", "entity": "SalesOrderHeaders", "filter": "CustomerAccount eq '{{customerId}}'" }
// Complex query with description
{ "name": "recent_sales", "description": "Recent sales for analysis", "entity": "SalesOrderLines", "select": ["ItemNumber", "LineAmount"], "filter": "CreatedDateTime ge {{startDate}}", "orderBy": "CreatedDateTime desc", "top": 100 }
execute_saved_query
以前に保存したクエリテンプレートを実行します。
パラメーター:
name(string, required):保存済みクエリの名前params(object, optional):置換するパラメーター値fetchAll(boolean, optional):すべてのページを取得するかどうか(デフォルト:false)maxRecords(number, optional):fetchAll=trueの場合の最大レコード数(デフォルト:50000)
例:
// Simple execution
{ "name": "active_customers" }
// With parameters
{ "name": "customer_orders", "params": {"customerId": "US-001"} }
// Multiple parameters with pagination
{ "name": "date_range_sales", "params": {"startDate": "2024-01-01", "endDate": "2024-12-31"}, "fetchAll": true }
delete_saved_query
保存済みのクエリテンプレートを削除します。
パラメーター:
name(string, required):削除するクエリの名前
join_entities
OData $expand またはクライアント側の結合を使用して、エンティティを横断して結合します。
パラメーター:
primaryEntity(string, required):プライマリエンティティ名primaryKey(string, required):結合に使用するプライマリキーのフィールドsecondaryEntity(string, required):セカンダリエンティティ名secondaryKey(string, required):結合に使用するセカンダリキーのフィールドprimarySelect(string[], optional):プライマリエンティティから取得するフィールドsecondarySelect(string[], optional):セカンダリエンティティから取得するフィールドprimaryFilter(string, optional):プライマリエンティティのフィルターjoinType("inner" | "left", optional):結合タイプ(デフォルト:"inner")maxRecords(number, optional):最大レコード数(デフォルト:5000)
例:
// Join orders with customers
{ "primaryEntity": "SalesOrderHeadersV2", "primaryKey": "OrderingCustomerAccountNumber", "secondaryEntity": "CustomersV3", "secondaryKey": "CustomerAccount", "primarySelect": ["SalesOrderNumber", "OrderCreatedDateTime"], "secondarySelect": ["CustomerName", "CustomerGroup"] }
batch_query
複数の D365 OData クエリを並列で実行し、すべての結果を単一のレスポンスで返します。
パラメーター:
queries(array, required):クエリ仕様の配列(1~10 件):name(string, optional):このクエリ結果のラベルentity(string, required):エンティティ名filter(string, optional):OData $filter 式select(string[], optional):含めるフィールドtop(number, optional):レコード数の上限(デフォルト:100)orderby(string, optional):OData $orderby 式fetchAll(boolean, optional):すべてのページを自動的に取得するかどうかmaxRecords(number, optional):fetchAll=trueの場合の最大レコード数
stopOnError(boolean, optional):最初の失敗時に停止するかどうか(デフォルト:false)
例:
// Multiple parallel queries
{
"queries": [
{ "name": "recent_orders", "entity": "SalesOrderHeadersV2", "top": 10, "orderby": "CreatedDateTime desc" },
{ "name": "customers", "entity": "CustomersV3", "filter": "CustomerGroup eq 'US'", "select": ["CustomerAccount", "CustomerName"] },
{ "name": "all_invoices", "entity": "SalesInvoiceHeadersV2", "fetchAll": true, "maxRecords": 1000 }
]
}
search_entity
自動フォールバック戦略を備えた堅牢なエンティティ検索です。標準の OData contains() で問題を引き起こす特殊文字(会社名の & など)に対応します。
検索戦略(次の順に試行):
contains()- 標準的な OData テキスト検索(最速)startswith()- 前方一致(D365 でより信頼性があります)exact- フィールドの完全一致client_filter- 取得後にクライアント側でフィルタリング(常に機能します)
パラメーター:
entity(string、必須):検索するエンティティsearchTerm(string、必須):検索するテキストsearchField(string、必須):検索対象のフィールドselect(string[]、任意):結果で返すフィールドtop(number、任意):最大結果数(デフォルト:10)
例:
// Search customers with special characters
{ "entity": "CustomersV3", "searchTerm": "S&S", "searchField": "CustomerName" }
// Search with specific fields
{ "entity": "CustomersV3", "searchTerm": "Contoso", "searchField": "CustomerName", "select": ["CustomerAccount", "CustomerName", "CustomerGroup"], "top": 5 }
// Search vendors
{ "entity": "VendorsV3", "searchTerm": "Microsoft", "searchField": "VendorName" }
analyze_customer
1 回の呼び出しで顧客を包括的に分析します。プロファイル、注文、支出、推移データを収集するために、並列クエリを実行します。
機能:
- 顧客プロファイルの検索(フォールバック検索戦略を使用)
- 注文統計(件数、総支出、平均注文額)
- 注文日の範囲(最初と最後の注文)
- 最近の注文一覧
- 月別注文推移
支出を正確に計算するため、明細レベル(SalesOrderLinesV2)で効率的に集計し、ヘッダーの合計金額が $0 になる問題を回避します。
パラメーター:
customerAccount(string、任意):顧客のアカウント番号customerName(string、任意):検索する顧客名(特殊文字に対応)includeOrders(boolean、任意):最近の注文一覧を含めるかどうか(デフォルト:true)includeSpend(boolean、任意):総支出の計算を含めるかどうか(デフォルト:true)includeTrending(boolean、任意):月別推移分析を含めるかどうか(デフォルト:true)recentOrdersLimit(number、任意):表示する最近の注文数(デフォルト:10)trendPeriods(number、任意):推移の対象となる月数(デフォルト:12)
例:
// Analyze by account number
{ "customerAccount": "SS0011" }
// Analyze by name (handles special characters like &)
{ "customerName": "S&S" }
// Quick analysis without trending (faster)
{ "customerAccount": "US-001", "includeTrending": false }
// Full analysis with custom periods
{ "customerName": "Contoso", "recentOrdersLimit": 20, "trendPeriods": 24 }
出力内容:
- 顧客プロファイル(名前、アカウント、グループ、住所)
- 概要統計(注文総数、総支出、平均注文額、最初と最後の注文日)
- 最近の注文一覧
- 注文数と売上を含む月別注文推移テーブル
d365://queries
保存済みのクエリテンプレートをすべて一覧表示するリソースです。
レスポンス内容:
- クエリ数と一覧
- 各クエリの名前、説明、エンティティ、パラメーター
- 使用方法
dashboard
環境のダッシュボードを表示します。正常性ステータス、API 統計、最近の操作を確認できます。
パラメーター:
checkHealth(boolean、任意):ライブ接続チェックを実行するかどうか(デフォルト:false)
例:
{
"checkHealth": true
}
レスポンス内容:
- 環境ごとの正常性ステータス
- API 呼び出し統計(呼び出し総数、成功率)
- 最近の操作ログ
- 環境設定の概要
OData クエリ構文
フィルターの例
// Equality
$filter=CustomerAccount eq 'US-001'
// Comparison
$filter=CreditLimit gt 10000
// String functions
$filter=startswith(CustomerName, 'Contoso')
$filter=contains(CustomerName, 'Inc')
// Logical operators
$filter=CustomerGroup eq 'US' and CreditLimit gt 5000
// Enum values
$filter=Status eq Microsoft.Dynamics.DataEntities.SalesStatus'Invoiced'
// Date comparison
$filter=OrderDate gt 2024-01-01
Select と Expand
// Select specific fields
$select=CustomerAccount,CustomerName,CreditLimit
// Expand navigation property
$expand=SalesOrderLines
// Expand with nested select
$expand=SalesOrderLines($select=ItemId,Quantity)
並べ替えとページネーション
// Sort ascending
$orderby=CustomerName asc
// Sort descending
$orderby=OrderDate desc
// Multiple sort columns
$orderby=CustomerGroup asc,CustomerName asc
// Pagination
$top=50&$skip=100
開発
# Build
npm run build
# Watch mode
npm run dev
# Run directly (requires environment variables)
npm start
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
プロジェクト構成
src/
├── index.ts # Entry point and server setup
├── config-loader.ts # Configuration loading (JSON + env var fallback)
├── environment-manager.ts # Multi-environment management and write guards
├── auth.ts # Azure AD OAuth2 authentication
├── d365-client.ts # D365 OData API client with read/write methods
├── metadata-cache.ts # EDMX metadata parser and cache (24h TTL)
├── progress.ts # Progress reporting for long operations
├── types.ts # TypeScript type definitions
├── metrics/
│ ├── index.ts # Metrics module exports
│ ├── metrics-tracker.ts # API call statistics tracking
│ ├── health-checker.ts # Environment connectivity health checks
│ └── operation-log.ts # Operation history tracking
├── resources/
│ ├── index.ts # Resource registration
│ ├── entities.ts # d365://entities resource
│ ├── entity.ts # d365://entity/{name} resource
│ ├── navigation.ts # d365://navigation/{name} resource
│ ├── enums.ts # d365://enums resource
│ ├── queries.ts # d365://queries resource
│ └── dashboard.ts # d365://dashboard resource
├── utils/
│ ├── date-utils.ts # Date period calculations
│ ├── csv-utils.ts # CSV/TSV formatting
│ ├── env-utils.ts # Environment variable parsing with validation
│ └── pagination.ts # Shared pagination utilities (fetchPageWithRetry, paginatedFetch)
└── tools/
├── index.ts # Tool registration
├── common.ts # Shared tool utilities and error formatting
├── list-environments.ts
├── set-environment.ts
├── describe-entity.ts
├── execute-odata.ts
├── aggregate.ts
├── get-related.ts
├── export.ts
├── compare-periods.ts
├── trending.ts
├── saved-queries.ts # save/execute/delete query templates
├── join-entities.ts
├── batch-query.ts
├── batch-crud.ts # Batch create/update/delete via $batch (non-production only)
├── compare-schemas.ts # Cross-environment schema comparison
├── search-entity.ts
├── analyze-customer.ts
├── create-record.ts # Write operation (non-production only)
├── update-record.ts # Write operation (non-production only)
├── delete-record.ts # Write operation (non-production only)
└── dashboard.ts
tests/
├── auth.test.ts # Token caching, refresh dedup, invalidation
├── d365-client.test.ts # Retry logic, key formatting, CRUD operations
├── config-loader.test.ts # JSON loading, env var fallback, validation
├── pagination.test.ts # Shared pagination utilities
└── env-utils.test.ts # parseInt validation utility
書き込み操作(非本番環境のみ)
書き込み操作は、type: "non-production" を設定した環境でのみ使用できます。本番環境は常に読み取り専用です。
create_record
D365 エンティティに新しいレコードを作成します。
パラメーター:
entity(string、必須):エンティティ名data(object、必須):新しいレコードのフィールド値environment(string、任意):対象環境
例:
{
"entity": "CustomersV3",
"data": {
"CustomerAccount": "CUST-001",
"CustomerName": "Contoso Ltd",
"CustomerGroup": "US"
},
"environment": "uat"
}
update_record
既存のレコードを更新します。
パラメーター:
entity(string、必須):エンティティ名key(string | object、必須):レコードキーdata(object、必須):更新するフィールド値etag(string、任意):楽観的同時実行制御用の ETagenvironment(string、任意):対象環境
例:
{
"entity": "CustomersV3",
"key": "CUST-001",
"data": {
"CustomerName": "Contoso Corporation"
},
"environment": "dev"
}
delete_record
エンティティからレコードを削除します。
パラメーター:
entity(string、必須):エンティティ名key(string | object、必須):レコードキーetag(string、任意):楽観的同時実行制御用の ETagenvironment(string、任意):対象環境
例:
{
"entity": "CustomersV3",
"key": "CUST-001",
"environment": "dev"
}
セキュリティ
認証情報の保護
このプロジェクトでは、Azure AD の認証情報を保護するために複数の層を実装しています。
| 保護機能 | 説明 |
|---|---|
.gitignore | .env と d365-environments.json をバージョン管理から除外します |
.gitattributes | 機密ファイルを git archive によるエクスポートから除外します |
| プリコミットフック | コミットを許可する前に、ステージされたファイルからシークレットのパターンをスキャンします |
| エラーのサニタイズ | Azure AD のエラーレスポンスは内部でログに記録されますが、呼び出し元には公開されません |
保護対象ファイル
次のファイルには認証情報が含まれており、保護されています。
.env- 環境変数(従来の単一環境構成)d365-environments.json- シークレットを含む複数環境の構成*.local.json- ローカル構成のオーバーライド
安全なファイル(プレースホルダーが含まれているため、コミットしても問題ありません):
.env.example- プレースホルダー値を含むテンプレートd365-environments.example.json- 構成例
認証情報を公開してしまった場合
誤って認証情報をコミットまたは公開してしまった場合:
-
直ちに Azure AD クライアントシークレットをローテーションします:
- Azure Portal > Azure Active Directory > アプリの登録に移動します
- D365 アプリの登録を選択します
- 「証明書とシークレット」に移動します
- 新しいクライアントシークレットを作成します
- ローカルの
.envまたはd365-environments.jsonを新しいシークレットで更新します - Azure AD から古いシークレットを削除します
-
Azure AD のサインインログを確認します:
- 不正なアクセスの試行がないか確認します
- Azure Portal > Azure AD > サインインログ > アプリでフィルター
-
git にコミットした場合:
- 新しいコミットでシークレットを削除しても、git の履歴には残ります
git filter-branchまたは BFG Repo-Cleaner を使用して履歴を削除することを検討してください- 履歴を整理したリポジトリを強制プッシュします(共同作業者と調整してください)
Azure AD シークレットのローテーション
ベストプラクティスとして、シークレットは定期的に(90~180 日ごとに)ローテーションしてください。
- Azure Portal で新しいシークレットを作成します(古いシークレットの有効期限が切れる前に)
- 構成ファイルを更新します:
# Edit .env or d365-environments.json with new secret - 接続をテストします:
npm start # Verify authentication works - Azure Portal から古いシークレットを削除します
プリコミットフック
プリコミットフックは、次のようなパターンをスキャンします。
- Azure AD クライアントシークレット(
clientSecretの後に続く 30 文字以上の文字列) - テナント ID(
tenantIdの後に続く UUID 形式の文字列) - シークレットを含む環境変数の代入
バイパスする場合(誤検知の場合のみ):
git commit --no-verify
実行時の保護
- 本番環境は読み取り専用:本番環境では書き込み操作が構造的にブロックされます
- 非本番環境での書き込みアクセス:作成、更新、削除は
type: "non-production"の環境でのみ利用できます - 認証情報の非公開:認証情報はサーバー側で管理されます
- OData インジェクションの防止:パラメーターは適切にエンコードされます
トラブルシューティング
MCP サーバーが表示されない
-
Claude Desktop を完全に再起動します - macOS では Cmd+Q を押して(ウィンドウを閉じるだけではなく)から、再度開きます。Windows では Ctrl+Q を押すか、システムトレイから終了します。
-
サーバー構成を確認します - 構成ファイルのパスが正しいことを確認します:
D365_CONFIG_FILE=./d365-environments.json D365_SINGLE_ENV=uat node dist/index.js -
構成パスを確認します - Claude の構成にある
D365_CONFIG_FILEがd365-environments.jsonの実際の場所を指していることを確認します。 -
Claude のログを確認します - macOS:
~/Library/Logs/Claude/、Windows:%APPDATA%\Claude\logs\
認証エラー
- テナント ID、クライアント ID、シークレットが正しいことを確認します
- Azure AD アプリに必要な API アクセス許可があることを確認します
- 管理者の同意が付与されていることを確認します
エンティティが見つからない
d365://entitiesを使用して利用可能なエンティティを確認します- エンティティ名では大文字と小文字が区別されます
- 一部のエンティティは OData 経由で公開されない場合があります
タイムアウトエラー
$topと$filterを使用してクエリの範囲を絞り込みます- 大規模なデータセットでは、
$skipを使用してページネーションを行います batch_queryを使用して複数のクエリを並列実行します
大規模なデータセットの集計に関する改善:
- ページネーションのリクエストでは、60 秒のタイムアウトと自動再試行(指数バックオフによる 2 回の再試行)が使用されるようになりました
D365_PAGINATION_TIMEOUT_MS環境変数でタイムアウトを設定します- 非常に大規模なデータセット(100K 件以上のレコード)では、
aggregateツールでsampling=trueを使用すると、高速な統計的推定値を取得できます accurate=trueモードでは、ページネーションの途中で中断された場合に部分的な結果が報告されるようになりました
ライセンス
MIT
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。