> Discover all available pages from the documentation index: https://mastra.zisheng.pro/ja/llms.txt # MCPServer `MCPServer` クラスは、既存の Mastra Tool と Agent を Model Context Protocol(MCP)サーバーとして公開する機能を提供します。これにより、Cursor、Windsurf、Claude Desktop などの MCP クライアントがこれらの機能へ接続し、Agent から利用できるようになります。 Mastra アプリケーション内で Tool や Agent を直接使用するだけなら、MCP サーバーを作成する必要はありません。この API は、Mastra の Tool と Agent を\_外部の\_ MCP クライアントへ公開するためのものです。 [stdio(サブプロセス)と SSE(HTTP)の両方の MCP Transport](https://modelcontextprotocol.io/docs/concepts/transports)に対応しています。 ## コンストラクター 新しい `MCPServer` を作成するには、サーバーの基本情報、提供する Tool、必要に応じて Tool として公開する Agent を指定します。 ```typescript import { Agent } from '@mastra/core/agent' import { createTool } from '@mastra/core/tools' import { MCPServer } from '@mastra/mcp' import { z } from 'zod' import { dataProcessingWorkflow } from '../workflows/dataProcessingWorkflow' const myAgent = new Agent({ id: 'my-example-agent', name: 'MyExampleAgent', description: 'A generalist to help with basic questions.', instructions: 'You are a helpful assistant.', model: 'openai/gpt-5.6-sol', }) const weatherTool = createTool({ id: 'getWeather', description: 'Gets the current weather for a location.', inputSchema: z.object({ location: z.string() }), execute: async inputData => `Weather in ${inputData.location} is sunny.`, }) const server = new MCPServer({ id: 'my-custom-server', name: 'My Custom Server', version: '1.0.0', description: 'A server that provides weather data and agent capabilities', instructions: 'Use the available tools to help users with weather information and data processing tasks.', tools: { weatherTool }, agents: { myAgent }, // this agent will become tool "ask_myAgent" workflows: { dataProcessingWorkflow, // this workflow will become tool "run_dataProcessingWorkflow" }, }) ``` ### 設定プロパティ コンストラクターは、次のプロパティを持つ `MCPServerConfig` オブジェクトを受け取ります。 **id** (`string`): サーバーの一意な識別子。サーバーを Mastra に登録してもこの ID は保持され、getMCPServerById() でサーバーを取得する際に使用できます。 **name** (`string`): サーバーを表す名前(例: 'My Weather and Agent Server')。 **version** (`string`): サーバーのセマンティックバージョン(例: '1.0.0')。 **tools** (`ToolsInput`): キーが Tool 名、値が Mastra Tool の定義(createTool または Vercel AI SDK で作成)であるオブジェクト。これらの Tool は直接公開されます。 **agents** (`Record`): キーが Agent の識別子、値が Mastra Agent インスタンスであるオブジェクト。各 Agent は ask\_\ という名前の Tool に自動変換されます。Agent のコンストラクター設定には、空でない文字列の description プロパティを\*\*必ず\*\*定義してください。この値が Tool の説明に使用されます。Agent の説明が未指定または空の場合、MCPServer の初期化時にエラーがスローされます。 **workflows** (`Record`): キーが Workflow の識別子、値が Mastra Workflow インスタンスであるオブジェクト。各 Workflow は run\_\ という名前の Tool に変換されます。Workflow の inputSchema が Tool の入力スキーマになります。Workflow には、Tool の説明に使われる空でない文字列の description プロパティを\*\*必ず\*\*指定してください。説明が未指定または空の場合、エラーがスローされます。Tool は workflow\.createRun()、続いて run.start({ inputData: \ }) を呼び出して Workflow を実行します。Agent または Workflow から生成した Tool 名(例: ask\_myAgent、run\_myWorkflow)が明示的に定義した Tool 名や別の生成名と重複した場合、明示的に定義した Tool が優先され、警告が記録されます。それ以降に重複する Agent や Workflow はスキップされます。 **description** (`string`): MCP サーバーの機能を示す省略可能な説明。 **instructions** (`string`): サーバーとその機能の使用方法を示す省略可能な指示。 **mapAuthInfoToUser** (`({ authInfo, extra, requestContext }) => unknown | null | undefined | Promise`): extra.authInfo の MCP Transport 認証データを、Mastra の FGA チェックで使用する user 値へマッピングします。OAuth で保護された MCP サーバーを、FGA Provider を持つ Mastra インスタンスへ登録する場合に使用します。 **fga** (`{ resourceMapping?: Partial string | undefined }>>; permissionMapping?: Record }`): この MCP サーバーの tools/list と tools/call の FGA チェックに使用するリソースおよび権限のマッピングを上書きします。MCP の認可スコープを、内部の Agent や Workflow による Tool 実行とは別に設定する場合に使用します。 **repository** (`Repository`): サーバーのソースコードに関する省略可能なリポジトリ情報。 **releaseDate** (`string`): このサーバーバージョンの省略可能なリリース日(ISO 8601 文字列)。未指定の場合、インスタンス化した時刻がデフォルトになります。 **isLatest** (`boolean`): 最新バージョンかどうかを示す省略可能なフラグ。未指定の場合、デフォルトは true です。 **packageCanonical** (`'npm' | 'docker' | 'pypi' | 'crates' | string`): サーバーをパッケージとして配布する場合の省略可能な標準パッケージ形式(例: 'npm'、'docker')。 **packages** (`PackageInfo[]`): このサーバー用にインストールできるパッケージの省略可能なリスト。 **remotes** (`RemoteInfo[]`): このサーバーのリモートアクセスポイントの省略可能なリスト。 **resources** (`MCPServerResources`): サーバーによる MCP Resource の処理方法を定義するオブジェクト。詳細は Resource handling セクションを参照してください。 **prompts** (`MCPServerPrompts`): サーバーによる MCP Prompt の処理方法を定義するオブジェクト。詳細は Prompt handling セクションを参照してください。 **appResources** (`AppResources`): ui:// URI から App Resource 設定へのマップ。各エントリーは、MCP Apps 拡張機能(SEP-1865)を介して提供されるインタラクティブな HTML UI を定義します。詳細は MCP Apps セクションを参照してください。 ## Agent を Tool として公開する `MCPServer` には、Mastra Agent を呼び出し可能な Tool として自動公開する機能があります。設定の `agents` プロパティに Agent を指定すると、次のように処理されます。 - **Tool の命名**: 各 Agent は `ask_` という名前の Tool に変換されます。`` は、`agents` オブジェクトでその Agent に使用したキーです。たとえば `agents: { myAgentKey: myAgentInstance }` と設定すると、`ask_myAgentKey` という Tool が作成されます。 - **Tool の機能**: - **説明**: 生成される Tool の説明は「Agent `` に質問します。元の Agent の指示: ``」という形式になります。 - **入力**: Tool は、文字列の `message` プロパティを持つ単一のオブジェクト引数を受け取ります: `{ message: "Your question for the agent" }`。 - **実行**: この Tool が呼び出されると、指定された `query` で対応する Agent の `generate()` メソッドを呼び出します。 - **出力**: Agent の `generate()` メソッドから得た結果を、そのまま Tool の出力として返します。 - **名前の重複。** `tools` 設定で明示的に定義した Tool と Agent から生成した Tool の名前が同じ場合(たとえば `myAgentKey` というキーの Agent と `ask_myAgentKey` という Tool がある場合)、_明示的に定義した Tool が優先されます_。重複した Agent は Tool に変換されず、警告が記録されます。 これにより、MCP クライアントは他の Tool と同様に、自然言語のクエリで Agent と簡単にやり取りできます。 ### Agent から Tool への変換 `agents` 設定プロパティに Agent を指定すると、`MCPServer` は Agent ごとに対応する Tool を自動作成します。Tool 名は `ask_` となり、`` には `agents` オブジェクトで使用したキーが入ります。 生成される Tool の説明は「Agent `` に質問します。Agent の説明: ``」となります。 Agent を Tool に変換するには、インスタンス化時の設定に空でない文字列の `description` プロパティを**必ず**指定してください(例: `new Agent({ id: 'my-agent', name: 'myAgent', description: 'This agent does X.', ... })`)。`description` が未指定または空の Agent を `MCPServer` に渡すと、`MCPServer` のインスタンス化時にエラーがスローされ、サーバーのセットアップに失敗します。 これにより、Agent の生成機能を MCP 経由ですぐに公開し、クライアントから Agent へ直接質問できるようになります。 ### Tool から MCP Context にアクセスする `MCPServer` で公開した Tool は、その呼び出し方に応じて2つの異なるプロパティから MCP Request Context(認証、セッション ID など)へアクセスできます。 | 呼び出しパターン | アクセス方法 | | ------------------- | ------------------------------------------- | | Tool の直接呼び出し | `context?.mcp?.extra` | | Agent による Tool 呼び出し | `context?.requestContext?.get("mcp.extra")` | **共通パターン**(どちらの Context でも使用可能): ```typescript const mcpExtra = context?.mcp?.extra ?? context?.requestContext?.get('mcp.extra') const authInfo = mcpExtra?.authInfo ``` #### 例: 両方の Context で動作する Tool ```typescript import { createTool } from '@mastra/core/tools' import { z } from 'zod' const fetchUserData = createTool({ id: 'fetchUserData', description: 'Fetches user data using authentication from MCP context', inputSchema: z.object({ userId: z.string().describe('The ID of the user to fetch'), }), execute: async (inputData, context) => { // Access MCP authentication context // When called directly via MCP: context.mcp.extra // When called via agent: context.requestContext.get('mcp.extra') const mcpExtra = context?.mcp?.extra || context?.requestContext?.get('mcp.extra') const authInfo = mcpExtra?.authInfo if (!authInfo?.token) { throw new Error('Authentication required') } const response = await fetch(`https://api.example.com/users/${inputData.userId}`, { headers: { Authorization: `Bearer ${authInfo.token}`, }, }) return response.json() }, }) ``` ## メソッド 以下の関数を `MCPServer` インスタンスで呼び出すことで、その動作を制御したり、情報を取得したりできます。 ### `startStdio()` このメソッドは、標準入出力(stdio)で通信するサーバーを起動します。通常、サーバーをコマンドラインプログラムとして実行する場合に使用します。 ```typescript async startStdio(): Promise ``` stdio を使用してサーバーを起動する例を次に示します。 ```typescript const server = new MCPServer({ id: 'my-server', name: 'My Server', version: '1.0.0', tools: {/* ... */}, }) await server.startStdio() ``` ### `startSSE()` このメソッドを使用すると、MCP server を既存の Web サーバーに統合し、Server-Sent Events(SSE)で通信できます。Web サーバーが SSE パスまたはメッセージパスへのリクエストを受信したときに、Web サーバーのコードから呼び出します。 ```typescript async startSSE({ url, ssePath, messagePath, req, res, }: { url: URL; ssePath: string; messagePath: string; req: any; res: any; }): Promise ``` HTTP サーバーのリクエストハンドラー内で `startSSE` を使用する例を次に示します。この例では、MCP client は `http://localhost:1234/sse` で MCP server に接続できます。 ```typescript import http from 'http' const httpServer = http.createServer(async (req, res) => { await server.startSSE({ url: new URL(req.url || '', `http://localhost:1234`), ssePath: '/sse', messagePath: '/message', req, res, }) }) httpServer.listen(PORT, () => { console.log(`HTTP server listening on port ${PORT}`) }) ``` `startSSE` メソッドに必要な値の詳細は次のとおりです。 **url** (`URL`): ユーザーがリクエストしている Web アドレス。 **ssePath** (`string`): client が SSE に接続する URL のパス(例:'/sse')。 **messagePath** (`string`): client がメッセージを送信する URL のパス(例:'/message')。 **req** (`any`): Web サーバーから受け取るリクエストオブジェクト。 **res** (`any`): データの返信に使用する、Web サーバーのレスポンスオブジェクト。 ### `startHonoSSE()` このメソッドを使用すると、MCP server を既存の Web サーバーに統合し、Server-Sent Events(SSE)で通信できます。Web サーバーが SSE パスまたはメッセージパスへのリクエストを受信したときに、Web サーバーのコードから呼び出します。 ```typescript async startHonoSSE({ url, ssePath, messagePath, req, res, }: { url: URL; ssePath: string; messagePath: string; req: any; res: any; }): Promise ``` HTTP サーバーのリクエストハンドラー内で `startHonoSSE` を使用する例を次に示します。この例では、MCP client は `http://localhost:1234/hono-sse` で MCP server に接続できます。 ```typescript import http from 'http' const httpServer = http.createServer(async (req, res) => { await server.startHonoSSE({ url: new URL(req.url || '', `http://localhost:1234`), ssePath: '/hono-sse', messagePath: '/message', req, res, }) }) httpServer.listen(PORT, () => { console.log(`HTTP server listening on port ${PORT}`) }) ``` `startHonoSSE` メソッドに必要な値の詳細は次のとおりです。 **url** (`URL`): ユーザーがリクエストしている Web アドレス。 **ssePath** (`string`): client が SSE に接続する URL のパス(例:'/hono-sse')。 **messagePath** (`string`): client がメッセージを送信する URL のパス(例:'/message')。 **req** (`any`): Web サーバーから受け取るリクエストオブジェクト。 **res** (`any`): データの返信に使用する、Web サーバーのレスポンスオブジェクト。 ### `startHTTP()` このメソッドを使用すると、MCP server を既存の Web サーバーに統合し、Streamable HTTP で通信できます。Web サーバーが HTTP リクエストを受信したときに、Web サーバーのコードから呼び出します。 ```typescript async startHTTP({ url, httpPath, req, res, options = { sessionIdGenerator: () => randomUUID() }, }: { url: URL; httpPath: string; req: http.IncomingMessage; res: http.ServerResponse; options?: StreamableHTTPServerTransportOptions; }): Promise ``` HTTP サーバーのリクエストハンドラー内で `startHTTP` を使用する例を次に示します。この例では、MCP client は `http://localhost:1234/http` で MCP server に接続できます。 ```typescript import http from 'http' const httpServer = http.createServer(async (req, res) => { await server.startHTTP({ url: new URL(req.url || '', 'http://localhost:1234'), httpPath: `/mcp`, req, res, options: { sessionIdGenerator: () => randomUUID(), }, }) }) httpServer.listen(PORT, () => { console.log(`HTTP server listening on port ${PORT}`) }) ``` **サーバーレス環境**(Supabase Edge Functions、Cloudflare Workers、Vercel Edge など)では、`serverless: true` を使用してステートレス動作を有効にします。 ```typescript // Supabase Edge Function example import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' import { MCPServer } from '@mastra/mcp' // Note: You will need to convert req/res format from Deno to Node import { toReqRes, toFetchResponse } from 'fetch-to-node' const server = new MCPServer({ id: 'my-serverless-mcp', name: 'My Serverless MCP', version: '1.0.0', tools: {/* your tools */}, }) serve(async req => { const url = new URL(req.url) if (url.pathname === '/mcp') { // Convert Deno Request to Node.js-compatible format const { req: nodeReq, res: nodeRes } = toReqRes(req) await server.startHTTP({ url, httpPath: '/mcp', req: nodeReq, res: nodeRes, options: { serverless: true, // ← Enable stateless mode for serverless }, }) return toFetchResponse(nodeRes) } return new Response('Not found', { status: 404 }) }) ``` > **Serverless: true を使用する場合:** 各リクエストが新しいステートレスな実行コンテキストで処理される環境にデプロイする場合は、`serverless: true` を使用します。 > > - Supabase Edge Functions > - Cloudflare Workers > - Vercel Edge Functions > - Netlify Edge Functions > - AWS Lambda > - Deno Deploy > > 次の環境では、デフォルトのセッションベースモード(`serverless: true` なし)を使用します。 > > - 長時間稼働する Node.js サーバー > - Docker コンテナ > - 従来型のホスティング(VPS、専用サーバー) > > サーバーレスモードでは、セッション管理を無効にし、リクエストごとに新しいサーバーインスタンスを作成します。これは、呼び出し間でメモリが保持されないステートレス環境に必要です。 > > デフォルトでは、サーバーレスモードは各リクエストを単一の JSON レスポンスにバッファリングするため、Tool が送信した `notifications/progress` は client に届きません。代わりにリクエストスコープの SSE ストリーミングでリクエストを処理するには、`serverlessStreaming: true` を設定します。これにより、最終結果より前に進捗通知が配信されます。 > > ```typescript > await server.startHTTP({ > url, > httpPath: '/mcp', > req: nodeReq, > res: nodeRes, > options: { > serverless: true, > serverlessStreaming: true, // ← Stream request-scoped notifications/progress > }, > }) > ``` > > この場合もステートレスであり、`mcp-session-id` は不要で、保持もされません。有効になるのは、進捗など、現在のリクエストに限定された通知のみです。次のセッション依存機能は引き続き使用できません。 > > 次の MCP 機能にはセッション状態または永続的な接続が必要なため、`serverlessStreaming: true` を使用する場合も含め、サーバーレスモードでは**動作しません**。 > > - **Elicitation** - Tool の実行中に対話形式でユーザー入力を求めるには、レスポンスを正しい client に返すためのセッション管理が必要です > - **Resource の購読** - 購読状態を維持するため、`resources/subscribe` と `resources/unsubscribe` には永続的な接続が必要です > - **Resource 更新通知** - `resources.notifyUpdated()` で client に通知するには、有効な購読と永続的な接続が必要です > - **Prompt リスト変更通知** - `prompts.notifyListChanged()` で client に更新をプッシュするには、永続的な接続が必要です > - **Tool リスト変更通知** - `toolActions.notifyListChanged()` で client に更新をプッシュするには、永続的な接続が必要です > - **サーバーログ通知** - `sendLoggingMessage()` で client にログメッセージをプッシュするには、永続的な接続が必要です > > これらの機能は、長時間稼働するサーバー環境(Node.js サーバー、Docker コンテナなど)では通常どおり動作します。 `startHTTP` メソッドに必要な値の詳細は次のとおりです。 **url** (`URL`): ユーザーがリクエストしている Web アドレス。 **httpPath** (`string`): MCP server が HTTP リクエストを処理する URL のパス(例:'/mcp')。 **req** (`http.IncomingMessage`): Web サーバーから受け取るリクエストオブジェクト。 **res** (`http.ServerResponse`): データの返信に使用する、Web サーバーのレスポンスオブジェクト。 **options** (`StreamableHTTPServerTransportOptions`): HTTP transport のオプション設定。詳細は次のオプション表を参照してください。 `StreamableHTTPServerTransportOptions` オブジェクトを使用すると、HTTP transport の動作をカスタマイズできます。利用可能なオプションは次のとおりです。 **serverless** (`boolean`): true の場合、セッション管理なしのステートレスモードで動作します。各リクエストは新しいサーバーインスタンスで個別に処理されます。呼び出し間でセッションを保持できないサーバーレス環境(Cloudflare Workers、Supabase Edge Functions、Vercel Edge など)に不可欠です。デフォルトは false です。 **serverlessStreaming** (`boolean`): true の場合、サーバーレスリクエストはバッファリングされた JSON レスポンスの代わりに、リクエストスコープの SSE ストリーミングを使用します。これにより、リクエスト内の notifications/progress が最終結果より前に client に届きます。serverless: true と併用した場合にのみ有効です。デフォルトは、後方互換性のある動作を維持する false(バッファリングされた JSON レスポンス)です。有効になるのは進捗などのリクエストスコープの通知だけであり、Elicitation、購読、リクエスト外の通知には引き続きセッション状態が必要です。 **sessionIdGenerator** (`(() => string) | undefined`): 一意のセッション ID を生成する関数。暗号学的に安全で、グローバルに一意な文字列を使用する必要があります。セッション管理を無効にするには undefined を返します。 **onsessioninitialized** (`(sessionId: string) => void`): 新しいセッションが初期化されたときに呼び出されるコールバック。有効な MCP セッションの追跡に役立ちます。 **enableJsonResponse** (`boolean`): true の場合、サーバーは Server-Sent Events(SSE)によるストリーミングの代わりに、通常の JSON レスポンスを返します。デフォルトは false です。 **eventStore** (`EventStore`): メッセージを再開可能にするためのイベントストア。指定すると、client は再接続してメッセージストリームを再開できます。 ### `close()` このメソッドはサーバーを閉じ、すべてのリソースを解放します。 ```typescript async close(): Promise ``` ### `getServerInfo()` このメソッドはサーバーの基本情報を返します。 ```typescript getServerInfo(): ServerInfo ``` ### `getServerDetail()` このメソッドはサーバー情報の詳細を返します。 ```typescript getServerDetail(): ServerDetail ``` ### `getToolListInfo()` このメソッドは、サーバーの作成時に設定された Tool を返します。読み取り専用のリストで、デバッグに役立ちます。 ```typescript getToolListInfo(): ToolListInfo ``` ### `getToolInfo()` このメソッドは、特定の Tool の詳細を返します。 ```typescript getToolInfo(toolName: string): ToolInfo ``` ### `executeTool()` このメソッドは、指定した Tool を実行して結果を返します。 ```typescript executeTool(toolName: string, input: any): Promise ``` ### `getStdioTransport()` `startStdio()` でサーバーを起動した場合、このメソッドを使用して stdio 通信を管理するオブジェクトを取得できます。主に内部確認やテストに使用します。 ```typescript getStdioTransport(): StdioServerTransport | undefined ``` ### `getSseTransport()` `startSSE()` でサーバーを起動した場合、このメソッドを使用して SSE 通信を管理するオブジェクトを取得できます。`getStdioTransport` と同様、主に内部確認やテストに使用します。 ```typescript getSseTransport(): SSEServerTransport | undefined ``` ### `getSseHonoTransport()` `startHonoSSE()` でサーバーを起動した場合、このメソッドを使用して SSE 通信を管理するオブジェクトを取得できます。`getSseTransport` と同様、主に内部確認やテストに使用します。 ```typescript getSseHonoTransport(): SSETransport | undefined ``` ### `getStreamableHTTPTransport()` `startHTTP()` でサーバーを起動した場合、このメソッドを使用して HTTP 通信を管理するオブジェクトを取得できます。`getSseTransport` と同様、主に内部確認やテストに使用します。 ```typescript getStreamableHTTPTransport(): StreamableHTTPServerTransport | undefined ``` ### `tools()` この MCP server が提供する指定の Tool を実行します。 ```typescript async executeTool( toolId: string, args: any, executionContext?: { messages?: any[]; toolCallId?: string }, ): Promise ``` **toolId** (`string`): 実行する Tool の ID または名前。 **args** (`any`): Tool の execute 関数に渡す引数。 **executionContext** (`object`): messages や toolCallId など、Tool の実行に使用するオプションのコンテキスト。 ## Resource の処理 ### MCP Resource とは? Resource は Model Context Protocol(MCP)の中核となるプリミティブで、サーバーがクライアントから読み取り可能なデータやコンテンツを公開し、LLM との対話のコンテキストとして利用できるようにします。MCP サーバーが提供する次のようなあらゆる種類のデータを表します。 - ファイルの内容 - データベースのレコード - API レスポンス - リアルタイムのシステムデータ - スクリーンショットや画像 - ログファイル Resource は一意の URI(例:`file:///home/user/documents/report.pdf`、`postgres://database/customers/schema`)で識別され、テキスト(UTF-8 エンコード)またはバイナリデータ(base64 エンコード)を格納できます。 クライアントは、次の方法で Resource を検出できます。 1. **直接 Resource**:サーバーは `resources/list` エンドポイントを介して、具体的な Resource の一覧を公開します。 2. **Resource テンプレート**:実行時に定義される Resource の場合、サーバーはクライアントが Resource URI の構築に使用する URI テンプレート(RFC 6570)を公開できます。 Resource を読み取るには、クライアントが URI を指定して `resources/read` リクエストを送信します。クライアントがその Resource を購読している場合、サーバーは Resource 一覧の変更(`notifications/resources/list_changed`)や、特定の Resource の内容の更新(`notifications/resources/updated`)をクライアントに通知することもできます。 詳しくは、[Resource に関する MCP 公式ドキュメント](https://modelcontextprotocol.io/docs/concepts/resources)を参照してください。 ### `MCPServerResources` 型 `resources` オプションには、`MCPServerResources` 型のオブジェクトを指定します。この型は、サーバーが Resource リクエストを処理するために使用するコールバックを定義します。 ```typescript export type MCPServerResources = { // Callback to list available resources listResources: () => Promise // Callback to get the content of a specific resource getResourceContent: ({ uri, }: { uri: string }) => Promise // Optional callback to list available resource templates resourceTemplates?: () => Promise } export type MCPServerResourceContent = { text?: string } | { blob?: string } ``` 例: ```typescript import { MCPServer } from '@mastra/mcp' import type { MCPServerResourceContent, Resource, ResourceTemplate } from '@mastra/mcp' // Resources/resource templates will generally be dynamically fetched. const myResources: Resource[] = [ { uri: 'file://data/123.txt', name: 'Data File', mimeType: 'text/plain' }, ] const myResourceContents: Record = { 'file://data.txt/123': { text: 'This is the content of the data file.' }, } const myResourceTemplates: ResourceTemplate[] = [ { uriTemplate: 'file://data/{id}', name: 'Data File', description: 'A file containing data.', mimeType: 'text/plain', }, ] const myResourceHandlers: MCPServerResources = { listResources: async () => myResources, getResourceContent: async ({ uri }) => { if (myResourceContents[uri]) { return myResourceContents[uri] } throw new Error(`Resource content not found for ${uri}`) }, resourceTemplates: async () => myResourceTemplates, } const serverWithResources = new MCPServer({ id: 'resourceful-server', name: 'Resourceful Server', version: '1.0.0', tools: {/* ... your tools ... */}, resources: myResourceHandlers, }) ``` ### Resource の変更をクライアントに通知する 利用可能な Resource やその内容が変更された場合、サーバーは該当する Resource を購読している接続済みクライアントに通知できます。 #### `server.resources.notifyUpdated({ uri: string })` `uri` で識別される特定の Resource の内容が更新されたときに、このメソッドを呼び出します。この URI を購読しているクライアントがある場合、そのクライアントは `notifications/resources/updated` メッセージを受信します。 ```typescript async server.resources.notifyUpdated({ uri: string }): Promise ``` 例: ```typescript // After updating the content of 'file://data.txt' await serverWithResources.resources.notifyUpdated({ uri: 'file://data.txt' }) ``` #### `server.resources.notifyListChanged()` 利用可能な Resource の一覧が変更されたとき(Resource が追加または削除された場合など)に、このメソッドを呼び出します。クライアントに `notifications/resources/list_changed` メッセージが送信され、Resource 一覧の再取得が促されます。 ```typescript async server.resources.notifyListChanged(): Promise ``` 例: ```typescript // After adding a new resource to the list managed by 'myResourceHandlers.listResources' await serverWithResources.resources.notifyListChanged() ``` ## Prompt の処理 ### MCP Prompt とは? Prompt は、MCP サーバーがクライアントに公開する再利用可能なテンプレートまたは Workflow です。引数を受け取り、Resource のコンテキストを含めることができます。また、バージョニングをサポートし、LLM との対話を標準化します。 Prompt は一意の名前(および任意のバージョン)で識別され、実行時に定義することも、静的に定義することもできます。 ### `MCPServerPrompts` 型 `prompts` オプションには、`MCPServerPrompts` 型のオブジェクトを指定します。この型は、サーバーが Prompt リクエストを処理するために使用するコールバックを定義します。 ```typescript export type MCPServerPrompts = { // Callback to list available prompts listPrompts: () => Promise // Callback to get the messages/content for a specific prompt getPromptMessages?: ({ name, version, args, }: { name: string version?: string args?: any }) => Promise<{ prompt: Prompt; messages: PromptMessage[] }> } ``` 例: ```typescript import { MCPServer } from '@mastra/mcp' import type { Prompt, PromptMessage, MCPServerPrompts } from '@mastra/mcp' const prompts: Prompt[] = [ { name: 'analyze-code', description: 'Analyze code for improvements', version: 'v1', }, { name: 'analyze-code', description: 'Analyze code for improvements (new logic)', version: 'v2', }, ] const myPromptHandlers: MCPServerPrompts = { listPrompts: async () => prompts, getPromptMessages: async ({ name, version, args }) => { if (name === 'analyze-code') { if (version === 'v2') { const prompt = prompts.find(p => p.name === name && p.version === 'v2') if (!prompt) throw new Error('Prompt version not found') return { prompt, messages: [ { role: 'user', content: { type: 'text', text: `Analyze this code with the new logic: ${args.code}`, }, }, ], } } // Default or v1 const prompt = prompts.find(p => p.name === name && p.version === 'v1') if (!prompt) throw new Error('Prompt version not found') return { prompt, messages: [ { role: 'user', content: { type: 'text', text: `Analyze this code: ${args.code}` }, }, ], } } throw new Error('Prompt not found') }, } const serverWithPrompts = new MCPServer({ id: 'promptful-server', name: 'Promptful Server', version: '1.0.0', tools: {/* ... */}, prompts: myPromptHandlers, }) ``` ### Prompt の変更をクライアントに通知する 利用可能な Prompt が変更された場合、サーバーは接続済みクライアントに通知できます。 #### `server.prompts.notifyListChanged()` 利用可能な Prompt の一覧が変更されたとき(Prompt が追加または削除された場合など)に、このメソッドを呼び出します。クライアントに `notifications/prompts/list_changed` メッセージが送信され、Prompt 一覧の再取得が促されます。 ```typescript await serverWithPrompts.prompts.notifyListChanged() ``` ### Prompt 処理のベストプラクティス - 明確で内容が分かる Prompt 名と説明を使用します。 - `getPromptMessages` ですべての必須引数を検証します。 - 破壊的変更を行う可能性がある場合は、`version` フィールドを含めます。 - `version` パラメーターを使用して、適切な Prompt ロジックを選択します。 - Prompt 一覧が変更されたらクライアントに通知します。 - 分かりやすいメッセージでエラーを処理します。 - 引数の要件と利用可能なバージョンを文書化します。 ## Tool の動的管理 通常、Tool は `MCPServer` の構築時に指定しますが、サーバーの実行中に追加または削除することもできます。サーバーは、これらの操作を `toolActions` プロパティを介して公開します。Tool 一覧が変更されると、接続済みクライアントは `notifications/tools/list_changed` メッセージを受信し、Tool 一覧の再取得を促されます。 登録済みの Tool レジストリを返すメソッドが `tools()` であるため、このプロパティには `toolActions` という名前が付いています。 ### `toolActions.add(tools)` 実行中のサーバーに新しい Tool を登録し、接続済みクライアントに通知します。Tool は、コンストラクターに渡す Tool と同様に、レコードのキーで識別されます。既存のキーで Tool を追加すると、その Tool が置き換えられます。 ```typescript async server.toolActions.add(tools: ToolsInput): Promise ``` 例: ```typescript import { createTool } from '@mastra/core/tools' import { z } from 'zod' const searchTool = createTool({ id: 'search', description: 'Searches the knowledge base.', inputSchema: z.object({ query: z.string() }), execute: async ({ query }) => ({ results: [] }), }) await server.toolActions.add({ searchTool }) ``` ### `toolActions.remove(toolIds)` 実行中のサーバーから Tool ID を指定して Tool を削除し、接続済みクライアントに通知します。不明な Tool ID は無視されます。少なくとも 1 つの Tool が削除された場合にのみ通知が送信されます。 ```typescript async server.toolActions.remove(toolIds: string[]): Promise ``` 例: ```typescript await server.toolActions.remove(['searchTool']) ``` ### `toolActions.notifyListChanged()` Tool レジストリを変更せずに、接続済みクライアントへ `notifications/tools/list_changed` メッセージを送信します。認可の変更など、別の理由で Tool の利用可否が変わった場合に呼び出します。 ```typescript async server.toolActions.notifyListChanged(): Promise ``` ### Mastra レジストリとの同期 サーバーが Mastra インスタンスに登録されている場合、`toolActions.add()` と `toolActions.remove()` は、起動時に行われる Tool の自動登録と同様に、Mastra インスタンスの Tool レジストリも更新します。追加した Tool は `mastra.listTools()` から利用可能になり(Tool 固有の `id` がある場合はそれをキーとして使用)、削除した Tool はレジストリから削除されます。 ## ロギング MCP サーバーは、`notifications/message` を使用して構造化ログメッセージをクライアントに送信できます。クライアントは `logging/setLevel` リクエストを送信して詳細度を制御します。サーバーは、指定された最低レベルを下回るメッセージを破棄します(RFC 5424 の重大度順に準拠)。レベルはセッションごとに追跡されるため、クライアントごとに異なる詳細度を指定できます。 ### `sendLoggingMessage()` 各クライアントの最低ログレベルに従い、接続済みのすべてのクライアントにログ通知を送信します。 ```typescript async server.sendLoggingMessage(params: { level: LoggingLevel; data: unknown; logger?: string; }): Promise ``` 例: ```typescript await server.sendLoggingMessage({ level: 'info', data: { message: 'Sync completed', itemsProcessed: 42 }, }) ``` ### `context.mcp.log()` Tool の `execute` 関数内で `context.mcp.log()` を使用すると、その Tool を呼び出したクライアントにログメッセージを送信できます。 ```typescript async context.mcp.log( level: LoggingLevel, message: string, data?: Record ): Promise ``` 例: ```typescript execute: async ({ location }, context) => { await context.mcp.log('debug', 'Fetching weather', { location }) const weather = await fetchWeather(location) await context.mcp.log('info', 'Weather fetched') return weather } ``` ## 進捗通知 長時間実行される Tool は、`notifications/progress` を使用して呼び出し元のクライアントに進捗を報告できます。進捗は、呼び出し元がリクエストに `progressToken` を含めて進捗追跡を要求した場合にのみ送信されます(Mastra の `MCPClient` は、`enableProgressTracking` が設定されている場合にこれを行います)。トークンが送信されていない場合、`context.mcp.progress()` は何も行いません。 ### `context.mcp.progress()` ```typescript async context.mcp.progress(params: { progress: number; total?: number; message?: string; }): Promise ``` 例: ```typescript execute: async ({ items }, context) => { for (const [index, item] of items.entries()) { await processItem(item) await context.mcp.progress({ progress: index + 1, total: items.length, message: `Processed ${item.name}`, }) } return { done: true } } ``` ## 通知の配信 通知メソッド(`resources.notifyListChanged()`、`prompts.notifyListChanged()`、`toolActions.notifyListChanged()`、`sendLoggingMessage()`)は、stdio/SSE 接続や各 Streamable HTTP セッションなど、すべてのトランスポートを介して接続済みの全クライアントに通知をブロードキャストします。例外は `resources.notifyUpdated()` で、`resources/subscribe` を介して Resource URI を購読したクライアントにのみ通知します。Streamable HTTP クライアントの購読はセッションごとに追跡されます。従来の SSE クライアントはメインのサーバーインスタンスを共有するため、1 つの購読セットを共有します。ステートレスな serverless モードを使用するクライアントは、リクエストごとに一時的なサーバーインスタンスが使用されるため、通知を受信できません。 ## 例 MCPServer のセットアップとデプロイの実践的な例については、[MCP Server の公開ガイド](https://mastra.zisheng.pro/ja/guides/guide/publishing-mcp-server)を参照してください。 このページの冒頭にある例でも、Tool と Agent の両方を指定して `MCPServer` をインスタンス化する方法を示しています。 ## Elicitation ### Elicitation とは Elicitation は Model Context Protocol(MCP)の機能で、サーバーからユーザーに構造化された情報を要求できます。サーバーが実行時に追加データを収集する、対話型のワークフローを実現します。 `MCPServer` クラスには Elicitation 機能が自動的に組み込まれます。Tool は `context.mcp` オブジェクトを `execute` 関数で受け取り、このオブジェクトにはユーザー入力を要求する `elicitation.sendRequest()` メソッドが含まれます。 ### Tool 実行時のシグネチャ MCP サーバーのコンテキスト内で Tool を実行すると、`context.mcp` オブジェクトを介して MCP 固有の機能を利用できます。 ```typescript execute: async (inputData, context) => { // input contains the tool's inputData parameters // context.mcp contains server capabilities like elicitation and authentication info // Access authentication information (when available) if (context.mcp?.extra?.authInfo) { console.log('Authenticated request from:', context.mcp.extra.authInfo.clientId) } // Use elicitation capabilities const result = await context.mcp.elicitation.sendRequest({ message: 'Please provide information', requestedSchema: {/* schema */}, }) return result } ``` ### Elicitation の仕組み 一般的なユースケースは、Tool の実行中にユーザー入力が必要になる場合です。context パラメーターから提供される Elicitation 機能を使用できます。 1. Tool がメッセージとスキーマを指定して `context.mcp.elicitation.sendRequest()` を呼び出す 2. 接続済みの MCP クライアントにリクエストが送信される 3. クライアントが UI やコマンドラインなどを介してユーザーにリクエストを提示する 4. ユーザーが情報を入力するか、リクエストを拒否またはキャンセルする 5. クライアントがサーバーにレスポンスを返す 6. Tool がレスポンスを受け取り、実行を続ける ### Tool で Elicitation を使用する Elicitation を使用してユーザーの連絡先情報を収集する Tool の例を次に示します。 ```typescript import { MCPServer } from '@mastra/mcp' import { createTool } from '@mastra/core/tools' import { z } from 'zod' const server = new MCPServer({ id: 'interactive-server', name: 'Interactive Server', version: '1.0.0', tools: { collectContactInfo: createTool({ id: 'collectContactInfo', description: 'Collects user contact information through elicitation', inputSchema: z.object({ reason: z.string().optional().describe('Reason for collecting contact info'), }), execute: async (inputData, context) => { const { reason } = inputData // Log session info if available console.log('Request from session:', context.mcp?.extra?.sessionId) try { // Request user input via elicitation const result = await context.mcp.elicitation.sendRequest({ message: reason ? `Please provide your contact information. ${reason}` : 'Please provide your contact information', requestedSchema: { type: 'object', properties: { name: { type: 'string', title: 'Full Name', description: 'Your full name', }, email: { type: 'string', title: 'Email Address', description: 'Your email address', format: 'email', }, phone: { type: 'string', title: 'Phone Number', description: 'Your phone number (optional)', }, }, required: ['name', 'email'], }, }) // Handle the user's response if (result.action === 'accept') { return `Contact information collected: ${JSON.stringify(result.content, null, 2)}` } else if (result.action === 'decline') { return 'Contact information collection was declined by the user.' } else { return 'Contact information collection was cancelled by the user.' } } catch (error) { return `Error collecting contact information: ${error}` } }, }), }, }) ``` ### Elicitation リクエストのスキーマ `requestedSchema` は、プリミティブ型のプロパティだけを持つフラットなオブジェクトにする必要があります。次の型を使用できます。 - **文字列**:`{ type: 'string', title: 'Display Name', description: 'Help text' }` - **数値**:`{ type: 'number', minimum: 0, maximum: 100 }` - **真偽値**:`{ type: 'boolean', default: false }` - **列挙型**:`{ type: 'string', enum: ['option1', 'option2'] }` スキーマの例: ```typescript { type: 'object', properties: { name: { type: 'string', title: 'Full Name', description: 'Your complete name', }, age: { type: 'number', title: 'Age', minimum: 18, maximum: 120, }, newsletter: { type: 'boolean', title: 'Subscribe to Newsletter', default: false, }, }, required: ['name'], } ``` ### レスポンスのアクション ユーザーは Elicitation リクエストに対して、次の 3 つの方法で応答できます。 1. **承認**(`action: 'accept'`):ユーザーがデータを入力し、送信を確定した - 送信されたデータを含む `content` フィールドがある 2. **拒否**(`action: 'decline'`):ユーザーが情報の提供を明示的に拒否した - content フィールドはない 3. **キャンセル**(`action: 'cancel'`):ユーザーが判断せずにリクエストを閉じた - content フィールドはない Tool では、3 種類すべてのレスポンスを適切に処理してください。 ### セキュリティ上の考慮事項 - パスワード、社会保障番号(SSN)、クレジットカード番号などの**機密情報は絶対に要求しない** - すべてのユーザー入力を指定したスキーマに照らして検証する - 拒否とキャンセルを適切に処理する - データ収集の理由を明確に示す - ユーザーのプライバシーと設定を尊重する ### Tool 実行 API Elicitation 機能は、Tool 実行時の `options` パラメーターを介して利用できます。 ```typescript // Within a tool's execute function execute: async (inputData, context) => { // Use elicitation for user input const result = await context.mcp.elicitation.sendRequest({ message: string, // Message to display to user requestedSchema: object // JSON schema defining expected response structure }): Promise // Access authentication info if needed if (context.mcp?.extra?.authInfo) { // Use context.mcp.extra.authInfo.token, etc. } } ``` HTTP ベースのトランスポート(SSE または HTTP)を使用する場合、Elicitation は**セッションを認識**します。複数のクライアントが同じサーバーに接続されている場合、Elicitation リクエストは Tool の実行を開始したクライアントセッションに送られます。 `ElicitResult` 型: ```typescript type ElicitResult = { action: 'accept' | 'decline' | 'cancel' content?: any // Only present when action is 'accept' } ``` ## OAuth による保護 [MCP Auth Specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) に従って OAuth 認証で MCP サーバーを保護するには、`createOAuthMiddleware` 関数を使用します。 ```typescript import http from 'node:http' import { MCPServer, createOAuthMiddleware, createStaticTokenValidator } from '@mastra/mcp' const mcpServer = new MCPServer({ id: 'protected-server', name: 'Protected MCP Server', version: '1.0.0', tools: {/* your tools */}, }) // Create OAuth middleware const oauthMiddleware = createOAuthMiddleware({ oauth: { resource: 'https://mcp.example.com/mcp', authorizationServers: ['https://auth.example.com'], scopesSupported: ['mcp:read', 'mcp:write'], resourceName: 'My Protected MCP Server', validateToken: createStaticTokenValidator(['allowed-token-1']), }, mcpPath: '/mcp', }) // Create HTTP server with OAuth protection const httpServer = http.createServer(async (req, res) => { const url = new URL(req.url || '', 'https://mcp.example.com') // Apply OAuth middleware first const result = await oauthMiddleware(req, res, url) if (!result.proceed) return // Middleware handled response (401, metadata, etc.) // Token is valid, proceed to MCP handler await mcpServer.startHTTP({ url, httpPath: '/mcp', req, res }) }) httpServer.listen(3000) ``` このミドルウェアは、次の処理を自動的に行います。 - `/.well-known/oauth-protected-resource` で **Protected Resource Metadata** を提供する(RFC 9728) - 認証が必要な場合、`401 Unauthorized` を適切な `WWW-Authenticate` ヘッダーとともに返す - 指定されたバリデーターを使用して Bearer Token を検証する ### Token の検証 本番環境では、適切な Token 検証を使用してください。 ```typescript import { createOAuthMiddleware, createIntrospectionValidator } from '@mastra/mcp' // Option 1: Token introspection (RFC 7662) const middleware = createOAuthMiddleware({ oauth: { resource: 'https://mcp.example.com/mcp', authorizationServers: ['https://auth.example.com'], validateToken: createIntrospectionValidator('https://auth.example.com/oauth/introspect', { clientId: 'mcp-server', clientSecret: 'secret', }), }, }) // Option 2: Custom validation (JWT, database lookup, etc.) const customMiddleware = createOAuthMiddleware({ oauth: { resource: 'https://mcp.example.com/mcp', authorizationServers: ['https://auth.example.com'], validateToken: async (token, resource) => { const decoded = await verifyJWT(token) if (!decoded) { return { valid: false, error: 'invalid_token' } } return { valid: true, scopes: decoded.scope?.split(' ') || [], subject: decoded.sub, } }, }, }) ``` ### OAuth ミドルウェアのオプション **oauth.resource** (`string`): MCP サーバーの正規 URL。Protected Resource Metadata で返されます。 **oauth.authorizationServers** (`string[]`): このリソースの Token を発行できる認可サーバーの URL。 **oauth.scopesSupported** (`string[]`): この MCP サーバーがサポートする Scope。 (Default: `['mcp:read', 'mcp:write']`) **oauth.resourceName** (`string`): このリソースサーバーの人が読める名前。 **oauth.validateToken** (`(token: string, resource: string) => Promise`): Access Token を検証する関数。指定しない場合、Token は検証なしで受け入れられます(本番環境では非推奨)。 **mcpPath** (`string`): MCP エンドポイントを提供するパス。このパスへのリクエストにのみ認証が必要です。 (Default: `'/mcp'`) ## 認証コンテキスト HTTP ベースの Transport を使用する場合、Tool は `context.mcp.extra` を介してリクエストのメタデータにアクセスできます。これにより、HTTP ミドルウェアから MCP Tool へ認証情報、ユーザーコンテキスト、任意のカスタムデータを渡せます。 ### 仕組み HTTP ミドルウェアで `req.auth` に設定した内容は、Tool 内で `context.mcp.extra.authInfo` として利用できます。 ```text req.auth = { ... } → context?.mcp?.extra?.authInfo.extra = { ... } ``` ### FGA 用に認証データをマッピングする きめ細かな認可(FGA)Provider を持つ Mastra インスタンスに `MCPServer` を登録すると、Mastra は Tool の一覧取得または呼び出しの前に `requestContext.get('user')` を確認します。HTTP MCP Transport は認証済みデータを `extra.authInfo` として渡すため、`mapAuthInfoToUser` を使用して FGA Provider が想定するユーザー形式を設定してください。 ```typescript const server = new MCPServer({ id: 'my-server', name: 'My Server', version: '1.0.0', tools: { getUserData }, mapAuthInfoToUser: ({ authInfo }) => { const user = authInfo as { extra?: { userId?: string organizationMembershipId?: string } } if (!user.extra?.userId) { return null } return { id: user.extra.userId, organizationMembershipId: user.extra.organizationMembershipId, } }, }) ``` ### MCP Tool の FGA スコープを分ける MCP Client に、内部の Agent または Workflow による Tool 実行とは異なる認可スコープが必要な場合は、`fga.resourceMapping` と `fga.permissionMapping` を使用します。この上書きは、この MCP Server の `tools/list` と `tools/call` のチェックにのみ適用されます。 ```typescript import { MastraFGAPermissions } from '@mastra/core/auth/ee' const server = new MCPServer({ id: 'my-server', name: 'My Server', version: '1.0.0', tools: { getUserData }, mapAuthInfoToUser: ({ authInfo }) => { const user = authInfo as { extra?: { userId?: string organizationMembershipId?: string } } if (!user.extra?.userId) { return null } return { id: user.extra.userId, organizationMembershipId: user.extra.organizationMembershipId, } }, fga: { resourceMapping: { tool: { fgaResourceType: 'user', deriveId: ({ user }) => (user as { id: string }).id, }, }, permissionMapping: { [MastraFGAPermissions.TOOLS_EXECUTE]: 'read', }, }, }) ``` ### 認証ミドルウェアの設定 Tool にデータを渡すには、HTTP Server のミドルウェアで Node.js のリクエストオブジェクトの `req.auth` に値を設定してから、`server.startHTTP()` を呼び出します。 ```typescript import express from 'express' type MCPAuthenticatedRequest = express.Request & { auth?: { token: string clientId: string scopes: string[] expiresAt?: number extra?: Record } } const app = express() // Auth middleware - set req.auth before the MCP handler app.use('/mcp', async (req, res, next) => { const authorization = req.headers.authorization if (!authorization?.startsWith('Bearer ')) { res.status(401).json({ error: 'Missing bearer token' }) return } const token = authorization.slice('Bearer '.length) try { const user = await verifyToken(token) // This entire object becomes context.mcp.extra.authInfo const authenticatedRequest = req as MCPAuthenticatedRequest authenticatedRequest.auth = { token, clientId: user.clientId, scopes: user.scopes, expiresAt: user.expiresAt, extra: { userId: user.userId, email: user.email, }, } next() } catch { res.status(401).json({ error: 'Invalid or expired token' }) } }) app.all('/mcp', async (req, res) => { const url = new URL(req.url, `http://${req.headers.host}`) await server.startHTTP({ url, httpPath: '/mcp', req, res }) }) ``` ### Tool から認証データにアクセスする `req.auth` オブジェクトは、Tool の execute 関数内で `context.mcp.extra.authInfo` として利用できます。 ```typescript execute: async (inputData, context) => { // Access the auth data you set in middleware const authInfo = context?.mcp?.extra?.authInfo if (!authInfo?.extra?.userId) { return { error: 'Authentication required' } } // Use the auth data console.log('User ID:', authInfo.extra.userId) console.log('Email:', authInfo.extra.email) const response = await fetch('/api/data', { headers: { Authorization: `Bearer ${authInfo.token}` }, signal: context?.mcp?.extra?.signal, }) return response.json() } ``` ### Agent に `RequestContext` を渡す ```typescript execute: async (inputData, context) => { // Access the auth data you set in middleware const authInfo = context?.mcp?.extra?.authInfo const requestContext = context.requestContext || new RequestContext().set('someKey', authInfo) if (!authInfo?.extra?.userId) { return { error: 'Authentication required' } } // Use the auth data console.log('User ID:', authInfo.extra.userId) console.log('Email:', authInfo.extra.email) const agent = context?.mastra?.getAgentById('some-agent-id') if (!agent) { return { error: "Agent 'some-agent-id' not found" } } const response = await agent.generate(prompt, { requestContext }) return response.text } ``` ### `extra` オブジェクト 完全な `context.mcp.extra` オブジェクトには、次の値が含まれます。 | プロパティ | 説明 | | ------------------ | ---------------------------- | | `authInfo` | ミドルウェアで `req.auth` に設定した内容 | | `sessionId` | MCP 接続のセッション識別子 | | `signal` | リクエストをキャンセルするための AbortSignal | | `sendNotification` | 通知を送信する MCP プロトコル関数 | | `sendRequest` | リクエストを送信する MCP プロトコル関数 | ### 完全な例 Identity Provider の JSON Web Key Set(JWKS)を使用して JSON Web Token(JWT)を検証するために、[`jose`](https://github.com/panva/jose) をインストールします。 **npm**: ```shell npm install jose ``` **pnpm**: ```shell pnpm add jose ``` **Yarn**: ```shell yarn add jose ``` **Bun**: ```shell bun add jose ``` 次の例では、ユーザーデータを Tool に渡す前に、Token の署名、発行者、Audience、アルゴリズム、有効期限、必須 Claim を検証します。 ```typescript import express from 'express' import { createRemoteJWKSet, jwtVerify } from 'jose' import { MCPServer } from '@mastra/mcp' import { createTool } from '@mastra/core/tools' import { z } from 'zod' type MCPAuthenticatedRequest = express.Request & { auth?: { token: string clientId: string scopes: string[] expiresAt?: number extra?: Record } } const issuer = process.env.JWT_ISSUER const audience = process.env.JWT_AUDIENCE const jwksUri = process.env.JWT_JWKS_URI if (!issuer || !audience || !jwksUri) { throw new Error('JWT_ISSUER, JWT_AUDIENCE, and JWT_JWKS_URI are required') } const jwks = createRemoteJWKSet(new URL(jwksUri)) const verifyToken = async (token: string) => { const { payload } = await jwtVerify(token, jwks, { issuer, audience, algorithms: ['RS256'], requiredClaims: ['exp'], }) const clientId = typeof payload.client_id === 'string' ? payload.client_id : typeof payload.azp === 'string' ? payload.azp : undefined if (!payload.sub || typeof payload.email !== 'string' || !clientId || !payload.exp) { throw new Error('Token must contain sub, email, exp, and client_id or azp claims') } return { userId: payload.sub, clientId, email: payload.email, expiresAt: payload.exp, scopes: typeof payload.scope === 'string' ? payload.scope.split(' ') : [], } } // 1. Define your tool that uses auth context const getUserData = createTool({ id: 'get-user-data', description: 'Fetches data for the authenticated user', inputSchema: z.object({}), execute: async (inputData, context) => { const authInfo = context?.mcp?.extra?.authInfo if (!authInfo?.extra?.userId) { return { error: 'Authentication required' } } // Access the data you set in middleware return { userId: authInfo.extra.userId, email: authInfo.extra.email, } }, }) // 2. Create the MCP server with your tools const server = new MCPServer({ id: 'my-server', name: 'My Server', version: '1.0.0', tools: { getUserData }, }) // 3. Set up Express with auth middleware const app = express() app.use('/mcp', async (req, res, next) => { const authorization = req.headers.authorization if (!authorization?.startsWith('Bearer ')) { res.status(401).json({ error: 'Missing bearer token' }) return } const token = authorization.slice('Bearer '.length) try { const user = await verifyToken(token) // This entire object becomes context.mcp.extra.authInfo const authenticatedRequest = req as MCPAuthenticatedRequest authenticatedRequest.auth = { token, clientId: user.clientId, scopes: user.scopes, expiresAt: user.expiresAt, extra: { userId: user.userId, email: user.email, }, } next() } catch { res.status(401).json({ error: 'Invalid or expired token' }) } }) app.all('/mcp', async (req, res) => { const url = new URL(req.url, `http://${req.headers.host}`) await server.startHTTP({ url, httpPath: '/mcp', req, res }) }) app.listen(3000) ``` ## MCP Apps(`appResources`) `appResources` オプションを使用すると、[MCP Apps 拡張機能](https://github.com/modelcontextprotocol/ext-apps)を介して MCP Server からインタラクティブな HTML UI を提供できます。各エントリは `ui://` URI を、Mastra Studio のサンドボックス化された iframe 内でレンダリングされる HTML アプリにマッピングします。 ### `AppResources` 型 **Key (URI)** (`string`): アプリリソースを識別する ui:// URI(例: ui://calculator/main)。 各値は `AppResource` オブジェクトです。 **name** (`string`): UI リソースの表示名。 **description** (`string`): UI リソースの説明(任意)。 **html** (`string`): UI のインライン HTML コンテンツ。html または htmlPath のいずれかを指定します。 **htmlPath** (`string`): HTML ファイルへのパス。Server の起動時に解決されます。html または htmlPath のいずれかを指定します。 **meta** (`McpUiResourceMeta`): 公式 ext-apps SDK の UI リソースメタデータ(CSP、権限、レンダリング設定)。 ### 例 ```typescript import { MCPServer } from '@mastra/mcp' import { createTool } from '@mastra/core/tools' import { z } from 'zod' const calculatorTool = createTool({ id: 'calculatorWithUI', description: 'An interactive calculator', inputSchema: z.object({ num1: z.number(), num2: z.number(), operation: z.enum(['add', 'subtract']), }), execute: async ({ num1, num2, operation }) => { const result = operation === 'add' ? num1 + num2 : num1 - num2 return { content: [{ type: 'text', text: 'An interactive calculator is displayed.' }], structuredContent: { result }, } }, }) const server = new MCPServer({ id: 'app-server', name: 'App Server', version: '1.0.0', tools: { calculatorTool }, appResources: { 'ui://calculator/main': { name: 'Interactive Calculator', html: '

Calculator

...', }, }, }) ``` Tool の `_meta.ui.resourceUri` に対応する `ui://` URI を設定して、Tool をそのアプリリソースに関連付けます。Server は Tool の登録時に、このメタデータを自動的に正規化します。アプリブリッジ API の全仕様と使用パターンについては、[MCP Apps](https://mastra.zisheng.pro/ja/docs/mcp/overview)を参照してください。 ## 関連情報 - Mastra から MCP Server に接続する方法については、[MCPClient のドキュメント](https://mastra.zisheng.pro/ja/reference/tools/mcp-client)を参照してください。 - Model Context Protocol の詳細については、[@modelcontextprotocol/sdk のドキュメント](https://github.com/modelcontextprotocol/typescript-sdk)を参照してください。