はじめに
2026年1月27日頃に、MCPの拡張機能として「MCP Apps」がリリースされました。
これにより、チャットアプリなどでAIエージェントとの対話中に、テキストベースだけではなく、動的にUIを表示することが出来るようになります。(= Generative UIという考え方)
今回はStrandsAgentsとCopilotKitを使用したAIエージェントアプリに、MCP Appsを使ったenrative UIを実装してみます。
参考:Gerative UIについては、以下の記事でまとめました。
前提
- MCP、AIエージェント、StrandsAgentsについての詳細説明は省略します
- 本記事のMCP Appsの実装は、現在時刻を表示する公式QuickStartがベースです
↓ とてつもなく分かりにくいのですが、画像下部の「Server Time:」のところはiframeで別のHTMLが埋め込まれています...地図とかグラフを用意すれば、そのHTMLが埋め込まれます
↓ コードはこちら
先にまとめ
分かったこと
- StrandsAgentsのAIエージェントでも「CopilotKit」を使えばMCP Appsを組み込める
- AWSの「AgentCore Runtime」 を使えばMCPサーバーを構築できる
悩みどころ
-
CopilotKit自体のドキュメントが少なく処理の流れがブラックボックスで詳細を理解するのが難しい
- ドキュメントのMCPサーバーが提供されているので使ってますが、イマイチ情報が古い
-
フロントエンドやバックエンドでCopilotKitが処理を隠蔽しているため、エラーが起きるとデバッグの当たりをつけるのが大変
-
現在時刻を表示するだけなのに、チャット送信から表示まで1分以上かかる...(未調査)
概要
MCP Apps について
MCP Appsは、2026年1月27日頃に登場したMCPの拡張機能です。
ざっくりイメージ
具体的には、MCPサーバーが提供するプリミティブである「Tool」と「Resource」が拡張され、実現されています。
各プリミティブの役割は以下になります。
- Tool
- 従来通り、MCPホストからToolが呼ばれて、ロジックが実行されます
- ただし、実行結果に
_meta.ui.resourceUriを含めることで、対応するUIリソースを指定できます
- Resource
- MCPホストは、ツールの実行結果に指定されたUIリソースを確認します
- Resourceを呼び出して対応するHTMLコンテンツを取得します
こうして取得されたUIリソースは、Web画面上にiframeとして埋め込んで表示されます。
CopilotKitによるAIエージェントアプリの構築について
CopilotKitは、AIエージェントとフロントエンドを統合し、アプリケーションとして構築するためのフレームワークです。AG-UI(Agent-User Interaction Protocol)やMCP Appsにも対応しています。
AG-UIは、AIエージェントとフロントエンド間のリアルタイム通信を標準化したプロトコルです。
https://docs.ag-ui.com/introduction
CopilotKitは、バックエンドと通信やUI制御を行えるUIコンポーネントを提供しています。
フロントエンドとAIエージェントの間にはバックエンドとして「CopilotRuntime」を挟み、通信を仲介してもらう必要があります。
今回は、AIエージェントにStrandsAgentsを使用しますが、StrandsAgentsはAG-UIに対応していないため、AG-UI SDKを組み込むことでAG-UI対応版にします。
CopilotKitでMCP Appsを実装する場合
CopilotKitは、MCP Appsの機能を搭載済みです。なので、実装のポイントは以下の3つです。
- CopilotKit、AG-UIのバージョンは最新か?
- MCP Apps Middlewareを設定したか?
- MCPサーバーはMCP Apps対応版として実装したか?
参考
さらにAWS上で構築する場合
今回のAWS上にデプロイする場合の一例は以下になります。
- AgentCore Runtimeは、AIエージェントとMCPサーバーをホストするために使用します
- AWS Lambda + AgentCore Gatewayパターンも有効そうでしたが、手っ取り早かったのでAgetnCore Runtimeを使いました
- 認証は、JWT(アクセストークン)を使用しており、インフラ自体はCDKで実装しました。
実装内容
実装の流れですが、主に以下の4つに分けて実装します。
- AgentCore RuntimeでMCP Apps対応MPCサーバーを構築
- StrandsAgentsでAIエージェントを構築(AG-UI対応版)
- CopilotKitでバックエンドを構築
- CopilotKitでフロントエンドを構築
1. AgentCore RuntimeでMCP Apps対応MPCサーバーを構築
ホストするサーバーですが、シンプルに実装できそうなHonoを使用しました。
MCPサーバーのエンドポイントとは、/mcpとする必要があります。
トランスポートは、StreamableHTTPを指定しています。
const transport = new WebStandardStreamableHTTPServerTransport()
app.all('/mcp', async (c) => {
if (!mcpServer.isConnected()) {
console.log("connect")
await mcpServer.connect(transport)
}
const response = await transport.handleRequest(c.req.raw)
return response
})
サンプルも提供されていたので、参考にさせていただきました。
server.ts
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
import { mcpServer } from './mcp-server.js'
const app = new Hono()
app.use('/*', cors({
origin: '*',
allowMethods: ["*"],
allowHeaders: ["*"],
}));
const transport = new WebStandardStreamableHTTPServerTransport()
app.all('/mcp', async (c) => {
if (!mcpServer.isConnected()) {
console.log("connect")
await mcpServer.connect(transport)
}
const response = await transport.handleRequest(c.req.raw)
return response
})
const port = Number(process.env.PORT) || 8000
serve({
fetch: app.fetch,
port,
})
console.log(`Server is running on port ${port}`)
MCPサーバーの構築の際、MCP Appsに対応したToolとResourceを作成します。
resourceUriをキーにToolとResourceで同期を取るので、一致している必要があります。
肝心のHTMLは、Resourceのtextフィールドで別ファイルのバンドルしたHTMLを指定しています。
export const mcpServer = new McpServer({
name: 'simple-server',
version: '0.0.1'
})
const resourceUri = 'ui://get-time'
registerAppTool(
mcpServer,
'get-time',
{
title: 'Get Time',
description: 'Returns the current server time.',
inputSchema: {},
_meta: { ui: { resourceUri } }
},
async () => {
const time = new Date().toISOString()
return { content: [{ type: 'text', text: time }] }
}
)
registerAppResource(mcpServer, resourceUri, resourceUri, { mimeType: RESOURCE_MIME_TYPE }, async () => {
return {
contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }]
}
})
mcp-server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
import { readFile } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'
const __dirname = dirname(fileURLToPath(import.meta.url))
let html: string;
if (process.env.NODE_ENV === 'production') {
html = await readFile(join(__dirname, './index.html'), 'utf-8');
} else {
html = await readFile(join(__dirname, './dist/index.html'), 'utf-8');
}
export const mcpServer = new McpServer({
name: 'simple-server',
version: '0.0.1'
})
const resourceUri = 'ui://get-time'
registerAppTool(
mcpServer,
'get-time',
{
title: 'Get Time',
description: 'Returns the current server time.',
inputSchema: {},
_meta: { ui: { resourceUri } }
},
async () => {
const time = new Date().toISOString()
return { content: [{ type: 'text', text: time }] }
}
)
registerAppResource(mcpServer, resourceUri, resourceUri, { mimeType: RESOURCE_MIME_TYPE }, async () => {
return {
contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }]
}
})
AgentCore用のCDKのコードは以下です。
mcp-server-stack.ts
import * as path from "path";
import * as cdk from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import * as agentcore from "@aws-cdk/aws-bedrock-agentcore-alpha";
import { ProtocolType } from "@aws-cdk/aws-bedrock-agentcore-alpha";
import * as cognito from 'aws-cdk-lib/aws-cognito';
import * as ssm from 'aws-cdk-lib/aws-ssm';
export class McpServerStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// SSMパラメータストアから既存のCognito情報を取得
const userPoolId = ssm.StringParameter.valueForStringParameter(
this,
"/common-auth/user-pool-id"
);
const userPoolClientId = ssm.StringParameter.valueForStringParameter(
this,
"/common-auth/user-pool-client-id"
);
// 既存のCognito User Poolを参照
const existingUserPool = cognito.UserPool.fromUserPoolId(
this,
'ExistingUserPool',
userPoolId
);
// 既存のUser Pool Clientを参照
const existingUserPoolClient = cognito.UserPoolClient.fromUserPoolClientId(
this,
'ExistingUserPoolClient',
userPoolClientId
);
const agentRuntimeArtifact = agentcore.AgentRuntimeArtifact.fromAsset(
path.join(process.cwd(), "../server")
);
const runtime = new agentcore.Runtime(this, 'McpRuntime', {
runtimeName: 'mcpRuntime',
description: "MCP Remote Server",
agentRuntimeArtifact: agentRuntimeArtifact,
protocolConfiguration: ProtocolType.MCP,
authorizerConfiguration:
agentcore.RuntimeAuthorizerConfiguration.usingCognito(
existingUserPool,
[existingUserPoolClient]
),
});
new cdk.CfnOutput(this, "RuntimeArn", {
value: runtime.agentRuntimeArn,
description: "MCP Server Runtime ARN",
});
}
}
2. CopilotKitでバックエンドを構築
使用バージョン
- "@ag-ui/client": "^0.0.44",
- "@ag-ui/mcp-apps-middleware": "^0.0.3",
- "@copilotkit/runtime": "^1.51.3",
- "hono": "^4.11.7"
ポイント1:CopilotKitのMCP Apps対応
CopilotKitでMCP Appsを使用するためには、@ag-ui/mcp-apps-middlewareを使用します。
import { MCPAppsMiddleware } from "@ag-ui/mcp-apps-middleware";
CopilotKitでStrandsAgentsを設定する際、MCPAppsMiddlewareを使用してMCPサーバーを設定します。
- type:トランスポートを「SSE」か「StreamableHTTP」を指定します。
- url:MCPサーバーのURLを指定します。今回はAgentCore RuntimeのURLになります。
- serverId:複数のMCPサーバーと接続するため、識別できるユニークな値を指定します。
const agent = new HttpAgent({
url: fullUrl,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
'X-Amzn-Bedrock-AgentCore-Runtime-Custom-User-Sub': userSub,
'X-Amzn-Bedrock-AgentCore-Runtime-Custom-Session-Id': sessionId,
}
}).use(
new MCPAppsMiddleware({
mcpServers: [
{
type: "http",
url: `${BEDROCK_AGENT_CORE_ENDPOINT_URL}/runtimes/${encodeURIComponent("arn:aws:bedrock-agentcore:ap-northeast-1:0123456789012:runtime/mcpRuntime-TTWGUu11Tq")}/invocations`,
serverId: "mcp-server"
},
],
}),
)
ポイント2:MCPサーバーへhttpで接続する際はヘッダーが使えない(裏技有り)
typeにはhttp(StreamableHTTP)を指定しましたが、HTTPヘッダーが指定できませんでした。
そのため、今回は裏技的にhttpでMCPサーバーへの接続を捉えて、ヘッダーを上書きしています。
globalThis.fetch = async (input: any, init: any = {}) => {
let url = "";
if (typeof input === "string") {
url = input;
} else if (input && input.href) {
url = input.href;
}
if (
url &&
(url.includes(`mcpRuntime-D5WRUu54Tq`)) &&
currentAccessToken
) {
// Authorizationヘッダーを追加
console.log("Init headers:", init.headers);
init.headers.set('Authorization', `Bearer ${currentAccessToken}`);
}
return originalFetch(input, init);
};
詳細はこちら
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { streamHandle } from 'hono/aws-lambda'
import {
createCopilotEndpoint,
CopilotRuntime
} from '@copilotkitnext/runtime';
import { HttpAgent } from "@ag-ui/client";
import { MCPAppsMiddleware } from "@ag-ui/mcp-apps-middleware";
const BEDROCK_AGENT_CORE_ENDPOINT_URL = "https://bedrock-agentcore.ap-northeast-1.amazonaws.com"
const originalFetch = globalThis.fetch;
let currentAccessToken: string | null = null;
function setMcpAccessToken(token: string) {
currentAccessToken = token;
}
globalThis.fetch = async (input: any, init: any = {}) => {
let url = "";
if (typeof input === "string") {
url = input;
} else if (input && input.href) {
url = input.href;
}
if (
url &&
(url.includes(`mcpRuntime-D5WRUu54Tq`)) &&
currentAccessToken
) {
// Authorizationヘッダーを追加
console.log("Init headers:", init.headers);
init.headers.set('Authorization', `Bearer ${currentAccessToken}`);
}
return originalFetch(input, init);
};
const app = new Hono()
// Add CORS middleware
app.use('/*', cors({
origin: '*',
allowMethods: ['*'],
allowHeaders: ['*'],
maxAge: 86400
}))
// 環境変数からエージェントエンドポイントIDを取得
const agentEndpoint = process.env.AGENT_RUNTIME_ARN || '';
const fullUrl = `${BEDROCK_AGENT_CORE_ENDPOINT_URL}/runtimes/${encodeURIComponent(agentEndpoint)}/invocations`;
console.log("fullUrl:", fullUrl);
app.all('/copilotkit/*', async (c) => {
console.log("request: ", c.req)
console.log("request: ", c.res)
console.log("url: ", c.req.raw.url)
try {
const accessToken = c.req.header('x-access-token');
if (!accessToken) {
throw new Error('Missing access token');
}
setMcpAccessToken(accessToken);
const userSub = c.req.header('x-user-sub');
if (!userSub) {
throw new Error('Missing userSub');
}
const sessionId = c.req.header('x-session-id');
if (!sessionId) {
throw new Error('Missing sessionId');
}
const agent = new HttpAgent({
url: fullUrl,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
'X-Amzn-Bedrock-AgentCore-Runtime-Custom-User-Sub': userSub,
'X-Amzn-Bedrock-AgentCore-Runtime-Custom-Session-Id': sessionId,
}
}).use(
new MCPAppsMiddleware({
mcpServers: [
{
type: "http",
url: `${BEDROCK_AGENT_CORE_ENDPOINT_URL}/runtimes/${encodeURIComponent("arn:aws:bedrock-agentcore:ap-northeast-1:0123456789012:runtime/mcpRuntime-TTWGUu11Tq")}/invocations`,
serverId: "mcp-server"
},
],
}),
)
const runtime = new CopilotRuntime({
agents: { "strands-agent": agent }
});
const handler = createCopilotEndpoint({
runtime,
basePath: '/copilotkit',
});
const response = await handler.fetch(c.req.raw);
return response;
} catch (error) {
console.error('Error:', error);
return c.json(
{
error: error instanceof Error ? error.message : 'Unknown error',
},
500
);
}
})
export const handler = streamHandle(app)
3. StrandsAgentsでAIエージェントを構築(AG-UI対応版)
CopilotKitは、AG-UIに準拠しているため、StrandsAgents側もAG-UI化する必要があります。
ポイント:ag_ui_strandsのバージョンがv0.1.1以上必要
バックエンド側のag-ui/mcp-apps-middlewareは、AIエージェントを実行する際にMCPツールを注入し、AIエージェントがMCP Apps用のツールを使用できるようにします。
ツールの受け渡しはAG-UIプロトコルに沿っているのですが、つい最近までStrandsAgents側でAG-UIツール→StrandsAgentsツールへ変換できていませんでした。これがマージされたのがv0.1.1なので対応したバージョンを使用する必要があります。
詳細はこちら
import os
import jwt
from datetime import datetime
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from ag_ui_strands import StrandsAgent, StrandsAgentConfig
from ag_ui.core import RunAgentInput
from ag_ui.encoder import EventEncoder
from strands import Agent, tool
from strands.models import BedrockModel
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig, RetrievalConfig
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
from bedrock_agentcore.identity.auth import requires_api_key
# FastAPIアプリを作成
app = FastAPI(title="AWS Strands Agent")
# CORS設定
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
MEMORY_ID = os.environ.get("MEMORY_ID", "")
TAVILY_API_KEY = ""
SYSTEM_PROMPT = f"""
あなたは 私の親友エージェントです。ユーザーからの質問に回答してください。
## 回答時のルール
- 日本語で応答してください
- ツールが使えなかったり、自分の知らない知識は、はっきりと「わからない」と伝えてください
## 利用可能な機能
- 現在時刻:ツールを使って現在時刻を調べてください
"""
from typing import Callable, Optional
from strands.session import SessionManager
SessionManagerProvider = Callable[[RunAgentInput], SessionManager]
original_init = StrandsAgentConfig.__init__
def patched_config_init(self, tool_behaviors=None, state_context_builder=None, session_manager_provider=None):
original_init(self, tool_behaviors or {}, state_context_builder)
self.session_manager_provider: Optional[SessionManagerProvider] = session_manager_provider
# パッチを適用
StrandsAgentConfig.__init__ = patched_config_init
# パッチ適用前に元のrun()を保存
original_run = StrandsAgent.run
def patched_run(self, input_data):
"""session_manager対応のrun()メソッド"""
thread_id = input_data.thread_id or "default"
# エージェント作成時にsession_managerを追加
if thread_id not in self._agents_by_thread:
from strands import Agent as StrandsAgentCore
agent_kwargs = {
"model": self._model,
"system_prompt": self._system_prompt,
"tools": self._tools,
**self._agent_kwargs,
}
# session_managerを追加
if self.config and self.config.session_manager_provider:
session_manager = self.config.session_manager_provider(input_data)
agent_kwargs["session_manager"] = session_manager
self._agents_by_thread[thread_id] = StrandsAgentCore(**agent_kwargs)
# 保存済みの元のrun()を呼び出し
return original_run(self, input_data)
# パッチを適用
StrandsAgent.run = patched_run
def create_session_manager(
memory_id: str,
session_id: str,
actor_id: str,
region: str = "ap-northeast-1"
) -> AgentCoreMemorySessionManager:
"""AgentCore Memory用のセッションマネージャーを作成"""
NAMESPACE = f"/strategies/{session_id}/actors/{actor_id}"
memory_config = AgentCoreMemoryConfig(
memory_id=memory_id,
session_id=session_id,
actor_id=actor_id
)
return AgentCoreMemorySessionManager(
agentcore_memory_config=memory_config,
region_name=region
)
@requires_api_key(provider_name="TAVILY_API_KEY")
async def need_api_key(*, api_key: str):
global TAVILY_API_KEY
print("received api key for async func")
TAVILY_API_KEY = api_key
@tool
def tavily_search(query: str) -> dict:
"""Web検索用ツール
Args:
query: 検索内容
Returns:
検索結果
"""
tavily = TavilyClient(api_key=TAVILY_API_KEY)
return tavily.search(query)
@tool
def get_location(latitude, longitude):
"""
Display a map for the given coordinates.
Args:
latitude: The latitude coordinate (e.g., 35.6762 for Tokyo)
longitude: The longitude coordinate (e.g., 139.6503 for Tokyo)
Returns:
dict: A dictionary containing the latitude and longitude values
"""
return {
"latitude": latitude,
"longitude": longitude,
}
@app.post("/invocations")
async def invocations(input_data: RunAgentInput, request: Request):
print("========== Start ==========")
# リクエストヘッダーから情報取得
actor_id = request.headers.get("X-Amzn-Bedrock-AgentCore-Runtime-Custom-User-Sub", "")
session_id = request.headers.get("X-Amzn-Bedrock-AgentCore-Runtime-Custom-Session-Id", "")
def session_provider(input_data: RunAgentInput):
"""AgentCore Memoryのセッションマネージャ作成"""
return create_session_manager(
memory_id=MEMORY_ID,
session_id=session_id,
actor_id=actor_id
)
model = BedrockModel(
model_id="jp.anthropic.claude-haiku-4-5-20251001-v1:0"
)
# エージェント作成
agent = Agent(
model=model,
system_prompt=SYSTEM_PROMPT,
# tools=[
# tavily_search,
# get_location
# ]
)
strands_agent = StrandsAgent(
agent=agent,
name="strands_agent",
config=StrandsAgentConfig(
session_manager_provider=session_provider
)
)
# レスポンス処理
accept_header = request.headers.get("accept")
print(f"!!Accept header: {accept_header}")
encoder = EventEncoder(accept=accept_header)
async def event_generator():
try:
async for event in strands_agent.run(input_data):
print(f"!!Event: {event}")
yield encoder.encode(event)
except Exception as e:
print(f"!!Error occurred: {str(e)}")
from ag_ui.core import RunErrorEvent, EventType
error_event = RunErrorEvent(
type=EventType.RUN_ERROR,
message=f"Agent error: {str(e)}",
code="AGENT_ERROR"
)
yield encoder.encode(error_event)
return StreamingResponse(
event_generator(),
media_type=encoder.get_content_type()
)
@app.get("/ping")
async def ping():
"""ヘルスチェック用のエンドポイント"""
return {"status": "ok", "message": "Agent is running"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
4. CopilotKitでフロントエンドを構築
フロントエンドは、Reactで実装します。
実装と言っても、CopilotKitがフロントエンド向けにUIコンポーネントやHooksを提供しているので、簡単なチャットであれば呼び出すだけです。
使用バージョン
- "@copilotkitnext/react": "^1.51.3",
- "react": "^19.2.0",
ポイント1:CopilotKitのインポート元に注意
このあたりは私もイマイチ把握できていないのですが、CopilotKitには複数の系統があります。
どうやら12月ごろにv1.5がリリースされた際に、大幅に変更や機能の追加がされたようです。
今回は、copilotkitnextを使用しており、v2に該当します。
(こちらは公式にクイックスタートに載ってないのでもしかすると、まだ実験段階なのかもしれません。)
import { CopilotKitProvider } from "@copilotkitnext/react";
ポイント2:バックエンドと接続する設定
実際にバックエンドと接続する際には、CopilotKitProviderコンポーネントを使用します。
-
runtimeUrl:今回はバックエンドにあたるAPI GatewayのURLを指定します。 -
headers:AgentCoreの認証のため、Authenticationにアクセストークンを指定します
useSingleEndpoint
false(デフォルト)を指定すると、https://xxxxx/copilotkitの1本のパスでバックエンドにアクセスします。
tureを指定すると、https://xxxxx/copilotkit/infoなど機能ごとのパスでアクセスします。
このパス自体はCopilotKitが勝手に制御するのですが、1本だとエラーが起きた際に何の処理で発生しているか切り分けが困難だったため、tureを指定しています。
<CopilotKitProvider
runtimeUrl={`${custom.copilotkitFunctionUrl}copilotkit`}
headers={headers}
useSingleEndpoint={false}
showDevConsole={true}
>
{children}
</CopilotKitProvider>
詳細はこちら
import { useState, useEffect } from "react";
import { CopilotKitProvider } from "@copilotkitnext/react";
import '@copilotkitnext/react/styles.css';
import { useAuth } from "../hooks/useAuth";
import { custom } from '../../amplify_outputs.json';
export default function CopilotkitProvider({
children,
}: {
children: React.ReactNode;
}) {
const [sessionId] = useState(() => crypto.randomUUID());
const { isAuthenticated, getAccessToken, user } = useAuth();
const [accessToken, setAccessToken] = useState<string | null>(null);
useEffect(() => {
if (isAuthenticated) {
getAccessToken().then(token => setAccessToken(token || null));
}
}, [isAuthenticated, getAccessToken]);
// すべての必須値が揃っているかチェック
const isReady = isAuthenticated && accessToken && user?.userId;
const headers: Record<string, string> = {
"x-access-token": accessToken || "",
"x-user-sub": user?.userId || "",
"x-session-id": sessionId || ""
};
return (
isReady ?
<CopilotKitProvider
runtimeUrl={`${custom.copilotkitFunctionUrl}copilotkit`}
headers={headers}
useSingleEndpoint={false}
showDevConsole={true}
>
{children}
</CopilotKitProvider>
: null
);
}
import { CopilotChat } from "@copilotkitnext/react";
export default function Chat() {
return (
<CopilotChat
agentId="strands-agent"
className="copilotKitChat"
/>
);
}
以上です!
次回はMCP Appsで表示するUIを自前でカスタマイズしていきたいと思います!
グラフとか地図といったリッチなUIを表示してみたいですね。




