跳至主要內容

MCPServer

MCPServer 類別可將現有 Mastra Tool 與 Agent 公開為 Model Context Protocol(MCP)server,讓任何 MCP client(例如 Cursor、Windsurf 或 Claude Desktop)連線並將這些功能提供給 Agent。

請注意,若只需在 Mastra 應用程式中直接使用 Tool 或 Agent,不一定要建立 MCP server。此 API 專門用於向_外部_ MCP client 公開 Mastra Tool 與 Agent。

它同時支援 stdio(subprocess)與 SSE(HTTP)MCP transport

Constructor
「Constructor」的直接連結

若要建立新的 MCPServer,需要提供 server 的基本資訊、server 將提供的 Tool,以及選填的要公開為 Tool 的 Agent。

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"
},
})

設定屬性
「設定屬性」的直接連結

Constructor 接受具有下列屬性的 MCPServerConfig 物件:

id:

string
Server 的唯一識別碼。Server 註冊至 Mastra 時會保留此 ID,並可透過 getMCPServerById() 擷取 server。

name:

string
Server 的描述性名稱(例如 'My Weather and Agent Server')。

version:

string
Server 的 semantic version(例如 '1.0.0')。

tools:

ToolsInput
Key 為 Tool 名稱、值為 Mastra Tool 定義(使用 createTool 或 Vercel AI SDK 建立)的物件。這些 Tool 會直接公開。

agents?:

Record<string, Agent>
Key 為 Agent 識別碼、值為 Mastra Agent instance 的物件。每個 Agent 都會自動轉換為名為 ask_<agentIdentifier> 的 Tool。Agent constructor 設定中**必須**定義非空白的 description 字串屬性,此內容會用於 Tool 說明。若 Agent 的 description 缺少或為空白,MCPServer 初始化期間會擲回錯誤。

workflows?:

Record<string, Workflow>
Key 為 Workflow 識別碼、值為 Mastra Workflow instance 的物件。每個 Workflow 都會轉換為名為 run_<workflowKey> 的 Tool。Workflow 的 inputSchema 會成為 Tool 的輸入 schema。Workflow **必須**具有非空白的 description 字串屬性,供 Tool 說明使用。若 Workflow 的 description 缺少或為空白,則會擲回錯誤。Tool 會先呼叫 workflow.createRun(),再呼叫 run.start({ inputData: <tool_input> }) 以執行 Workflow。若衍生自 Agent 或 Workflow 的 Tool 名稱(例如 ask_myAgentrun_myWorkflow)與明確定義的 Tool 名稱或其他衍生名稱衝突,明確定義的 Tool 優先,且系統會記錄警告。造成後續衝突的 Agent/Workflow 會略過。

description?:

string
MCP server 功能的選填說明。

instructions?:

string
描述 server 及其功能使用方式的選填 instructions。

mapAuthInfoToUser?:

({ authInfo, extra, requestContext }) => unknown | null | undefined | Promise<unknown | null | undefined>
extra.authInfo 中的 MCP transport 驗證資料對應至 Mastra FGA 檢查使用的 user 值。當受 OAuth 保護的 MCP server 註冊至具有 FGA Provider 的 Mastra instance 時,請使用此屬性。

fga?:

{ resourceMapping?: Partial<Record<'tool' | 'tools', { fgaResourceType: string; deriveId?: ({ user, resourceId, requestContext }) => string | undefined }>>; permissionMapping?: Record<string, string> }
覆寫此 MCP server tools/listtools/call FGA 檢查的資源和權限對應。當 MCP authorization 的 scope 應不同於內部 Agent 或 Workflow Tool 執行時,請使用此屬性。

repository?:

Repository
Server 原始碼的選填 repository 資訊。

releaseDate?:

string
此 server 版本的選填發布日期(ISO 8601 字串)。若未提供,則預設為建立 instance 的時間。

isLatest?:

boolean
指出這是否為最新版本的選填旗標。若未提供,則預設為 true。

packageCanonical?:

'npm' | 'docker' | 'pypi' | 'crates' | string
Server 以套件形式發布時使用的選填標準封裝格式(例如 'npm'、'docker')。

packages?:

PackageInfo[]
此 server 的選填可安裝套件清單。

remotes?:

RemoteInfo[]
此 server 的選填遠端存取點清單。

resources?:

MCPServerResources
定義 server 應如何處理 MCP 資源的物件。詳情請參閱「資源處理」一節。

prompts?:

MCPServerPrompts
定義 server 應如何處理 MCP prompt 的物件。詳情請參閱「Prompt 處理」一節。

appResources?:

AppResources
ui:// URI 對應至 app resource 設定的 map。每個項目都定義透過 MCP Apps extension(SEP-1865)提供的互動式 HTML UI。詳情請參閱 MCP Apps 一節。

將 Agent 公開為 Tool
「將 Agent 公開為 Tool」的直接連結

MCPServer 的強大功能之一,是能自動將 Mastra Agent 公開為可呼叫的 Tool。在設定的 agents 屬性中提供 Agent 時:

  • Tool 命名:每個 Agent 都會轉換為名為 ask_<agentKey> 的 Tool,其中 <agentKey> 是該 Agent 在 agents 物件中使用的 key。例如,若設定 agents: { myAgentKey: myAgentInstance },系統會建立名為 ask_myAgentKey 的 Tool。

  • Tool 功能

    • 說明:產生的 Tool 說明格式為:"Ask agent <AgentName> a question. Original agent instructions: <agent description>"。
    • 輸入:Tool 預期收到具有 message 屬性(字串)的單一物件引數:{ message: "Your question for the agent" }
    • 執行:呼叫此 Tool 時,會使用提供的 query 叫用對應 Agent 的 generate() 方法。
    • 輸出:Agent generate() 方法的直接結果會作為 Tool 輸出回傳。
  • 名稱衝突。tools 設定中明確定義的 Tool 與 Agent 衍生 Tool 同名(例如名為 ask_myAgentKey 的 Tool 與 key 為 myAgentKey 的 Agent 並存),則_明確定義的 Tool 優先_。發生衝突時,不會將 Agent 轉換為 Tool,且系統會記錄警告。

如此一來,MCP client 便能像使用其他 Tool 一樣,以自然語言查詢與 Agent 互動。

將 Agent 轉換為 Tool
「將 Agent 轉換為 Tool」的直接連結

agents 設定屬性中提供 Agent 時,MCPServer 會自動為每個 Agent 建立對應 Tool。Tool 名稱為 ask_<agentIdentifier>,其中 <agentIdentifier>agents 物件中使用的 key。

產生的 Tool 說明為:"Ask agent <agent.name> a question. Agent description: <agent.description>"。

若要將 Agent 轉換為 Tool,建立 instance 時的設定中必須具有非空白的 description 字串屬性(例如 new Agent({ id: 'my-agent', name: 'myAgent', description: 'This agent does X.', ... }))。若傳給 MCPServer 的 Agent 缺少 description 或其值為空白,建立 MCPServer instance 時會擲回錯誤,且 server 設定會失敗。

這可讓你快速透過 MCP 公開 Agent 的生成能力,讓 client 能直接向 Agent「提問」。

在 Tool 中存取 MCP context
「在 Tool 中存取 MCP context」的直接連結

透過 MCPServer 公開的 Tool 可根據呼叫方式,使用兩種不同屬性存取 MCP request context(驗證、session ID 等):

呼叫模式存取方式
直接呼叫 Toolcontext?.mcp?.extra
Agent Tool 呼叫context?.requestContext?.get("mcp.extra")

通用模式(適用於兩種 context):

const mcpExtra = context?.mcp?.extra ?? context?.requestContext?.get('mcp.extra')
const authInfo = mcpExtra?.authInfo

範例:適用於兩種 context 的 Tool
「範例:適用於兩種 context 的 Tool」的直接連結

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 instance 上呼叫,以控制其行為並取得資訊。

startStdio()
「startstdio」的直接連結

使用此方法啟動 server,讓它透過標準輸入與輸出(stdio)通訊。以命令列程式執行 server 時通常會使用此方式。

async startStdio(): Promise<void>

以下示範如何使用 stdio 啟動 server:

const server = new MCPServer({
id: 'my-server',
name: 'My Server',
version: '1.0.0',
tools: {/* ... */},
})
await server.startStdio()

startSSE()
「startsse」的直接連結

此方法可將 MCP server 與現有 Web server 整合,並使用 Server-Sent Events(SSE)通訊。Web server 收到 SSE 或 message 路徑請求時,請從 Web server 程式碼呼叫此方法。

async startSSE({
url,
ssePath,
messagePath,
req,
res,
}: {
url: URL;
ssePath: string;
messagePath: string;
req: any;
res: any;
}): Promise<void>

下列範例示範如何在 HTTP server request handler 中使用 startSSE。在此範例中,MCP client 可透過 http://localhost:1234/sse 連線至 MCP server:

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 server 的傳入 request 物件。

res:

any
來自 Web server、用於傳回資料的 response 物件。

startHonoSSE()
「starthonosse」的直接連結

此方法可將 MCP server 與現有 Web server 整合,並使用 Server-Sent Events(SSE)通訊。Web server 收到 SSE 或 message 路徑請求時,請從 Web server 程式碼呼叫此方法。

async startHonoSSE({
url,
ssePath,
messagePath,
req,
res,
}: {
url: URL;
ssePath: string;
messagePath: string;
req: any;
res: any;
}): Promise<void>

下列範例示範如何在 HTTP server request handler 中使用 startHonoSSE。在此範例中,MCP client 可透過 http://localhost:1234/hono-sse 連線至 MCP server:

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 server 的傳入 request 物件。

res:

any
來自 Web server、用於傳回資料的 response 物件。

startHTTP()
「starthttp」的直接連結

此方法可將 MCP server 與現有 Web server 整合,並使用 streamable HTTP 通訊。Web server 收到 HTTP 請求時,請從 Web server 程式碼呼叫此方法。

async startHTTP({
url,
httpPath,
req,
res,
options = { sessionIdGenerator: () => randomUUID() },
}: {
url: URL;
httpPath: string;
req: http.IncomingMessage;
res: http.ServerResponse<http.IncomingMessage>;
options?: StreamableHTTPServerTransportOptions;
}): Promise<void>

下列範例示範如何在 HTTP server request handler 中使用 startHTTP。在此範例中,MCP client 可透過 http://localhost:1234/http 連線至 MCP server:

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}`)
})

若是 serverless 環境(Supabase Edge Functions、Cloudflare Workers、Vercel Edge 等),請使用 serverless: true 啟用無狀態操作:

// 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

部署至每個請求都在全新無狀態 execution context 中執行的環境時,請使用 serverless: true

  • Supabase Edge Functions
  • Cloudflare Workers
  • Vercel Edge Functions
  • Netlify Edge Functions
  • AWS Lambda
  • Deno Deploy

下列環境請使用預設的 session 模式(不設定 serverless: true):

  • 長時間執行的 Node.js server
  • Docker container
  • 傳統託管環境(VPS、專用 server)

Serverless 模式會停用 session 管理,並為每個請求建立全新的 server instance。這是 invocation 之間不會保留記憶體的無狀態環境所必需的。

根據預設,serverless 模式會將每個請求緩衝為單一 JSON response,因此 Tool 傳送的 notifications/progress 不會抵達 client。請設定 serverlessStreaming: true,改用限於請求範圍的 SSE 串流處理請求,在最終結果前傳遞進度通知:

await server.startHTTP({
url,
httpPath: '/mcp',
req: nodeReq,
res: nodeRes,
options: {
serverless: true,
serverlessStreaming: true, // ← Stream request-scoped notifications/progress
},
})

這仍是無狀態模式:不需要也不會保存 mcp-session-id。它只會啟用限於目前請求範圍的通知(例如進度)。下列依賴 session 的功能仍無法使用。

下列 MCP 功能需要 session state 或持久連線,因此在 serverless 模式下無法使用(包括設定 serverlessStreaming: true 時):

  • Elicitation:Tool 執行期間的互動式使用者輸入請求,需要 session 管理才能將 response 路由回正確的 client
  • 資源訂閱resources/subscriberesources/unsubscribe 需要持久連線以維護訂閱狀態
  • 資源更新通知resources.notifyUpdated() 需要作用中的訂閱與持久連線,才能通知 client
  • Prompt 清單變更通知prompts.notifyListChanged() 需要持久連線才能將更新推送至 client
  • Tool 清單變更通知toolActions.notifyListChanged() 需要持久連線才能將更新推送至 client
  • Server log 通知sendLoggingMessage() 需要持久連線才能將 log 訊息推送至 client

這些功能可在長時間執行的 server 環境(Node.js server、Docker container 等)中正常運作。

以下是 startHTTP 方法所需值的詳細資訊:

url:

URL
使用者要求的 Web 位址。

httpPath:

string
MCP server 處理 HTTP 請求的特定 URL 部分(例如 '/mcp')。

req:

http.IncomingMessage
來自 Web server 的傳入 request 物件。

res:

http.ServerResponse
來自 Web server、用於傳回資料的 response 物件。

options:

StreamableHTTPServerTransportOptions
HTTP transport 的選填設定。詳情請參閱下方選項表。

StreamableHTTPServerTransportOptions 物件可用來自訂 HTTP transport 行為。可用選項如下:

serverless:

boolean
若為 true,則在不使用 session 管理的無狀態模式下執行。每個請求都由全新的 server instance 獨立處理。這對 invocation 之間無法保留 session 的 serverless 環境(Cloudflare Workers、Supabase Edge Functions、Vercel Edge 等)至關重要。預設為 false

serverlessStreaming:

boolean
若為 true,serverless 請求會使用限於請求範圍的 SSE 串流,而不是緩衝的 JSON response,讓請求內的 notifications/progress 能在最終結果前抵達 client。只有搭配 serverless: true 才會生效。預設為 false(緩衝的 JSON response),以保留向後相容行為。它只會啟用進度等限於請求範圍的通知;elicitation、訂閱與請求外通知仍需要 session state。

sessionIdGenerator:

(() => string) | undefined
產生唯一 session ID 的函式。此字串應具備密碼學安全性,並且在全域中唯一。回傳 undefined 可停用 session 管理。

onsessioninitialized:

(sessionId: string) => void
初始化新 session 時叫用的 callback,適合用來追蹤作用中的 MCP session。

enableJsonResponse:

boolean
若為 true,server 會回傳一般 JSON response,而不使用 Server-Sent Events(SSE)進行串流。預設為 false

eventStore:

EventStore
用於恢復訊息的 event store。提供此值後,client 可重新連線並繼續訊息串流。

close()
「close」的直接連結

此方法會關閉 server 並釋放所有資源。

async close(): Promise<void>

getServerInfo()
「getserverinfo」的直接連結

此方法會回傳 server 的基本資訊。

getServerInfo(): ServerInfo

getServerDetail()
「getserverdetail」的直接連結

此方法會回傳 server 資訊的詳細內容。

getServerDetail(): ServerDetail

getToolListInfo()
「gettoollistinfo」的直接連結

此方法會回傳建立 server 時設定的 Tool。這是唯讀清單,適合用於除錯。

getToolListInfo(): ToolListInfo

getToolInfo()
「gettoolinfo」的直接連結

此方法會回傳特定 Tool 的詳細資訊。

getToolInfo(toolName: string): ToolInfo

executeTool()
「executetool」的直接連結

此方法會執行特定 Tool 並回傳結果。

executeTool(toolName: string, input: any): Promise<any>

getStdioTransport()
「getstdiotransport」的直接連結

若使用 startStdio() 啟動 server,可使用此方法取得管理 stdio 通訊的物件,主要用於內部檢查或測試。

getStdioTransport(): StdioServerTransport | undefined

getSseTransport()
「getssetransport」的直接連結

若使用 startSSE() 啟動 server,可使用此方法取得管理 SSE 通訊的物件。與 getStdioTransport 相同,主要用於內部檢查或測試。

getSseTransport(): SSEServerTransport | undefined

getSseHonoTransport()
「getssehonotransport」的直接連結

若使用 startHonoSSE() 啟動 server,可使用此方法取得管理 SSE 通訊的物件。與 getSseTransport 相同,主要用於內部檢查或測試。

getSseHonoTransport(): SSETransport | undefined

getStreamableHTTPTransport()
「getstreamablehttptransport」的直接連結

若使用 startHTTP() 啟動 server,可使用此方法取得管理 HTTP 通訊的物件。與 getSseTransport 相同,主要用於內部檢查或測試。

getStreamableHTTPTransport(): StreamableHTTPServerTransport | undefined

tools()
「tools」的直接連結

執行此 MCP server 提供的特定 Tool。

async executeTool(
toolId: string,
args: any,
executionContext?: { messages?: any[]; toolCallId?: string },
): Promise<any>

toolId:

string
要執行之 Tool 的 ID/名稱。

args:

any
要傳給 Tool execute 函式的引數。

executionContext?:

object
Tool 執行的選填 context,例如 messages 或 toolCallId。

資源處理
「資源處理」的直接連結

什麼是 MCP Resource?
「什麼是 MCP Resource?」的直接連結

Resource 是 Model Context Protocol(MCP)的核心 primitive,讓 server 能公開可由 client 讀取,並作為 LLM 互動 context 的資料與內容。它可代表 MCP server 想提供的任何資料,例如:

  • 檔案內容
  • 資料庫記錄
  • API 回應
  • 即時系統資料
  • 螢幕截圖與圖片
  • Log 檔案

Resource 以唯一 URI 識別(例如 file:///home/user/documents/report.pdfpostgres://database/customers/schema),並可包含文字(UTF-8 編碼)或 binary 資料(base64 編碼)。

Client 可透過下列方式探索 resource:

  1. 直接 resource:Server 透過 resources/list endpoint 公開具體 resource 清單。
  2. Resource template:對於執行階段定義的 resource,server 可公開 URI template(RFC 6570),讓 client 用來建構 resource URI。

若要讀取 resource,client 會使用 URI 發出 resources/read 請求。若 client 已訂閱 resource,server 也可通知 client resource 清單變更(notifications/resources/list_changed)或特定 resource 內容更新(notifications/resources/updated)。

如需詳細資訊,請參閱 MCP Resource 官方文件

MCPServerResources 型別
「mcpserverresources-type」的直接連結

resources 選項接受 MCPServerResources 型別的物件。此型別定義 server 用來處理 resource 請求的 callback:

export type MCPServerResources = {
// Callback to list available resources
listResources: () => Promise<Resource[]>

// Callback to get the content of a specific resource
getResourceContent: ({
uri,
}: {
uri: string
}) => Promise<MCPServerResourceContent | MCPServerResourceContent[]>

// Optional callback to list available resource templates
resourceTemplates?: () => Promise<ResourceTemplate[]>
}

export type MCPServerResourceContent = { text?: string } | { blob?: string }

範例:

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<string, MCPServerResourceContent> = {
'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,
})

通知 client resource 變更
「通知 client resource 變更」的直接連結

若可用 resource 或其內容變更,server 可通知已連線且訂閱特定 resource 的 client。

server.resources.notifyUpdated({ uri: string })
「serverresourcesnotifyupdated-uri-string-」的直接連結

當以 uri 識別的特定 resource 內容更新時,請呼叫此方法。若有 client 訂閱此 URI,便會收到 notifications/resources/updated 訊息。

async server.resources.notifyUpdated({ uri: string }): Promise<void>

範例:

// After updating the content of 'file://data.txt'
await serverWithResources.resources.notifyUpdated({ uri: 'file://data.txt' })

server.resources.notifyListChanged()
「serverresourcesnotifylistchanged」的直接連結

可用 resource 清單變更時(例如新增或移除 resource),請呼叫此方法。這會向 client 傳送 notifications/resources/list_changed 訊息,提示 client 重新擷取 resource 清單。

async server.resources.notifyListChanged(): Promise<void>

範例:

// After adding a new resource to the list managed by 'myResourceHandlers.listResources'
await serverWithResources.resources.notifyListChanged()

Prompt 處理
「Prompt 處理」的直接連結

什麼是 MCP Prompt?
「什麼是 MCP Prompt?」的直接連結

Prompt 是 MCP server 向 client 公開的可重複使用 template 或 Workflow。它們可以接受引數並包含 resource context,也支援版本管理,並將 LLM 互動標準化。

Prompt 以唯一名稱(及選填版本)識別,可以在執行階段定義,也可以是靜態內容。

MCPServerPrompts 型別
「mcpserverprompts-type」的直接連結

prompts 選項接受 MCPServerPrompts 型別的物件。此型別定義 server 用來處理 prompt 請求的 callback:

export type MCPServerPrompts = {
// Callback to list available prompts
listPrompts: () => Promise<Prompt[]>

// 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[] }>
}

範例:

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,
})

通知 client Prompt 變更
「通知 client Prompt 變更」的直接連結

若可用 prompt 變更,server 可通知已連線的 client:

server.prompts.notifyListChanged()
「serverpromptsnotifylistchanged」的直接連結

可用 prompt 清單變更時(例如新增或移除 prompt),請呼叫此方法。這會向 client 傳送 notifications/prompts/list_changed 訊息,提示 client 重新擷取 prompt 清單。

await serverWithPrompts.prompts.notifyListChanged()

Prompt 處理最佳做法
「Prompt 處理最佳做法」的直接連結

  • 使用清楚且具描述性的 prompt 名稱與說明。
  • 驗證 getPromptMessages 中的所有必要引數。
  • 若預期進行破壞性變更,請包含 version 欄位。
  • 使用 version 參數選取正確的 prompt 邏輯。
  • Prompt 清單變更時通知 client。
  • 使用資訊完整的訊息處理錯誤。
  • 記錄預期引數與可用版本。

動態 Tool 管理
「動態 Tool 管理」的直接連結

Tool 通常在建立 MCPServer 時提供,但也可在 server 執行期間新增或移除。Server 會透過 toolActions 屬性公開這些操作。Tool 清單變更時,已連線的 client 會收到 notifications/tools/list_changed 訊息,提示它們重新擷取 Tool 清單。

此屬性命名為 toolActions,因為 tools() 是回傳已註冊 Tool registry 的方法。

toolActions.add(tools)
「toolactionsaddtools」的直接連結

在執行中的 server 上註冊新 Tool,並通知已連線的 client。Tool 以 record key 作為 key,與傳給 constructor 的 Tool 相同。在現有 key 下新增 Tool 會取代原有 Tool。

async server.toolActions.add(tools: ToolsInput): Promise<void>

範例:

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)
「toolactionsremovetoolids」的直接連結

依 Tool ID 從執行中的 server 移除 Tool,並通知已連線的 client。系統會忽略未知的 Tool ID。只有至少移除一個 Tool 時才會傳送通知。

async server.toolActions.remove(toolIds: string[]): Promise<void>

範例:

await server.toolActions.remove(['searchTool'])

toolActions.notifyListChanged()
「toolactionsnotifylistchanged」的直接連結

在不修改 Tool registry 的情況下,向已連線的 client 傳送 notifications/tools/list_changed 訊息。當 Tool 可用性因其他方式變更時(例如 authorization 變更),請呼叫此方法。

async server.toolActions.notifyListChanged(): Promise<void>

Mastra registry 同步
「Mastra registry 同步」的直接連結

Server 註冊至 Mastra instance 後,toolActions.add()toolActions.remove() 也會更新 Mastra instance 的 Tool registry,與啟動時的自動 Tool 註冊一致。新增的 Tool 可透過 mastra.listTools() 使用(若有 Tool 本身的 id,則以此作為 key),移除的 Tool 則會從 registry 刪除。

記錄
「記錄」的直接連結

MCP server 可使用 notifications/message 向 client 傳送結構化 log 訊息。Client 可傳送 logging/setLevel 請求控制詳細程度。Server 會捨棄低於要求最低層級的訊息(遵循 RFC 5424 嚴重性排序)。層級會依 session 追蹤,因此不同 client 可要求不同的詳細程度。

sendLoggingMessage()
「sendloggingmessage」的直接連結

向所有已連線的 client 傳送 log 通知,並遵守各 client 的最低 logging 層級。

async server.sendLoggingMessage(params: {
level: LoggingLevel;
data: unknown;
logger?: string;
}): Promise<void>

範例:

await server.sendLoggingMessage({
level: 'info',
data: { message: 'Sync completed', itemsProcessed: 42 },
})

context.mcp.log()
「contextmcplog」的直接連結

在 Tool 的 execute 函式內,使用 context.mcp.log() 向呼叫該 Tool 的 client 傳送 log 訊息。

async context.mcp.log(
level: LoggingLevel,
message: string,
data?: Record<string, unknown>
): Promise<void>

範例:

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 向呼叫端 client 回報進度。只有呼叫端在請求中包含 progressToken,要求追蹤進度時才會傳送進度(設定 enableProgressTracking 時,Mastra MCPClient 會執行此操作)。未傳送 token 時,context.mcp.progress() 不會執行任何操作。

context.mcp.progress()
「contextmcpprogress」的直接連結

async context.mcp.progress(params: {
progress: number;
total?: number;
message?: string;
}): Promise<void>

範例:

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())會透過所有 transport 向每個已連線的 client 廣播:包括 stdio/SSE 連線與各個 streamable HTTP session。resources.notifyUpdated() 例外,只會通知透過 resources/subscribe 訂閱該 resource URI 的 client。Streamable HTTP client 的訂閱會依 session 追蹤;舊版 SSE client 共用主要 server instance,因此也共用一組訂閱。使用無狀態 serverless 模式的 client 無法接收通知,因為每個請求都使用暫時性的 server instance。

範例
「範例」的直接連結

如需設定與部署 MCPServer 的實際範例,請參閱發布 MCP Server 指南

本頁開頭的範例也示範如何使用 Tool 與 Agent 建立 MCPServer instance。

Elicitation
「Elicitation」的直接連結

什麼是 Elicitation?
「什麼是 Elicitation?」的直接連結

Elicitation 是 Model Context Protocol(MCP)的一項功能,可讓 server 向使用者要求結構化資訊。它支援 server 在執行階段收集額外資料的互動式 Workflow。

MCPServer 類別會自動包含 elicitation 功能。Tool 會在 execute 函式中收到 context.mcp 物件,其中包含用來要求使用者輸入的 elicitation.sendRequest() 方法。

Tool 執行 signature
「Tool 執行 signature」的直接連結

Tool 在 MCP server context 中執行時,會透過 context.mcp 物件接收 MCP 特定功能:

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 的運作方式
「Elicitation 的運作方式」的直接連結

常見使用情境是在 Tool 執行期間。Tool 需要使用者輸入時,可使用 context 參數提供的 elicitation 功能:

  1. Tool 使用訊息與 schema 呼叫 context.mcp.elicitation.sendRequest()
  2. Request 會傳送至已連線的 MCP client
  3. Client 向使用者呈現 request(透過 UI、命令列等)
  4. 使用者提供輸入、拒絕或取消 request
  5. Client 將 response 傳回 server
  6. Tool 收到 response 並繼續執行

在 Tool 中使用 Elicitation
「在 Tool 中使用 Elicitation」的直接連結

下列範例示範使用 elicitation 收集使用者聯絡資訊的 Tool:

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 request schema
「Elicitation request schema」的直接連結

requestedSchema 必須是只具有 primitive 屬性的扁平物件。支援的型別包括:

  • String{ type: 'string', title: 'Display Name', description: 'Help text' }
  • Number{ type: 'number', minimum: 0, maximum: 100 }
  • Boolean{ type: 'boolean', default: false }
  • Enum{ type: 'string', enum: ['option1', 'option2'] }

Schema 範例:

{
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 request:

  1. Acceptaction: 'accept'):使用者已提供資料並確認提交
    • 包含具有提交資料的 content 欄位
  2. Declineaction: 'decline'):使用者明確拒絕提供資訊
    • 沒有 content 欄位
  3. Cancelaction: 'cancel'):使用者未做決定便關閉 request
    • 沒有 content 欄位

Tool 應妥善處理這三種 response 型別。

安全性考量
「安全性考量」的直接連結

  • 絕不要求敏感資訊,例如密碼、社會安全號碼或信用卡號碼
  • 根據提供的 schema 驗證所有使用者輸入
  • 妥善處理拒絕與取消
  • 提供清楚的資料收集原因
  • 尊重使用者隱私與偏好

Tool 執行 API
「Tool 執行 API」的直接連結

Elicitation 功能可透過 Tool 執行中的 options 參數使用:

// 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<ElicitResult>

// Access authentication info if needed
if (context.mcp?.extra?.authInfo) {
// Use context.mcp.extra.authInfo.token, etc.
}
}

使用 HTTP transport(SSE 或 HTTP)時,Elicitation 會感知 session。多個 client 連線至同一個 server 時,elicitation request 會路由至啟動 Tool 執行的 client session。

The ElicitResult type:

type ElicitResult = {
action: 'accept' | 'decline' | 'cancel'
content?: any // Only present when action is 'accept'
}

OAuth 保護
「OAuth 保護」的直接連結

若要依 MCP Auth 規範使用 OAuth 驗證保護 MCP server,請使用 createOAuthMiddleware 函式:

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)

Middleware 會自動:

  • /.well-known/oauth-protected-resource 提供 Protected Resource Metadata(RFC 9728)
  • 需要驗證時,回傳具有正確 WWW-Authenticate header 的 401 Unauthorized
  • 使用你提供的 validator 驗證 bearer token

Token 驗證
「Token 驗證」的直接連結

在正式環境中,請使用適當的 token 驗證:

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 middleware 選項
「OAuth middleware 選項」的直接連結

oauth.resource:

string
MCP server 的 canonical URL。此值會在 Protected Resource Metadata 中回傳。

oauth.authorizationServers:

string[]
可為此 resource 發行 token 的 authorization server URL。

oauth.scopesSupported?:

string[]
= ['mcp:read', 'mcp:write']
此 MCP server 支援的 scope。

oauth.resourceName?:

string
此 resource server 的易讀名稱。

oauth.validateToken?:

(token: string, resource: string) => Promise<TokenValidationResult>
用於驗證 access token 的函式。若未提供,則會接受 token 而不進行驗證(不建議用於正式環境)。

mcpPath?:

string
= '/mcp'
提供 MCP endpoint 的路徑。只有對此路徑的請求需要驗證。

驗證 context
「驗證 context」的直接連結

使用 HTTP transport 時,Tool 可透過 context.mcp.extra 存取 request 中繼資料。如此便能將驗證資訊、使用者 context 或任何自訂資料從 HTTP middleware 傳給 MCP Tool。

運作方式
「運作方式」的直接連結

HTTP middleware 中在 req.auth 設定的任何內容,都可在 Tool 中透過 context.mcp.extra.authInfo 使用:

req.auth = { ... } → context?.mcp?.extra?.authInfo.extra = { ... }

為 FGA 對應驗證資料
「為 FGA 對應驗證資料」的直接連結

MCPServer 註冊至具有細粒度 authorization(FGA)Provider 的 Mastra instance 時,Mastra 會在列出或呼叫 Tool 前檢查 requestContext.get('user')。HTTP MCP transport 會將已驗證資料作為 extra.authInfo 傳遞,因此請使用 mapAuthInfoToUser 設定 FGA Provider 預期的 user 結構。

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 scope
「個別設定 MCP Tool FGA scope」的直接連結

若 MCP client 所需的 authorization scope 與內部 Agent 或 Workflow Tool 執行不同,請使用 fga.resourceMappingfga.permissionMapping。此覆寫只適用於該 MCP server 的 tools/listtools/call 檢查。

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',
},
},
})

設定驗證 middleware
「設定驗證 middleware」的直接連結

若要將資料傳給 Tool,請先在 HTTP server middleware 的 Node.js request 物件上填入 req.auth,再呼叫 server.startHTTP()

import express from 'express'

type MCPAuthenticatedRequest = express.Request & {
auth?: {
token: string
clientId: string
scopes: string[]
expiresAt?: number
extra?: Record<string, unknown>
}
}

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 中存取驗證資料
「在 Tool 中存取驗證資料」的直接連結

在 Tool 的 execute 函式中,req.auth 物件可作為 context.mcp.extra.authInfo 使用:

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()
}

RequestContext 傳遞至 Agent
「passing-requestcontext-through-to-agent」的直接連結

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 物件
「the-extra-object」的直接連結

完整的 context.mcp.extra 物件包含:

屬性說明
authInfoMiddleware 中在 req.auth 設定的任何內容
sessionIdMCP 連線的 session 識別碼
signal用於取消 request 的 AbortSignal
sendNotification用於傳送通知的 MCP protocol 函式
sendRequest用於傳送 request 的 MCP protocol 函式

完整範例
「完整範例」的直接連結

安裝 jose,使用身分 Provider 的 JSON Web Key Set(JWKS)驗證 JSON Web Token(JWT):

npm install jose

下列範例會先驗證 token 的 signature、issuer、audience、algorithm、expiration 與必要 claim,再將使用者資料傳給 Tool:

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<string, unknown>
}
}

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)
「mcp-apps-appresources」的直接連結

appResources 選項可讓 MCP server 透過 MCP Apps extension提供互動式 HTML UI。每個項目都會將 ui:// URI 對應至在 Mastra Studio 的 Sandbox iframe 中呈現的 HTML app。

AppResources 型別
「appresources-type」的直接連結

Key (URI):

string
識別 app resource 的 ui:// URI(例如 ui://calculator/main)。

每個值都是 AppResource 物件:

name:

string
UI resource 的顯示名稱。

description?:

string
UI resource 的選填說明。

html?:

string
UI 的 inline HTML 內容。請提供 htmlhtmlPath 其中之一。

htmlPath?:

string
HTML 檔案的路徑。會在 server 啟動時解析。請提供 htmlhtmlPath 其中之一。

meta?:

McpUiResourceMeta
來自官方 ext-apps SDK 的 UI resource 中繼資料(CSP、權限、呈現偏好)。

範例
「範例」的直接連結

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: '<html><body><h2>Calculator</h2>...</body></html>',
},
},
})

在 Tool 上將 _meta.ui.resourceUri 設為相符的 ui:// URI,即可將 Tool 連結至 app resource。Server 註冊 Tool 時會自動正規化此中繼資料。完整的 app bridge API 與使用模式請參閱 MCP Apps