Chapter 14 · Building an MCP Server
Chapter goals: Review the MCP protocol role division, learn to use MCPServer to expose Mastra's agents/tools/workflows to external systems, and connect Mastra as a client to third-party MCP services.
14.1 Two Roles
Mastra can play双向 roles in the MCP ecosystem (@mastra/mcp package):
- MCPClient — connect to others' MCP servers,接入 their tools into your Agent;
- MCPServer — publish your agents/tools/workflows/prompts/resources as standard MCP interfaces, usable by Claude Code, Cursor, or any MCP client.
14.2 Acting as a Client
// src/mastra/mcp/client.ts
import { MCPClient } from '@mastra/mcp'
export const mcpClient = new MCPClient({
id: 'my-mcp-client',
servers: {
// Local process型 server: launched via command
wikipedia: {
command: 'npx',
args: ['-y', 'wikipedia-mcp'],
},
// Remote HTTP server: with auth headers
weather: {
url: new URL('https://weather.example.com/mcp'),
requestInit: {
headers: { Authorization: `Bearer ${process.env.WEATHER_API_KEY}` },
},
},
},
})Pass tools to the 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 available MCP tools to answer questions; cite sources.',
model: 'openai/gpt-5-mini',
tools: await mcpClient.listTools(), // Static load all tools
})14.3 Static Tools vs Runtime Toolsets
| Approach | API | Scenario |
|---|---|---|
| Static | mcpClient.listTools() | Fixed config shared across all requests |
| Runtime | mcpClient.listToolsets() | Inject不同凭据 per user/request |
// Each user connects to the remote MCP with their own API Key
export async function handleRequest(prompt: string, userKey: string) {
const perUserClient = new MCPClient({
servers: {
weather: {
url: new URL('https://weather.example.com/mcp'),
requestInit: { headers: { Authorization: `Bearer ${userKey}` } },
},
},
})
return assistant.generate(prompt, {
toolsets: await perUserClient.listToolsets(),
})
}OAuth-protected servers use authenticate() to走 the browser authorization flow.
14.4 Publishing Your Own Capabilities as an MCPServer
// src/mastra/index.ts
import { Mastra } from '@mastra/core'
export const mastra = new Mastra({
agents: { support: supportAgent },
workflows: { refundFlow },
})
// Expose via MCPServer (mounted on an独立 HTTP入口)
export const mcpServer = new MCPServer({
id: 'company-mcp',
name: 'Company Tools',
version: '1.0.0',
agents: { support: supportAgent }, // Agents exposed as resources
workflows: { refundFlow }, // Workflows become invocable tools
})After配置, add this URL in Claude Code or Cursor to directly调用 your customer support Agent and refund workflow.
14.5 Chapter Summary
@mastra/mcpprovides both client and server capabilities;- Static tools via
listTools(); per-request credentials vialistToolsets()+toolsets; - MCPServer standardizes暴露 of agents/workflows to the entire MCP ecosystem;
- OAuth scenarios走
authenticate()browser authorization.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What are the respective responsibilities of MCPClient and MCPServer?
2. When each request needs不同凭据 to access an MCP server, what should you use?
3. How is a local command型 MCP server declared in configuration?
4. After exposing a workflow via MCPServer, what can external clients do?
🛠️ Hands-On Exercises
- Connect to a公开 Wikipedia MCP server with MCPClient; have the Agent answer encyclopedia questions.
- Expose your refund workflow via MCPServer; invoke it once from Claude Code.
- Implement per-user-credential runtime toolsets: two users with different tokens各自查询 their own weather service.