Aller au contenu principal

MCPServer

La classe MCPServer permet d’exposer vos outils et agents Mastra existants sous la forme d’un serveur Model Context Protocol (MCP). Tout client MCP (comme Cursor, Windsurf ou Claude Desktop) peut ainsi se connecter à ces fonctionnalités et les mettre à la disposition d’un agent.

Notez que si vous devez uniquement utiliser vos outils ou agents directement dans votre application Mastra, il n’est pas nécessaire de créer un serveur MCP. Cette API sert spécifiquement à exposer vos outils et agents Mastra à des clients MCP externes.

Elle prend en charge les deux transports MCP stdio (sous-processus) et SSE (HTTP).

Constructeur
Lien direct vers Constructeur

Pour créer un nouveau MCPServer, vous devez fournir quelques informations de base sur votre serveur, les outils qu’il proposera et, éventuellement, les agents que vous souhaitez exposer comme outils.

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({
id: 'my-example-agent',
name: 'MyExampleAgent',
description: 'A generalist to help with basic questions.',
instructions: 'You are a helpful assistant.',
model: 'openai/gpt-5.6-sol',
})

const weatherTool = createTool({
id: 'getWeather',
description: 'Gets the current weather for a location.',
inputSchema: z.object({ location: z.string() }),
execute: async inputData => `Weather in ${inputData.location} is sunny.`,
})

const server = new MCPServer({
id: 'my-custom-server',
name: 'My Custom Server',
version: '1.0.0',
description: 'A server that provides weather data and agent capabilities',
instructions:
'Use the available tools to help users with weather information and data processing tasks.',
tools: { weatherTool },
agents: { myAgent }, // this agent will become tool "ask_myAgent"
workflows: {
dataProcessingWorkflow, // this workflow will become tool "run_dataProcessingWorkflow"
},
})

Propriétés de configuration
Lien direct vers Propriétés de configuration

Le constructeur accepte un objet MCPServerConfig doté des propriétés suivantes :

id:

string
Identifiant unique du serveur. Cet ID est conservé lorsque le serveur est enregistré auprès de Mastra et permet de récupérer le serveur via getMCPServerById().

name:

string
Un nom descriptif pour votre serveur (par exemple, 'Mon serveur météo et agents').

version:

string
La version sémantique de votre serveur (par exemple, '1.0.0').

tools:

ToolsInput
Un objet dont les clés sont les noms des outils et les valeurs les définitions d’outils Mastra (créées avec createTool ou le SDK Vercel AI). Ces outils seront directement exposés.

agents?:

Record<string, Agent>
Un objet dont les clés sont les identifiants des agents et les valeurs des instances d’Agent Mastra. Chaque agent sera automatiquement converti en un outil nommé ask_<agentIdentifier>. L’agent **doit** disposer d’une propriété de chaîne description non vide définie dans la configuration de son constructeur. Cette description sera utilisée dans celle de l’outil. Si la description d’un agent est absente ou vide, une erreur sera déclenchée lors de l’initialisation de MCPServer.

workflows?:

Record<string, Workflow>
Un objet dont les clés sont les identifiants des workflows et les valeurs des instances de Workflow Mastra. Chaque workflow est converti en un outil nommé run_<workflowKey>. Le inputSchema du workflow devient le schéma d’entrée de l’outil. Le workflow **doit** disposer d’une propriété de chaîne description non vide, utilisée pour la description de l’outil. Si la description d’un workflow est absente ou vide, une erreur est déclenchée. L’outil exécute le workflow en appelant workflow.createRun(), puis run.start({ inputData: <tool_input> }). Si le nom d’un outil dérivé d’un agent ou d’un workflow (par exemple, ask_myAgent ou run_myWorkflow) entre en conflit avec un nom d’outil défini explicitement ou un autre nom dérivé, l’outil défini explicitement est prioritaire et un avertissement est consigné. Les agents/workflows entraînant des conflits ultérieurs sont ignorés.

description?:

string
Description facultative du rôle du serveur MCP.

instructions?:

string
Instructions facultatives décrivant comment utiliser le serveur et ses fonctionnalités.

mapAuthInfoToUser?:

({ authInfo, extra, requestContext }) => unknown | null | undefined | Promise<unknown | null | undefined>
Convertit les données d’authentification du transport MCP provenant de extra.authInfo en valeur user utilisée par les vérifications FGA de Mastra. Utilisez cette propriété lorsqu’un serveur MCP protégé par OAuth est enregistré sur une instance Mastra dotée d’un fournisseur FGA.

fga?:

{ resourceMapping?: Partial<Record<'tool' | 'tools', { fgaResourceType: string; deriveId?: ({ user, resourceId, requestContext }) => string | undefined }>>; permissionMapping?: Record<string, string> }
Remplace les mappages de ressources et d’autorisations pour les vérifications FGA tools/list et tools/call de ce serveur MCP. Utilisez cette propriété lorsque la portée de l’autorisation MCP doit différer de celle de l’exécution interne des outils d’agents ou de workflows.

repository?:

Repository
Informations facultatives sur le dépôt du code source du serveur.

releaseDate?:

string
Date de publication facultative de cette version du serveur (chaîne ISO 8601). Si elle n’est pas fournie, la date et l’heure d’instanciation sont utilisées par défaut.

isLatest?:

boolean
Indicateur facultatif précisant s’il s’agit de la dernière version. Sa valeur par défaut est true s’il n’est pas fourni.

packageCanonical?:

'npm' | 'docker' | 'pypi' | 'crates' | string
Format canonique de paquet facultatif si le serveur est distribué sous forme de paquet (par exemple, 'npm', 'docker').

packages?:

PackageInfo[]
Liste facultative des paquets installables pour ce serveur.

remotes?:

RemoteInfo[]
Liste facultative des points d’accès distants de ce serveur.

resources?:

MCPServerResources
Un objet définissant la façon dont le serveur doit gérer les ressources MCP. Consultez la section Gestion des ressources pour plus de détails.

prompts?:

MCPServerPrompts
Un objet définissant la façon dont le serveur doit gérer les prompts MCP. Consultez la section Gestion des prompts pour plus de détails.

appResources?:

AppResources
Un mappage d’URI ui:// vers des configurations de ressources d’application. Chaque entrée définit une interface HTML interactive servie via l’extension MCP Apps (SEP-1865). Consultez la section MCP Apps pour plus de détails.

Exposer des agents comme outils
Lien direct vers Exposer des agents comme outils

Une fonctionnalité puissante de MCPServer est sa capacité à exposer automatiquement vos agents Mastra comme outils appelables. Lorsque vous fournissez des agents dans la propriété agents de la configuration :

  • Nom de l’outil : chaque agent est converti en un outil nommé ask_<agentKey>, où <agentKey> est la clé utilisée pour cet agent dans l’objet agents. Par exemple, si vous configurez agents: { myAgentKey: myAgentInstance }, un outil nommé ask_myAgentKey sera créé.

  • Fonctionnement de l’outil :

    • Description : la description de l’outil généré adopte le format suivant : « Poser une question à l’agent <AgentName>. Instructions d’origine de l’agent : <agent description> ».
    • Entrée : l’outil attend un seul argument objet doté d’une propriété message (chaîne) : { message: "Your question for the agent" }.
    • Exécution : lorsque cet outil est appelé, il invoque la méthode generate() de l’agent correspondant avec la query fournie.
    • Sortie : le résultat direct de la méthode generate() de l’agent est renvoyé comme sortie de l’outil.
  • Conflits de noms. Si un outil explicite défini dans la configuration tools porte le même nom qu’un outil dérivé d’un agent (par exemple, un outil nommé ask_myAgentKey associé à un agent dont la clé est myAgentKey), l’outil défini explicitement est prioritaire. Dans ce cas de conflit, l’agent n’est pas converti en outil et un avertissement est consigné.

Les clients MCP peuvent ainsi interagir facilement avec vos agents au moyen de requêtes en langage naturel, comme avec n’importe quel autre outil.

Conversion d’un agent en outil
Lien direct vers Conversion d’un agent en outil

Lorsque vous fournissez des agents dans la propriété de configuration agents, MCPServer crée automatiquement un outil correspondant pour chacun d’eux. Cet outil est nommé ask_<agentIdentifier>, où <agentIdentifier> est la clé utilisée dans l’objet agents.

La description de cet outil généré sera : « Poser une question à l’agent <agent.name>. Description de l’agent : <agent.description> ».

Pour être converti en outil, un agent doit avoir une propriété de chaîne description non vide définie dans sa configuration lors de son instanciation (par exemple, new Agent({ id: 'my-agent', name: 'myAgent', description: 'This agent does X.', ... })). Si un agent transmis à MCPServer possède une description absente ou vide, une erreur est déclenchée lors de l’instanciation de MCPServer et la configuration du serveur échoue.

Vous pouvez ainsi exposer rapidement les capacités génératives de vos agents via MCP, afin que les clients puissent leur poser directement des questions.

Accéder au contexte MCP dans les outils
Lien direct vers Accéder au contexte MCP dans les outils

Les outils exposés via MCPServer peuvent accéder au contexte de la requête MCP (authentification, ID de session, etc.) au moyen de deux propriétés différentes selon la manière dont l’outil est invoqué :

Mode d’appelMéthode d’accès
Appel direct de l’outilcontext?.mcp?.extra
Appel de l’outil par un agentcontext?.requestContext?.get("mcp.extra")

Approche universelle (fonctionne dans les deux contextes) :

const mcpExtra = context?.mcp?.extra ?? context?.requestContext?.get('mcp.extra')
const authInfo = mcpExtra?.authInfo

Exemple : outil fonctionnant dans les deux contextes
Lien direct vers Exemple : outil fonctionnant dans les deux contextes

import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

const fetchUserData = createTool({
id: 'fetchUserData',
description: 'Fetches user data using authentication from MCP context',
inputSchema: z.object({
userId: z.string().describe('The ID of the user to fetch'),
}),
execute: async (inputData, context) => {
// Access MCP authentication context
// When called directly via MCP: context.mcp.extra
// When called via agent: context.requestContext.get('mcp.extra')
const mcpExtra = context?.mcp?.extra || context?.requestContext?.get('mcp.extra')
const authInfo = mcpExtra?.authInfo

if (!authInfo?.token) {
throw new Error('Authentication required')
}

const response = await fetch(`https://api.example.com/users/${inputData.userId}`, {
headers: {
Authorization: `Bearer ${authInfo.token}`,
},
})

return response.json()
},
})

Méthodes
Lien direct vers Méthodes

Voici les fonctions que vous pouvez appeler sur une instance de MCPServer pour contrôler son comportement et obtenir des informations.

startStdio()
Lien direct vers startstdio

Utilisez cette méthode pour démarrer le serveur afin qu’il communique via l’entrée et la sortie standard (stdio). C’est le mode habituel lorsque le serveur s’exécute comme programme en ligne de commande.

async startStdio(): Promise<void>

Voici comment démarrer le serveur avec stdio :

const server = new MCPServer({
id: 'my-server',
name: 'My Server',
version: '1.0.0',
tools: {/* ... */},
})
await server.startStdio()

startSSE()
Lien direct vers startsse

Cette méthode facilite l’intégration du serveur MCP à un serveur web existant afin d’utiliser les événements envoyés par le serveur (SSE) pour communiquer. Appelez-la depuis le code de votre serveur web lorsqu’il reçoit une requête destinée aux chemins SSE ou de messages.

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

Voici un exemple d’utilisation de startSSE dans un gestionnaire de requêtes de serveur HTTP. Dans cet exemple, un client MCP peut se connecter à votre serveur MCP à l’adresse 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}`)
})

Voici le détail des valeurs requises par la méthode startSSE :

url:

URL
L’adresse web demandée par l’utilisateur.

ssePath:

string
La partie précise de l’URL à laquelle les clients se connecteront pour le SSE (par exemple, '/sse').

messagePath:

string
La partie précise de l’URL à laquelle les clients enverront des messages (par exemple, '/message').

req:

any
L’objet de requête entrante provenant de votre serveur web.

res:

any
L’objet de réponse de votre serveur web, utilisé pour renvoyer des données.

startHonoSSE()
Lien direct vers starthonosse

Cette méthode facilite l’intégration du serveur MCP à un serveur web existant afin d’utiliser les événements envoyés par le serveur (SSE) pour communiquer. Appelez-la depuis le code de votre serveur web lorsqu’il reçoit une requête destinée aux chemins SSE ou de messages.

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

Voici un exemple d’utilisation de startHonoSSE dans un gestionnaire de requêtes de serveur HTTP. Dans cet exemple, un client MCP peut se connecter à votre serveur MCP à l’adresse 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}`)
})

Voici le détail des valeurs requises par la méthode startHonoSSE :

url:

URL
L’adresse web demandée par l’utilisateur.

ssePath:

string
La partie précise de l’URL à laquelle les clients se connecteront pour le SSE (par exemple, '/hono-sse').

messagePath:

string
La partie précise de l’URL à laquelle les clients enverront des messages (par exemple, '/message').

req:

any
L’objet de requête entrante provenant de votre serveur web.

res:

any
L’objet de réponse de votre serveur web, utilisé pour renvoyer des données.

startHTTP()
Lien direct vers starthttp

Cette méthode facilite l’intégration du serveur MCP à un serveur web existant afin d’utiliser le transport HTTP diffusable pour communiquer. Appelez-la depuis le code de votre serveur web lorsqu’il reçoit des requêtes HTTP.

async startHTTP({
url,
httpPath,
req,
res,
options = { sessionIdGenerator: () => randomUUID() },
}: {
url: URL;
httpPath: string;
req: http.IncomingMessage;
res: http.ServerResponse<http.IncomingMessage>;
options?: StreamableHTTPServerTransportOptions;
}): Promise<void>

Voici un exemple d’utilisation de startHTTP dans un gestionnaire de requêtes de serveur HTTP. Dans cet exemple, un client MCP peut se connecter à votre serveur MCP à l’adresse 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'),
httpPath: `/mcp`,
req,
res,
options: {
sessionIdGenerator: () => randomUUID(),
},
})
})

httpServer.listen(PORT, () => {
console.log(`HTTP server listening on port ${PORT}`)
})

Pour les environnements sans serveur (Supabase Edge Functions, Cloudflare Workers, Vercel Edge, etc.), utilisez serverless: true afin d’activer le fonctionnement sans état :

// Supabase Edge Function example
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { MCPServer } from '@mastra/mcp'
// Note: You will need to convert req/res format from Deno to Node
import { toReqRes, toFetchResponse } from 'fetch-to-node'

const server = new MCPServer({
id: 'my-serverless-mcp',
name: 'My Serverless MCP',
version: '1.0.0',
tools: {/* your tools */},
})

serve(async req => {
const url = new URL(req.url)

if (url.pathname === '/mcp') {
// Convert Deno Request to Node.js-compatible format
const { req: nodeReq, res: nodeRes } = toReqRes(req)

await server.startHTTP({
url,
httpPath: '/mcp',
req: nodeReq,
res: nodeRes,
options: {
serverless: true, // ← Enable stateless mode for serverless
},
})

return toFetchResponse(nodeRes)
}

return new Response('Not found', { status: 404 })
})
Quand utiliser serverless: true

Utilisez serverless: true lors d’un déploiement dans des environnements où chaque requête s’exécute dans un nouveau contexte d’exécution sans état :

  • Supabase Edge Functions
  • Cloudflare Workers
  • Vercel Edge Functions
  • Netlify Edge Functions
  • AWS Lambda
  • Deno Deploy

Utilisez le mode par défaut fondé sur les sessions (sans serverless: true) pour :

  • Les serveurs Node.js de longue durée
  • Les conteneurs Docker
  • L’hébergement traditionnel (VPS, serveurs dédiés)

Le mode sans serveur désactive la gestion des sessions et crée de nouvelles instances de serveur pour chaque requête. Ce fonctionnement est nécessaire dans les environnements sans état où la mémoire n’est pas conservée entre les invocations.

Par défaut, le mode sans serveur met chaque requête en mémoire tampon dans une réponse JSON unique. Les notifications/progress envoyées par un outil n’atteignent donc jamais le client. Définissez serverlessStreaming: true pour traiter plutôt la requête avec un flux SSE limité à celle-ci, ce qui transmet les notifications de progression avant le résultat final :

await server.startHTTP({
url,
httpPath: '/mcp',
req: nodeReq,
res: nodeRes,
options: {
serverless: true,
serverlessStreaming: true, // ← Stream request-scoped notifications/progress
},
})

Le fonctionnement reste sans état : aucun mcp-session-id n’est requis ni conservé. Seules les notifications limitées à la requête en cours (comme la progression) sont activées. Les fonctionnalités ci-dessous, qui dépendent d’une session, restent indisponibles.

Les fonctionnalités MCP suivantes nécessitent un état de session ou des connexions persistantes et ne fonctionnent pas en mode sans serveur (y compris avec serverlessStreaming: true) :

  • Élicitation - Les demandes interactives de saisie utilisateur pendant l’exécution d’un outil nécessitent une gestion des sessions afin d’acheminer les réponses vers le bon client
  • Abonnements aux ressources - resources/subscribe et resources/unsubscribe nécessitent des connexions persistantes pour conserver l’état des abonnements
  • Notifications de mise à jour des ressources - resources.notifyUpdated() nécessite des abonnements actifs et des connexions persistantes pour avertir les clients
  • Notifications de modification de la liste des prompts - prompts.notifyListChanged() nécessite des connexions persistantes pour transmettre les mises à jour aux clients
  • Notifications de modification de la liste des outils - toolActions.notifyListChanged() nécessite des connexions persistantes pour transmettre les mises à jour aux clients
  • Notifications des journaux du serveur - sendLoggingMessage() nécessite des connexions persistantes pour transmettre les messages de journal aux clients

Ces fonctionnalités opèrent normalement dans les environnements de serveur de longue durée (serveurs Node.js, conteneurs Docker, etc.).

Voici le détail des valeurs requises par la méthode startHTTP :

url:

URL
L’adresse web demandée par l’utilisateur.

httpPath:

string
La partie précise de l’URL à laquelle le serveur MCP traitera les requêtes HTTP (par exemple, '/mcp').

req:

http.IncomingMessage
L’objet de requête entrante provenant de votre serveur web.

res:

http.ServerResponse
L’objet de réponse de votre serveur web, utilisé pour renvoyer des données.

options:

StreamableHTTPServerTransportOptions
Configuration facultative du transport HTTP. Consultez le tableau des options ci-dessous pour plus de détails.

L’objet StreamableHTTPServerTransportOptions vous permet de personnaliser le comportement du transport HTTP. Voici les options disponibles :

serverless:

boolean
Si true, s’exécute en mode sans état et sans gestion des sessions. Chaque requête est traitée indépendamment par une nouvelle instance de serveur. Cette option est indispensable aux environnements sans serveur (Cloudflare Workers, Supabase Edge Functions, Vercel Edge, etc.) dans lesquels les sessions ne peuvent pas persister entre les invocations. Sa valeur par défaut est false.

serverlessStreaming:

boolean
Si true, les requêtes sans serveur utilisent un flux SSE limité à la requête au lieu d’une réponse JSON mise en mémoire tampon. Les notifications/progress émises pendant la requête peuvent ainsi atteindre le client avant le résultat final. Cette option ne prend effet qu’avec serverless: true. Sa valeur par défaut est false (réponses JSON mises en mémoire tampon), ce qui préserve la rétrocompatibilité. Elle active uniquement les notifications limitées à la requête, comme la progression ; l’élicitation, les abonnements et les notifications hors requête nécessitent toujours un état de session.

sessionIdGenerator:

(() => string) | undefined
Une fonction qui génère un ID de session unique. Il doit s’agir d’une chaîne globalement unique et sécurisée par cryptographie. Renvoyez undefined pour désactiver la gestion des sessions.

onsessioninitialized:

(sessionId: string) => void
Une fonction de rappel invoquée lorsqu’une nouvelle session est initialisée. Elle permet de suivre les sessions MCP actives.

enableJsonResponse:

boolean
Si true, le serveur renvoie des réponses JSON simples au lieu d’utiliser les événements envoyés par le serveur (SSE) pour la diffusion. Sa valeur par défaut est false.

eventStore:

EventStore
Un magasin d’événements assurant la reprise des messages. Le fournir permet aux clients de se reconnecter et de reprendre les flux de messages.

close()
Lien direct vers close

Cette méthode ferme le serveur et libère toutes les ressources.

async close(): Promise<void>

getServerInfo()
Lien direct vers getserverinfo

Cette méthode renvoie les informations de base du serveur.

getServerInfo(): ServerInfo

getServerDetail()
Lien direct vers getserverdetail

Cette méthode renvoie les informations détaillées du serveur.

getServerDetail(): ServerDetail

getToolListInfo()
Lien direct vers gettoollistinfo

Cette méthode renvoie les outils configurés lors de la création du serveur. Il s’agit d’une liste en lecture seule, utile pour le débogage.

getToolListInfo(): ToolListInfo

getToolInfo()
Lien direct vers gettoolinfo

Cette méthode renvoie les détails d’un outil précis.

getToolInfo(toolName: string): ToolInfo

executeTool()
Lien direct vers executetool

Cette méthode exécute un outil précis et renvoie le résultat.

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

getStdioTransport()
Lien direct vers getstdiotransport

Si vous avez démarré le serveur avec startStdio(), cette méthode permet d’obtenir l’objet qui gère la communication stdio. Elle sert principalement aux vérifications internes ou aux tests.

getStdioTransport(): StdioServerTransport | undefined

getSseTransport()
Lien direct vers getssetransport

Si vous avez démarré le serveur avec startSSE(), cette méthode permet d’obtenir l’objet qui gère la communication SSE. Comme getStdioTransport, elle sert principalement aux vérifications internes ou aux tests.

getSseTransport(): SSEServerTransport | undefined

getSseHonoTransport()
Lien direct vers getssehonotransport

Si vous avez démarré le serveur avec startHonoSSE(), cette méthode permet d’obtenir l’objet qui gère la communication SSE. Comme getSseTransport, elle sert principalement aux vérifications internes ou aux tests.

getSseHonoTransport(): SSETransport | undefined

getStreamableHTTPTransport()
Lien direct vers getstreamablehttptransport

Si vous avez démarré le serveur avec startHTTP(), cette méthode permet d’obtenir l’objet qui gère la communication HTTP. Comme getSseTransport, elle sert principalement aux vérifications internes ou aux tests.

getStreamableHTTPTransport(): StreamableHTTPServerTransport | undefined

tools()
Lien direct vers tools

Exécute un outil précis fourni par ce serveur MCP.

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

toolId:

string
L’ID/nom de l’outil à exécuter.

args:

any
Les arguments à transmettre à la fonction d’exécution de l’outil.

executionContext?:

object
Contexte facultatif de l’exécution de l’outil, comme des messages ou un toolCallId.

Gestion des ressources
Lien direct vers Gestion des ressources

Que sont les ressources MCP ?
Lien direct vers Que sont les ressources MCP ?

Les ressources constituent une primitive fondamentale du Model Context Protocol (MCP). Elles permettent aux serveurs d’exposer des données et du contenu que les clients peuvent lire et utiliser comme contexte pour leurs interactions avec les LLM. Elles représentent tout type de données qu’un serveur MCP souhaite mettre à disposition, notamment :

  • Le contenu de fichiers
  • Des enregistrements de base de données
  • Des réponses d’API
  • Des données système en temps réel
  • Des captures d’écran et des images
  • Des fichiers journaux

Les ressources sont identifiées par des URI uniques (par exemple, file:///home/user/documents/report.pdf, postgres://database/customers/schema) et peuvent contenir du texte (encodé en UTF-8) ou des données binaires (encodées en base64).

Les clients peuvent découvrir les ressources de deux façons :

  1. Ressources directes : les serveurs exposent une liste de ressources concrètes via un point de terminaison resources/list.
  2. Modèles de ressources : pour les ressources définies à l’exécution, les serveurs peuvent exposer des modèles d’URI (RFC 6570) que les clients utilisent pour construire les URI des ressources.

Pour lire une ressource, les clients envoient une requête resources/read avec son URI. Les serveurs peuvent aussi avertir les clients des modifications de la liste des ressources (notifications/resources/list_changed) ou des mises à jour du contenu d’une ressource précise (notifications/resources/updated) si un client s’y est abonné.

Pour en savoir plus, consultez la documentation MCP officielle sur les ressources.

Type MCPServerResources
Lien direct vers mcpserverresources-type

L’option resources accepte un objet de type MCPServerResources. Ce type définit les fonctions de rappel que votre serveur utilisera pour traiter les requêtes de ressources :

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 }

Exemple :

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

Informer les clients des modifications de ressources
Lien direct vers Informer les clients des modifications de ressources

Si les ressources disponibles ou leur contenu changent, votre serveur peut en informer les clients connectés qui sont abonnés à la ressource concernée.

server.resources.notifyUpdated({ uri: string })
Lien direct vers serverresourcesnotifyupdated-uri-string-

Appelez cette méthode lorsque le contenu d’une ressource précise (identifiée par son uri) a été mis à jour. Les clients abonnés à cet URI recevront un message notifications/resources/updated.

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

Exemple :

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

server.resources.notifyListChanged()
Lien direct vers serverresourcesnotifylistchanged

Appelez cette méthode lorsque la liste des ressources disponibles a changé (par exemple, après l’ajout ou la suppression d’une ressource). Un message notifications/resources/list_changed est alors envoyé aux clients pour les inviter à récupérer de nouveau la liste des ressources.

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

Exemple :

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

Gestion des prompts
Lien direct vers Gestion des prompts

Que sont les prompts MCP ?
Lien direct vers Que sont les prompts MCP ?

Les prompts sont des modèles ou des workflows réutilisables que les serveurs MCP exposent aux clients. Ils peuvent accepter des arguments et inclure le contexte de ressources. Ils prennent également en charge le versionnage et standardisent les interactions avec les LLM.

Les prompts sont identifiés par un nom unique (et une version facultative) et peuvent être statiques ou définis à l’exécution.

Type MCPServerPrompts
Lien direct vers mcpserverprompts-type

L’option prompts accepte un objet de type MCPServerPrompts. Ce type définit les fonctions de rappel que votre serveur utilisera pour traiter les requêtes de prompts :

export type MCPServerPrompts = {
// Callback to list available prompts
listPrompts: () => Promise<Prompt[]>

// Callback to get the messages/content for a specific prompt
getPromptMessages?: ({
name,
version,
args,
}: {
name: string
version?: string
args?: any
}) => Promise<{ prompt: Prompt; messages: PromptMessage[] }>
}

Exemple :

import { MCPServer } from '@mastra/mcp'
import type { Prompt, PromptMessage, MCPServerPrompts } from '@mastra/mcp'

const prompts: Prompt[] = [
{
name: 'analyze-code',
description: 'Analyze code for improvements',
version: 'v1',
},
{
name: 'analyze-code',
description: 'Analyze code for improvements (new logic)',
version: 'v2',
},
]

const myPromptHandlers: MCPServerPrompts = {
listPrompts: async () => prompts,
getPromptMessages: async ({ name, version, args }) => {
if (name === 'analyze-code') {
if (version === 'v2') {
const prompt = prompts.find(p => p.name === name && p.version === 'v2')
if (!prompt) throw new Error('Prompt version not found')
return {
prompt,
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Analyze this code with the new logic: ${args.code}`,
},
},
],
}
}
// Default or v1
const prompt = prompts.find(p => p.name === name && p.version === 'v1')
if (!prompt) throw new Error('Prompt version not found')
return {
prompt,
messages: [
{
role: 'user',
content: { type: 'text', text: `Analyze this code: ${args.code}` },
},
],
}
}
throw new Error('Prompt not found')
},
}

const serverWithPrompts = new MCPServer({
id: 'promptful-server',
name: 'Promptful Server',
version: '1.0.0',
tools: {/* ... */},
prompts: myPromptHandlers,
})

Informer les clients des modifications de prompts
Lien direct vers Informer les clients des modifications de prompts

Si les prompts disponibles changent, votre serveur peut en informer les clients connectés :

server.prompts.notifyListChanged()
Lien direct vers serverpromptsnotifylistchanged

Appelez cette méthode lorsque la liste des prompts disponibles a changé (par exemple, après l’ajout ou la suppression d’un prompt). Un message notifications/prompts/list_changed est alors envoyé aux clients pour les inviter à récupérer de nouveau la liste des prompts.

await serverWithPrompts.prompts.notifyListChanged()

Bonnes pratiques de gestion des prompts
Lien direct vers Bonnes pratiques de gestion des prompts

  • Utilisez des noms et des descriptions de prompts clairs et explicites.
  • Validez tous les arguments requis dans getPromptMessages.
  • Incluez un champ version si vous prévoyez d’apporter des modifications incompatibles.
  • Utilisez le paramètre version pour sélectionner la logique de prompt appropriée.
  • Informez les clients lorsque les listes de prompts changent.
  • Gérez les erreurs avec des messages instructifs.
  • Documentez les arguments attendus et les versions disponibles.

Gestion dynamique des outils
Lien direct vers Gestion dynamique des outils

Les outils sont généralement fournis lors de la construction de MCPServer, mais vous pouvez aussi en ajouter ou en supprimer pendant l’exécution du serveur. Celui-ci expose ces opérations via la propriété toolActions. Lorsque la liste des outils change, les clients connectés reçoivent un message notifications/tools/list_changed les invitant à la récupérer de nouveau.

Cette propriété est nommée toolActions, car tools() est la méthode qui renvoie le registre des outils enregistrés.

toolActions.add(tools)
Lien direct vers toolactionsaddtools

Enregistre de nouveaux outils sur le serveur en cours d’exécution et en informe les clients connectés. Les outils sont indexés par la clé de leur enregistrement, comme ceux transmis au constructeur. L’ajout d’un outil sous une clé existante le remplace.

async server.toolActions.add(tools: ToolsInput): Promise<void>

Exemple :

import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

const searchTool = createTool({
id: 'search',
description: 'Searches the knowledge base.',
inputSchema: z.object({ query: z.string() }),
execute: async ({ query }) => ({ results: [] }),
})

await server.toolActions.add({ searchTool })

toolActions.remove(toolIds)
Lien direct vers toolactionsremovetoolids

Supprime des outils du serveur en cours d’exécution selon leur ID et en informe les clients connectés. Les ID d’outils inconnus sont ignorés. Une notification est envoyée uniquement si au moins un outil est supprimé.

async server.toolActions.remove(toolIds: string[]): Promise<void>

Exemple :

await server.toolActions.remove(['searchTool'])

toolActions.notifyListChanged()
Lien direct vers toolactionsnotifylistchanged

Envoie un message notifications/tools/list_changed aux clients connectés sans modifier le registre des outils. Appelez cette méthode lorsque la disponibilité des outils change par un autre moyen (par exemple, à la suite de modifications des autorisations).

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

Synchronisation du registre Mastra
Lien direct vers Synchronisation du registre Mastra

Lorsque le serveur est enregistré auprès d’une instance Mastra, toolActions.add() et toolActions.remove() mettent également à jour le registre des outils de cette instance, comme le fait l’enregistrement automatique au démarrage. Les outils ajoutés deviennent accessibles via mastra.listTools() (indexés par leur id intrinsèque lorsqu’il existe), tandis que les outils supprimés sont retirés du registre.

Journalisation
Lien direct vers Journalisation

Les serveurs MCP peuvent envoyer des messages de journal structurés aux clients avec notifications/message. Les clients contrôlent le niveau de détail en envoyant une requête logging/setLevel. Le serveur ignore les messages inférieurs au niveau minimal demandé (selon l’ordre de gravité de la RFC 5424). Le niveau est suivi par session, ce qui permet à chaque client de demander un niveau de détail différent.

sendLoggingMessage()
Lien direct vers sendloggingmessage

Envoie une notification de journal à tous les clients connectés en respectant le niveau minimal de journalisation de chacun.

async server.sendLoggingMessage(params: {
level: LoggingLevel;
data: unknown;
logger?: string;
}): Promise<void>

Exemple :

await server.sendLoggingMessage({
level: 'info',
data: { message: 'Sync completed', itemsProcessed: 42 },
})

context.mcp.log()
Lien direct vers contextmcplog

Dans la fonction execute d’un outil, utilisez context.mcp.log() pour envoyer un message de journal au client qui a appelé l’outil.

async context.mcp.log(
level: LoggingLevel,
message: string,
data?: Record<string, unknown>
): Promise<void>

Exemple :

execute: async ({ location }, context) => {
await context.mcp.log('debug', 'Fetching weather', { location })
const weather = await fetchWeather(location)
await context.mcp.log('info', 'Weather fetched')
return weather
}

Notifications de progression
Lien direct vers Notifications de progression

Les outils de longue durée peuvent signaler leur progression au client appelant avec notifications/progress. La progression est envoyée uniquement si l’appelant a demandé son suivi en incluant un progressToken dans la requête (le MCPClient de Mastra le fait lorsque enableProgressTracking est défini). Si aucun token n’a été envoyé, context.mcp.progress() n’effectue aucune opération.

context.mcp.progress()
Lien direct vers contextmcpprogress

async context.mcp.progress(params: {
progress: number;
total?: number;
message?: string;
}): Promise<void>

Exemple :

execute: async ({ items }, context) => {
for (const [index, item] of items.entries()) {
await processItem(item)
await context.mcp.progress({
progress: index + 1,
total: items.length,
message: `Processed ${item.name}`,
})
}
return { done: true }
}

Transmission des notifications
Lien direct vers Transmission des notifications

Les méthodes de notification (resources.notifyListChanged(), prompts.notifyListChanged(), toolActions.notifyListChanged() et sendLoggingMessage()) diffusent leurs messages à tous les clients connectés, quel que soit le transport : la connexion stdio/SSE et chaque session HTTP diffusable. resources.notifyUpdated() constitue l’exception : elle avertit uniquement les clients abonnés à l’URI de la ressource via resources/subscribe. Les abonnements sont suivis par session pour les clients HTTP diffusables ; les anciens clients SSE partagent l’instance principale du serveur et donc un même ensemble d’abonnements. Les clients qui utilisent le mode sans serveur et sans état ne peuvent pas recevoir de notifications, car chaque requête emploie une instance de serveur temporaire.

Exemples
Lien direct vers Exemples

Pour des exemples pratiques de configuration et de déploiement d’un MCPServer, consultez le guide de publication d’un serveur MCP.

L’exemple présenté au début de cette page montre également comment instancier MCPServer avec des outils et des agents.

Élicitation
Lien direct vers Élicitation

Qu’est-ce que l’élicitation ?
Lien direct vers Qu’est-ce que l’élicitation ?

L’élicitation est une fonctionnalité du Model Context Protocol (MCP) qui permet aux serveurs de demander des informations structurées aux utilisateurs. Elle prend en charge les workflows interactifs dans lesquels les serveurs peuvent recueillir des données supplémentaires à l’exécution.

La classe MCPServer intègre automatiquement des fonctionnalités d’élicitation. Les outils reçoivent un objet context.mcp dans leur fonction execute, comprenant une méthode elicitation.sendRequest() destinée à demander une saisie à l’utilisateur.

Signature d’exécution des outils
Lien direct vers Signature d’exécution des outils

Lorsque des outils sont exécutés dans le contexte d’un serveur MCP, ils reçoivent des fonctionnalités propres à MCP via l’objet context.mcp :

execute: async (inputData, context) => {
// input contains the tool's inputData parameters
// context.mcp contains server capabilities like elicitation and authentication info

// Access authentication information (when available)
if (context.mcp?.extra?.authInfo) {
console.log('Authenticated request from:', context.mcp.extra.authInfo.clientId)
}

// Use elicitation capabilities
const result = await context.mcp.elicitation.sendRequest({
message: 'Please provide information',
requestedSchema: {/* schema */},
})

return result
}

Fonctionnement de l’élicitation
Lien direct vers Fonctionnement de l’élicitation

L’élicitation est couramment utilisée pendant l’exécution d’un outil. Lorsqu’un outil a besoin d’une saisie utilisateur, il peut employer la fonctionnalité d’élicitation fournie par le paramètre de contexte :

  1. L’outil appelle context.mcp.elicitation.sendRequest() avec un message et un schéma
  2. La requête est envoyée au client MCP connecté
  3. Le client présente la requête à l’utilisateur (via une interface, la ligne de commande, etc.)
  4. L’utilisateur fournit une saisie, refuse ou annule la requête
  5. Le client renvoie la réponse au serveur
  6. L’outil reçoit la réponse et poursuit son exécution

Utiliser l’élicitation dans les outils
Lien direct vers Utiliser l’élicitation dans les outils

Voici un exemple d’outil qui utilise l’élicitation pour recueillir les coordonnées de l’utilisateur :

import { MCPServer } from '@mastra/mcp'
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

const server = new MCPServer({
id: 'interactive-server',
name: 'Interactive Server',
version: '1.0.0',
tools: {
collectContactInfo: createTool({
id: 'collectContactInfo',
description: 'Collects user contact information through elicitation',
inputSchema: z.object({
reason: z.string().optional().describe('Reason for collecting contact info'),
}),
execute: async (inputData, context) => {
const { reason } = inputData

// Log session info if available
console.log('Request from session:', context.mcp?.extra?.sessionId)

try {
// Request user input via elicitation
const result = await context.mcp.elicitation.sendRequest({
message: reason
? `Please provide your contact information. ${reason}`
: 'Please provide your contact information',
requestedSchema: {
type: 'object',
properties: {
name: {
type: 'string',
title: 'Full Name',
description: 'Your full name',
},
email: {
type: 'string',
title: 'Email Address',
description: 'Your email address',
format: 'email',
},
phone: {
type: 'string',
title: 'Phone Number',
description: 'Your phone number (optional)',
},
},
required: ['name', 'email'],
},
})

// Handle the user's response
if (result.action === 'accept') {
return `Contact information collected: ${JSON.stringify(result.content, null, 2)}`
} else if (result.action === 'decline') {
return 'Contact information collection was declined by the user.'
} else {
return 'Contact information collection was cancelled by the user.'
}
} catch (error) {
return `Error collecting contact information: ${error}`
}
},
}),
},
})

Schéma d’une requête d’élicitation
Lien direct vers Schéma d’une requête d’élicitation

Le requestedSchema doit être un objet plat contenant uniquement des propriétés primitives. Les types pris en charge comprennent :

  • Chaîne : { type: 'string', title: 'Display Name', description: 'Help text' }
  • Nombre : { type: 'number', minimum: 0, maximum: 100 }
  • Booléen : { type: 'boolean', default: false }
  • Énumération : { type: 'string', enum: ['option1', 'option2'] }

Exemple de schéma :

{
type: 'object',
properties: {
name: {
type: 'string',
title: 'Full Name',
description: 'Your complete name',
},
age: {
type: 'number',
title: 'Age',
minimum: 18,
maximum: 120,
},
newsletter: {
type: 'boolean',
title: 'Subscribe to Newsletter',
default: false,
},
},
required: ['name'],
}

Actions de réponse
Lien direct vers Actions de réponse

Les utilisateurs peuvent répondre aux requêtes d’élicitation de trois façons :

  1. Accepter (action: 'accept') : l’utilisateur a fourni des données et confirmé leur envoi
    • Contient un champ content avec les données envoyées
  2. Refuser (action: 'decline') : l’utilisateur a explicitement refusé de fournir des informations
    • Aucun champ de contenu
  3. Annuler (action: 'cancel') : l’utilisateur a fermé la requête sans prendre de décision
    • Aucun champ de contenu

Les outils doivent gérer correctement les trois types de réponses.

Considérations de sécurité
Lien direct vers Considérations de sécurité

  • Ne demandez jamais d’informations sensibles, comme des mots de passe, des numéros de sécurité sociale ou des numéros de carte bancaire
  • Validez toutes les saisies utilisateur par rapport au schéma fourni
  • Gérez correctement les refus et les annulations
  • Expliquez clairement les raisons de la collecte des données
  • Respectez la vie privée et les préférences des utilisateurs

API d’exécution des outils
Lien direct vers API d’exécution des outils

La fonctionnalité d’élicitation est accessible via le paramètre options lors de l’exécution d’un outil :

// Within a tool's execute function
execute: async (inputData, context) => {
// Use elicitation for user input
const result = await context.mcp.elicitation.sendRequest({
message: string, // Message to display to user
requestedSchema: object // JSON schema defining expected response structure
}): Promise<ElicitResult>

// Access authentication info if needed
if (context.mcp?.extra?.authInfo) {
// Use context.mcp.extra.authInfo.token, etc.
}
}

Avec les transports HTTP (SSE ou HTTP), l’élicitation tient compte des sessions. Lorsque plusieurs clients sont connectés au même serveur, les requêtes d’élicitation sont acheminées vers la session cliente qui a lancé l’exécution de l’outil.

Le type ElicitResult :

type ElicitResult = {
action: 'accept' | 'decline' | 'cancel'
content?: any // Only present when action is 'accept'
}

Protection OAuth
Lien direct vers Protection OAuth

Pour protéger votre serveur MCP par une authentification OAuth conforme à la spécification d’authentification MCP, utilisez la fonction createOAuthMiddleware :

import http from 'node:http'
import { MCPServer, createOAuthMiddleware, createStaticTokenValidator } from '@mastra/mcp'

const mcpServer = new MCPServer({
id: 'protected-server',
name: 'Protected MCP Server',
version: '1.0.0',
tools: {/* your tools */},
})

// Create OAuth middleware
const oauthMiddleware = createOAuthMiddleware({
oauth: {
resource: 'https://mcp.example.com/mcp',
authorizationServers: ['https://auth.example.com'],
scopesSupported: ['mcp:read', 'mcp:write'],
resourceName: 'My Protected MCP Server',
validateToken: createStaticTokenValidator(['allowed-token-1']),
},
mcpPath: '/mcp',
})

// Create HTTP server with OAuth protection
const httpServer = http.createServer(async (req, res) => {
const url = new URL(req.url || '', 'https://mcp.example.com')

// Apply OAuth middleware first
const result = await oauthMiddleware(req, res, url)
if (!result.proceed) return // Middleware handled response (401, metadata, etc.)

// Token is valid, proceed to MCP handler
await mcpServer.startHTTP({ url, httpPath: '/mcp', req, res })
})

httpServer.listen(3000)

Le middleware effectue automatiquement les opérations suivantes :

  • Sert les métadonnées de ressource protégée à l’adresse /.well-known/oauth-protected-resource (RFC 9728)
  • Renvoie 401 Unauthorized avec les en-têtes WWW-Authenticate appropriés lorsqu’une authentification est requise
  • Valide les tokens bearer au moyen du validateur fourni

Validation des tokens
Lien direct vers Validation des tokens

En production, utilisez une validation appropriée des tokens :

import { createOAuthMiddleware, createIntrospectionValidator } from '@mastra/mcp'

// Option 1: Token introspection (RFC 7662)
const middleware = createOAuthMiddleware({
oauth: {
resource: 'https://mcp.example.com/mcp',
authorizationServers: ['https://auth.example.com'],
validateToken: createIntrospectionValidator('https://auth.example.com/oauth/introspect', {
clientId: 'mcp-server',
clientSecret: 'secret',
}),
},
})

// Option 2: Custom validation (JWT, database lookup, etc.)
const customMiddleware = createOAuthMiddleware({
oauth: {
resource: 'https://mcp.example.com/mcp',
authorizationServers: ['https://auth.example.com'],
validateToken: async (token, resource) => {
const decoded = await verifyJWT(token)
if (!decoded) {
return { valid: false, error: 'invalid_token' }
}
return {
valid: true,
scopes: decoded.scope?.split(' ') || [],
subject: decoded.sub,
}
},
},
})

Options du middleware OAuth
Lien direct vers Options du middleware OAuth

oauth.resource:

string
L’URL canonique de votre serveur MCP. Elle est renvoyée dans les métadonnées de ressource protégée.

oauth.authorizationServers:

string[]
URL des serveurs d’autorisation qui peuvent émettre des tokens pour cette ressource.

oauth.scopesSupported?:

string[]
= ['mcp:read', 'mcp:write']
Portées prises en charge par ce serveur MCP.

oauth.resourceName?:

string
Nom lisible par l’utilisateur de ce serveur de ressources.

oauth.validateToken?:

(token: string, resource: string) => Promise<TokenValidationResult>
Fonction permettant de valider les tokens d’accès. Si elle n’est pas fournie, les tokens sont acceptés sans validation (NON recommandé en production).

mcpPath?:

string
= '/mcp'
Chemin sur lequel le point de terminaison MCP est servi. Seules les requêtes adressées à ce chemin nécessitent une authentification.

Contexte d’authentification
Lien direct vers Contexte d’authentification

Avec les transports HTTP, les outils peuvent accéder aux métadonnées des requêtes via context.mcp.extra. Vous pouvez ainsi transmettre à vos outils MCP des informations d’authentification, le contexte utilisateur ou toute donnée personnalisée provenant de votre middleware HTTP.

Fonctionnement
Lien direct vers Fonctionnement

Tout ce que vous définissez sur req.auth dans votre middleware HTTP devient accessible sous la forme context.mcp.extra.authInfo dans vos outils :

req.auth = { ... } → context?.mcp?.extra?.authInfo.extra = { ... }

Mapper les données d’authentification pour FGA
Lien direct vers Mapper les données d’authentification pour FGA

Lorsqu’un MCPServer est enregistré sur une instance Mastra dotée d’un fournisseur d’autorisation fine (FGA), Mastra vérifie requestContext.get('user') avant de répertorier ou d’appeler des outils. Les transports MCP HTTP transmettent les données authentifiées sous la forme extra.authInfo. Utilisez donc mapAuthInfoToUser pour définir la structure utilisateur attendue par votre fournisseur FGA.

const server = new MCPServer({
id: 'my-server',
name: 'My Server',
version: '1.0.0',
tools: { getUserData },
mapAuthInfoToUser: ({ authInfo }) => {
const user = authInfo as {
extra?: {
userId?: string
organizationMembershipId?: string
}
}

if (!user.extra?.userId) {
return null
}

return {
id: user.extra.userId,
organizationMembershipId: user.extra.organizationMembershipId,
}
},
})

Définir séparément la portée FGA des outils MCP
Lien direct vers Définir séparément la portée FGA des outils MCP

Utilisez fga.resourceMapping et fga.permissionMapping lorsque les clients MCP nécessitent une portée d’autorisation différente de celle de l’exécution interne des outils d’agents ou de workflows. Le remplacement s’applique uniquement aux vérifications tools/list et tools/call de ce serveur MCP.

import { MastraFGAPermissions } from '@mastra/core/auth/ee'

const server = new MCPServer({
id: 'my-server',
name: 'My Server',
version: '1.0.0',
tools: { getUserData },
mapAuthInfoToUser: ({ authInfo }) => {
const user = authInfo as {
extra?: {
userId?: string
organizationMembershipId?: string
}
}

if (!user.extra?.userId) {
return null
}

return {
id: user.extra.userId,
organizationMembershipId: user.extra.organizationMembershipId,
}
},
fga: {
resourceMapping: {
tool: {
fgaResourceType: 'user',
deriveId: ({ user }) => (user as { id: string }).id,
},
},
permissionMapping: {
[MastraFGAPermissions.TOOLS_EXECUTE]: 'read',
},
},
})

Configurer un middleware d’authentification
Lien direct vers Configurer un middleware d’authentification

Pour transmettre des données à vos outils, renseignez req.auth sur l’objet de requête Node.js dans le middleware de votre serveur HTTP avant d’appeler server.startHTTP().

import express from 'express'

type MCPAuthenticatedRequest = express.Request & {
auth?: {
token: string
clientId: string
scopes: string[]
expiresAt?: number
extra?: Record<string, unknown>
}
}

const app = express()

// Auth middleware - set req.auth before the MCP handler
app.use('/mcp', async (req, res, next) => {
const authorization = req.headers.authorization

if (!authorization?.startsWith('Bearer ')) {
res.status(401).json({ error: 'Missing bearer token' })
return
}

const token = authorization.slice('Bearer '.length)

try {
const user = await verifyToken(token)

// This entire object becomes context.mcp.extra.authInfo
const authenticatedRequest = req as MCPAuthenticatedRequest
authenticatedRequest.auth = {
token,
clientId: user.clientId,
scopes: user.scopes,
expiresAt: user.expiresAt,
extra: {
userId: user.userId,
email: user.email,
},
}
next()
} catch {
res.status(401).json({ error: 'Invalid or expired token' })
}
})

app.all('/mcp', async (req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`)
await server.startHTTP({ url, httpPath: '/mcp', req, res })
})

Accéder aux données d’authentification dans les outils
Lien direct vers Accéder aux données d’authentification dans les outils

L’objet req.auth est accessible sous la forme context.mcp.extra.authInfo dans la fonction d’exécution de votre outil :

execute: async (inputData, context) => {
// Access the auth data you set in middleware
const authInfo = context?.mcp?.extra?.authInfo

if (!authInfo?.extra?.userId) {
return { error: 'Authentication required' }
}

// Use the auth data
console.log('User ID:', authInfo.extra.userId)
console.log('Email:', authInfo.extra.email)

const response = await fetch('/api/data', {
headers: { Authorization: `Bearer ${authInfo.token}` },
signal: context?.mcp?.extra?.signal,
})

return response.json()
}

Transmettre RequestContext à l’agent
Lien direct vers passing-requestcontext-through-to-agent

execute: async (inputData, context) => {
// Access the auth data you set in middleware
const authInfo = context?.mcp?.extra?.authInfo

const requestContext = context.requestContext || new RequestContext().set('someKey', authInfo)

if (!authInfo?.extra?.userId) {
return { error: 'Authentication required' }
}

// Use the auth data
console.log('User ID:', authInfo.extra.userId)
console.log('Email:', authInfo.extra.email)

const agent = context?.mastra?.getAgentById('some-agent-id')

if (!agent) {
return { error: "Agent 'some-agent-id' not found" }
}

const response = await agent.generate(prompt, { requestContext })

return response.text
}

L’objet extra
Lien direct vers the-extra-object

L’objet context.mcp.extra complet contient :

PropriétéDescription
authInfoTout ce que vous définissez sur req.auth dans votre middleware
sessionIdIdentifiant de session de la connexion MCP
signalAbortSignal permettant d’annuler la requête
sendNotificationFonction du protocole MCP permettant d’envoyer des notifications
sendRequestFonction du protocole MCP permettant d’envoyer des requêtes

Exemple complet
Lien direct vers Exemple complet

Installez jose pour vérifier les JSON Web Tokens (JWT) par rapport au JSON Web Key Set (JWKS) de votre fournisseur d’identité :

npm install jose

L’exemple suivant valide la signature, l’émetteur, l’audience, l’algorithme, l’expiration et les claims requis du token avant de transmettre ses données utilisateur à l’outil :

import express from 'express'
import { createRemoteJWKSet, jwtVerify } from 'jose'
import { MCPServer } from '@mastra/mcp'
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

type MCPAuthenticatedRequest = express.Request & {
auth?: {
token: string
clientId: string
scopes: string[]
expiresAt?: number
extra?: Record<string, unknown>
}
}

const issuer = process.env.JWT_ISSUER
const audience = process.env.JWT_AUDIENCE
const jwksUri = process.env.JWT_JWKS_URI

if (!issuer || !audience || !jwksUri) {
throw new Error('JWT_ISSUER, JWT_AUDIENCE, and JWT_JWKS_URI are required')
}

const jwks = createRemoteJWKSet(new URL(jwksUri))

const verifyToken = async (token: string) => {
const { payload } = await jwtVerify(token, jwks, {
issuer,
audience,
algorithms: ['RS256'],
requiredClaims: ['exp'],
})

const clientId =
typeof payload.client_id === 'string'
? payload.client_id
: typeof payload.azp === 'string'
? payload.azp
: undefined

if (!payload.sub || typeof payload.email !== 'string' || !clientId || !payload.exp) {
throw new Error('Token must contain sub, email, exp, and client_id or azp claims')
}

return {
userId: payload.sub,
clientId,
email: payload.email,
expiresAt: payload.exp,
scopes: typeof payload.scope === 'string' ? payload.scope.split(' ') : [],
}
}

// 1. Define your tool that uses auth context
const getUserData = createTool({
id: 'get-user-data',
description: 'Fetches data for the authenticated user',
inputSchema: z.object({}),
execute: async (inputData, context) => {
const authInfo = context?.mcp?.extra?.authInfo

if (!authInfo?.extra?.userId) {
return { error: 'Authentication required' }
}

// Access the data you set in middleware
return {
userId: authInfo.extra.userId,
email: authInfo.extra.email,
}
},
})

// 2. Create the MCP server with your tools
const server = new MCPServer({
id: 'my-server',
name: 'My Server',
version: '1.0.0',
tools: { getUserData },
})

// 3. Set up Express with auth middleware
const app = express()

app.use('/mcp', async (req, res, next) => {
const authorization = req.headers.authorization

if (!authorization?.startsWith('Bearer ')) {
res.status(401).json({ error: 'Missing bearer token' })
return
}

const token = authorization.slice('Bearer '.length)

try {
const user = await verifyToken(token)

// This entire object becomes context.mcp.extra.authInfo
const authenticatedRequest = req as MCPAuthenticatedRequest
authenticatedRequest.auth = {
token,
clientId: user.clientId,
scopes: user.scopes,
expiresAt: user.expiresAt,
extra: {
userId: user.userId,
email: user.email,
},
}
next()
} catch {
res.status(401).json({ error: 'Invalid or expired token' })
}
})

app.all('/mcp', async (req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`)
await server.startHTTP({ url, httpPath: '/mcp', req, res })
})

app.listen(3000)

MCP Apps (appResources)
Lien direct vers mcp-apps-appresources

L’option appResources vous permet de servir des interfaces HTML interactives depuis votre serveur MCP via l’extension MCP Apps. Chaque entrée associe un URI ui:// à une application HTML affichée dans une iframe en bac à sable dans Mastra Studio.

Type AppResources
Lien direct vers appresources-type

Clé (URI):

string
Un URI ui:// qui identifie la ressource d’application (par exemple, ui://calculator/main).

Chaque valeur est un objet AppResource :

name:

string
Nom d’affichage de la ressource d’interface utilisateur.

description?:

string
Description facultative de la ressource d’interface utilisateur.

html?:

string
Contenu HTML intégré de l’interface utilisateur. Fournissez soit html, soit htmlPath.

htmlPath?:

string
Chemin vers un fichier HTML. Il est résolu au démarrage du serveur. Fournissez soit html, soit htmlPath.

meta?:

McpUiResourceMeta
Métadonnées de la ressource d’interface utilisateur (CSP, autorisations, préférences de rendu) provenant du SDK ext-apps officiel.

Exemple
Lien direct vers Exemple

import { MCPServer } from '@mastra/mcp'
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

const calculatorTool = createTool({
id: 'calculatorWithUI',
description: 'An interactive calculator',
inputSchema: z.object({
num1: z.number(),
num2: z.number(),
operation: z.enum(['add', 'subtract']),
}),
execute: async ({ num1, num2, operation }) => {
const result = operation === 'add' ? num1 + num2 : num1 - num2
return {
content: [{ type: 'text', text: 'An interactive calculator is displayed.' }],
structuredContent: { result },
}
},
})

const server = new MCPServer({
id: 'app-server',
name: 'App Server',
version: '1.0.0',
tools: { calculatorTool },
appResources: {
'ui://calculator/main': {
name: 'Interactive Calculator',
html: '<html><body><h2>Calculator</h2>...</body></html>',
},
},
})

Associez un outil à sa ressource d’application en définissant _meta.ui.resourceUri sur l’outil avec l’URI ui:// correspondant. Le serveur normalise automatiquement ces métadonnées lors de l’enregistrement des outils. Consultez MCP Apps pour découvrir l’API complète du pont d’application et ses modes d’utilisation.

On this page