Skip to main content

MCP overview

Mastra supports the Model Context Protocol (MCP), an open standard for connecting AI agents to external tools and resources.

Use MCPClient to connect to MCP servers. Use MCPServer to expose Mastra agents, tools, workflows, prompts, and resources to other MCP-compatible systems.

Connect to MCP servers
Direct link to Connect to MCP servers

Install the MCP package:

npm install @mastra/mcp@latest

Configure each server with a local command or remote URL:

src/mastra/mcp/client.ts
import { MCPClient } from '@mastra/mcp'

export const mcpClient = new MCPClient({
id: 'my-mcp-client',
servers: {
wikipedia: {
command: 'npx',
args: ['-y', 'wikipedia-mcp'],
},
weather: {
url: new URL('https://weather.example.com/mcp'),
requestInit: {
headers: {
Authorization: `Bearer ${process.env.WEATHER_API_KEY}`,
},
},
},
},
})
Authentication

For OAuth-protected servers, use authenticate() to complete the browser-based authorization flow. Visit OAuth authentication for configuration details.

Pass tools from the configured servers to an agent:

src/mastra/agents/assistant.ts
import { Agent } from '@mastra/core/agent'
import { mcpClient } from '../mcp/client'

export const assistant = new Agent({
id: 'assistant',
name: 'Assistant',
instructions: `
Use the available MCP tools to answer questions.
Include the source of any information you retrieve.
`,
model: 'openai/gpt-5.6-sol',
tools: await mcpClient.listTools(),
})

Static and runtime tools
Direct link to Static and runtime tools

Choose how to load tools based on whether the server configuration changes between requests:

Static toolsRuntime toolsets
Methodawait mcpClient.listTools()await mcpClient.listToolsets()
Use caseShared, fixed configurationPer-user or per-request configuration
CredentialsShared by all requestsCan vary between requests
Agent APItools in the Agent constructortoolsets in generate() or stream()

The preceding agent example uses static tools. For runtime credentials, create a client for the request and pass its toolsets when calling the agent:

src/handle-request.ts
import { MCPClient } from '@mastra/mcp'
import { mastra } from './mastra'

export async function handleRequest(prompt: string, apiKey: string) {
const userMcpClient = new MCPClient({
servers: {
weather: {
url: new URL('https://weather.example.com/mcp'),
requestInit: {
headers: { Authorization: `Bearer ${apiKey}` },
},
},
},
})

const agent = mastra.getAgent('assistant')
const response = await agent.generate(prompt, {
toolsets: await userMcpClient.listToolsets(),
})

await userMcpClient.disconnect()
return response.text
}

Visit listTools() and listToolsets() for their full APIs.

Tool approval
Direct link to Tool approval

Set requireToolApproval on a server to require approval for all its tools:

const mcpClient = new MCPClient({
servers: {
github: {
url: new URL('https://github.example.com/mcp'),
requireToolApproval: true,
},
},
})

You can also provide a function that decides based on the tool name, arguments, or annotations:

requireToolApproval: ({ toolName }) => toolName.startsWith('delete_')

Treat tool annotations from servers you don't control as untrusted hints. Visit tool approval for the callback context and security guidance.

MCP registries
Direct link to MCP registries

Registries provide hosted or packaged MCP servers. The client configuration above works with registry endpoints and commands.

RegistryConnectionNotes
Klavis AIHosted HTTPEnterprise authentication and managed servers
mcp.runSigned SSE URLTreat the profile URL as a secret
ComposioHosted SSE URLURLs are often tied to one user account
SmitheryCLI or hosted URLRun local packages through npx
ApifyHosted HTTPAuthenticate with an Apify API token
AmpersandSSE or stdioConnect to configured SaaS integrations

Store signed URLs, API keys, and tokens in environment variables. Follow the registry's documentation to obtain the endpoint, command, and credentials for each server.

Expose a Mastra MCP server
Direct link to Expose a Mastra MCP server

Create an MCPServer to expose Mastra primitives to external MCP clients:

src/mastra/mcp/server.ts
import { MCPServer } from '@mastra/mcp'
import { assistant } from '../agents/assistant'
import { weatherTool } from '../tools/weather'
import { weatherWorkflow } from '../workflows/weather'

export const mcpServer = new MCPServer({
id: 'my-mcp-server',
name: 'My MCP Server',
version: '1.0.0',
agents: { assistant },
tools: { weatherTool },
workflows: { weatherWorkflow },
})

Register the server on the main Mastra instance:

src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'
import { mcpServer } from './mcp/server'

export const mastra = new Mastra({
mcpServers: { mcpServer },
})
Authentication

Protect HTTP MCP servers with OAuth middleware. Visit OAuth protection for setup instructions.

Visit the MCPServer reference for prompts, resources, transports, and other server options.

Build MCP Apps
Direct link to Build MCP Apps

The MCP Apps extension lets MCP tools serve interactive HTML interfaces through ui:// resources. Mastra Studio renders these apps in sandboxed iframes on tool pages and in agent chat.

Use an MCP App when a tool result benefits from interaction, such as a form, calculator, color picker, or data visualization.

Define an app resource
Direct link to Define an app resource

Return a short content summary for the model and place UI data in structuredContent. Link the tool to its app by setting _meta.ui.resourceUri to the same ui:// URI used in appResources:

src/mastra/mcp/calculator.ts
import { MCPServer } from '@mastra/mcp'
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

export const calculatorTool = createTool({
id: 'calculatorWithUI',
description: 'Calculate the sum of two numbers',
inputSchema: z.object({
num1: z.number(),
num2: z.number(),
}),
execute: async ({ num1, num2 }) => ({
content: [{ type: 'text', text: 'The result is displayed in the calculator app.' }],
structuredContent: { result: num1 + num2 },
}),
})

calculatorTool._meta = {
ui: { resourceUri: 'ui://calculator/main' },
}

export const calculatorMcpServer = new MCPServer({
id: 'calculator-app-server',
name: 'Calculator App Server',
version: '1.0.0',
tools: { calculatorTool },
appResources: {
'ui://calculator/main': {
name: 'Calculator',
htmlPath: './src/mastra/mcp/calculator.html',
},
},
})

The model sees content, while the app receives structuredContent. Visit appResources for inline HTML, file paths, metadata, and content security policy options.

Connect the app to Studio
Direct link to Connect the app to Studio

Use the App class from @modelcontextprotocol/ext-apps inside the HTML resource. Register event handlers before calling connect():

src/mastra/mcp/calculator.html
<!doctype html>
<html>
<body>
<p id="result">Waiting for input</p>
<button id="recalculate">Recalculate</button>

<script type="module">
import { App } from 'https://cdn.jsdelivr.net/npm/@modelcontextprotocol/ext-apps/+esm'

const app = new App({ name: 'Calculator', version: '1.0.0' })
let toolInput

app.ontoolinput = params => {
toolInput = params.arguments
}

document.querySelector('#recalculate').addEventListener('click', async () => {
const result = await app.callServerTool({
name: 'calculatorWithUI',
arguments: toolInput,
})
document.querySelector('#result').textContent = JSON.stringify(result)

await app.sendMessage({
role: 'user',
content: [{ type: 'text', text: 'Explain the recalculated result.' }],
})
})

await app.connect()
</script>
</body>
</html>

The guest-side APIs serve different parts of the interaction:

APIPurpose
app.ontoolinputReceive the arguments from the host tool call
app.callServerTool()Call an MCP tool from inside the iframe
app.sendMessage()Add a user message to chat and start a new model turn
app.connect()Connect to the host after registering event handlers

The interaction follows this sequence:

  1. The agent calls the tool.
  2. The tool returns model-facing content and UI-facing structuredContent.
  3. Studio renders the associated app resource.
  4. The app receives tool input and can call server tools or send chat messages.

Visit the external App API reference for all guest-side methods and lifecycle hooks.

Register MCP Apps
Direct link to Register MCP Apps

For a local app, pass the tool to an agent and register its MCP server on Mastra:

src/mastra/index.ts
import { Agent } from '@mastra/core/agent'
import { Mastra } from '@mastra/core/mastra'
import { calculatorMcpServer, calculatorTool } from './mcp/calculator'

const calculatorAgent = new Agent({
id: 'calculator-agent',
name: 'Calculator Agent',
instructions: 'Use the calculator tool for arithmetic.',
model: 'openai/gpt-5-mini',
tools: { calculatorTool },
})

export const mastra = new Mastra({
agents: { calculatorAgent },
mcpServers: { calculatorMcpServer },
})

For an external MCP server that implements MCP Apps, load its tools with MCPClient.listTools() and register its proxy so Studio can resolve the remote app resources:

src/mastra/remote-apps.ts
import { Agent } from '@mastra/core/agent'
import { Mastra } from '@mastra/core/mastra'
import { mcpClient } from './mcp/client'

const tools = await mcpClient.listTools()
const mcpServers = mcpClient.toMCPServerProxies()

const agent = new Agent({
id: 'remote-app-agent',
name: 'Remote App Agent',
instructions: 'Use the available remote tools.',
model: 'openai/gpt-5-mini',
tools,
})

export const mastra = new Mastra({
agents: { agent },
mcpServers,
})

Tools loaded through listTools() include a serverId in _meta.ui, allowing Studio to resolve each app resource without scanning every server. Visit toMCPServerProxies() for proxy configuration details.

Sandbox security
Direct link to Sandbox security

Mastra Studio uses @mcp-ui/client to load app HTML through a sandbox proxy and communicate over JSON-RPC with postMessage.

App iframes allow scripts, forms, and popups. They can't access the parent page's DOM, cookies, or storage. The host controls all communication with the guest app.

Next steps
Direct link to Next steps