Skip to Content

MCPServer

The MCPServer class provides the functionality to expose your existing Mastra tools and Agents as a Model Context Protocol (MCP) server. This allows any MCP client (like Cursor, Windsurf, or Claude Desktop) to connect to these capabilities and make them available to an agent.

Note that if you only need to use your tools or agents directly within your Mastra application, you don’t necessarily need to create an MCP server. This API is specifically for exposing your Mastra tools and agents to external MCP clients.

It supports both stdio (subprocess) and SSE (HTTP) MCP transports.

Constructor

To create a new MCPServer, you need to provide some basic information about your server, the tools it will offer, and optionally, any agents you want to expose as tools.

import { openai } from "@ai-sdk/openai"; 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({ name: "MyExampleAgent", description: "A generalist to help with basic questions." instructions: "You are a helpful assistant.", model: openai("gpt-4o-mini"), }); const weatherTool = createTool({ id: "getWeather", description: "Gets the current weather for a location.", inputSchema: z.object({ location: z.string() }), execute: async ({ context }) => `Weather in ${context.location} is sunny.`, }); const server = new MCPServer({ name: "My Custom Server", version: "1.0.0", tools: { weatherTool }, agents: { myAgent }, // this agent will become tool "ask_myAgent" workflows: { dataProcessingWorkflow, // this workflow will become tool "run_dataProcessingWorkflow" } });

Configuration Properties

The constructor accepts an MCPServerConfig object with the following properties:

name:

string
A descriptive name for your server (e.g., 'My Weather and Agent Server').

version:

string
The semantic version of your server (e.g., '1.0.0').

tools:

ToolsInput
An object where keys are tool names and values are Mastra tool definitions (created with `createTool` or Vercel AI SDK). These tools will be directly exposed.

agents?:

Record<string, Agent>
An object where keys are agent identifiers and values are Mastra Agent instances. Each agent will be automatically converted into a tool named `ask_<agentIdentifier>`. The agent **must** have a non-empty `description` string property defined in its constructor configuration. This description will be used in the tool's description. If an agent's description is missing or empty, an error will be thrown during MCPServer initialization.

workflows?:

Record<string, Workflow>
An object where keys are workflow identifiers and values are Mastra Workflow instances. Each workflow is converted into a tool named `run_<workflowKey>`. The workflow's `inputSchema` becomes the tool's input schema. The workflow **must** have a non-empty `description` string property, which is used for the tool's description. If a workflow's description is missing or empty, an error will be thrown. The tool executes the workflow by calling `workflow.createRun().start({ inputData: <tool_input> })`. If a tool name derived from an agent or workflow (e.g., `ask_myAgent` or `run_myWorkflow`) collides with an explicitly defined tool name or another derived name, the explicitly defined tool takes precedence, and a warning is logged. Agents/workflows leading to subsequent collisions are skipped.

id?:

string
Optional unique identifier for the server. If not provided, a UUID will be generated. This ID is considered final and cannot be changed by Mastra if provided.

description?:

string
Optional description of what the MCP server does.

repository?:

Repository
Optional repository information for the server's source code.

releaseDate?:

string
Optional release date of this server version (ISO 8601 string). Defaults to the time of instantiation if not provided.

isLatest?:

boolean
Optional flag indicating if this is the latest version. Defaults to true if not provided.

packageCanonical?:

'npm' | 'docker' | 'pypi' | 'crates' | string
Optional canonical packaging format if the server is distributed as a package (e.g., 'npm', 'docker').

packages?:

PackageInfo[]
Optional list of installable packages for this server.

remotes?:

RemoteInfo[]
Optional list of remote access points for this server.

resources?:

MCPServerResources
An object defining how the server should handle MCP resources. See Resource Handling section for details.

Exposing Agents as Tools

A powerful feature of MCPServer is its ability to automatically expose your Mastra Agents as callable tools. When you provide agents in the agents property of the configuration:

  • Tool Naming: Each agent is converted into a tool named ask_<agentKey>, where <agentKey> is the key you used for that agent in the agents object. For instance, if you configure agents: { myAgentKey: myAgentInstance }, a tool named ask_myAgentKey will be created.

  • Tool Functionality:

    • Description: The generated tool’s description will be in the format: “Ask agent <AgentName> a question. Original agent instructions: <agent description>”.
    • Input: The tool expects a single object argument with a message property (string): { message: "Your question for the agent" }.
    • Execution: When this tool is called, it invokes the generate() method of the corresponding agent, passing the provided query.
    • Output: The direct result from the agent’s generate() method is returned as the output of the tool.
  • Name Collisions: If an explicit tool defined in the tools configuration has the same name as an agent-derived tool (e.g., you have a tool named ask_myAgentKey and also an agent with the key myAgentKey), the explicitly defined tool will take precedence. The agent will not be converted into a tool in this conflicting case, and a warning will be logged.

This makes it straightforward to allow MCP clients to interact with your agents using natural language queries, just like any other tool.

Agent-to-Tool Conversion

When you provide agents in the agents configuration property, MCPServer will automatically create a corresponding tool for each agent. The tool will be named ask_<agentIdentifier>, where <agentIdentifier> is the key you used in the agents object.

The description for this generated tool will be: “Ask agent <agent.name> a question. Agent description: <agent.description>”.

Important: For an agent to be converted into a tool, it must have a non-empty description string property set in its configuration when it was instantiated (e.g., new Agent({ name: 'myAgent', description: 'This agent does X.', ... })). If an agent is passed to MCPServer with a missing or empty description, an error will be thrown when the MCPServer is instantiated, and server setup will fail.

This allows you to quickly expose the generative capabilities of your agents through the MCP, enabling clients to “ask” your agents questions directly.

Methods

These are the functions you can call on an MCPServer instance to control its behavior and get information.

startStdio()

Use this method to start the server so it communicates using standard input and output (stdio). This is typical when running the server as a command-line program.

async startStdio(): Promise<void>

Here’s how you would start the server using stdio:

const server = new MCPServer({ // example configuration above }); await server.startStdio();

startSSE()

This method helps you integrate the MCP server with an existing web server to use Server-Sent Events (SSE) for communication. You’ll call this from your web server’s code when it receives a request for the SSE or message paths.

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

Here’s an example of how you might use startSSE within an HTTP server request handler. In this example an MCP client could connect to your MCP server at http://localhost:1234/sse:

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

Here are the details for the values needed by the startSSE method:

url:

URL
The web address the user is requesting.

ssePath:

string
The specific part of the URL where clients will connect for SSE (e.g., '/sse').

messagePath:

string
The specific part of the URL where clients will send messages (e.g., '/message').

req:

any
The incoming request object from your web server.

res:

any
The response object from your web server, used to send data back.

startHonoSSE()

This method helps you integrate the MCP server with an existing web server to use Server-Sent Events (SSE) for communication. You’ll call this from your web server’s code when it receives a request for the SSE or message paths.

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

Here’s an example of how you might use startHonoSSE within an HTTP server request handler. In this example an MCP client could connect to your MCP server at http://localhost:1234/hono-sse:

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

Here are the details for the values needed by the startHonoSSE method:

url:

URL
The web address the user is requesting.

ssePath:

string
The specific part of the URL where clients will connect for SSE (e.g., '/hono-sse').

messagePath:

string
The specific part of the URL where clients will send messages (e.g., '/message').

req:

any
The incoming request object from your web server.

res:

any
The response object from your web server, used to send data back.

startHTTP()

This method helps you integrate the MCP server with an existing web server to use Server-Sent Events (SSE) for communication. You’ll call this from your web server’s code when it receives a request for the SSE or message paths.

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

Here’s an example of how you might use startHTTP within an HTTP server request handler. In this example an MCP client could connect to your MCP server at http://localhost:1234/http:

import http from "http"; const httpServer = http.createServer(async (req, res) => { await server.startHTTP({ url: new URL(req.url || "", `http://localhost:1234`), ssePath: "/http", messagePath: "/message", req, res, }); }); httpServer.listen(PORT, () => { console.log(`HTTP server listening on port ${PORT}`); });

Here are the details for the values needed by the startHTTP method:

url:

URL
The web address the user is requesting.

ssePath:

string
The specific part of the URL where clients will connect for SSE (e.g., '/http').

messagePath:

string
The specific part of the URL where clients will send messages (e.g., '/message').

req:

any
The incoming request object from your web server.

res:

any
The response object from your web server, used to send data back.

close()

This method closes the server and releases all resources.

async close(): Promise<void>

getServerInfo()

This method gives you a look at the server’s basic information.

getServerInfo(): ServerInfo

getServerDetail()

This method gives you a detailed look at the server’s information.

getServerDetail(): ServerDetail

getToolListInfo()

This method gives you a look at the tools that were set up when you created the server. It’s a read-only list, useful for debugging purposes.

getToolListInfo(): ToolListInfo

getToolInfo()

This method gives you detailed information about a specific tool.

getToolInfo(toolName: string): ToolInfo

executeTool()

This method executes a specific tool and returns the result.

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

getStdioTransport()

If you started the server with startStdio(), you can use this to get the object that manages the stdio communication. This is mostly for checking things internally or for testing.

getStdioTransport(): StdioServerTransport | undefined

getSseTransport()

If you started the server with startSSE(), you can use this to get the object that manages the SSE communication. Like getStdioTransport, this is mainly for internal checks or testing.

getSseTransport(): SSEServerTransport | undefined

getSseHonoTransport()

If you started the server with startHonoSSE(), you can use this to get the object that manages the SSE communication. Like getSseTransport, this is mainly for internal checks or testing.

getSseHonoTransport(): SSETransport | undefined

getStreamableHTTPTransport()

If you started the server with startHTTP(), you can use this to get the object that manages the HTTP communication. Like getSseTransport, this is mainly for internal checks or testing.

getStreamableHTTPTransport(): StreamableHTTPServerTransport | undefined

tools()

Executes a specific tool provided by this MCP server.

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

toolId:

string
The ID/name of the tool to execute.

args:

any
The arguments to pass to the tool's execute function.

executionContext?:

object
Optional context for the tool execution, like messages or a toolCallId.

Resource Handling

What are MCP Resources?

Resources are a core primitive in the Model Context Protocol (MCP) that allow servers to expose data and content that can be read by clients and used as context for LLM interactions. They represent any kind of data that an MCP server wants to make available, such as:

  • File contents
  • Database records
  • API responses
  • Live system data
  • Screenshots and images
  • Log files

Resources are identified by unique URIs (e.g., file:///home/user/documents/report.pdf, postgres://database/customers/schema) and can contain either text (UTF-8 encoded) or binary data (base64 encoded).

Clients can discover resources through:

  1. Direct resources: Servers expose a list of concrete resources via a resources/list endpoint.
  2. Resource templates: For dynamic resources, servers can expose URI templates (RFC 6570) that clients use to construct resource URIs.

To read a resource, clients make a resources/read request with the URI. Servers can also notify clients about changes to the resource list (notifications/resources/list_changed) or updates to specific resource content (notifications/resources/updated) if a client has subscribed to that resource.

For more detailed information, refer to the official MCP documentation on Resources.

MCPServerResources Type

The resources option takes an object of type MCPServerResources. This type defines the callbacks your server will use to handle resource requests:

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 };

Example:

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({ name: "Resourceful Server", version: "1.0.0", tools: { /* ... your tools ... */ }, resources: myResourceHandlers, });

Notifying Clients of Resource Changes

If the available resources or their content change, your server can notify connected clients that are subscribed to the specific resource.

server.resources.notifyUpdated({ uri: string })

Call this method when the content of a specific resource (identified by its uri) has been updated. If any clients are subscribed to this URI, they will receive a notifications/resources/updated message.

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

Example:

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

server.resources.notifyListChanged()

Call this method when the overall list of available resources has changed (e.g., a resource was added or removed). This will send a notifications/resources/list_changed message to clients, prompting them to re-fetch the list of resources.

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

Example:

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

Examples

For practical examples of setting up and deploying an MCPServer, see the Deploying an MCPServer Example.

The example at the beginning of this page also demonstrates how to instantiate MCPServer with both tools and agents.