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).
ConstructeurLien 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 configurationLien direct vers Propriétés de configuration
Le constructeur accepte un objet MCPServerConfig doté des propriétés suivantes :
id:
name:
version:
tools:
createTool ou le SDK Vercel AI). Ces outils seront directement exposés.agents?:
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?:
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?:
instructions?:
mapAuthInfoToUser?:
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?:
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?:
releaseDate?:
isLatest?:
packageCanonical?:
packages?:
remotes?:
resources?:
prompts?:
appResources?:
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 outilsLien 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’objetagents. Par exemple, si vous configurezagents: { myAgentKey: myAgentInstance }, un outil nomméask_myAgentKeysera 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 laqueryfournie. - Sortie : le résultat direct de la méthode
generate()de l’agent est renvoyé comme sortie de l’outil.
- Description : la description de l’outil généré adopte le format suivant : « Poser une question à l’agent
-
Conflits de noms. Si un outil explicite défini dans la configuration
toolsporte le même nom qu’un outil dérivé d’un agent (par exemple, un outil nomméask_myAgentKeyassocié à un agent dont la clé estmyAgentKey), 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 outilLien 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 outilsLien 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’appel | Méthode d’accès |
|---|---|
| Appel direct de l’outil | context?.mcp?.extra |
| Appel de l’outil par un agent | context?.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 contextesLien 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éthodesLien 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:
ssePath:
messagePath:
req:
res:
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:
ssePath:
messagePath:
req:
res:
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 })
})
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/subscribeetresources/unsubscribené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:
httpPath:
req:
res:
options:
L’objet StreamableHTTPServerTransportOptions vous permet de personnaliser le comportement du transport HTTP. Voici les options disponibles :
serverless:
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:
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:
undefined pour désactiver la gestion des sessions.onsessioninitialized:
enableJsonResponse:
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:
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:
args:
executionContext?:
Gestion des ressourcesLien 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 :
- Ressources directes : les serveurs exposent une liste de ressources concrètes via un point de terminaison
resources/list. - 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 MCPServerResourcesLien 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 ressourcesLien 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 promptsLien 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 MCPServerPromptsLien 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 promptsLien 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 promptsLien 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
versionsi vous prévoyez d’apporter des modifications incompatibles. - Utilisez le paramètre
versionpour 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 outilsLien 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 MastraLien 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.
JournalisationLien 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 progressionLien 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 notificationsLien 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.
ExemplesLien 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.
ÉlicitationLien 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 outilsLien 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’élicitationLien 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 :
- L’outil appelle
context.mcp.elicitation.sendRequest()avec un message et un schéma - La requête est envoyée au client MCP connecté
- Le client présente la requête à l’utilisateur (via une interface, la ligne de commande, etc.)
- L’utilisateur fournit une saisie, refuse ou annule la requête
- Le client renvoie la réponse au serveur
- L’outil reçoit la réponse et poursuit son exécution
Utiliser l’élicitation dans les outilsLien 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’élicitationLien 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éponseLien direct vers Actions de réponse
Les utilisateurs peuvent répondre aux requêtes d’élicitation de trois façons :
- Accepter (
action: 'accept') : l’utilisateur a fourni des données et confirmé leur envoi- Contient un champ
contentavec les données envoyées
- Contient un champ
- Refuser (
action: 'decline') : l’utilisateur a explicitement refusé de fournir des informations- Aucun champ de contenu
- 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 outilsLien 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 OAuthLien 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 Unauthorizedavec les en-têtesWWW-Authenticateappropriés lorsqu’une authentification est requise - Valide les tokens bearer au moyen du validateur fourni
Validation des tokensLien 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 OAuthLien direct vers Options du middleware OAuth
oauth.resource:
oauth.scopesSupported?:
oauth.resourceName?:
oauth.validateToken?:
mcpPath?:
Contexte d’authentificationLien 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.
FonctionnementLien 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 FGALien 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 MCPLien 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’authentificationLien 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 outilsLien 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’agentLien 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 extraLien direct vers the-extra-object
L’objet context.mcp.extra complet contient :
| Propriété | Description |
|---|---|
authInfo | Tout ce que vous définissez sur req.auth dans votre middleware |
sessionId | Identifiant de session de la connexion MCP |
signal | AbortSignal permettant d’annuler la requête |
sendNotification | Fonction du protocole MCP permettant d’envoyer des notifications |
sendRequest | Fonction du protocole MCP permettant d’envoyer des requêtes |
Exemple completLien 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
- pnpm
- Yarn
- Bun
npm install jose
pnpm add jose
yarn add jose
bun add 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 AppResourcesLien direct vers appresources-type
Clé (URI):
ui:// qui identifie la ressource d’application (par exemple, ui://calculator/main).Chaque valeur est un objet AppResource :
name:
description?:
html?:
html, soit htmlPath.htmlPath?:
html, soit htmlPath.meta?:
ExempleLien 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.
Informations connexesLien direct vers Informations connexes
- Pour connecter des serveurs MCP dans Mastra, consultez la documentation de MCPClient.
- Pour en savoir plus sur le Model Context Protocol, consultez la documentation de @modelcontextprotocol/sdk.