entra-scim-mcp
entra-scim-mcp
Microsoft Entra SCIM 2.0 プロビジョニング API(2026年4月 GA)向けの Model Context Protocol サーバー。https://graph.microsoft.com/rp/scim に対するユーザーおよびグループのライフサイクル操作を、Claude などのエージェント向けの MCP ツールとして公開します。
できること
テナントの SCIM 機能を検出する(
get_service_provider_config、list_resource_types、list_schemas)ユーザーのプロビジョニング、読み取り、更新、プロビジョニング解除を行う(Custom Security Attributes とライフサイクル属性を含む)
グループの作成、更新、削除を行い、API の厳格な PATCH ルールを自動的に尊重したメンバーシップ管理を行う
Related MCP server: mcp-server-microsoft365
前提条件
このサーバーがテナントと通信できるようにするには、Microsoft のドキュメントに記載された一度きりのセットアップを完了してください。
Entra ID P1(または P1 を含む任意の SKU)と、課金のためにリンクする Azure サブスクリプション。
ID ガバナンス → ダッシュボードで SCIM Provisioning API を有効にし、課金リソースグループをリンクする。
必要な Microsoft Graph アプリケーション権限を持つアプリケーションを登録する:
User.ReadWrite.All、Group.ReadWrite.All(コアのライフサイクル)CustomSecAttributeAssignment.ReadWrite.All、CustomSecAttributeDefinition.Read.All(CSA ツール)User-LifeCycleInfo.ReadWrite.All(ライフサイクルツール)User-Mail.ReadWrite.All、User-Phone.ReadWrite.All、User.EnableDisableAccount.All(最小権限の代替) 管理者の同意を付与する。
クライアントシークレット を作成するか、PEM クライアント証明書をアップロードする。
すべての SCIM API 呼び出しは課金対象です。このサーバーは、API が要求する範囲を超えたバッチ処理は行いません。
Entra テナントなしで試す
パッケージには Entra SCIM API のローカルモック(entra-scim-mock-server)が同梱されており、Azure のセットアップも API の課金もなしですべてのツールを操作できます:
# shell 1 — start the mock (seeds a small demo tenant)
npx -y --package entra-scim-mcp entra-scim-mock-server次に、MCP サーバーをモックに向けます:
{
"mcpServers": {
"entra-scim-mock": {
"command": "npx",
"args": ["-y", "entra-scim-mcp"],
"env": {
"ENTRA_SCIM_BASE_URL": "http://127.0.0.1:8990",
"ENTRA_SCIM_STATIC_TOKEN": "dev-token"
}
}
}
}モックのフラグ:--port、--token、--seed <file.json>、--no-seed、--capture <file.jsonl>(すべてのリクエスト/レスポンスを記録)、--validator-compat(Microsoft SCIM Validator 向けの RFC 標準準拠の動作 — docs/scim-validator.md を参照)。
インストール / 実行方法
このサーバーは stdio MCP サーバーであり、MCP クライアント(Claude Desktop、Claude Code など)から起動されることを想定しています。
npx -y entra-scim-mcp必要な環境変数:
変数 | 必須 | 説明 |
| はい | ディレクトリ(テナント)GUID |
| はい | アプリ登録(クライアント)GUID |
| いずれか | クライアントシークレットの値(開発用) |
| いずれか | 証明書 と 秘密鍵を含む PEM へのパス |
| 任意 | PEM が暗号化されている場合のパスワード |
ENTRA_CLIENT_SECRET と ENTRA_CLIENT_CERT_PATH のいずれか一方のみを設定してください。
開発 / テスト用の環境変数
変数 | 説明 |
| SCIM のベース URL を上書きします(デフォルトは |
| Azure AD の代わりに固定のベアラートークンを使用します。ガードレール: |
|
|
ドライラン結果は成功したペイロードとして返されます:
{
"dryRun": true,
"request": {
"method": "DELETE",
"url": "https://graph.microsoft.com/rp/scim/users/u-1",
"headers": {}
}
}(DELETE は Accept ヘッダーを付けません。API はその場所で特定の JSON メディアタイプを拒否します。それ以外のすべてのメソッドは Accept: application/json; charset=utf-8 を送信します。)
複数リクエストを行うツール(20 を超える ID を扱う add_group_members など)は、ドライランでは最初のチャンク要求のみを表示します。
Claude Desktop の設定
~/Library/Application Support/Claude/claude_desktop_config.json(macOS)または %APPDATA%\Claude\claude_desktop_config.json(Windows):
{
"mcpServers": {
"entra-scim": {
"command": "npx",
"args": ["-y", "entra-scim-mcp"],
"env": {
"ENTRA_TENANT_ID": "00000000-0000-0000-0000-000000000000",
"ENTRA_CLIENT_ID": "11111111-1111-1111-1111-111111111111",
"ENTRA_CLIENT_SECRET": "..."
}
}
}
}本番環境では、シークレットを証明書に置き換えます:
{
"env": {
"ENTRA_TENANT_ID": "...",
"ENTRA_CLIENT_ID": "...",
"ENTRA_CLIENT_CERT_PATH": "/secure/path/entra-scim-mcp.pem"
}
}ツール
ツール | 目的 |
| 一度きりの機能検出。 |
| SCIM リソースタイプ(User、Group)を列挙する。 |
| SCIM スキーマと Entra 拡張機能を列挙する。 |
| ユーザーを一覧表示する。API の制限されたフィルター(eq/ew、and のみ)とカーソルページネーションをサポートする。 |
| ID により単一のユーザーを読み取る。必要に応じて属性の射影を行う。 |
| 必須属性セット(userName、password、displayName、name.givenName、name.familyName、mailNickname)を強制してユーザーを作成する。 |
| ユーザーを PATCH する。mailNickname の |
| ユーザーを削除する。 |
| 終了属性(例: |
| 属性セットによって射影されたユーザーの CSA を読み取る。 |
| ユーザーに CSA を PATCH する。 |
| API の制限されたフィルタセットでグループを一覧表示する。 |
| 単一のグループを読み取する(メンバーは返されません — |
| グループを作成します。Entra 拡張機能により |
| グループ属性のみを PATCH します(メンバーシップ操作はここでは拒否されます)。 |
| グループに 1 人以上のユーザーを追加します — 1 回の PATCH(API 上限)につき 20 ID で自動的にチャンク分割し、PATCH ごとに 1 つの Operation を実行します。シーケンス途中で失敗した場合は、 |
| グループから 1 人のユーザーを削除します(API は 1 回の PATCH で他の操作と同時に削除できるのは 1 件のみです)。 |
| グループを削除します。 |
このサーバーが代わりに強制する内容
Entra SCIM API には見逃しがちな制約があります。ツールレイヤーは、リクエスト送信前に不正な入力を拒否します。
フィルタの許可リスト:リソースごとに文書化された属性と演算子のみ。
orは拒否されます。externalIdは別の節と組み合わせることはできません。クエリ文字列:
=の周囲に空白は含めない(API は空白があると 400 を返します)。ユーザー PATCH:mailNickname の
removeはブロックされます。アドレスパスのフィルタは正確に[type eq "work"]でなければなりません。グループ PATCH:メンバーシップ操作は専用ツールを通るため、20 メンバー追加の上限と単一削除のルールが保証されます。
メンバー追加のべき等性:API は再追加を成功として扱います
add_group_membersは入力を重複排除します。
エラーは、status、scimType、detail を含む構造化ペイロードとしてエージェントに返されます。
知っておく価値のある API の動作
ドキュメントが曖昧にしか述べていない、または全く述べていないライブ API の動作です。それぞれ実テナントまたは Microsoft SCIM Validator に対する実行で発見されたもので、すべてが処理されます。レスポンスの読み方に影響を与えるため、ここに列挙しています。
DELETE には Accept ヘッダーを付けてはならない。 API は 400 Accept header application/json is invalid と応答する。4 つのバリアントをすべてライブで検証した: ヘッダーなし → 204、*/* → 204、application/json → 400、application/scim+json → 400。このルールを逆転させているのは DELETE だけである。他のすべてのメソッドは JSON の Accept を 必須 とし、それを省略すると文書化された 400 になる。この問題は、最初のライブ実行まで deprovision_user と delete_group を静かに壊し続けた。
Custom Security Attributes は、通常の読み取りでは一切返ってこない。 /Schemas では、属性セットレベルの属性は returned: "request" である。したがって get_user は何を要求しても CSA を含まない。意図が明確な名前で指定したときだけ出現し、射影は セット単位 である: urn:...:CustomSecurityAttributes:<Set>。裸の拡張 URN は即座に拒否される (400 ... not supported in the "attributes" or "excludedAttributes" query parameter)。そのため attributeSets はオプションではなく必須の入力になっている。
CSA の値は型付きであり、型が強制される。 Boolean、Integer、String、複数値 String のすべては、一致する JSON 型として送れば往復しても無傷のまま保持される。1 つの PATCH で複数の属性を同時に送ることもできる。削除に関する動作は 2 つあり、どちらも Microsoft のドキュメントには存在しない:
CSA パスに対する
op: "remove"は、その 1 つの割り当てをクリアし、他の割り当てを残す。複数値属性に対する
[]を使ったreplaceは、割り当てを 削除 する。その後の読み取りでは、空配列ではなく属性自体が省略される。
password は作成時に必須だが、読み取ることは決してできない。 これは writeOnly / returned: never であり、どの応答にも反映されない。作成に必要な完全なセットは userName, password, displayName, name.givenName, name.familyName、 mailNickname である。userName しか要求しない RFC 7643 より,かなり厳格。
グループの displayName は一意ではない。 Entra は重複グループ名を受け入れ、201 を返す。RFC 志向のツールはここで 409 を想定することが多いので、既存グループの検出方法として作成失敗に依存せず、まずフィルターで特定すること。
グループメンバーの削除は、ユーザーではなくメンバーシップの問題である。 members[value eq "<id>"] に対する remove が何にも一致しない場合は、その id が誰のものであっても 404 になる。つまり、単にメンバーではなかったライブユーザーも、ユーザーではなかった GUID と同じ拒否される。これは、メンバーが常に 1 人以上残っている実テナントに対して検証しており、グループが空になった結果ではない。
ケース | 結果 |
実際のメンバー | 204 |
一度もメンバーでない実在ユーザー | 404 |
ユーザーとして存在したことのない well-formed GUID | 404 |
先にユーザーが削除されたメンバー | 404 |
次の 2 つの結果を認識しておくとよい。ユーザーを削除すると、そのユーザーのメンバーシップは消える。つまり、「削除してからメンバーシップを削除する」順序は、他の非メンバーとまったく同じ結果になる。これは削除の前後に members.value フィルター付きで list_groups を検索して確認した。また、エラーメッセージが名前を挙げるのはメンバーではなく グループ である。(Resource '<groupId>' does not exist or one of its queried reference-property objects are not present)。グループは明らかに存在するので、変に読める。意図的に存在しないグループ id で探したプローブは同じ文章でその id を名指しした。つまりテキストは単に PATCH ターゲットを反映しているだけである。モックはこの挙動を修正しはせず、まさにこのとおりを再現する。npx tsx scripts/probe-member-removal.ts --confirm (約 17 課金呼び出し) で再実行できる。
エラーは構造化されており、そのまま表示する価値がある。 失敗は status、scimType、detail を持ち、detail のテキストは異常になるほど具体的である (失敗した操作特徴インデックスと制約を特定する)。ツールはエラーを単なるメッセージに平坦化せず、そのまま通過させる。
すべての呼び出しが課金される。 API 自体が必須とする以外にバッチ処理は存在しない。そのため、頻繁に話しかけるエージェントは実際のコストになる。add_group_members は API の 20 メンバー上限で分割され、バッチが発生する唯一の場所である。
実テナントでのテスト
テストスイートは実テナントには一切触れない。ライブの Entra に対してツールを検証するには、gitignore している .env に資格情報を入れ、スモーク スクリプトを実行する。
cd node
cp .env.example .env # then fill in tenant id, client id, and the secret VALUE.env を読むのは scripts/ 内のスクリプトだけである。公開されるサーバーは常に process.env を読むため、MCP クライアントが起動するどのディレクトリにも関係なく、.env が誤って拾われることはない。環境変数にすでに設定されている内容は、常にファイルより優先される。
変数 | 目的 |
| 使い捨ての |
| 属性セット名。2 つの Custom Security Attribute ツールをカバーするように設定する |
| そのセット内の属性名 |
| 割り当てる値。CSA は型付きなので、API は型の不一致を拒否する: |
スモーク スクリプト
ENTRA_SCIM_LIVE=1 npm run smoke:live # bash
$env:ENTRA_SCIM_LIVE=1; npm run smoke:live # PowerShellフラグを渡すには、スクリプトを直接呼び出すこと。npm run x -- --flag は Windows では確実に転送されない:
npx tsx scripts/live-smoke.ts --confirm18 個すべてのツールを順序付きで一回パスし、約 21 課金呼び出しを消費する。2 つのユーザーと 1 つのグループを作成し、すべての読み取り、PATCH、delete をそれらに対して実行し、最後に削除する。重要な点:
うっかり実行されない。
ENTRA_SCIM_LIVE=1または--confirmがない場合、テナント、エンドポイント、コストを表示して終了する。さらにENTRA_SCIM_DRY_RUNまたはENTRA_SCIM_STATIC_TOKENが設定されている場合は、--rehearseを付けない限り完全に拒否する。それは実行してもライブ API について何も証明しないからだ。最初の失敗では止まらない。 失敗したステップは、それに依存するものを
skipとマークし、独立なものはすべて実行し続ける。したがって 1 回の実行で、ライブ API がどのツールを受け入れるのかがわかる。失敗がの場合は終了コウードが非ゼロになる。テスト・アイデンティティは一目瞭然。
scim-smoke-<runId>-1@<domain>とSCIM Smoke <runId>グループ。クリーンアップは保証されている。 作成されたものはすべて
finallyブロックで削除され、残ったものには id が表示される。クラッシュによる実行からはnpx tsx scripts/live-smoke.ts --sweepで、取り残されたscim-smoke-*ユーザーを一覧表示し、次に--confirmを付けて削除する。sweep はscim-smoke-プレフィックスを持たないアカウントには絶対に触れない。
2 つの Custom Security Attribute ツールは、テナントに属性セットが存在し、Entra 管理ポータル -> Protection -> Custom security attributes で ENTRA_SCIM_SMOKE_CSA_SET / ENTRA_SCIM_SMOKE_CSA_ATTR がそれを指定するまで skip を報告する。それ以外はすべて無人で実行される。
セットが存在すると、約 9 課金呼び出しで、その 2 つのツールだけを検証でき、21 回の代わりに済む。これはすべての値を読み戻すもので、これは API が受け取るが何も保存しない PATCH が 合格に見えてしまうのを防ぎ、各データ型をカバーし、削除も実行する:
npx tsx scripts/live-smoke.ts --csa-only --confirmセットの形を一度宣言すると、実行で型ごとにテスト値が導出される:
ENTRA_SCIM_SMOKE_CSA_ATTRS=isManaged:bool,accountType:string,trustLevel:int,locations:string[]4 つのすべての型でライブ確認済み: 値は無損で往復し、op: "remove" は 1 つの割り当てをクリアして残りを残す。複数値属性を [] に置換するとその属性は削除され、読み返すと空配列ではなく属性自体が消える。
お金を使う本番前にゼロコストでリハーサルする — これは API ではなくスクリプトを検証する:
# no network at all
ENTRA_SCIM_DRY_RUN=1 npx tsx scripts/live-smoke.ts --rehearse
# or against the local mock: start it in one shell...
npm run mock
# ...and in another, aim the script at it
export ENTRA_SCIM_BASE_URL=http://127.0.0.1:8990
export ENTRA_SCIM_STATIC_TOKEN=dev-token
npx tsx scripts/live-smoke.ts --rehearse価値があるのはモック・リハーサルである。これは実 HTTP、実 ids、完全な create/patch/delete の順序を実施するため、コストがかかる前に順序バグとクリーンアップバグを発見できる。ライブ実行の代替にはならない。最初のライブ実行は、どのモック実行でも見つけられなかった 2 つのバグを見つけた (テストレグが実際に捉えたもの を参照)。
テストレグが実際に発見したもの
3 つの独立したレグがあり、それぞれが他には発見できないことを見つけた。この 3 つを常に揃えている理由である:
レグ | コスト | 発見した内容 |
モック + ユニット スイート | 無料 | 連続、検証、クリーンアップのバグ。高速だが、同じ前提を共有しているため、誤った前提を検出できない。 |
ライブ テナント ( | 約 21 課金呼び出し | DELETE の |
無料 | モックの忠実さの 7 箇所のギャップ — 実 GSCIM クライアントが期待するより寛容 だった箇所が、それぞれ実際の挙動を隠していた。 |
持ち帰る価値のあるパターン: モックの寛容さは実際の推移を隠す。 ライブ実行が発見したすべての不具合は、まず完全なモックスイートを通り抜けていた。その理由は、モックがクライアントと同じドキュメントの読み方から書かれていたからだ。三方クライアント (SCIM Validator) と実際のテナントだけが、その循環を打ち破ることができた。
ライブテナントを対話的に操作する
リポジトリルートの .mcp.json は、scripts/dev-server.mjs を使ってサーバーを Claude Code に登録する。これは node/.env を読み、ビルド済みサーバーを起動するため、コミット済みの設定ファイルにシークエレットが含まれることはない。
これはビルド済みサーバーを実行するので、MCP クライアントが起動する前に node/dist が存在していなければならない。新規クローンの場合は次の手順で作成する:
cd node && npm install # the "prepare" script builds as part of installソースコードを変更した後は、再ビルドし、クライアントを再起動することでこのコマンドが再起動する:
cd node && npm run build開発
cd node
npm install # installs, then builds via "prepare"
npm test
npm run lint # ESLint, type-aware
npm run format:check # Prettier
npm run typecheck # strict tsc over src, test and scripts
npm run test:coverage # vitest with the coverage gate
npm run build # rebuild after a source change
npm run mock # run the local mock server (tsx, no build needed)
npm run mock:capture # mock in validator-compat mode, capturing traffic to captures/npm audit --audit-level=high に加えて、CI はすべてのプッシュとプル リクエストで npm run lint, format:check, typecheck, test の 4 つのゲートを実行する。
サーバーは、実テナントへのテスト依存を持たない。ユニットテストはフィルター、パッチ、クエリ、クライアント層をカバーし、結合テストはプロセス内モックサーバーを起動し、実際の HTTP を経由して すべての MCP ツールをエンドツーエンドで実行する (node/test/integration/)。取得した SCIM Validator セッションは npm run fixtures:convert でリプレイフィクスチャに変換できる。それらでは証明できない唯一のこと、つまりライブ API が実際にペイロードを受け付けることは、実テナントでのテスト の章を参照すること。
リリース
バージョンは次の 4 箇所に定義されている: node/package.json, node/package-lock.json (2 箇所)、そして server.json (2 箇所、一回はレジストリ記録、もう一回は npm パッケージの指定)。1 つのコマンドでこれらすべてに書き込む:
cd node
npm version minor # or patch / major — writes all four, stages three
cd ..
git commit -m "v0.2.0" # the version npm just printed
git tag -a v0.2.0 -m v0.2.0
git push --follow-tags-a が重要です。--follow-tags は注釈付きタグのみをプッシュするため、軽量タグの git tag v0.2.0 はマシン上に残り、プッシュはタグを一切送らずに成功を報告します — つまり、リリースが単に実行されないのです。
npm version は package.json とロックファイルを更新し、その後 version ライフサイクルスクリプトがその内容を server.json に反映し、結果をステージします。通常 npm version はコミットとタグの両方を行いますが、ここではどちらも 行いません。npm はバージョニング対象のパッケージの隣に .git を探しますが、このパッケージは node/ にあり、リポジトリの .git はその一つ上にあるため、npm は自分が git リポジトリの中にいないと判断し、何も言わずにそれらの手順をスキップします。だからこそ、上記の明示的なコミットとタグ付けが必要なのです。これを間違えると、git push --follow-tags は黙って何もプッシュしません。追従するはずだったタグが作成されていないからです。
npm run check:version は4つのすべてが一致することを検証します。CI はすべてのプッシュでそれを実行し、リリースワークフローはタグ自体に対してもう一度実行します。つまり、package.json と不一致のタグは、何かが公開される前に失敗します。サーバーが MCP ハンドシェイクで報告するバージョンは、実行時に package.json から読み取られるため、自動的に追従されます。
v* タグをプッシュすると、.github/workflows/release.yml が実行されます:
verify — lint、format、typecheck、カバレッジ付きテスト、バージョン/タグのチェック、そしてライブレジストリに対する
mcp-publisher validateを実行します。verify on Windows —
windows-latest上で同じテストを再度実行します。モックが実際のソケットにバインドし、キャプチャシンクが実際のパスを書き込むためです。これがないと、リリースゲートは通常のコミットに対するゲート(CI は Windows 上でも実行します)よりも弱くなります。publish —
npm publishを実行し、新しいバージョンが npm 上で表示可能になるまで待機し、その後server.jsonを MCP Registry に公開します。
これらすべてが成功するまで、何も公開されません。
どちらの公開も GitHub OIDC で認証されるため、リポジトリには秘密情報が一切存在しません — 漏洩したり、最悪の瞬間に失効していると判明する publish トークンは存在しないのです。
レジストリ | このワークフローを認証する方法 |
npm | パッケージの trusted publisher を、このリポジトリと、環境指定なしの workflow ファイル |
MCP Registry |
|
この両者が、公開ジョブが id-token: write を要求する理由です。
MCP Registry は、package.json を取得して server.json の name と mcpName を比較することで、npm パッケージの所有を証明します。両方とも io.github.darrenjrobinson/entra-scim-mcp であり、check:version はそれらが一致し続けることを検証します。
ワークフローのファイル名を変更したり、公開ジョブに environment: を追加したりすると、npmjs.com 上の設定が一致するよう更新されるまで、npm の trusted publisher は機能しません — OIDC クレームは厳密に照合されます。
ライセンス
MIT — LICENSE を参照してください。
This server cannot be deployed
Maintenance
Related MCP Connectors
- mcp-serverOAuthcom.make
Give your AI agents the tools to build, manage, and run automation workflows.
Context and control for any AI. Agent identity, scoped access, and proof across tools.
- FullmaktOAuthai.fullmakt
Credential broker for AI agents: scoped, revocable API access with policy enforcement and audit.
Connect AI agents to 1000+ apps with managed authentication and tool-calling.
Related MCP Servers
- FlicenseAqualityDmaintenanceEnables AI assistants to inspect employee access, list failed onboarding events, and retry provisioning operations.3-
- AlicenseAqualityBmaintenanceEnables identity provisioning and management for Microsoft 365/Entra ID via Microsoft Graph, including user creation, license assignment, group membership management, and more, with a focus on least-privilege and idempotency.18Apache 2.0
- AlicenseNot gradedqualityFmaintenanceEnables AI agents to query Microsoft Entra data using natural language, converting requests into Microsoft Graph API calls for read-only enterprise IT scenarios.58CC BY-4.0
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to read conversations, send messages, and manage members in Microsoft Teams via the Teams Chat Service REST API.291 npm8MIT