【Okta for AI Agents】Notion MCP を Cross App Access で呼び出してみた【生成AIセキュリティ対策シリーズ】
はじめに
こんにちは、ネクストモード株式会社テクニカルサポート担当です。
前回は Slack MCP を STS(Brokered Consent)で扱いました。今回はその続きとして、ID-JAG を受け取れる Notion MCP を Cross App Access(XAA) で呼び出します。
なお、Notion MCP の Enterprise-managed connections は Enterprise プラン前提の機能として案内されています。
AgentCore Runtime 上のエージェントには常設の Notion トークンを置かず、XAA でその都度発行される access token だけで remote Notion MCP を呼び、情報が取得できることを検証しました。併せて、Okta Admin Console で Notion の接続を切ることでエージェントが Notion から情報が取得できなくなることも検証できました。Notion 側の OAuth クライアントと、Okta 側の Resource server / Resource connections もセットです。前回の Slack では初回に同意画面がありましたが、今回は出ていません。
この記事では AWS の ECR や CloudWatch の画面にはあまり立ち入らず、次の 3 点に絞っています。
- Notion 側の OAuth クライアントと Resource server(XAA)設定
- Okta Admin Console での接続先登録と Resource connections
- Notion Resource connection の許可/拒否が、実際の Notion 読取結果として見えること
いちばん最初からうまくいったわけではなく、Resource Indicator の扱い違いや、SSO・Enterprise-managed connections まわりの寄り道もありました。本筋のあとに、その話も書きます。
※本記事は執筆時点の情報に基づきます。Early Access や仕様変更により、画面や挙動が変わる場合があります。最新情報は公式ドキュメントをご確認ください。
今回確認したこと
- Notion アプリ統合を追加し、Okta の Resource server(XAA) を Enable する(STS は使わない)
- AI Agent の Resource connections に Notion を追加する
- Runtime に 常設の Notion トークンを置かず、検証用ワークスペースのデータベースを実際に読む
- Notion Resource connection を Deactivate すると読めなくなり、Activate すると再び読める
- 同意画面が出ないことと、Resource Indicator・SSO まわりのハマりどころ
「呼べた/呼べない」だけだと味気ないので、読めた中身が本物の Notion であることと、接続を外すと止まることを同じ検証のなかで揃える、というのが今回のゴールです。
今回の検証構成
構成は remote Notion MCP + Okta Cross App Access(XAA) です。エージェントは https://mcp.notion.com/mcp を、XAA で受け取った Notion access token だけで呼びます。常設の Notion トークンは置きません。トークンの最終発行は Notion 側の認可サーバです。
環境は、AgentCore Runtime(東京)、Okta for AI Agents に取り込み済みの agentcore_demo、検証用 Notion ワークスペース、remote MCP です。前回と同じ Okta Org / エージェントです。同じ Runtime には Slack STS も載せていますが、今回の検証では Notion の Resource connection だけを活用しています。エージェントに Management API 用のトークンは持たせていません。応答の画面は、同じエージェントをローカルのチャットから叩いた結果です。
Notion 側の準備
今回の Notion 側の前提は、次の 3 つです。Notion MCP 用の OAuth クライアントを用意すること、Okta と同じユーザーで SSO できる状態にすること、Enterprise-managed connections を有効にすることです。
XAA で仲介するにしても、Notion 側にはエージェント用の OAuth クライアントが必要です。今回は Notion MCP の Dynamic Client Registration(https://mcp.notion.com/register)で作り、返ってきた Client ID を Okta の Resource connection に載せました。Client secret は Runtime の環境変数に置きますが、Notion の access token を常設する構成ではありません。値は公開していません。
あわせて、Okta を Notion の IdP にしました。SAML SSO を有効にし、Okta と同じメールのユーザーがワークスペースのメンバーとして入れることが必要です。
また、以下の手順で Notion 側の Enterprise-managed connections を有効にする必要があります。
- Notion 組織コンソール内の一般メニューから「エンタープライズ管理のコネクト」を有効にします。

- 確認画面を進めます。

- 以下のように反映されます。

Okta 側の設定
ここからが今回の検証の本編です。Okta Admin Console で、Notion 向けの Resource server(XAA)と、エージェントの Resource connections を設定します。
補足:2026.08.0 から画面構成が変わり、Resource server / MCP Servers は Applications and Resources 配下にあります。AI Agents は従来どおり Directory 配下です。
設定の流れは次のとおりです。
- Notion アプリ統合の Resource server で XAA を有効にします。
- AI Agent の Resource connections に Notion を追加し、ACTIVE にします。
- 接続詳細の AI agent's client ID registered in this app に、Notion 側の OAuth クライアント ID を載せます。
今回の Resource server では XAA を Enable します。執筆時点の OIN Notion には Cross App Access があり、Brokered Consent(STS)の項目自体は出ませんでした。実行時に効いたスイッチは AI Agent の Notion Resource connection でした。
Resource server を設定する
- Okta Admin Console > Applications and Resources > Applications で、Notion のアプリ統合を開きます。
- Resource server タブを開きます。
- Cross App Access (XAA) を選び、Edit をクリックします。

- Issuer URL には
https://mcp.notion.comを設定し、Save します。Audience は空のままで問題ありません。
- Enabled と表示されていることを確認します。

Resource connections に載せる
続いて、前回までの記事で取り込み済みの AI Agent(検証では agentcore_demo)に、Notion を接続先として追加します。ここで許可したアプリに対してだけ、エージェントが XAA を進められることを確認します。
- Okta Admin Console > Directory > AI Agents で対象エージェントを開き、Resource connections を表示します。

- Resource type として Application を選び、Connect to では App configured for AI Agent access を選択します。
- Application instance で Notion を選び Add します。この画面で表示される Resource Indicator は AgentCore Runtime に設定します。AI agent's client ID registered in this app には、Okta エージェントの Client ID ではなく、先に Notion へ登録した OAuth クライアントの Client ID を入れます。 ここを Okta エージェントの Client ID と取り違えると、Notion 側の token エンドポイントで
invalid_clientになります。
- Resource connections に Application として Notion が追加されていることを確認します。

チャット UI で挙動を確認する
動きとしては、次の順です。
- ユーザーの
id_tokenを付けてエージェントを呼ぶ - エージェントが Okta で
id_token→ ID-JAG(audはhttps://mcp.notion.com)に換える - エージェントがその ID-JAG を Notion の token エンドポイントへ渡し、Notion access token を得る
- エージェントが Bearer で Notion MCP を呼ぶと、読取結果が返る
つまり、Okta は Notion に渡すための ID-JAG を発行し、最終的な Notion access token は Notion 側が発行します。
つまり、Runtime が長期保持する Notion トークンで直接読むのではなく、Okta と Notion のトークン交換で都度 Notion access token を取得して読む構成です。
常設の Notion トークンは Runtime の環境変数に置きません。止める/通すは Okta Admin の Notion Resource connection です。AWS はエージェントが動く場所、という位置づけで、今回の主題は Okta で行う接続制御のほうです。同意画面は出ていません。
許可時に読めた内容
MCP が「呼べた」だけだと、中身が本物かどうか分かりません。そこでエージェントを呼び出し、応答に Notion の実データが乗っているかを見ました。対象は検証用ワークスペースです。プロンプトでは 1 件検索してタイトルだけ報告させ、作成・更新・削除は禁止しました。
応答にはタイトルの「🤖 プロジェクト&タスク」とURLが出力されました。

常設トークンを置かなくても、エージェントの応答に Notion のタイトルが載る、というのがここで見たかったポイントです。
接続を無効化すると本当に止まるのか
同じ構成のまま、Admin の Notion Resource connection だけを切り替えました。Authorization server の行や、Slack の Resource connection は触っていません。INACTIVE にすると、読取は Notion に届く前に止まります。チャットには、管理者によって許可されていない、と返ります。
| Notion Resource connection | エージェントの応答 |
|---|---|
| ACTIVE | タイトル「🤖 プロジェクト&タスク」が返る |
| INACTIVE | 接続は管理者によって許可されていない、と返る |
| 再 ACTIVE | 再び同じタイトルを読める |
やったことは次のとおりです。
- Resource connection から Notion を INACTIVE 状態にします。

- 同じ呼び出しが失敗することを確認します。管理者によって許可されていない、と返ります。

- もう一度 Activate して、同じ呼び出しを行います。再び Notion ページの情報を取得できました。
なお、エージェント側には常設の Notion トークンは設定しておらず、XAA 経由で取得した access token だけで Notion MCP を呼び出しています。
設定時に注意したいポイント
ここからは、検証中につまずいたポイントを整理します。特に Resource Indicator、OAuth クライアント ID、SSO、Enterprise-managed connections は、設定ミスに気づきにくい箇所でした。
Resource Indicator は Slack と同じ扱いにしない
今回の Notion XAA では、Okta で id_token から ID-JAG を取得するときに ORN を resource として渡さず、audience=https://mcp.notion.com だけを指定するのがポイントでした。
前回 Slack では、接続先として MCP URL をそのまま渡すと invalid_target になり、Admin の接続詳細にある ORN を STS の resource に載せて通しました。今回も最初は同じつもりで、Okta で ID-JAG を取得するときに ORN を付けました。
ORN を付けると、Okta がそれを ID-JAG の resource クレームに載せます。Notion の token エンドポイントはそれを受けず、invalid_target / Invalid resource になりました。今回は、Okta で ID-JAG を取得するときに audience=https://mcp.notion.com だけを指定し、resource は送っていません。画面に ORN が出ること自体は問題ではなく、ID-JAG 取得時にそれを送らないのが今回の違いでした。
INACTIVE のときは、ORN を付けずに ID-JAG を取得する流れでも止まりました。トークン交換単体では HTTP 400 invalid_target です。接続のスイッチは ORN を送らなくても効いています。
AI agent's client ID
Resource connection の AI agent's client ID registered in this app は、Okta に取り込み済みの agentcore_demo の Client ID かと最初は思っていました。しかし反映すべきは、接続先の認可サーバに登録した、エージェント用 OAuth クライアントです。前回 Slack では api.slack.com のアプリ Client ID が同じ役割でした。
今回は https://mcp.notion.com/register の Dynamic Client Registration で作り、返ってきた client_id を Okta の欄へ、client_secret を Runtime へ置いています。secret なしで token を叩くと invalid_client になるので、クライアント自体は Notion に認識されていました。
SSO ユーザーとの対応
XAA では、Okta の id_token のユーザーと Notion のユーザーが対応している必要がありました。
検証当初は Okta と Notion それぞれの検証環境でメールドメインが異なることによりうまく進めることができずハマりました。通常の環境であれば問題ないかと思いますが念のため記載しておきます。
Enterprise-managed connections
SSO まで通っても、Notion 側の Enterprise-managed connections がオフだと、ID-JAG を Notion の token エンドポイントに渡す段階で落ちました。https://mcp.notion.com/token へ jwt-bearer したところ、invalid_grant / Invalid assertion となりました。これは Okta は ID-JAG を出せていても、Notion がそれを受け取っていない状態です。
Enterprise-managed connections をオンにし進めることが必要な点も注意が必要です。
XAA で進めた理由
前の記事で予告していた Notion MCP × XAA の検証です。
Slack MCP では、先に XAA で通せないかを見ました。OIN に Cross App Access が出ていても、接続先 MCP の認可サーバが ID-JAG を受け取るとは限りません。確認したのは認可サーバの .well-known/oauth-authorization-server です。jwt-bearer と id-jag が grant_types / authorization_grant_profiles に載っているかが目安でした。
Slack 側(metadata)には authorization_code と refresh_token だけで、jwt-bearer はありませんでした。そのため Slack MCP では STS になりました。
Notion 側(metadata)は次のとおりです。判断に使ったキーだけ抜粋します。

jwt-bearer と id-jag が載っています。Okta OIN の Notion にも Cross App Access があり、Brokered Consent はありません。カタログと接続先の metadata が揃っていたので、今回は XAA で進めています。
OIN にそう書いてあっても、接続先が実際に受け取る grant と同じとは限らない、というのが前回からの続きでした。接続先が本当に ID-JAG を受け取るかは、その認可サーバの metadata を当たると迷いにくかったです。
今回わかったこと
今回の検証では、Notion MCP を Okta の XAA 経由で呼び出し、Resource connection の切り替えがエージェントの応答に反映されるところまで確認できました。
- Notion アプリ統合、Resource server(XAA)、SSO、Enterprise-managed connections を組み合わせて設定できました
- Dynamic Client Registration で作成した Client ID を Resource connection に載せ、常設 Notion トークンなしで remote Notion MCP を呼び出せました
- 同意画面なしで、検証用ワークスペースのデータベースタイトルを具体的に取得できました
- Okta で ID-JAG を取得するときは Resource Indicator(ORN)を載せず、audience だけを指定する必要があると分かりました
- Notion Resource connection を ACTIVE/INACTIVE/再 ACTIVE に切り替えることで、許可・拒否・復帰をエージェントの応答として確認できました
今回の検証範囲外
今回は Notion MCP を XAA で呼び出し、Resource connection による接続制御を確認するところに絞りました。次の内容は扱っていません。
- 監査ログを含めた運用設計
- Notion MCP で利用する権限範囲の最小化
- 書き込み・更新・削除系の MCP 操作
- 非公開ページや権限差の網羅
おわりに
前回の Slack MCP × STS に続き、今回は Notion MCP を Cross App Access(XAA)で呼び出し、Okta for AI Agents の 「What can they connect to?(何に接続できるか)」 をもう一歩進めて確認しました。
常設の Notion トークンを置かずに Notion の情報を読めること、そして Notion Resource connection を切ると読めなくなり、戻すと再び読めることを、エージェントの応答として確認できたのは大きなポイントです。接続先が変わると、STS なのか XAA なのか、Resource Indicator をどう扱うのかも変わります。ここは、実際に手を動かしてみて特に差を感じられました。
Slack と Notion で経路は違いましたが、どちらも最終的には「エージェントが何に接続できるか」を管理者側で制御できる、という同じテーマにつながります。ネクストモードとしては、AI エージェントの活用を進めるうえで、常設トークンに頼らず、必要な接続だけを管理者が制御できる設計が重要だと考えています。利便性だけでなく、こうした接続制御まで含めて整えていくことが、これからの AI エージェント活用には欠かせないポイントになりそうです。
参考リンク
- 【Okta for AI Agents】Slack MCP 呼び出しを Resource connections で制御してみた【生成AIセキュリティ対策シリーズ】
- 【Okta for AI Agents】Amazon Bedrock AgentCore のエージェントをインポートしてみた
- 【Okta】取り込み済み AgentCore エージェントの認可土台として ID-JAG トークン交換を検証してみた
- 【Okta】Okta for AI Agents とは?シャドー AI 時代に備えるエージェント管理の全体像【生成AIセキュリティ対策シリーズ】
- Slack MCP の認可サーバ metadata
- Notion MCP の認可サーバ metadata
- Okta Integration Network: Notion
- Configure Cross App Access
- Configure resource server connectors
- Connect AI agents to resources
- Enterprise-managed connections for Notion MCP
Okta に関するお問い合わせ
AI エージェントのアイデンティティ管理やセキュリティ統制に関するご相談は、ネクストモードまでお気軽にお問い合わせください。