Menu

AI

Mastra MCP Tutorial: MCPClient, Tool Approval and Publishing an MCPServer

Connect MCP servers to Mastra agents with MCPClient, choose static or per-user toolsets, require approvals, lock down hosts, and expose your agents with MCPServer.

By: Irina ShvayaNovember 2, 2026

Mastra MCP Tutorial: MCPClient, Tool Approval and Publishing an MCPServer

This tutorial is part of Build an MCP Server, within The MCP Guide. Want it built for you? See MCP development.

Mastra, the TypeScript agent framework, handles MCP in both directions from one package, @mastra/mcp. MCPClient connects your agents to outside MCP servers. MCPServer publishes your Mastra agents, tools and workflows so Claude, Cursor or any other MCP client can call them.

This tutorial connects a server, wires its tools into an agent, adds approvals and security settings, then exposes a Mastra server. Code follows Mastra's MCP overview. On 2 November 2026 the MCPClient listing snippet ran locally with @mastra/mcp 2.7.0, @mastra/core 1.81.0 and Node.js 24 against the official @modelcontextprotocol/server-everything 2.1.0 test server. Agent and server code was checked against the docs only.

Key Takeaways

  • MCPClient and MCPServer both "speak the MCP 2026-07-28 revision over stdio and Streamable HTTP," per Mastra's docs.
  • Tool names are namespaced by server: in the local run, the server key everything turned echo into everything_echo.
  • Use listTools() for shared, fixed credentials and listToolsets() when each request or user needs different credentials.
  • requireToolApproval can gate every tool on a server or decide per call by name, arguments or annotations.
  • Registered MCPServer instances are served at /api/mcp/:serverId/mcp over Streamable HTTP and work on serverless hosts.

Prerequisites

  • Node.js and an existing Mastra project (or @mastra/core installed).
  • A model provider key. The docs' examples use model strings such as 'openai/gpt-6.1-sol'.
  • An MCP server. For zero setup, npm install @modelcontextprotocol/server-everything gives you a reference server.

Upgrading from @mastra/mcp 1.x? Mastra publishes a v2 migration guide; read it before you copy older examples.

Install

npm install @mastra/mcp@latest

Minimal working example

Create a client with one local server and list its tools:

import { MCPClient } from '@mastra/mcp'

const mcpClient = new MCPClient({
  id: 'demo-client',
  servers: {
    everything: {
      command: 'node',
      args: ['node_modules/@modelcontextprotocol/server-everything/dist/index.js', 'stdio'],
    },
  },
})

try {
  const tools = await mcpClient.listTools()
  console.log(Object.keys(tools))
} finally {
  await mcpClient.disconnect()
}

In the local run this printed 22 tools, named everything_echo, everything_get-annotated-message, everything_get-env and so on. The <server>_<tool> prefix stops two servers with a search tool from colliding, and it is the name your agent and your logs will see.

Give the tools to an agent

The docs' pattern keeps the client in its own module and passes its tools to the agent:

// 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}`,
        },
      },
    },
  },
})
// 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-6.1-sol',
  tools: await mcpClient.listTools(),
})

A server entry with command and args runs over stdio. An entry with url uses Streamable HTTP. For OAuth-protected servers, the docs point to authenticate() for the browser sign-in flow.

Static tools or per-request toolsets

Static toolsRuntime toolsets
Methodawait mcpClient.listTools()await mcpClient.listToolsets()
CredentialsShared by all requestsCan differ per request
Wheretools in the Agent constructortoolsets in generate() or stream()

If your app acts for many users, build a client per request with that user's token and pass its toolsets at call time:

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
}

Do not put one user's token into a shared static client. Every request would act as that user.

Require approval

Set requireToolApproval per server:

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

Or decide per call:

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

The docs say to treat annotations from servers you do not control "as untrusted hints." Our tool annotations guide explains why, and the tool permission planner helps you decide which tools need a human.

Security settings worth turning on

From the docs' security section:

  • stdio environment. Subprocesses inherit only the MCP SDK's curated environment allowlist (such as PATH and HOME), not your full shell. Set inheritDefaultEnv: false to pass only what you list in env.
  • Outbound hosts. Set allowedHosts when server URLs come from untrusted configuration. The default fetch path blocks redirect hops; a custom fetch must enforce its own redirect policy.
  • Tool output. "Tool results are untrusted model input." Mastra's input and output processors can sanitize content before it reaches the model.

Mastra's docs also list hosted registries such as Klavis AI, Composio, Smithery and Apify. Check each one's current status before you rely on it: Smithery shut down its MCP proxy on 31 October 2026 and remains only as a public, non-proxied registry.

Add a tool

For servers you consume, tools come from the server. For your own tools, write a Mastra tool and either give it to the agent directly or publish it through MCPServer below. If a server's schemas are stable, Mastra can generate TypeScript types for them with npx @mastra/mcp generate ./src/mcp/client.ts, which lists schemas "without executing tools."

To use generated types, export the client with an output path and pass the generated interface as a type parameter, as the docs show:

import { MCPClient } from '@mastra/mcp'
import type { MCPServers } from './mcp-types.generated'

export const mcp = new MCPClient<MCPServers>({
  id: 'weather-client',
  servers: { weather: { url: new URL(process.env.WEATHER_MCP_URL!) } },
  typegen: { outFile: 'src/mcp/mcp-types.generated.ts' },
})

The docs warn that tools can be absent at runtime and results can be an error envelope or undefined, so narrow the result before you use it. Regenerate when the server's schemas change.

Publish your own MCP server

MCPServer exposes agents, tools and workflows to any MCP client:

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 it on your Mastra instance with mcpServers: { mcpServer }. The docs say registered servers are served at /api/mcp/:serverId/mcp over Streamable HTTP, and "because every request is self-contained, the same server runs unchanged on long-lived hosts and serverless platforms." Protect HTTP servers with Mastra's OAuth middleware.

To ship a stdio package instead, add an entry file that calls mcpServer.startStdio(), point your package.json bin field at the built file, and publish to npm. Clients then run it with npx -y @your-org/my-mcp-server@1.0.0.

Test with the MCP Inspector

Point the Inspector CLI at your running server:

npx @modelcontextprotocol/inspector --cli http://<your-mastra-host>/api/mcp/my-mcp-server/mcp --transport http --method tools/list

Replace <your-mastra-host> with the host and port your Mastra app runs on. Our MCP Inspector lesson shows tool calls and CI checks.

Pitfalls

  • Forgetting disconnect(). Per-request clients must be closed, or connections pile up.
  • Unprefixed tool names in prompts. Refer to server_tool names, not the raw server names.
  • Shared credentials in static tools. Use listToolsets() for anything user-specific.
  • Copying 1.x examples. Check the v2 migration guide first.

Next step

The free MCP course explains the protocol pieces Mastra wraps. If you want production MCP servers for a Mastra or TypeScript stack, our MCP development team can help design and secure them.

More in the MCP Guide

Free tools: server.json Validator · MCP Error Code Lookup

Latest research: State of MCP: October 2026

Frequently asked questions

Does Mastra support the 2026-07-28 MCP spec?

Yes. Mastra's MCP docs say MCPClient and MCPServer both speak the 2026-07-28 revision over stdio and Streamable HTTP.

Why do my Mastra MCP tool names have a prefix?

Mastra namespaces tools by the server key in your servers config. A server named everything with an echo tool appears as everything_echo, which avoids clashes between servers.

Can a Mastra MCP server pause and ask the user for input?

Yes. A tool can call context.suspend() with a resumeSchema. The server turns that into an input_required round, and clients answer through the inputRequests handler on their server definition.

Can Mastra serve MCP Apps with a UI?

Yes. The docs describe the MCP Apps extension, where tools serve interactive HTML through ui:// resources that Mastra Studio renders in sandboxed iframes.

You Might Also like to Read

Get a FREE Website Audit

Enter your website and get a 20–30 page SEO, GEO, AEO & CRO audit within 24 hours. Completely free.

Don't have a site yet? Get in touch →

Ready to speak with a marketing expert?
Give us a call! (916) 866-7893

Websites Launched
1407
Websites Launched
Marketing Experts
29
Marketing Experts
Hours of Dedicated Work
204533
Hours of Dedicated Work
Irina S.Benjamin G.Jeanette M.David G.