跳至主要內容

MCPServer

MCPServer 類別可將你現有的 Mastra Tools 及 Agents 公開為 Model Context Protocol (MCP) 伺服器。任何 MCP 用戶端(例如 Cursor、Windsurf 或 Claude Desktop)均可連接這些功能,並讓 agent 使用。

請注意,如果你只需在 Mastra 應用程式內直接使用 Tools 或 Agents,未必需要建立 MCP 伺服器。此 API 專門用於向_外部_ MCP 用戶端公開 Mastra Tools 及 Agents。

它同時支援 stdio(子程序)及 SSE (HTTP) MCP 傳輸

建構函式
建構函式 的直接連結

要建立新的 MCPServer,你需要提供伺服器的基本資料、它所提供的 Tools,以及選擇性提供任何想公開為 Tools 的 Agents。

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
伺服器的 semantic version(例如 '1.0.0')。

tools:

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

agents?:

Record<string, Agent>
以 agent 識別碼作為鍵、Mastra Agent 實例作為值的物件。每個 agent 都會自動轉換為名為 ask_<agentIdentifier> 的 Tool。Agent 的建構函式設定中**必須**定義非空白的 description 字串屬性,此描述會用作 Tool 描述。如果 agent 的描述缺漏或為空白,MCPServer 初始化期間會拋出錯誤。

workflows?:

Record<string, Workflow>
以 Workflow 識別碼作為鍵、Mastra Workflow 實例作為值的物件。每個 Workflow 都會轉換為名為 run_<workflowKey> 的 Tool。Workflow 的 inputSchema 會成為 Tool 的輸入 schema。Workflow **必須**具有非空白的 description 字串屬性,用作 Tool 描述;如描述缺漏或為空白,系統會拋出錯誤。Tool 會先呼叫 workflow.createRun(),再呼叫 run.start({ inputData: <tool_input> }) 以執行 Workflow。如果由 agent 或 Workflow 衍生的 Tool 名稱(例如 ask_myAgentrun_myWorkflow)與明確定義的 Tool 名稱或另一衍生名稱衝突,則明確定義的 Tool 優先,並會記錄警告。引致後續衝突的 Agents/Workflows 會被略過。

description?:

string
MCP 伺服器功能的選擇性描述。

instructions?:

string
說明如何使用伺服器及其功能的選擇性指示。

mapAuthInfoToUser?:

({ authInfo, extra, requestContext }) => unknown | null | undefined | Promise<unknown | null | undefined>
extra.authInfo 中的 MCP 傳輸驗證資料映射至 Mastra FGA 檢查使用的 user 值。受 OAuth 保護的 MCP 伺服器在具有 FGA Provider 的 Mastra 實例上註冊時,請使用此屬性。

fga?:

{ resourceMapping?: Partial<Record<'tool' | 'tools', { fgaResourceType: string; deriveId?: ({ user, resourceId, requestContext }) => string | undefined }>>; permissionMapping?: Record<string, string> }
覆寫此 MCP 伺服器 tools/listtools/call FGA 檢查的 Resource 及權限映射。當 MCP 授權範圍應與內部 agent 或 Workflow Tool 執行不同時,請使用此屬性。

repository?:

Repository
伺服器原始碼的選擇性 repository 資料。

releaseDate?:

string
此伺服器版本的選擇性發佈日期(ISO 8601 字串)。如未提供,預設為實例化時間。

isLatest?:

boolean
表示這是否為最新版本的選擇性旗標。如未提供,預設為 true。

packageCanonical?:

'npm' | 'docker' | 'pypi' | 'crates' | string
伺服器以套件形式發佈時的選擇性 canonical 封裝格式(例如 'npm'、'docker')。

packages?:

PackageInfo[]
此伺服器可安裝套件的選擇性清單。

remotes?:

RemoteInfo[]
此伺服器遠端存取點的選擇性清單。

resources?:

MCPServerResources
定義伺服器如何處理 MCP Resources 的物件。詳情請參閱 Resource 處理章節。

prompts?:

MCPServerPrompts
定義伺服器如何處理 MCP Prompts 的物件。詳情請參閱 Prompt 處理章節。

appResources?:

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

將 Agents 公開為 Tools
將 Agents 公開為 Tools 的直接連結

MCPServer 的一項強大功能,是自動將 Mastra Agents 公開為可呼叫的 Tools。當你在設定的 agents 屬性中提供 Agents 時:

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

  • Tool 功能

    • 描述:產生的 Tool 描述格式為:「向 agent <AgentName> 提問。原始 agent 指示:<agent description>」。
    • 輸入:Tool 預期接收一個具有 message 屬性(字串)的物件引數:{ message: "Your question for the agent" }
    • 執行:呼叫此 Tool 時,它會使用所提供的 query 呼叫相應 agent 的 generate() 方法。
    • 輸出:直接將 agent 的 generate() 方法結果作為 Tool 輸出傳回。
  • 名稱衝突。 如果在 tools 設定中明確定義的 Tool,與由 agent 衍生的 Tool 同名(例如名為 ask_myAgentKey 的 Tool 與鍵為 myAgentKey 的 agent 並存),則會_優先採用明確定義的 Tool_。發生此衝突時,該 agent 不會轉換為 Tool,並會記錄警告。

這讓 MCP 用戶端能像使用其他 Tool 一樣,以自然語言查詢直接與 Agents 互動。

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

當你在 agents 設定屬性中提供 Agents 時,MCPServer 會自動為每個 agent 建立相應的 Tool。Tool 名稱為 ask_<agentIdentifier>,其中 <agentIdentifier> 是你在 agents 物件中使用的鍵。

此產生的 Tool 描述為:「向 agent <agent.name> 提問。Agent 描述:<agent.description>」。

要將 agent 轉換為 Tool,實例化時的設定中必須包含非空白的 description 字串屬性(例如 new Agent({ id: 'my-agent', name: 'myAgent', description: 'This agent does X.', ... }))。如果傳入 MCPServer 的 agent 缺少 description 或其值為空白,實例化 MCPServer 時會拋出錯誤,伺服器設定亦會失敗。

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

在 Tools 中存取 MCP Context
在 Tools 中存取 MCP Context 的直接連結

透過 MCPServer 公開的 Tools,可根據 Tool 的呼叫方式,經由兩個不同屬性存取 MCP 請求 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 實例上呼叫以下函式,以控制其行為及取得資料。

startStdio()
startstdio 的直接連結

使用此方法啟動伺服器,讓它透過標準輸入及輸出 (stdio) 通訊。伺服器作為命令列程式執行時,通常會採用此方式。

async startStdio(): Promise<void>

以下說明如何使用 stdio 啟動伺服器:

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

startSSE()
startsse 的直接連結

此方法協助你將 MCP 伺服器整合至現有網頁伺服器,並使用 Server-Sent Events (SSE) 通訊。網頁伺服器收到 SSE 或訊息路徑的請求時,便會從其程式碼呼叫此方法。

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

以下範例說明如何在 HTTP 伺服器請求處理器中使用 startSSE。在此範例中,MCP 用戶端可透過 http://localhost:1234/sse 連接 MCP 伺服器:

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
使用者要求的網址。

ssePath:

string
用戶端連接 SSE 的指定 URL 部分(例如 '/sse')。

messagePath:

string
用戶端傳送訊息的指定 URL 部分(例如 '/message')。

req:

any
來自網頁伺服器的傳入請求物件。

res:

any
網頁伺服器用作傳回資料的回應物件。

startHonoSSE()
starthonosse 的直接連結

此方法協助你將 MCP 伺服器整合至現有網頁伺服器,並使用 Server-Sent Events (SSE) 通訊。網頁伺服器收到 SSE 或訊息路徑的請求時,便會從其程式碼呼叫此方法。

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

以下範例說明如何在 HTTP 伺服器請求處理器中使用 startHonoSSE。在此範例中,MCP 用戶端可透過 http://localhost:1234/hono-sse 連接 MCP 伺服器:

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
使用者要求的網址。

ssePath:

string
用戶端連接 SSE 的指定 URL 部分(例如 '/hono-sse')。

messagePath:

string
用戶端傳送訊息的指定 URL 部分(例如 '/message')。

req:

any
來自網頁伺服器的傳入請求物件。

res:

any
網頁伺服器用作傳回資料的回應物件。

startHTTP()
starthttp 的直接連結

此方法協助你將 MCP 伺服器整合至現有網頁伺服器,並使用可串流 HTTP 通訊。網頁伺服器收到 HTTP 請求時,便會從其程式碼呼叫此方法。

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 伺服器請求處理器中使用 startHTTP。在此範例中,MCP 用戶端可透過 http://localhost:1234/http 連接 MCP 伺服器:

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

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

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

以下情況請使用預設的 session 模式(不設置 serverless: true):

  • 長時間運行的 Node.js 伺服器
  • Docker 容器
  • 傳統託管(VPS、專用伺服器)

serverless 模式會停用 session 管理,並為每項請求建立新的伺服器實例。無狀態環境不會在呼叫之間保留記憶體,因此必須採用此方式。

預設情況下,serverless 模式會將每項請求緩衝為單一 JSON 回應,因此 Tool 傳送的 notifications/progress 永遠不會送達用戶端。設置 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 狀態或持久連線,因此在 serverless 模式中無法運作(包括使用 serverlessStreaming: true 時):

  • Elicitation — Tool 執行期間的互動式使用者輸入請求,需要 session 管理才能將回應路由至正確的用戶端
  • Resource 訂閱resources/subscriberesources/unsubscribe 需要持久連線以維持訂閱狀態
  • Resource 更新通知resources.notifyUpdated() 需要有效訂閱及持久連線才能通知用戶端
  • Prompt 清單變更通知prompts.notifyListChanged() 需要持久連線才能向用戶端推送更新
  • Tool 清單變更通知toolActions.notifyListChanged() 需要持久連線才能向用戶端推送更新
  • 伺服器記錄通知sendLoggingMessage() 需要持久連線才能向用戶端推送記錄訊息

這些功能在長時間運行的伺服器環境(Node.js 伺服器、Docker 容器等)中可正常運作。

startHTTP 方法所需值的詳細資料如下:

url:

URL
使用者要求的網址。

httpPath:

string
MCP 伺服器處理 HTTP 請求的指定 URL 部分(例如 '/mcp')。

req:

http.IncomingMessage
來自網頁伺服器的傳入請求物件。

res:

http.ServerResponse
網頁伺服器用作傳回資料的回應物件。

options:

StreamableHTTPServerTransportOptions
HTTP 傳輸的選擇性設定。詳情請參閱下方選項表。

StreamableHTTPServerTransportOptions 物件讓你自訂 HTTP 傳輸的行為。可用選項如下:

serverless:

boolean
如為 true,則以無狀態模式運行而不進行 session 管理。每項請求均由全新的伺服器實例獨立處理。這對無法在呼叫之間保留 session 的 serverless 環境(Cloudflare Workers、Supabase Edge Functions、Vercel Edge 等)至關重要。預設為 false

serverlessStreaming:

boolean
如為 true,serverless 請求會使用請求範圍的 SSE 串流,而非已緩衝的 JSON 回應,讓請求內的 notifications/progress 可在最終結果前送達用戶端。只有與 serverless: true 同時使用才會生效。預設為 false(已緩衝的 JSON 回應),以保持向後兼容。它只啟用進度等請求範圍通知;Elicitation、訂閱及請求外通知仍需要 session 狀態。

sessionIdGenerator:

(() => string) | undefined
產生獨有 session ID 的函式。它應是密碼學上安全且全域獨有的字串。傳回 undefined 可停用 session 管理。

onsessioninitialized:

(sessionId: string) => void
初始化新 session 時呼叫的回呼函式,適合用於追蹤有效的 MCP sessions。

enableJsonResponse:

boolean
如為 true,伺服器會傳回純 JSON 回應,而非使用 Server-Sent Events (SSE) 串流。預設為 false

eventStore:

EventStore
用於讓訊息可恢復的 event store。提供此選項後,用戶端可重新連接並恢復訊息串流。

close()
close 的直接連結

此方法會關閉伺服器並釋放所有資源。

async close(): Promise<void>

getServerInfo()
getserverinfo 的直接連結

此方法傳回伺服器的基本資料。

getServerInfo(): ServerInfo

getServerDetail()
getserverdetail 的直接連結

此方法傳回伺服器資料的詳細內容。

getServerDetail(): ServerDetail

getToolListInfo()
gettoollistinfo 的直接連結

此方法傳回建立伺服器時設定的 Tools。這是唯讀清單,適合用於除錯。

getToolListInfo(): ToolListInfo

getToolInfo()
gettoolinfo 的直接連結

此方法傳回指定 Tool 的詳細資料。

getToolInfo(toolName: string): ToolInfo

executeTool()
executetool 的直接連結

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

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

getStdioTransport()
getstdiotransport 的直接連結

如果使用 startStdio() 啟動伺服器,可透過此方法取得管理 stdio 通訊的物件。這主要用於內部檢查或測試。

getStdioTransport(): StdioServerTransport | undefined

getSseTransport()
getssetransport 的直接連結

如果使用 startSSE() 啟動伺服器,可透過此方法取得管理 SSE 通訊的物件。與 getStdioTransport 一樣,這主要用於內部檢查或測試。

getSseTransport(): SSEServerTransport | undefined

getSseHonoTransport()
getssehonotransport 的直接連結

如果使用 startHonoSSE() 啟動伺服器,可透過此方法取得管理 SSE 通訊的物件。與 getSseTransport 一樣,這主要用於內部檢查或測試。

getSseHonoTransport(): SSETransport | undefined

getStreamableHTTPTransport()
getstreamablehttptransport 的直接連結

如果使用 startHTTP() 啟動伺服器,可透過此方法取得管理 HTTP 通訊的物件。與 getSseTransport 一樣,這主要用於內部檢查或測試。

getStreamableHTTPTransport(): StreamableHTTPServerTransport | undefined

tools()
tools 的直接連結

執行此 MCP 伺服器提供的指定 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,例如訊息或 toolCallId。

Resource 處理
Resource 處理 的直接連結

甚麼是 MCP Resources?
甚麼是 MCP Resources? 的直接連結

Resources 是 Model Context Protocol (MCP) 的核心基本元素,讓伺服器可公開供用戶端讀取的資料及內容,並用作 LLM 互動的 context。它們可代表 MCP 伺服器希望提供的任何資料,例如:

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

Resources 以獨有 URI 識別(例如 file:///home/user/documents/report.pdfpostgres://database/customers/schema),可包含文字(UTF-8 編碼)或二進制資料(base64 編碼)。

用戶端可透過以下方式探索 Resources:

  1. 直接 Resources:伺服器透過 resources/list 端點公開具體 Resources 清單。
  2. Resource 範本:對於執行階段定義的 Resources,伺服器可公開 URI 範本 (RFC 6570),讓用戶端用來建構 Resource URI。

要讀取 Resource,用戶端需使用 URI 發出 resources/read 請求。如果用戶端已訂閱該 Resource,伺服器亦可通知它 Resource 清單變更 (notifications/resources/list_changed) 或指定 Resource 內容更新 (notifications/resources/updated)。

詳情請參閱 MCP 官方 Resources 文件

MCPServerResources 類型
mcpserverresources-type 的直接連結

resources 選項接受 MCPServerResources 類型的物件。此類型定義伺服器處理 Resource 請求時使用的回呼函式:

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

通知用戶端 Resource 變更
通知用戶端 Resource 變更 的直接連結

如果可用 Resources 或其內容有變,伺服器可通知已連接並訂閱指定 Resource 的用戶端。

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

指定 Resource(由其 uri 識別)的內容更新時,請呼叫此方法。已訂閱此 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 的直接連結

可用 Resources 清單有變(例如新增或移除 Resource)時,請呼叫此方法。這會向用戶端傳送 notifications/resources/list_changed 訊息,提示它們重新擷取 Resources 清單。

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 Prompts?
甚麼是 MCP Prompts? 的直接連結

Prompts 是 MCP 伺服器向用戶端公開的可重用範本或 Workflows。它們可接受引數、包含 Resource context,亦支援版本控制並將 LLM 互動標準化。

Prompts 以獨有名稱(及選擇性版本)識別,並可在執行階段定義或設為靜態。

MCPServerPrompts 類型
mcpserverprompts-type 的直接連結

prompts 選項接受 MCPServerPrompts 類型的物件。此類型定義伺服器處理 Prompt 請求時使用的回呼函式:

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

通知用戶端 Prompt 變更
通知用戶端 Prompt 變更 的直接連結

如果可用 Prompts 有變,伺服器可通知已連接的用戶端:

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

可用 Prompts 清單有變(例如新增或移除 Prompt)時,請呼叫此方法。這會向用戶端傳送 notifications/prompts/list_changed 訊息,提示它們重新擷取 Prompts 清單。

await serverWithPrompts.prompts.notifyListChanged()

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

  • 使用清晰且具描述性的 Prompt 名稱及描述。
  • 驗證 getPromptMessages 中所有必要引數。
  • 如果預期會作出重大變更,請加入 version 欄位。
  • 使用 version 參數選擇正確的 Prompt 邏輯。
  • Prompt 清單有變時通知用戶端。
  • 使用資訊清楚的訊息處理錯誤。
  • 記錄預期引數及可用版本。

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

Tools 通常在建構 MCPServer 時提供,但你亦可在伺服器運行期間新增或移除 Tools。伺服器透過 toolActions 屬性公開這些操作。Tool 清單有變時,已連接的用戶端會收到 notifications/tools/list_changed 訊息,提示它們重新擷取 Tool 清單。

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

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

在運行中的伺服器註冊新 Tools,並通知已連接的用戶端。Tools 使用其 record key 作為鍵,與傳給建構函式的 Tools 相同。在現有鍵下新增 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 從運行中的伺服器移除 Tools,並通知已連接的用戶端。不明的 Tool ID 會被忽略。只有在至少移除一個 Tool 時才會傳送通知。

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

範例:

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

toolActions.notifyListChanged()
toolactionsnotifylistchanged 的直接連結

向已連接的用戶端傳送 notifications/tools/list_changed 訊息,而不修改 Tool registry。Tool 可用性透過其他方式變更(例如授權變更)時,請呼叫此方法。

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

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

伺服器向 Mastra 實例註冊後,toolActions.add()toolActions.remove() 亦會更新 Mastra 實例的 Tool registry,與啟動時自動註冊 Tools 的行為一致。新增的 Tools 可透過 mastra.listTools() 使用(如有內在 id,則以它作為鍵),而移除的 Tools 則會從 registry 刪除。

記錄
記錄 的直接連結

MCP 伺服器可使用 notifications/message 向用戶端傳送結構化記錄訊息。用戶端透過傳送 logging/setLevel 請求控制詳細程度。伺服器會捨棄低於所要求最低級別的訊息(依照 RFC 5424 嚴重程度排序)。級別按 session 追蹤,因此不同用戶端可要求不同的詳細程度。

sendLoggingMessage()
sendloggingmessage 的直接連結

向所有已連接的用戶端傳送記錄通知,並遵從各用戶端的最低記錄級別。

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 的用戶端傳送記錄訊息。

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
}

進度通知
進度通知 的直接連結

長時間運行的 Tools 可使用 notifications/progress 向呼叫用戶端報告進度。只有當呼叫者在請求中加入 progressToken 以要求追蹤進度時,才會傳送進度(Mastra MCPClient 設置 enableProgressTracking 時會這樣做)。如未傳送 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())會透過所有傳輸向每個已連接的用戶端廣播:包括 stdio/SSE 連線及每個可串流 HTTP session。resources.notifyUpdated() 是例外;它只會通知透過 resources/subscribe 訂閱 Resource URI 的用戶端。對於可串流 HTTP 用戶端,訂閱會按 session 追蹤;舊版 SSE 用戶端共用主要伺服器實例,因此亦共用同一組訂閱。使用無狀態 serverless 模式的用戶端無法接收通知,因為每項請求都使用暫時的伺服器實例。

範例
範例 的直接連結

如需設定及部署 MCPServer 的實用範例,請參閱發佈 MCP Server 指南

本頁開首的範例亦示範如何使用 Tools 及 Agents 實例化 MCPServer

Elicitation
Elicitation 的直接連結

甚麼是 Elicitation?
甚麼是 Elicitation? 的直接連結

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

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

Tool 執行簽署
Tool 執行簽署 的直接連結

Tools 在 MCP 伺服器 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 執行期間需要使用者輸入時,透過 context 參數所提供的 Elicitation 功能取得輸入:

  1. Tool 使用訊息及 schema 呼叫 context.mcp.elicitation.sendRequest()
  2. 請求傳送至已連接的 MCP 用戶端
  3. 用戶端向使用者顯示請求(透過 UI、命令列等)
  4. 使用者提供輸入、拒絕或取消請求
  5. 用戶端將回應傳回伺服器
  6. Tool 收到回應並繼續執行

在 Tools 中使用 Elicitation
在 Tools 中使用 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 請求 Schema
Elicitation 請求 Schema 的直接連結

requestedSchema 必須是僅包含基本類型屬性的扁平物件。支援的類型包括:

  • 字串{ type: 'string', title: 'Display Name', description: 'Help text' }
  • 數字{ type: 'number', minimum: 0, maximum: 100 }
  • 布林值{ type: 'boolean', default: false }
  • 列舉{ 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 請求:

  1. 接受 (action: 'accept'):使用者提供資料並確認提交
    • 包含帶有所提交資料的 content 欄位
  2. 拒絕 (action: 'decline'):使用者明確拒絕提供資料
    • 不含 content 欄位
  3. 取消 (action: 'cancel'):使用者未作決定便關閉請求
    • 不含 content 欄位

Tools 應妥善處理三種回應類型。

保安考慮
保安考慮 的直接連結

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

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

Tool 執行時,可透過 options 參數使用 Elicitation 功能:

// 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 傳輸(SSE 或 HTTP)時,Elicitation 可識別 session。多個用戶端連接至同一伺服器時,Elicitation 請求會路由至發起 Tool 執行的用戶端 session。

ElicitResult 類型:

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

OAuth 保護
OAuth 保護 的直接連結

要按照 MCP Auth Specification 使用 OAuth 驗證保護 MCP 伺服器,請使用 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 伺服器的 canonical URL。此 URL 會在 Protected Resource Metadata 中傳回。

oauth.authorizationServers:

string[]
可為此 Resource 發出 token 的授權伺服器 URL。

oauth.scopesSupported?:

string[]
= ['mcp:read', 'mcp:write']
此 MCP 伺服器支援的 scopes。

oauth.resourceName?:

string
此 Resource 伺服器便於閱讀的名稱。

oauth.validateToken?:

(token: string, resource: string) => Promise<TokenValidationResult>
驗證 access token 的函式。如未提供,系統會接受 token 而不作驗證(不建議在正式環境使用)。

mcpPath?:

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

驗證 context
驗證 context 的直接連結

使用 HTTP 傳輸時,Tools 可透過 context.mcp.extra 存取請求 metadata。這讓你可將驗證資料、使用者 context 或任何自訂資料,從 HTTP middleware 傳給 MCP Tools。

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

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

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

為 FGA 映射驗證資料
為 FGA 映射驗證資料 的直接連結

MCPServer 在具有 fine-grained authorization (FGA) Provider 的 Mastra 實例上註冊時,Mastra 會在列出或呼叫 Tools 前檢查 requestContext.get('user')。HTTP MCP 傳輸會以 extra.authInfo 傳遞已驗證資料,因此請使用 mapAuthInfoToUser 設置 FGA Provider 預期的使用者結構。

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 Tool 的 FGA 範圍 的直接連結

當 MCP 用戶端所需的授權範圍與內部 agent 或 Workflow Tool 執行不同時,請使用 fga.resourceMappingfga.permissionMapping。覆寫只套用至此 MCP 伺服器的 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 的直接連結

要將資料傳給 Tools,請先在 HTTP 伺服器 middleware 的 Node.js 請求物件上填入 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 })
})

在 Tools 中存取驗證資料
在 Tools 中存取驗證資料 的直接連結

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

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 物件包含:

屬性描述
authInfo你在 middleware 的 req.auth 上設置的內容
sessionIdMCP 連線的 session 識別碼
signal用於取消請求的 AbortSignal
sendNotification用於傳送通知的 MCP protocol 函式
sendRequest用於傳送請求的 MCP protocol 函式

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

安裝 jose,以根據身份提供者的 JSON Web Key Set (JWKS) 驗證 JSON Web Tokens (JWTs):

npm install jose

以下範例會驗證 token 的簽署、issuer、audience、algorithm、到期時間及必要 claims,然後才將其使用者資料傳給 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 Apps extension,從 MCP 伺服器提供互動式 HTML UI。每個項目會將 ui:// URI 映射至在 Mastra Studio 沙盒 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 檔案的路徑,在伺服器啟動時解析。請提供 htmlhtmlPath

meta?:

McpUiResourceMeta
來自官方 ext-apps SDK 的 UI Resource metadata(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。伺服器註冊 Tools 時會自動將此 metadata 標準化。請瀏覽 MCP Apps,了解完整 app bridge API 及使用模式。