Power Platform のテナント / 環境ガバナンスを確認・設定する管理スキル。開発着手前の環境チェック(既定環境ではないか・マネージド環境・Dataverse / Code Apps / MCP の有効化・セキュリティ ロール・管理 API アクセス)と DLP 事前チェックを非対話スクリプトで実行し、必要ならマネージド環境設定・カスタムコネクタの DLP 分類・ACP(Advanced connector policies)の許可コネクタを dry-run 付きで変更する。Microsoft 第一者サービスだけを許可する ACP 推奨プロファイルの適用と、クラシック DLP から ACP への移行も支援する。クラシック DLP と ACP は既定の混成モードで併用され、より制限の厳しい方が適用されるため両方を確認する。オプションとして、既定環境 / 個人開発者環境 / 市民開発者環境 / AI CoE セントラル / AI CoE 内製開発の 5 グループからなるテナント全体の環境戦略を、読み取り専用スキャン → 移行プラン(admin-migration-plan.md)→ レビュー → 適用の順で策定・実行する。設定は環境グループのルールで行うのを原則とし、グループ ルールに無い項目(既定環境ルーティング・Dataverse for Teams 禁止・Dataverse 検索・グループへの割り当て・Copilot クレジット配分)だけをテナント設定・環境個別設定・Dataverse の組織設定で補う。IP 制限・テナント分離・監査ログ・ライセンス配分などの管理設定は references にまとめる。
mcp-server
Copilot Studio から利用する自前 MCP Server を Azure Functions 上に構築する。JSON-RPC 2.0 の最小実装、受信 Entra ID JWT 検証 / 送信 Managed Identity のキーレス認証、Private Endpoint 下でのデータ投入、Entra アプリ登録のスコープ公開と事前承認、デプロイの実測検証までを非対話スクリプトで完結させる。
インストール方法を見る含まれるファイル(27)
- SKILL.md22.4 KB
- references/.env.example2.7 KB
- references/auth-model.md6.5 KB
- references/copilot-studio-dlp.md6.9 KB
- references/copilot-studio-registration.md10.4 KB
- references/file-backed-tools.md7.6 KB
- references/indexed-file-db-access.md6.0 KB
- references/private-data-seeding.md7.4 KB
- references/protocol.md6.3 KB
- references/sql-tools-pattern.md8.4 KB
- references/troubleshooting.md31.3 KB
- scripts/add_connector_redirect_uri.py3.0 KB
- scripts/cleanup_admin_endpoints.py3.4 KB
- scripts/configure_connector_oauth.py12.6 KB
- scripts/configure_entra_api.py4.8 KB
- scripts/configure_function_storage.py3.3 KB
- scripts/deploy_mcp_function.py10.1 KB
- scripts/diagnose_connector_token.py8.1 KB
- scripts/diagnose_copilot_dlp.py3.0 KB
- scripts/generate_copilot_studio_guide.py11.9 KB
- scripts/query_host_logs.py2.3 KB
- scripts/seed_mcp_data.py2.7 KB
- scripts/test_cleanup_admin_endpoints.py2.3 KB
- scripts/test_generate_copilot_studio_guide.py4.8 KB
- scripts/test_update_connector_oauth.py2.9 KB
- scripts/update_connector_oauth.py4.7 KB
- scripts/verify_mcp_server.py5.4 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
MCP Server 開発スキル
Copilot Studio のエージェントから 社内の業務データ(DB・ファイル共有・業務 API) を参照させるための 自前 MCP Server を Azure Functions 上に構築する。
役割分離: VNet / Private Endpoint / Managed Identity といった Azure 基盤の構成は azure-infra スキル に委譲する。本スキルは MCP プロトコル層・Entra 認可・データ投入・ Copilot Studio 登録 を担当する。
設計原則
| 原則 | 内容 |
|---|---|
| データ層は非公開、コンピュート層は公開 | SQL / Storage は Private Endpoint のみ。Function App の HTTP エンドポイントは公開する(Copilot Studio は SaaS からアウトバウンド接続するため、非公開にすると到達できない) |
| キーレス | 受信 = Entra ID Bearer JWT 検証、送信 = Managed Identity。関数キー・接続文字列・共有キーを使わない |
| プロトコルは最小実装 | initialize / tools/list / tools/call / ping のみ。SSE ストリーム・セッション管理は実装しないが、Streamable HTTP の規約には従う(Copilot Studio は Streamable のみ対応) |
| 成否はルート実測で判定 | デプロイの終了コードや ARM のメタデータを信用せず、HTTP プローブで実際のルートを確認する |
| 非対話で完走 | Azure 操作も auth_helper 経由(az login を手順に含めない)。→ 認証リファレンス |
サブリファレンス
| リファレンス | 内容 |
|---|---|
| MCP プロトコル最小実装 | JSON-RPC 2.0 の実装と Streamable HTTP の必須要件、ツール定義の書き方 |
| 認証モデル | 受信 JWT 検証 / 送信 Managed Identity の実装 |
| Private 環境でのデータ投入 | Private Endpoint 下でシードするための管理エンドポイントパターン |
| ファイルを読ませるツールの設計 | サイドカーテキストレイヤー・パストラバーサル対策・出力上限・プロンプトインジェクション防御 |
| SQL バックエンドのツール設計 | パラメータ化クエリ・集計軸のホワイトリスト・トークン寿命と接続プール・読み取り専用権限 |
| File / DB の認可とページ画像 | 本人認可、索引 ID、改訂・ページ照合、投入時画像キャッシュ、欠損404と実測ゲート |
| Copilot Studio への登録 | オンボーディングウィザード、コピペ用 MD 生成、OAuth 接続。OpenAPI 方式もここ |
| Copilot Studio の DLP 診断 | MCP ツールが DLP でブロックされた場合の読み取り診断と最小変更 |
| admin スキル | 実装着手前の環境チェックと DLP 事前チェック、カスタムコネクタの DLP 分類変更 |
| .env サンプル | 本スキルのパラメータ |
| 異常系・トラブルシュート | 実際に踏んだ失敗と恒久対策 |
ワークフロー(正常系)
Step 1: ツール定義を先に確定する
MCP は「エージェントがツール名と入力スキーマだけを見て呼ぶ」ため、実装より先にツール定義を決める。
- 接続するデータソース(Azure SQL / Azure Files / 業務 API)を列挙する。
- データソースごとに 1 つの MCP Server を立てる(責務分割・障害分離のため)。
- ツールは 「一覧」「検索」「取得」の 3 系統 を基本形にする。エージェントは一覧で語彙を得てから検索するため、
list_*が無いと的外れな検索語で空振りする。 - 各ツールの
name/description/inputSchemaを確定する。→ protocol.md - 一覧・検索・取得のすべてで、認証済み利用者が参照できる範囲を決める。MI のデータアクセス権を本人権限と扱わない。設計確定後に admin の環境・DLP/ACP チェックを実行し、NG があれば停止する。索引取得は 認可契約 に従う。
例: 部品DB MCP -> list_categories / search_parts / get_part_inquiries / search_inquiries
例: 文書共有 MCP -> list_categories / list_documents / get_document / search_documents
Step 2: Entra アプリ登録でスコープを公開し、クライアントを事前承認する
MCP Server を 保護対象 API として登録する。ここを飛ばすと、後でトークン取得が AADSTS650057 で失敗する。
python .github/skills/mcp-server/scripts/configure_entra_api.py
このスクリプトは以下を行う。
identifierUrisにapi://{app-id}を設定する。- OAuth2 権限スコープ(既定
MCP.Access)を公開する。 - パブリッククライアント(Azure CLI / Azure PowerShell)を 事前承認し、同意画面なしでトークンを取得できるようにする。
重要: スコープ公開と事前承認を 1 回の PATCH にまとめてはいけない。 新規スコープ ID が未登録扱いになり
InvalidValue ... delegatedPermissionIdsで失敗する。 スクリプトは 2 段階に分割して送信している。
Step 3: Azure 基盤を構築する
azure-infra スキル の手順で以下を構築する。本スキル固有の要件のみ以下に示す。
| リソース | 本スキル固有の要件 |
|---|---|
| Function App | Flex Consumption + VNet 統合 + システム割り当て MI。HTTP は公開のまま |
| データストア | Private Endpoint のみ(publicNetworkAccess=Disabled・共有キー禁止) |
| RBAC | Function App の MI にデータ層へのデータプレーンロールを付与 |
| Functions 用ストレージ | 共有キー禁止のため AzureWebJobsStorage は使わず、AzureWebJobsStorage__accountName の ID ベース接続にする。MI に Storage Blob Data Owner が必要 |
| ファイル共有 | 共有の作成はマネジメントプレーンで行う(データプレーンのロールでは共有を作成できない) |
Function App の作成直後は AzureWebJobsStorage に共有キーの接続文字列が入る。共有キー禁止のストレージでは
この状態でホストが起動できず、デプロイは成功するのに全ルートが 404 になる。作成直後に必ず切り替える。
python .github/skills/mcp-server/scripts/configure_function_storage.py --app <function-app-name> --account <storage-account>
Step 4: MCP Server を実装する
Azure Functions(Node.js 20 / TypeScript / v4 プログラミングモデル)で実装する。
<server-name>/
├── local.settings.json # ★ 必須(Step 5 参照)
├── host.json
├── package.json
├── src/
│ ├── functions/
│ │ ├── mcp.ts # route: mcp 認可 + JSON-RPC ディスパッチ
│ │ └── adminSeed.ts # route: seed-* 一時的なデータ投入用(Step 8 で削除)
│ ├── lib/
│ │ ├── auth.ts # 受信 JWT 検証
│ │ └── <datasource>.ts # 送信 Managed Identity アクセス
│ └── tools/
│ └── <domain>Tools.ts # ツール定義 + ハンドラ
- ハンドラは
authLevel: 'anonymous'にし、認可はコード側の JWT 検証で行う(関数キーを使わない)。 src/index.tsは各関数モジュールをimportするだけにする。存在しないモジュールを 1 行でも import すると worker が 起動できず、全ルートが 404 になる(関数を削除・退避したら import も必ず消す)。- Copilot Studio から使うなら Streamable HTTP の必須要件を満たす(通知には 202 + 本文なし、
protocolVersionは2025-03-26以降をネゴシエート、GET / DELETE に 405)。これを外すと curl では成功するのにコネクタ接続だけが失敗する。 - 実装の詳細は protocol.md と auth-model.md を参照。
- ストレージ上の文書を読ませるツールを作るなら、パス検証・出力上限・戻り値の注意書きを必ず入れる。 → file-backed-tools.md
- SQL を読むツールを作るなら、全クエリのパラメータ化と読み取り専用権限を前提にする。 → sql-tools-pattern.md
- MCP Server は Copilot Studio のツールとして登録し、Code App のデータソースへ直接追加しない。 Code App に表示するページ画像は、認可された投入処理で描画・検証して Dataverse の索引へ保存する。 エージェント応答へ Base64 画像を載せず、会話経路と画面表示経路を分離する。
Step 5: デプロイする
python .github/skills/mcp-server/scripts/deploy_mcp_function.py --project <path> --app <function-app-name>
このスクリプトは デプロイ前チェック → ビルド → publish → ルート実測検証 を通しで行う。手作業で func を叩かない。
事前チェックの内容(いずれも実際に失敗した事象への恒久対策):
| チェック | 理由 |
|---|---|
local.settings.json の存在と FUNCTIONS_WORKER_RUNTIME | 無いと Worker runtime cannot be 'None' で publish が失敗する。このファイルは .gitignore 対象のため clone 直後は存在しない |
func コマンドの実行可否 | npm グローバルインストールで zip が未展開のまま残ることがある。失敗時は自動で展開して復旧する |
ビルド出力(dist/)の存在 | 空パッケージのままデプロイされ、ルートが 404 になるのを防ぐ |
ビルド前の dist/ 削除 | tsc は dist/ をクリーンしない。削除した関数の .js が残り、ルートが復活する |
src/index.ts の相対 import が全て実在すること | 解決できない import が 1 行でもあると worker が起動できず、残すはずのルートまで含めて全滅する |
AzureWebJobsStorage が ID ベース接続であること | 共有キー禁止のストレージに接続文字列で繋ごうとするとホストが起動しない(Step 3) |
| publish 後のルート実測 | func publish は成功しても終了コード 1 と "appears to be unhealthy" を返すことがある。終了コードで判定しない |
ルートが 404 のままなら、ホストの起動ログで原因を特定する(0 functions loaded なら worker の起動失敗)。
python .github/skills/mcp-server/scripts/query_host_logs.py --component <app-insights-name>
Step 6: データを投入する
データ層が Private Endpoint 内にあるため、ローカル PC からは接続できない。 VNet 統合された Function App 内の一時的な管理エンドポイント経由で投入する。
python .github/skills/mcp-server/scripts/seed_mcp_data.py
- 管理エンドポイントは 共有シークレット(
ADMIN_SEED_SECRET) で保護し、アプリ設定に置く。 - DB のスキーマ作成・MI へのロール付与は Entra 管理者権限が要るため、実行者のアクセストークンを渡して実行する。
- 詳細は private-data-seeding.md。
- 継続的な文書取り込みは一時シードと分け、SharePoint / 業務ストレージの作成イベントから Agent flow を起動する。 Function は PDF 正本と検索用サイドカーを書き、図番・改訂・ページを照合した画像キャッシュを Dataverse の 検証済み索引へ保存する。既存の人手注記サイドカーは上書きしない。
Step 7: エンドツーエンドで検証する
python .github/skills/mcp-server/scripts/verify_mcp_server.py
api://{app-id}/.default のトークンを取得し、Streamable HTTP 準拠の検査に続けて
tools/list でツール一覧、tools/call で実データが返ることを確認する。
ツール一覧が返るだけでは不十分で、必ず 1 つ以上のツールを実行して中身を見る。
Step 8: 管理エンドポイントを削除して Copilot Studio に登録する
-
投入用の管理エンドポイントを削除して再デプロイする(攻撃面を残さない)。
python .github/skills/mcp-server/scripts/cleanup_admin_endpoints.py --project <path> --app <function-app-name> --route seed-upload --route seed-sql
このスクリプトは関数ファイルの削除に加えて **`src/index.ts` から該当 `import` を除去**し、
`dist/` をクリーンしてから再デプロイする。どちらか一方でも漏れると、残すべき `mcp` を含む全ルートが落ちる。
`--route` は実際の削除対象すべてを列挙する必須引数。上記2ルートは例であり、関数登録の `route` と照合する。独自名の管理関数は自動検出されないため、削除計画に明示してから実行する。
2. アプリ設定から `ADMIN_SEED_SECRET` を削除する。
3. 残すルートが 401、削除したルートが 404 であることを HTTP で実測する。
4. Copilot Studio の MCP オンボーディングウィザードで、エージェントにツールとして追加する。
→ [copilot-studio-registration.md](references/copilot-studio-registration.md)
```powershell
python .github/skills/mcp-server/scripts/configure_connector_oauth.py --audience $env:MCP_API_AUDIENCE --secret-out .secrets/connector-oauth.json
python .github/skills/mcp-server/scripts/generate_copilot_studio_guide.py `
--server-name example-files-mcp `
--server-description "文書を検索して内容を取得します。" `
--server-url "https://<function-app>.azurewebsites.net/api/mcp" `
--display-name "文書 MCP 接続" `
--environment-id $env:POWER_PLATFORM_ENVIRONMENT_ID `
--output "<server-dir>/copilot-studio-connection.md"
-
Server nameは 1~64 文字の英字・数字・ハイフン・ドットのみ。日本語や空白は Power Platform の内部コネクタ名作成で 400 になるため、生成スクリプトが事前に拒否する。 -
生成する Scopes は
<API scope> offline_accessとする。offline_accessがないとリフレッシュトークンが 発行されず、アクセストークン失効後に接続がMissing refresh tokenで無効になる。生成スクリプトが必ず付与する。 -
Copilot Studioで利用する場合は、エージェント固有の
https://copilotstudio.microsoft.com/c2/tenants/<tenant-id>/environments/<environment-id>/bots/<bot-schema>/channels/pva-studio/conversations/<conversation-id>/user-connectionsを直接提示する。Power Apps / Power Automateのみで利用する場合は、コネクタ固有のhttps://make.preview.powerapps.com/environments/<environment-id>/connections/available/<connector-id>だけを提示し、Copilot Studio URLは出さない。 接続作成、サインイン、同意、Studioでの接続選択は利用者本人が行い、エージェントは事後検証を担当する。 -
接続作成後は
pac connection list --environment <environment-id>で各接続のIDを取得し、https://make.preview.powerapps.com/environments/<environment-id>/connections/<connector-id>/<connection-id>/details形式のリンクを接続ごとに利用者へ提示する。接続の作成や再認証は代行しない。 -
OAuthポップアップはPower Appsへ戻るまで閉じない。認可中断で残った
Error接続は再利用せず、 個別詳細ページから削除して利用者本人が新しい接続を作成する。 -
生成 MD は Client secret を含む。MCP Server ごとに分け、先に
.gitignoreへ追加する。 -
pac connector downloadのapiProperties.jsonはclientSecretを含まない。そのままpac connector updateへ渡すと有効なsecretが失われるため、既存コネクタの更新には必ず次を使う。python .github/skills/mcp-server/scripts/update_connector_oauth.py ` --environment $env:POWER_PLATFORM_ENVIRONMENT_ID ` --connector-id <connector-id> ` --secret-file .secrets/connector-oauth.json -
認証は
OAuth 2.0、構成はManualを選ぶ。接続画面では任意の表示名も生成 MD から貼り付ける。 -
コネクタ作成後に表示された callback URL、または
AADSTS50011に表示された URI は、次で Entra に追加する。python .github/skills/mcp-server/scripts/add_connector_redirect_uri.py ` --audience $env:MCP_API_AUDIENCE ` --redirect-uri "https://global.consent.azure-apim.net/redirect/<connector-id>" -
This tool is blocked by your data loss prevention policyと表示された場合は、公開やポリシー変更を 繰り返さず、Copilot Studio の DLP 診断 に従って適用ポリシーと コネクタ分類を読み取り確認する。認証はstandardスキルのauth_helper.pyに統一し、 PowerShell の対話サインインやブラウザ認証を追加しない。 -
DLP を解消したのに Edit 画面で
Couldn't load MCP tools ... HTTP 401が出る場合は、 DLP やアプリ登録を疑う前に必ず次のスクリプトで原因を分類する。python .github/skills/mcp-server/scripts/diagnose_connector_token.py --apps $env:MCP_FUNCTION_APPS繰り返す
jwt expiredと診断された場合は設定不備ではなく、Copilot Studio 側が保持する アクセストークンが失効したまま更新されていないだけ。Copilot Studio(または Power Apps > Connections)で対象コネクタの接続を選び、**再認証(reconnect)**すれば解消する。 詳細は troubleshooting.md を参照。
OpenAPI をコード管理する必要がある場合だけ、カスタムコネクタのインポート方式を使う。
複数の MCP Server を 1 エージェントに束ねる場合は、コネクタの description とエージェントの指示文の
**両方に「どの質問でどのサーバーを使うか」**を明記しないと選択を誤る。
エージェント本体の構築は copilot-studio-v2 スキル に委譲する。
- (任意)M365 Copilot(Cowork)からも使う場合は、同じ MCP Server を Cowork プラグインの
agentConnectors.remoteMcpServerとしても登録する。Server の実装は増えない (API アプリを 1 つに集約しておけば登録が 2 系統になるだけ)。 → cowork/references/custom-mcp-connector.md
検証チェックリスト
- ツール定義に
list_*系があり、エージェントが語彙を獲得できる - Entra アプリ登録でスコープを公開し、クライアントを事前承認した(Step 2)
- Function App の HTTP は公開、データ層は Private Endpoint のみ
- 関数キー・接続文字列・共有キーを一切使っていない
-
local.settings.jsonが存在しFUNCTIONS_WORKER_RUNTIMEが設定されている -
AzureWebJobsStorage__accountNameによる ID ベース接続になっている(接続文字列が残っていない) -
src/index.tsの相対 import が全て実在するモジュールを指している - デプロイ成否を 終了コードではなくルートの HTTP 実測で判定した
-
tools/callで実データが返ることを確認した - Streamable HTTP 準拠(通知に 202 / バージョンネゴシエーション / GET に 405)を検証した
- 管理エンドポイントを削除し、
ADMIN_SEED_SECRETをアプリ設定から消した - 削除後に「残すルート = 401 / 削除したルート = 404」を HTTP で実測した
- スクリプトが
auth_helper経由で非対話に完走する(az loginを要求しない) - Copilot Studio 接続の Scopes に
<API scope> offline_accessが含まれ、再接続後に自動更新できる - 利用者へ対象環境の Power Apps 接続一覧と Copilot Studio エージェント一覧のURLを提示した
- 作成済みの各接続について Power Apps の個別詳細URLを利用者へ提示した
- Copilot Studio の Edit 画面で 401 が出たら、DLP/設定変更の前に
diagnose_connector_token.pyで 「未接続・トークン失効・その他」を切り分けた
参考リンク
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
Copilot Studio 新 UI の Agent flows / Workflows を構築・公開・検証する。Dataverse レコード作成/更新トリガーから既存の発行済み Copilot Studio v2 を Agent ノードで呼ぶ標準経路、Code Apps との非同期要求/結果連携、および手動 Start + inline Agent の API ライフサイクル検証を扱う。
株主総会の想定問答を、IR 抜粋(決算短信・説明資料・招集通知など)を根拠に下書きし、利用者の確認後に Dataverse の想定問答テーブルへ「下書き」として登録するスキル。 Use when ユーザーが「配当について想定問答を作って」「この論点の想定問答を 3 件追加して」「招集通知から想定問答を作って」「想定問答を登録して」と依頼したとき。 Dataverse MCP コネクタ(describe / read_query / search_data / create_record / update_record)を使用する。削除・テーブル変更のツールは使わない。
株主総会の想定問答を点検し、根拠の IR 抜粋に無い数値・存在しない根拠 ID・回答者や注意事項の抜け・趣旨の重複・下書きのまま残っているものを一覧にするスキル(書き込みはしない)。 Use when ユーザーが「想定問答を点検して」「根拠の無い数値が無いか確認して」「下書きの想定問答を一覧にして」と依頼したとき。 Dataverse MCP コネクタ(describe / read_query / search_data)を使用する。書き込み・削除のツールは使わない。
株主総会の質疑応答のリハーサル台本(議長・株主・回答役員の読み上げ原稿)を、承認済みの想定問答から作り、Dataverse のリハーサル台本テーブルへ登録するスキル。 Use when ユーザーが「リハーサルの台本を作って」「QA-001〜QA-010 で読み上げ原稿を作って」「番号を言わない株主も入れた台本を作って」と依頼したとき。 Dataverse MCP コネクタ(describe / read_query / search_data / create_record / update_record)を使用する。削除・テーブル変更のツールは使わない。
AI Builder の AI プロンプト(GPT Dynamic Prompt)を Dataverse API で作成し、Copilot Studio エージェントにツール(アクション)として追加する。Power Automate フローとの統合パターンも含む。