Skip to content

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 ​

typescript
// 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:

typescript
// 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 ​

ApproachAPIScenario
StaticmcpClient.listTools()Fixed config shared across all requests
RuntimemcpClient.listToolsets()Inject不同凭据 per user/request
typescript
// 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 ​

typescript
// 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/mcp provides both client and server capabilities;
  • Static tools via listTools(); per-request credentials via listToolsets() + 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 ​

  1. Connect to a公开 Wikipedia MCP server with MCPClient; have the Agent answer encyclopedia questions.
  2. Expose your refund workflow via MCPServer; invoke it once from Claude Code.
  3. Implement per-user-credential runtime toolsets: two users with different tokens各自查询 their own weather service.

Next: Chapter 15 · Evals: Evaluation System