
MuseでAWS上のRemote MCP Server向けのCustom Connector作成を試す
はじめに
これまで、MetaのパーソナルAIエージェント「Muse」とM5Stack Core2 for AWSをつなぎ、Muse Gadget SDKの導入とペアリング、Amazon Pollyによる日本語読み上げを試してきました。
今回は、AWS上の会議室予約アプリをMuseのCustom Connectorから操作します。まずDCR対応・認証ありのRemote MCPでの接続を紹介し、次に認証なしのRemote MCPでMuseの認識や確認の流れがどう変わるかを見ます。
検証の結果、2つの接続先では、Museでの扱いが大きく違いました。
- DCR対応・認証あり:Museにコネクターとして登録され、コネクター画面に表示されました。Cognitoにログインして接続し、検索・予約・キャンセルまで実行できました。ただし、トークンの更新に失敗すると401になり、再認証が必要です。コネクターの詳細画面では、ツールごとの許可は設定できなそうです。
- 認証なし:コネクターとして認識されず、コネクター画面にも表示されませんでした。それでも、Museの中に作ったクライアントからネットワーク経由でツールが呼び出され、検索の結果が返りました。
今回の構成
同じ会議室予約ツールを、次の順に試します。DCR(Dynamic Client Registration)は、OAuthクライアントを動的に登録する仕組みです。
| 順序 | 接続先 | Museとの接続 |
|---|---|---|
| 1 | DCR対応・認証ありのRemote MCP | FastMCP OAuth Proxyでクライアントを登録し、Cognitoでログイン |
| 2 | 認証なしのRemote MCP | 認証なしのAgentCore Gatewayへ直接接続 |
FastMCP OAuth ProxyはAWS Lambda Web Functions上で動かします。MuseにはProxyが発行したトークンを渡し、ProxyからGatewayへはCognitoのアクセストークンで接続します。登録情報と暗号化したトークンはDynamoDB、鍵はSecrets Managerに保存します。
認証あり・なしで、予約処理用LambdaのIAMロールとDSQLスキーマ(booking_auth・booking_noauth)を分けています。
| ツール | 処理 |
|---|---|
search_available_rooms |
日時・人数に合う空き会議室を探す |
create_reservation |
会議室を予約する |
list_reservations |
指定日の予約一覧を取得する |
cancel_reservation |
予約を取り消す |
MuseのConnectorについて
2026年10月28日時点で確認した公式情報を整理します。
ConnectorとCustom Connector
ConnectorとCustom Connectorについては、公式ヘルプに説明があります。
MuseとMetaやサードパーティーのアプリ・サービスとの連携をConnectorと呼びます。カレンダーの確認やメールの作成など、Museから外部サービスを利用するための仕組みです。
一覧にないサービスについては、MuseにCustom Connectorの作成を依頼できます。公式ヘルプにも、サービスのAPI情報の取得などを含めてMuseが作成を案内すると記載されています。前回までのGadget SDKがデバイスとMuseをつなぐためのものだったのに対し、今回はMuseから外部サービスを利用するための連携を試します。
Connectorの審査と要件
Connectorの申請窓口として、Muse Connector Platformがあります。
申請したConnectorはMetaの審査を受け、承認後にMuse内で見つけられるようになります。一方、公式ヘルプでは、Custom ConnectorはMetaが審査しないと説明されています。
Connectorの要件は、公式ガイドラインにまとまっています。
予約サービスなら、検索から予約の確定までMuseで完了できることが求められます。途中でログインが必要になっても、そこから再開できるようにします。
MuseはWebサイトも閲覧できるため、リンクを返すだけでは不十分です。操作の手間を減らしたり、アカウントの情報を取得したりする利点が必要です。レポートの取得や在庫分析など、情報を得ることが目的なら読み取り専用でも構いません。
ツールの分類と許可
公式ガイドラインの3.3 Permission choicesでは、ツールごとの許可を次のように定めています。
| ツールの分類 | 許可を求めるタイミング | 選択肢 |
|---|---|---|
| Read | 初回。以降も「常に許可」を選ばなければ毎回 | 一度だけ許可・常に許可・拒否 |
| Write, not sensitive | 初回。以降も「常に許可」を選ばなければ毎回 | 一度だけ許可・常に許可・拒否 |
| Write, sensitive | 毎回 | 一度だけ許可・拒否 |
Custom Connectorでも同じ分類・許可画面になるかは未確認です。
MCPのツール注釈
MCPには、ツールの性質をクライアントに伝える注釈があります。使い方はMCP公式ブログで説明されています。
readOnlyHint: trueはデータなどを変更しないこと、destructiveHintは変更が破壊的かどうかを示します。いずれもヒントであり、Museの分類や許可画面との対応は未確認です。destructiveHintとMuseのSensitive writeも、同じ意味とは限りません。
今回のtools/listにannotationsは含まれていません。注釈による許可画面の違いは、本記事では検証しません。
MCPへの対応と接続・認証の公開情報
公式ガイドラインの5.4 Integration credentials and tool documentationでは、APIまたはMCPのドキュメントを提出するよう案内されています。接続先のエンドポイント、認証設定、必要なスコープなども提出対象で、認証情報の例としてOAuthクライアントの認証情報やAPIキーが挙げられています。
ガイドラインにはMCPへの言及がありますが、Custom Connectorで設定できる認証方式までは公開資料から確認できませんでした。今回Museの画面に表示された選択肢は、後述します。
今回確認した公開資料では、Streamable HTTPの正式な対応範囲や対応プロトコルバージョン、Remote MCPでのOAuthフロー・設定項目までは確認できませんでした。後述の検証では今回のGatewayとの通信を確認していますが、それだけでMuseの対応範囲全体が分かるわけではありません。
AWSにMCP接続先を用意する
検証用のmuse-mcp-server-testリポジトリにCDKで構成を定義し、東京リージョン(ap-northeast-1)へデプロイしました。スタック名はdev-muse-mcp-testです。ソースコードは次のリポジトリで公開しています。
CDKはiac/awsでcdkコマンドを直接実行し、ビルドとDSQLの初期化・確認にはpackage.jsonのスクリプトを使います。各手順では、中で何をしているかも説明します。
Node.js 24、Bun 1.4.2、uv、Python 3、AWS CLI、jqを用意し、AWS認証済みのシェルで実行します。対象アカウントではCDK bootstrapを実施済みです。未実施の場合は、iac/awsでbunx cdk bootstrap 'aws://ACCOUNT_ID/ap-northeast-1'を先に実行します。
既存のスタックを削除する(作り直す場合)
前回の検証環境が残っている場合は、先に削除します。意図したアカウントであることを確認してから実行します。
aws sts get-caller-identity --query Account --output text
cd iac/aws
bunx cdk destroy
cd ../..
OAuth Proxyをパッケージする
bun install --frozen-lockfile
bun run proxy:build
proxy:build(tools/build-oauth-proxy.sh)は、Lambda Web Functionsに載せるZIPを作ります。Web FunctionsはNode.js 24で起動し、そこから同梱したPythonでFastMCPのサーバーを動かします。
- uvで、Linux ARM64向けのPython 3.13(
cpython-3.13.8-linux-aarch64-gnu)を取得する apps/oauth-proxy/requirements.txtの依存パッケージ(FastMCP、py-key-valueのDynamoDB対応など)を、ARM64向けのwheelに限定し、ハッシュを検証してインストールする- Python本体・依存パッケージ・
server.py・proxy_app.py・index.mjsを.local/oauth-proxy-web.zipにまとめる
コードを変更した場合は、bun run type-check・bun run test・bun run lintでも確認します。
Checked 186 installs across 273 packages (no changes) [43.00ms]
cpython-3.13.8-linux-aarch64-gnu is already installed
Using CPython 3.14.6
Resolved 82 packages in 363ms
Audited 82 packages in 2ms
Packaged 88 MiB
Using CPython 3.14.6は、uvがパッケージの解決に使った手元のPythonです。依存パッケージは--python-version 3.13と--python-platform aarch64-manylinux2014の指定により、Web Functionsで動かすPython 3.13向けに解決されます。ZIPは88MiBになりました。
差分を確認してデプロイする
cd iac/aws
bunx cdk diff
bunx cdk deploy --outputs-file ../../.local/outputs.json
cd ../..
作成される主なリソースは次のとおりです。
| リソース | 役割 |
|---|---|
| Aurora DSQLクラスター | 会議室と予約のデータ。スキーマはDSQLの初期化時に作成 |
| Lambda関数(2つ) | 予約ツール本体。booking_noauth用とbooking_auth用で、関数とIAMロールを分ける |
| AgentCore Gateway(2つ) | MCPの接続先。受信認証はNONEとCUSTOM_JWT(Cognitoのアクセストークンを検証) |
| Cognitoユーザープール | ログイン用。アプリクライアントはOAuth Proxy用とローカル検証用の2つ |
| Lambda Web Functions | FastMCP OAuth Proxy。DCR・Cognitoログイン・Gatewayへの中継を担当 |
| DynamoDB・Secrets Manager | Proxyの登録情報・トークンの保存先と、署名・暗号化の鍵 |
| SSM Parameter | ProxyのURL・CognitoのクライアントID・GatewayのURL。Proxyが起動時に読む |
diffではIAMの変更が一覧で表示されます。新規作成ではすべて追加(+)になり、Gateway用ロールの信頼ポリシーはaws:SourceAccountとaws:SourceArnで絞り込まれています。deployでも同じ一覧が表示され、承認すると作成が始まります。
ProxyのURLは、Web Functionsのエンドポイントを作るまで決まりません。CognitoのコールバックURL(<ProxyのURL>/auth/callback)はこのURLを使い、GatewayはProxy用クライアントのIDを検証し、ProxyはGatewayのURLを使います。これらをWeb Functionsの環境変数で渡すと、CloudFormationで循環参照になります。そこで3つの値をSSM Parameterに置き、Proxyが起動時に読み込むようにしました。デプロイは1回で完了します。
アカウントIDと、接続先URLのランダムな部分はxで伏せています。
Stack includes security-sensitive updates and "--require-approval" is set to 'broadening'.
Do you wish to deploy these changes? (y/n) y
dev-muse-mcp-test: deploying... [1/1]
✅ dev-muse-mcp-test
✨ Deployment time: 128.17s
Outputs:
dev-muse-mcp-test.AuthenticatedGatewayUrl = https://dev-muse-mcp-authenticated-xxxxxxxxxx.gateway.bedrock-agentcore.ap-northeast-1.amazonaws.com/mcp
dev-muse-mcp-test.AuthenticatedToolFunctionName = dev-muse-mcp-booking_auth
dev-muse-mcp-test.AuthenticatedToolLogGroup = /aws/lambda/dev-muse-mcp-booking_auth
dev-muse-mcp-test.AuthenticatedToolRoleArn = arn:aws:iam::xxxxxxxxxxxx:role/dev-muse-mcp-test-AuthenticatedToolRoleEA46F3F5-wFjXGdQIPti7
dev-muse-mcp-test.AuthorizationEndpoint = https://dev-muse-mcp-xxxxxxxxxxxx-ap-northeast-1.auth.ap-northeast-1.amazoncognito.com/oauth2/authorize
dev-muse-mcp-test.CallbackUrls = http://localhost:8765/callback
dev-muse-mcp-test.ClientId = 6le7p2p3iqic2v770d0gqoso34
dev-muse-mcp-test.DatabaseClusterId = mvugcorh3pbautc2lemxuymvpi
dev-muse-mcp-test.DatabaseEndpoint = mvugcorh3pbautc2lemxuymvpi.dsql.ap-northeast-1.on.aws
dev-muse-mcp-test.DcrProxyBaseUrl = https://xxxxxxxxxxxxxxxx.j71.lambda-web.ap-northeast-1.on.aws
dev-muse-mcp-test.DcrProxyLogGroup = /aws/lambda-web/dev-muse-mcp-oauth-proxy
dev-muse-mcp-test.DcrProxyUrl = https://xxxxxxxxxxxxxxxx.j71.lambda-web.ap-northeast-1.on.aws/mcp
dev-muse-mcp-test.DiscoveryUrl = https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_zU6I29dAB/.well-known/openid-configuration
dev-muse-mcp-test.LocalCallbackUrl = http://localhost:8765/callback
dev-muse-mcp-test.ProxyClientId = 41tfoof6g1gtib50b6hj6q6gb2
dev-muse-mcp-test.PublicGatewayUrl = https://dev-muse-mcp-public-xxxxxxxxxx.gateway.bedrock-agentcore.ap-northeast-1.amazonaws.com/mcp
dev-muse-mcp-test.PublicToolFunctionName = dev-muse-mcp-booking_noauth
dev-muse-mcp-test.PublicToolLogGroup = /aws/lambda/dev-muse-mcp-booking_noauth
dev-muse-mcp-test.PublicToolRoleArn = arn:aws:iam::xxxxxxxxxxxx:role/dev-muse-mcp-test-PublicToolRoleDCB315D3-T14sYuds0pJG
dev-muse-mcp-test.Region = ap-northeast-1
dev-muse-mcp-test.Scope = dev-muse-mcp/invoke
dev-muse-mcp-test.TokenEndpoint = https://dev-muse-mcp-xxxxxxxxxxxx-ap-northeast-1.auth.ap-northeast-1.amazoncognito.com/oauth2/token
dev-muse-mcp-test.UserPoolId = ap-northeast-1_zU6I29dAB
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/dev-muse-mcp-test/af117f50-d32b-11f1-ab91-0e08a3827279
✨ Total time: 150.44s
接続先を確認する
デプロイの出力は.local/outputs.jsonに保存されます。Museとの接続に使う値を確認します。
jq '.["dev-muse-mcp-test"] | {DcrProxyUrl, PublicGatewayUrl, UserPoolId}' .local/outputs.json
{
"DcrProxyUrl": "https://xxxxxxxxxxxxxxxx.j71.lambda-web.ap-northeast-1.on.aws/mcp",
"PublicGatewayUrl": "https://dev-muse-mcp-public-xxxxxxxxxx.gateway.bedrock-agentcore.ap-northeast-1.amazonaws.com/mcp",
"UserPoolId": "ap-northeast-1_zU6I29dAB"
}
| 出力 | 用途 |
|---|---|
DcrProxyUrl |
DCR対応・認証ありのRemote MCPの接続先 |
PublicGatewayUrl |
認証なしのRemote MCPの接続先 |
UserPoolId |
ログイン用ユーザーの作成先 |
ProxyとGatewayのURLは、スタックを作り直すたびに変わります。
ProxyがDCRに対応していることは、OAuthのメタデータで確認できます。Museも同じメタデータを読み、registration_endpointにクライアントを登録します。
proxy_base=$(jq -r '.["dev-muse-mcp-test"].DcrProxyBaseUrl' .local/outputs.json)
curl -s "$proxy_base/.well-known/oauth-authorization-server" \
| jq '{issuer, registration_endpoint, code_challenge_methods_supported}'
{
"issuer": "https://xxxxxxxxxxxxxxxx.j71.lambda-web.ap-northeast-1.on.aws/",
"registration_endpoint": "https://xxxxxxxxxxxxxxxx.j71.lambda-web.ap-northeast-1.on.aws/register",
"code_challenge_methods_supported": [
"S256"
]
}
registration_endpointがDCRの登録先です。PKCEはS256に対応しています。
DSQLを初期化する
bun run db:init --date 2026-10-29
db:init(tools/init-database.ts)は、実行者のIAM権限(dsql:DbConnectAdmin)でadminとして接続し、スキーマごとにテーブルとDBロールを作成します。AWS IAM GRANTでDBロールとLambdaのIAMロールを対応付けるため、各Lambdaはdsql:DbConnectのIAM認証で接続し、自分のスキーマだけを操作できます。
db:initが実行する主なSQL(booking_noauthの場合)
CREATE SCHEMA IF NOT EXISTS booking_noauth;
CREATE ROLE booking_noauth WITH LOGIN; -- 未作成の場合のみ
REVOKE ALL ON SCHEMA booking_noauth FROM PUBLIC;
CREATE TABLE IF NOT EXISTS booking_noauth.rooms (
room_id text PRIMARY KEY, name text NOT NULL, capacity integer NOT NULL);
CREATE TABLE IF NOT EXISTS booking_noauth.reservations (
reservation_id uuid PRIMARY KEY, room_id text NOT NULL,
start_time timestamptz NOT NULL, end_time timestamptz NOT NULL,
title text NOT NULL, attendees integer NOT NULL);
CREATE TABLE IF NOT EXISTS booking_noauth.booking_guards (
room_id text PRIMARY KEY, version bigint NOT NULL);
GRANT USAGE ON SCHEMA booking_noauth TO booking_noauth;
GRANT SELECT ON booking_noauth.rooms TO booking_noauth;
GRANT SELECT, INSERT, DELETE ON booking_noauth.reservations TO booking_noauth;
GRANT SELECT, UPDATE ON booking_noauth.booking_guards TO booking_noauth;
AWS IAM GRANT booking_noauth TO '<PublicToolRoleArn>';
DSQLではDDLとDMLを同じトランザクションに含められないため、各DDLを単独で実行してから、初期データを1つのトランザクションで投入します。初期データはON CONFLICT DO NOTHINGで投入するので、再実行しても既存の予約は消えません。
booking_noauth・booking_authの両スキーマに、次の同じ初期データを投入しました。対象の時間帯は2026年10月29日14:00〜15:00(Asia/Tokyo)です。
| 会議室ID | 会議室名 | 定員 | 対象の時間帯 |
|---|---|---|---|
room-sakura |
さくら | 2人 | 空き。4人の検索では定員不足で候補外 |
room-kaede |
かえで | 4人 | 空き |
room-aoba |
あおば | 8人 | デモ用の既存予約あり |
booking_noauth: 初期化完了(既存予約を保持)
booking_auth: 初期化完了(既存予約を保持)
デモ検索: 2026-10-29 14:00–15:00 Asia/Tokyo、4人 → room-kaede
Museから接続する前に確認する
bun run db:verify --date 2026-10-29
bun run mcp:test --date 2026-10-29
db:verifyはMCPを通さずに両Lambdaを直接呼び出し、予約・一覧・キャンセルの結果をDSQLの行と照合します。mcp:testは認証なしのGatewayにStreamable HTTPで接続し、initialize・ツールの検出・同じ予約シナリオを実行します。どちらも、作成した予約を終了時にキャンセルします。
booking_noauth: 予約・取消・同時予約・DSQL照合・権限分離 OK
booking_auth: 予約・取消・同時予約・DSQL照合・権限分離 OK
initialize / tools/list / 会議室予約・取消・同時予約テスト 成功
結果: .local/result-public.json
ログイン用のユーザーを作成する
Cognitoのセルフサインアップは無効にしているため、管理者APIでユーザーを作成します。パスワードはopenssl randで生成し、ポリシー(12文字以上、大文字・小文字・数字・記号)を必ず満たすよう先頭にAa1_を付けます。
pool_id=$(jq -r '.["dev-muse-mcp-test"].UserPoolId' .local/outputs.json)
password="Aa1_$(openssl rand -base64 24 | tr -d '/+=')"
aws cognito-idp admin-create-user --region ap-northeast-1 \
--user-pool-id "$pool_id" --username muse-demo --message-action SUPPRESS
aws cognito-idp admin-set-user-password --region ap-northeast-1 \
--user-pool-id "$pool_id" --username muse-demo --password "$password" --permanent
echo "username: muse-demo"
echo "password: $password"
--message-action SUPPRESSで招待メールを送らず、--permanentで初回ログイン時のパスワード変更を不要にします。
{
"User": {
"Username": "muse-demo",
"Attributes": [
{
"Name": "sub",
"Value": "97b44ad8-6031-70a1-d15b-7be8cd8a61e2"
}
],
"UserCreateDate": "2026-10-29T09:05:28.359000+09:00",
"UserLastModifiedDate": "2026-10-29T09:05:28.359000+09:00",
"Enabled": true,
"UserStatus": "FORCE_CHANGE_PASSWORD"
}
}
username: muse-demo
password: Aa1_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
JSONはadmin-create-userの応答で、パスワードを設定する前の状態のためFORCE_CHANGE_PASSWORDと表示されます。続くadmin-set-user-password --permanentでCONFIRMEDになり、表示されたパスワードでそのままログインできます。パスワードは伏せています。
表示されたユーザー名とパスワードは、MuseからDCR対応の接続先につなぐときにCognitoのログイン画面で使うので、控えておきます。Cognitoから後でパスワードを確認することはできません。
認証ありの場合
DCRに対応したFastMCP OAuth Proxyを接続先にすると、Museとの連携に成功しました。
接続前のコネクター画面です。この一覧に、作成したCustom Connectorが追加されることを確認します。Web版で撮影しましたが、設定項目はアプリでも同じです。
Create a Custom Connector for my remote MCP server.
Name: custom.dev-muse-mcp-auth
Transport: Streamable HTTP
URL: https://lvqqpvh855ytndwp.j71.lambda-web.ap-northeast-1.on.aws/mcp
Authentication: OAuth 2.0 Authorization Code with PKCE (S256)
Client registration: Dynamic Client Registration (DCR)
Discover the OAuth metadata, register the client, and guide me through Cognito login.
After login, discover the available tools and test the connection using read-only tools.
接続の流れ
作成を依頼
iPhoneのMuseで新しいサイドチャットを作り、上のプロンプトを送信して開始しました。
認証情報の保存
MuseはOAuthのメタデータを読み、DCRでの接続を始めました。応答の下に「セキュアな認証情報ストレージ」と「カスタムコネクターを追加」が表示されます。
Museの応答(全文)
Metadata discovered for the new endpoint — this is a new Lambda Web host, different from the previous
9z7w2jhnbkry7rw1...one:Protected resource (
/.well-known/oauth-protected-resource/mcp)
- resource:
https://lvqqpvh855ytndwp.j71.lambda-web.ap-northeast-1.on.aws/mcp- scopes_supported:
dev-muse-mcp/invoke- bearer_methods_supported:
header- Unauthenticated
POST /mcpreturns 401 withWWW-Authenticatepointing to that metadata — as expectedAuthorization server (
/.well-known/oauth-authorization-server, issuerhttps://lvqqpvh855ytndwp.j71.lambda-web.ap-northeast-1.on.aws/)
- authorization:
/authorize- token:
/token—token_endpoint_auth_methods_supported: ["none"]- registration (DCR):
/register- revocation:
/revoke- grant types:
authorization_code,refresh_token- code_challenge_methods:
S256I've started the connection for custom.dev-muse-mcp-auth using OAuth 2.0 Authorization Code with PKCE (S256) and Dynamic Client Registration, for read/write access to your remote MCP server's meeting-room tools, scoped to
dev-muse-mcp/invokewith that/mcpURL as the resource.You don't need to create an app or enter a client ID/secret — DCR registers the client automatically. Open the link below, sign in with Cognito when prompted, and authorize the
dev-muse-mcp/invokescope. Once you finish, I'll update the connector client/doc to this new endpoint, runlist-tools, and test with the read-only tools.
カスタムコネクターを追加
「追加」を押すと、MetaがCustom Connectorを審査していないことの注意と、接続先のドメインが表示されました。
ここで「追加」を押すと、MuseがProxyのドメインをサインインに使うことの確認がiOSから求められます。「続ける」を押しました。
接続に同意
FastMCP OAuth Proxyの同意画面が開きました。MuseがProxyに接続しようとしていることと、認証情報の送り先(MuseのリダイレクトURI)が表示されます。
「Advanced Details」を開くと、DCRで登録されたApplication ID(client_id)、リダイレクトURI、要求されたスコープを確認できます。「Allow Access」を押しました。
Cognitoにログイン
Cognitoのログイン画面に移ります。作成したmuse-demoユーザーでサインインしました。
作成完了
ログイン後はMuseの画面に戻り、「追加済み」になってから接続の確認が続きました。
接続できたことと、ツールの検出・読み取りテストの結果が返りました。
Museは「旧ホストと同じデモ用データベース」と説明していますが、スタックを作り直したため、DSQLクラスターは新しいものです。同じ初期データを投入したため、同じデータベースに見えたと考えられます。
コネクター画面で確認
接続後に、コネクター画面を確認しました。Custom Connectorの名前は、custom.dev-muse-mcp-authから「Dev Muse Mcp Auth」に変わって表示されています。
Web版では、「利用可能」タブには表示されず、「リンク済み」タブにだけ表示されました。
アプリでは、「利用可能」と「リンク済み」の両方に表示されました。「利用可能」では一覧の下に「カスタム」が増え、「カスタムコネクター」を開くとDev Muse Mcp Authがあります。詳細画面には「バックグラウンド同期」と「リンクを解除」が表示されます。
公式のコネクターの詳細画面と比べると、違いがはっきりします。マネーフォワード MEでは、「口座残高、保有銘柄の評価金額を読み取る」などの項目ごとに、「許可する」「リクエストする」「許可しない」を選べます。Instagramでも、「読み取りアクセス許可」と「書き込みと削除のアクセス許可」に分けて、項目ごとに設定できます。
カスタムコネクターの詳細画面には、こうした項目がありません。予約の作成やキャンセルといった書き込み系のツールだけを止めるような、ツールごとの制御はできなそうです。
会議室を予約する
認証が切れたあと(後述)に再認証してから、次の順に依頼しました。各ステップの説明にあるツールの呼び出しは、予約用Lambda(booking_auth)のログで確認したものです。
空き会議室を探す
認証ありの custom.dev-muse-mcp-auth を使って、2026年10月29日14時から1時間、4人で使える空き会議室を探して。
Museはsearch_available_roomsを2回呼び、どちらも「かえで」だけが返っていました。日本時間の14時は、UTCの2026-10-29T05:00:00.000Zに直して渡されています。「あおば」には同じ時間帯に既存の予約があるため、候補に入りません。
予約する
その会議室を「設計レビュー」で予約して。
Museはlist_reservationsで既存の予約を確認してからcreate_reservationを呼び、続けてもう一度list_reservationsを呼んでいました。「直後の予約一覧でも同内容で表示される」という説明は、この呼び出しと一致します。
予約一覧を見る
2026年10月29日の予約一覧を見せて。
list_reservationsは、この2件を返していました。
もう一度空き会議室を探す
同じ時間帯の空き会議室をもう一度探して。
search_available_roomsは空のroomsを返していました。予約した「かえで」が候補から外れています。
予約をキャンセルする
今作った「設計レビュー」の予約をキャンセルして。
Museはcancel_reservationのあとにlist_reservationsを呼んでおり、結果は「あおば」の1件だけでした。
キャンセル後に空き会議室を探す
同じ時間帯の空き会議室をもう一度探して。
search_available_roomsは、再び「かえで」を返していました。
予約用Lambdaのログ(11:35〜11:38、抜粋)
ログからツール名・入力・結果を取り出しています。時刻は日本時間です。
11:35:09 {"tool":"search_available_rooms","input":{"start":"2026-10-29T05:00:00.000Z","end":"2026-10-29T06:00:00.000Z","attendees":4},"result":{"ok":true,"timezone":"Asia/Tokyo","rooms":[{"room_id":"room-kaede","name":"かえで","capacity":4}]}}
11:35:25 {"tool":"search_available_rooms","input":{"start":"2026-10-29T05:00:00.000Z","end":"2026-10-29T06:00:00.000Z","attendees":4},"result":{"ok":true,"timezone":"Asia/Tokyo","rooms":[{"room_id":"room-kaede","name":"かえで","capacity":4}]}}
11:36:11 {"tool":"list_reservations","input":{"date":"2026-10-29","start":"2026-10-28T15:00:00.000Z","end":"2026-10-29T15:00:00.000Z"},"result":{"ok":true,"date":"2026-10-29","timezone":"Asia/Tokyo","reservations":[{"reservation_id":"00000000-0000-4000-8000-202610290001","room_id":"room-aoba","start_time":"2026-10-29T05:00:00.000Z","end_time":"2026-10-29T06:00:00.000Z","title":"デモ用の既存予約","attendees":6,"room_name":"あおば"}]}}
11:36:18 {"tool":"create_reservation","input":{"start":"2026-10-29T05:00:00.000Z","end":"2026-10-29T06:00:00.000Z","attendees":4,"roomId":"room-kaede","title":"設計レビュー"},"result":{"ok":true,"reservation":{"reservation_id":"8523b810-7e80-4c32-b96b-e70b563f031b","room_id":"room-kaede","start_time":"2026-10-29T05:00:00.000Z","end_time":"2026-10-29T06:00:00.000Z","title":"設計レビュー","attendees":4,"room_name":"かえで"}}}
11:36:23 {"tool":"list_reservations","input":{"date":"2026-10-29","start":"2026-10-28T15:00:00.000Z","end":"2026-10-29T15:00:00.000Z"},"result":{"ok":true,"date":"2026-10-29","timezone":"Asia/Tokyo","reservations":[{"reservation_id":"00000000-0000-4000-8000-202610290001","room_id":"room-aoba","start_time":"2026-10-29T05:00:00.000Z","end_time":"2026-10-29T06:00:00.000Z","title":"デモ用の既存予約","attendees":6,"room_name":"あおば"},{"reservation_id":"8523b810-7e80-4c32-b96b-e70b563f031b","room_id":"room-kaede","start_time":"2026-10-29T05:00:00.000Z","end_time":"2026-10-29T06:00:00.000Z","title":"設計レビュー","attendees":4,"room_name":"かえで"}]}}
11:37:03 {"tool":"list_reservations","input":{"date":"2026-10-29","start":"2026-10-28T15:00:00.000Z","end":"2026-10-29T15:00:00.000Z"},"result":{"ok":true,"date":"2026-10-29","timezone":"Asia/Tokyo","reservations":[{"reservation_id":"00000000-0000-4000-8000-202610290001","room_id":"room-aoba","start_time":"2026-10-29T05:00:00.000Z","end_time":"2026-10-29T06:00:00.000Z","title":"デモ用の既存予約","attendees":6,"room_name":"あおば"},{"reservation_id":"8523b810-7e80-4c32-b96b-e70b563f031b","room_id":"room-kaede","start_time":"2026-10-29T05:00:00.000Z","end_time":"2026-10-29T06:00:00.000Z","title":"設計レビュー","attendees":4,"room_name":"かえで"}]}}
11:37:24 {"tool":"search_available_rooms","input":{"start":"2026-10-29T05:00:00.000Z","end":"2026-10-29T06:00:00.000Z","attendees":4},"result":{"ok":true,"timezone":"Asia/Tokyo","rooms":[]}}
11:38:03 {"tool":"cancel_reservation","input":{"reservationId":"8523b810-7e80-4c32-b96b-e70b563f031b"},"result":{"ok":true,"cancelled":true,"reservation_id":"8523b810-7e80-4c32-b96b-e70b563f031b"}}
11:38:07 {"tool":"list_reservations","input":{"date":"2026-10-29","start":"2026-10-28T15:00:00.000Z","end":"2026-10-29T15:00:00.000Z"},"result":{"ok":true,"date":"2026-10-29","timezone":"Asia/Tokyo","reservations":[{"reservation_id":"00000000-0000-4000-8000-202610290001","room_id":"room-aoba","start_time":"2026-10-29T05:00:00.000Z","end_time":"2026-10-29T06:00:00.000Z","title":"デモ用の既存予約","attendees":6,"room_name":"あおば"}]}}
11:38:36 {"tool":"search_available_rooms","input":{"start":"2026-10-29T05:00:00.000Z","end":"2026-10-29T06:00:00.000Z","attendees":4},"result":{"ok":true,"timezone":"Asia/Tokyo","rooms":[{"room_id":"room-kaede","name":"かえで","capacity":4}]}}
認証が切れたときの挙動
接続してから時間を空けてMuseに依頼すると、ツールの呼び出しが401になり、再認証が必要になりました。今回の検証では2回起き、原因はそれぞれ違いました。
トークンは2段になっています。
| 区間 | アクセストークン | リフレッシュトークン |
|---|---|---|
| MuseとProxyの間 | 15分(Cognitoと同じ値) | FastMCPが発行する。使うたびに新しいものに入れ替わり、古いものは無効になる |
| ProxyとCognitoの間 | 15分 | 1時間(refreshTokenValidity)。使っても入れ替わらず、期限も延びない |
正常に更新できるときは、次の流れになります。Museが更新を依頼するたびに、ProxyはCognitoのトークンも更新し、Museのリフレッシュトークンを新しいものに入れ替えます。
経過は次のとおりです。
- 09:15 Cognitoにログインして接続
- 09:33 401(1回目)。10:06にも同じ理由で401
- 10:08ごろ 再認証(10:09にツールを呼べていた)
- 11:33 401(2回目)
- 11:34ごろ 再認証。このあとに、前述の会議室の予約を実施
以下はOAuth ProxyのCloudWatch Logs(/aws/lambda-web/dev-muse-mcp-oauth-proxy)の抜粋です。時刻は日本時間に直し、折り返された行は1行にまとめています。
1回目:入れ替え済みのリフレッシュトークンが送られる
最初の接続から約18分後の09:33に、Museがアクセストークンを更新しようとしました。Proxyは09:20に停止していたため、この更新でコールドスタートが起きています。
09:20:59 platform.spinDownStart
09:33:27 platform.initStart
09:33:32 platform.initReport (totalDurationMs: 5016)
09:33:32 INFO Issued new FastMCP tokens (rotated refresh) for client=abb4aa9f-97f4-48bb-a2e2-a21c33468577 (access_jti=APcqdDQZ, refresh_jti=3O3K9b5m)
09:33:37 WARNING Refresh token not found for client=abb4aa9f-97f4-48bb-a2e2-a21c33468577 (token_hash=46a759ba); it was already rotated, expired, or revoked. Rejecting with invalid_grant, which forces the client to re-authenticate.
09:33:38 INFO Auth error returned: invalid_token (status=401)
09:33:51 INFO Auth error returned: invalid_token (status=401)
09:35:28 WARNING Refresh token not found for client=abb4aa9f-97f4-48bb-a2e2-a21c33468577 (token_hash=46a759ba); it was already rotated, expired, or revoked. Rejecting with invalid_grant, which forces the client to re-authenticate.
09:35:28 INFO Auth error returned: invalid_token (status=401)
ログの各行は、次のことを示しています。
Issued new FastMCP tokens (rotated refresh):更新に成功し、新しいリフレッシュトークンを発行して、古いものを無効にしたRefresh token not found ... already rotated:届いたリフレッシュトークンが無効なので、invalid_grantで拒否したAuth error returned: invalid_token (status=401):期限切れのアクセストークンで/mcpを呼んだリクエストを拒否した
更新に成功した5秒後に、同じクライアントから無効なリフレッシュトークンが届いています。コールドスタートで応答まで約5秒かかったため、Museは応答を受け取れないまま、古いトークンで再試行したと考えられます(Muse側のログは見られないので推測です)。10:06にも同じtoken_hash=46a759baが届いており、Museは09:33:32に発行された新しいリフレッシュトークンを保存していないようです。
FastMCP 4.1.4のリフレッシュトークンは1回しか使えず、入れ替え済みのトークンを受け付ける設定もありません。一度こうなると、再認証するしかありません。
2回目:Cognitoのリフレッシュトークンが期限切れになる
10:08ごろに再認証し、10:09にはツールを呼べていました。その後1時間以上空けて11:33に依頼すると、また401になりました。
10:14:11 platform.spinDownStart
11:33:01 platform.initStart
11:33:05 platform.initReport (totalDurationMs: 3942)
11:33:06 WARNING Upstream OAuth token refresh failed: invalid_grant
11:33:22 WARNING Upstream OAuth token refresh failed: invalid_grant
11:33:23 INFO Auth error returned: invalid_token (status=401)
今回はRefresh token not foundは出ていません。Museから届いたリフレッシュトークンは有効で、ProxyはCognitoにトークンの更新を依頼しています。Upstream OAuth token refresh failed: invalid_grantは、その依頼をCognitoが拒否したことを示します。
Cognitoのリフレッシュトークンは1時間で期限が切れる設定です。10:08ごろに発行されたものは、11:08ごろに切れていました。使っても期限は延びないため、ログインから1時間を過ぎると、Museが更新を依頼しても必ず失敗します。
再認証の方法
401になったあと、Museに「解除して再設定して」と依頼しました。Museは、自分では接続を解除できないと答えました。そのうえで、Settings > Connectorsで「Dev Muse MCP Auth」をDisconnectしてから、リンクで再設定する手順を示しました。
応答の下に表示されるボタンは、モバイルとWeb版で違いました。
モバイルでは、「セキュアな認証情報ストレージ」が「追加済み」のままで、押せませんでした。ここからは再認証できないため、コネクターの詳細画面にある「リンクを解除」で接続を解除してから、接続し直す必要がありそうです。
Web版では、同じ応答の下に「Dev Muse Mcp Auth」の「連携する」ボタンが表示されました。接続を解除しなくても、ここから再認証できます。
対策の候補
今回は設定を変えずに再認証しました。次の変更で起きにくくなるはずですが、試してはいません。
- 2回目への対策:Proxy用のCognitoクライアントの
refreshTokenValidityを延ばす。今回はmcp-test-stack.tsで1時間に短くしている。指定しなければ、Cognitoの既定値の30日になる - 1回目への対策:
OIDCProxyのfastmcp_access_token_expiry_secondsで、Museに渡すアクセストークンを長くする。Museが更新を依頼する回数が減り、Cognitoのアクセストークンの更新はProxyが裏で行う。ただし、Cognitoのリフレッシュトークンが切れれば失敗するので、上の対策と組み合わせる必要がある
認証なしの場合
次は認証なしの接続先です。
始める前に、DCR側のコネクターの接続を解除しておきます。両方つないだままでも問題はありませんが、どちらも同じ4ツールを公開しているので、両方のツールが呼ばれないようにするためです。アプリのコネクター画面で「Dev Muse Mcp Auth」の詳細を開き、「リンクを解除」を押します。
iPhoneのMuseで、次のプロンプトを入力します。
Create a Custom Connector for my remote MCP server.
Transport: Streamable HTTP
URL: https://dev-muse-mcp-public-hm7plx0a2f.gateway.bedrock-agentcore.ap-northeast-1.amazonaws.com/mcp
Please discover the available tools and test the connection.
接続の流れ
作成を依頼
iPhoneのMuseで新しいサイドチャットを作り、上のプロンプトを送信して開始しました。Museは「Creating connector」と表示して作業を始めます。
初めて接続するときは、ここで接続先への許可画面が表示され、「一度だけ許可」「このサイトで常に許可」「許可しない」から選びます。今回は以前の検証ですでに許可していたため、許可画面は表示されませんでした。
作成完了
Museは新しく作らず、以前の検証で作ったcustom.dev-muse-mcpの接続先を、新しいGatewayのURLに更新しました。名前は認証ありの場合と同じ形ですが、後述のとおり、これはコネクターではありません。ツールの検出と読み取りテストの結果が返りました。
Museの応答(全文)
Done — this is the same server behind a new gateway, so I updated the existing
custom.dev-muse-mcpinstead of creating a duplicate.New endpoint
https://dev-muse-mcp-public-hm7plx0a2f.gateway.bedrock-agentcore.ap-northeast-1.amazonaws.com/mcp
- Replaces:
dev-muse-mcp-public-hqcflgspkq(which replacedpne4qzygrj)- Updated:
bin/client.pyENDPOINT andCONNECTOR.md(host + Verified section)Discovery — probe
initialize: protocol2025-11-25, serverInfodev-muse-mcp-public/1.0.0, instructions identical to beforetools/list: same 4 tools, nonextCursor
booking___search_available_rooms— read-only:start_time,end_time,attendeesbooking___list_reservations— read-only:datebooking___create_reservation— write:room_id,start_time,end_time,attendees,titlebooking___cancel_reservation— destructive write:reservation_idTest via the client
list-tools: 4 tools returned, exit 0booking___list_reservationsfor 2026-10-29:ok: true— same pre-existing demo booking, room-aoba (あおば), 05:00–06:00Z, “デモ用の既存予約”booking___search_available_roomsfor 2026-10-30 14:00–15:00, 4 attendees:ok: true— room-kaede (かえで, capacity 4) and room-aoba (あおば, capacity 8)So the new gateway fronts the same demo database. No write tools were called.
Registration note, separate from whether it works: the tools work, but as before this public, no-auth endpoint has no hosted connection record — it runs as the file-based client + network access under the
custom.dev-muse-mcpname you use for it.
読み取りテストの2つの呼び出しは、認証なし側の予約用Lambda(booking_noauth)のログにも残っていました(11:57:16のlist_reservations、11:57:19のsearch_available_rooms)。DCRのときと同じく「同じデモ用データベース」と説明していますが、DSQLクラスターは作り直した新しいものです。
応答の最後には、認証なしの接続先には「hosted connection record」(Museに登録された接続の記録)がないと書かれています。認証ありの場合のようにコネクターとしてMuseに登録するのではなく、Museの中に作ったクライアント(bin/client.py)から、ネットワーク経由で呼び出している、という説明です。
コネクター画面で確認
接続後に、アプリのコネクター画面の「リンク済み」タブを確認しました。DCRのときと違い、custom.dev-muse-mcpは表示されません。DCR側は解除済みなので、カスタムコネクターは1件もありません。Museはこの接続先を、コネクターとして認識していません。応答の「hosted connection record がない」という説明と一致します。
空き会議室を探す
認証なしでは、空き会議室の検索だけを試します。
認証なしの custom.dev-muse-mcp を使って、2026年10月29日14時から1時間、4人で使える空き会議室を探して。
booking_noauthのログでは、12:30:35にsearch_available_roomsが1回呼ばれ、「かえで」だけが返っていました。ツールに渡された条件は開始2026-10-29T05:00:00.000Z、終了2026-10-29T06:00:00.000Z、人数4で、DCRで検索したときと同じです。「あおば」が返らないのは、同じ時間帯に既存の予約があるためです。
12:30:35 {"tool":"search_available_rooms","input":{"start":"2026-10-29T05:00:00.000Z","end":"2026-10-29T06:00:00.000Z","attendees":4},"result":{"ok":true,"timezone":"Asia/Tokyo","rooms":[{"room_id":"room-kaede","name":"かえで","capacity":4}]}}
さいごに
DCR対応・認証ありのRemote MCPは、Museにコネクターとして登録され、検索・予約・キャンセルまで実行できました。ただし、トークンの更新に失敗すると再認証が必要です。認証なしのRemote MCPは、コネクターとして認識されないまま、Museの中に作ったクライアントからツールが呼び出されました。














































