@mastra/mcp-docs-server 1.2.8-alpha.0 → 1.2.8-alpha.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/.docs/docs/agent-builder/deploying.md +1 -0
  2. package/.docs/docs/agent-builder/integrations.md +1 -3
  3. package/.docs/docs/agent-builder/overview.md +1 -1
  4. package/.docs/docs/agent-controller/overview.md +1 -1
  5. package/.docs/docs/agents/overview.md +17 -13
  6. package/.docs/docs/agents/skills.md +1 -3
  7. package/.docs/docs/agents/structured-output.md +14 -7
  8. package/.docs/docs/agents/using-tools.md +80 -26
  9. package/.docs/docs/browser/agent-browser.md +1 -0
  10. package/.docs/docs/browser/firecrawl.md +129 -0
  11. package/.docs/docs/browser/overview.md +16 -2
  12. package/.docs/docs/capabilities/channels/other-adapters.md +1 -1
  13. package/.docs/docs/capabilities/channels/overview.md +1 -0
  14. package/.docs/docs/capabilities/channels/slack.md +113 -3
  15. package/.docs/docs/deployment/mastra-server.md +35 -0
  16. package/.docs/docs/deployment/monorepo.md +2 -0
  17. package/.docs/docs/deployment/overview.md +6 -0
  18. package/.docs/docs/deployment/sandbox.md +277 -0
  19. package/.docs/docs/editor/overview.md +2 -6
  20. package/.docs/docs/editor/tools.md +2 -6
  21. package/.docs/docs/evals/multi-turn.md +178 -0
  22. package/.docs/docs/evals/overview.md +1 -0
  23. package/.docs/docs/evals/running-in-ci.md +3 -9
  24. package/.docs/docs/getting-started/file-based-agents.md +2 -2
  25. package/.docs/docs/index.md +17 -13
  26. package/.docs/docs/long-running-agents/background-tasks.md +2 -2
  27. package/.docs/docs/long-running-agents/durable-agents.md +2 -1
  28. package/.docs/docs/long-running-agents/schedules.md +2 -2
  29. package/.docs/docs/mastra-platform/database.md +13 -6
  30. package/.docs/docs/mastra-platform/deploy.md +17 -8
  31. package/.docs/docs/mastra-platform/environments.md +5 -4
  32. package/.docs/docs/mastra-platform/overview.md +1 -1
  33. package/.docs/docs/mastra-platform/regions.md +73 -0
  34. package/.docs/docs/mastra-platform/workspace.md +111 -0
  35. package/.docs/docs/mcp/overview.md +1 -1
  36. package/.docs/docs/memory/multi-user-threads.md +4 -4
  37. package/.docs/docs/memory/observational-memory.md +78 -3
  38. package/.docs/docs/memory/overview.md +4 -0
  39. package/.docs/docs/memory/working-memory.md +1 -1
  40. package/.docs/docs/observability/integrations/bridges/otel.md +1 -3
  41. package/.docs/docs/observability/integrations/exporters/braintrust.md +34 -2
  42. package/.docs/docs/observability/integrations/exporters/otel.md +1 -3
  43. package/.docs/docs/server/auth/simple-auth.md +1 -3
  44. package/.docs/docs/server/request-context.md +2 -0
  45. package/.docs/docs/studio/overview.md +2 -0
  46. package/.docs/docs/voice/livekit.md +1 -1
  47. package/.docs/docs/workflows/control-flow.md +1 -3
  48. package/.docs/docs/workspace/filesystem.md +1 -0
  49. package/.docs/docs/workspace/sandbox.md +1 -0
  50. package/.docs/guides/build-your-ui/ai-sdk-ui.md +1 -0
  51. package/.docs/guides/guide/signal-provider.md +2 -0
  52. package/.docs/guides/migrations/vnext-to-standard-apis.md +1 -3
  53. package/.docs/models/environment-variables.md +1 -0
  54. package/.docs/models/gateways/openrouter.md +340 -345
  55. package/.docs/models/gateways/vercel.md +6 -5
  56. package/.docs/models/index.md +1 -1
  57. package/.docs/models/providers/alibaba-token-plan-cn.md +22 -21
  58. package/.docs/models/providers/alibaba-token-plan.md +22 -21
  59. package/.docs/models/providers/ambient.md +12 -10
  60. package/.docs/models/providers/baseten.md +3 -2
  61. package/.docs/models/providers/crossmodel.md +2 -1
  62. package/.docs/models/providers/deepinfra.md +4 -7
  63. package/.docs/models/providers/empiriolabs.md +2 -1
  64. package/.docs/models/providers/evroc.md +3 -2
  65. package/.docs/models/providers/google.md +2 -2
  66. package/.docs/models/providers/kilo.md +1 -1
  67. package/.docs/models/providers/kimi-for-coding.md +10 -11
  68. package/.docs/models/providers/llmgateway.md +3 -8
  69. package/.docs/models/providers/moonshotai-cn.md +3 -2
  70. package/.docs/models/providers/moonshotai.md +3 -2
  71. package/.docs/models/providers/nebius.md +3 -1
  72. package/.docs/models/providers/novita-ai.md +3 -1
  73. package/.docs/models/providers/ollama-cloud.md +25 -42
  74. package/.docs/models/providers/opencode-go.md +5 -3
  75. package/.docs/models/providers/orcarouter.md +1 -1
  76. package/.docs/models/providers/privatemode-ai.md +10 -10
  77. package/.docs/models/providers/thinkingmachines.md +73 -0
  78. package/.docs/models/providers/togetherai.md +2 -6
  79. package/.docs/models/providers/zenmux.md +3 -1
  80. package/.docs/models/providers.md +1 -0
  81. package/.docs/reference/acp/acp-agent.md +2 -0
  82. package/.docs/reference/acp/create-acp-tool.md +28 -0
  83. package/.docs/reference/agent-controller/agent-controller-class.md +1 -1
  84. package/.docs/reference/agent-controller/session.md +15 -0
  85. package/.docs/reference/agents/agent.md +14 -2
  86. package/.docs/reference/agents/durable-agent.md +23 -0
  87. package/.docs/reference/agents/generate.md +19 -1
  88. package/.docs/reference/browser/firecrawl-browser.md +71 -0
  89. package/.docs/reference/cli/mastra.md +14 -6
  90. package/.docs/reference/code-sdk/mount-agent-controller.md +1 -3
  91. package/.docs/reference/core/getMCPServer.md +1 -3
  92. package/.docs/reference/core/getMCPServerById.md +1 -3
  93. package/.docs/reference/core/getTool.md +33 -0
  94. package/.docs/reference/core/getToolById.md +48 -0
  95. package/.docs/reference/core/getWorkflow.md +1 -3
  96. package/.docs/reference/core/listMCPServers.md +2 -6
  97. package/.docs/reference/core/listTools.md +38 -0
  98. package/.docs/reference/core/mastra-class.md +1 -1
  99. package/.docs/reference/datasets/addItem.md +1 -3
  100. package/.docs/reference/datasets/addItems.md +1 -3
  101. package/.docs/reference/datasets/compareExperiments.md +1 -3
  102. package/.docs/reference/datasets/create.md +1 -3
  103. package/.docs/reference/datasets/dataset.md +2 -6
  104. package/.docs/reference/datasets/datasets-manager.md +5 -15
  105. package/.docs/reference/datasets/delete.md +1 -3
  106. package/.docs/reference/datasets/deleteExperiment.md +1 -3
  107. package/.docs/reference/datasets/deleteItem.md +1 -3
  108. package/.docs/reference/datasets/deleteItems.md +1 -3
  109. package/.docs/reference/datasets/get.md +1 -3
  110. package/.docs/reference/datasets/getDetails.md +1 -3
  111. package/.docs/reference/datasets/getExperiment.md +1 -3
  112. package/.docs/reference/datasets/getItem.md +1 -3
  113. package/.docs/reference/datasets/getItemHistory.md +1 -3
  114. package/.docs/reference/datasets/list.md +1 -3
  115. package/.docs/reference/datasets/listExperimentResults.md +1 -3
  116. package/.docs/reference/datasets/listExperiments.md +1 -3
  117. package/.docs/reference/datasets/listItems.md +1 -3
  118. package/.docs/reference/datasets/listVersions.md +1 -3
  119. package/.docs/reference/datasets/startExperiment.md +1 -3
  120. package/.docs/reference/datasets/startExperimentAsync.md +1 -3
  121. package/.docs/reference/datasets/update.md +1 -3
  122. package/.docs/reference/datasets/updateItem.md +1 -3
  123. package/.docs/reference/editor/mastra-editor.md +1 -3
  124. package/.docs/reference/evals/context-recall.md +205 -0
  125. package/.docs/reference/evals/create-scorer.md +4 -10
  126. package/.docs/reference/evals/mastra-scorer.md +1 -3
  127. package/.docs/reference/evals/run-evals.md +94 -2
  128. package/.docs/reference/file-based-agents/logger.md +1 -1
  129. package/.docs/reference/file-based-agents/tools.md +7 -6
  130. package/.docs/reference/index.md +8 -0
  131. package/.docs/reference/memory/observational-memory.md +21 -0
  132. package/.docs/reference/observability/tracing/processors/sensitive-data-filter.md +1 -3
  133. package/.docs/reference/processors/token-limiter-processor.md +1 -3
  134. package/.docs/reference/rag/database-config.md +12 -0
  135. package/.docs/reference/storage/clickhouse.md +1 -1
  136. package/.docs/reference/storage/convex.md +17 -10
  137. package/.docs/reference/storage/retention.md +1 -1
  138. package/.docs/reference/streaming/agents/stream.md +3 -1
  139. package/.docs/reference/tools/create-tool.md +78 -52
  140. package/.docs/reference/tools/mcp-client.md +104 -24
  141. package/.docs/reference/tools/mcp-server.md +143 -44
  142. package/.docs/reference/tools/task-tools.md +2 -2
  143. package/.docs/reference/tools/tavily.md +1 -1
  144. package/.docs/reference/tools/vector-query-tool.md +22 -0
  145. package/.docs/reference/vectors/mongodb.md +2 -0
  146. package/.docs/reference/vectors/turbopuffer.md +4 -0
  147. package/.docs/reference/voice/mistral.md +127 -0
  148. package/.docs/reference/workspace/daytona-sandbox.md +2 -6
  149. package/.docs/reference/workspace/platform-filesystem.md +182 -0
  150. package/.docs/reference/workspace/platform-sandbox.md +200 -0
  151. package/CHANGELOG.md +71 -0
  152. package/dist/{chunk-GLPCVXXO.js → chunk-HGADBLKG.js} +2 -2
  153. package/dist/{chunk-GLPCVXXO.js.map → chunk-HGADBLKG.js.map} +1 -1
  154. package/dist/index.js +1 -1
  155. package/dist/stdio.js +1 -1
  156. package/dist/tools/docs.d.ts.map +1 -1
  157. package/package.json +8 -8
@@ -10,25 +10,29 @@ The `createTool()` function is used to define custom tools that your Mastra agen
10
10
  import { createTool } from '@mastra/core/tools'
11
11
  import { z } from 'zod'
12
12
 
13
- export const tool = createTool({
14
- id: 'test-tool',
15
- description: 'Reverse the input string',
13
+ export const weatherTool = createTool({
14
+ id: 'weather-tool',
15
+ description: 'Get the current weather for a location',
16
16
  inputSchema: z.object({
17
- input: z.string(),
17
+ location: z.string(),
18
18
  }),
19
19
  outputSchema: z.object({
20
- output: z.string(),
20
+ location: z.string(),
21
+ temperatureCelsius: z.number(),
22
+ conditions: z.string(),
21
23
  }),
22
- execute: async inputData => {
23
- const reversed = inputData.input.split('').reverse().join('')
24
-
24
+ execute: async ({ location }) => {
25
25
  return {
26
- output: reversed,
26
+ location,
27
+ temperatureCelsius: 21,
28
+ conditions: 'sunny',
27
29
  }
28
30
  },
29
31
  })
30
32
  ```
31
33
 
34
+ The first `execute` parameter is the validated value from `inputSchema`. Destructure schema fields directly in the function signature, as shown with `{ location }`. The optional second parameter contains the execution context.
35
+
32
36
  ## Parameters
33
37
 
34
38
  **id** (`string`): A unique identifier for the tool.
@@ -112,10 +116,12 @@ export const weatherTool = createTool({
112
116
  location: z.string(),
113
117
  }),
114
118
  outputSchema: z.object({
115
- weather: z.string(),
119
+ location: z.string(),
120
+ temperatureCelsius: z.number(),
121
+ conditions: z.string(),
116
122
  }),
117
- execute: async inputData => {
118
- // Fetch weather data
123
+ execute: async ({ location }) => {
124
+ return { location, temperatureCelsius: 21, conditions: 'sunny' }
119
125
  },
120
126
  })
121
127
  ```
@@ -137,11 +143,13 @@ export const weatherTool = createTool({
137
143
  ),
138
144
  outputSchema: toStandardJsonSchema(
139
145
  v.object({
140
- weather: v.string(),
146
+ location: v.string(),
147
+ temperatureCelsius: v.number(),
148
+ conditions: v.string(),
141
149
  }),
142
150
  ),
143
- execute: async inputData => {
144
- // Fetch weather data
151
+ execute: async ({ location }) => {
152
+ return { location, temperatureCelsius: 21, conditions: 'sunny' }
145
153
  },
146
154
  })
147
155
  ```
@@ -159,10 +167,12 @@ export const weatherTool = createTool({
159
167
  location: 'string',
160
168
  }),
161
169
  outputSchema: type({
162
- weather: 'string',
170
+ location: 'string',
171
+ temperatureCelsius: 'number',
172
+ conditions: 'string',
163
173
  }),
164
- execute: async inputData => {
165
- // Fetch weather data
174
+ execute: async ({ location }) => {
175
+ return { location, temperatureCelsius: 21, conditions: 'sunny' }
166
176
  },
167
177
  })
168
178
  ```
@@ -176,18 +186,22 @@ import { createTool } from '@mastra/core/tools'
176
186
  import { z } from 'zod'
177
187
 
178
188
  export const weatherTool = createTool({
179
- id: 'get-weather',
180
- description: 'Get weather for a city',
189
+ id: 'weather-tool',
190
+ description: 'Get the current weather for a location',
181
191
  strict: true,
182
192
  inputSchema: z.object({
183
- city: z.string(),
184
- units: z.enum(['metric', 'imperial']),
193
+ location: z.string(),
194
+ }),
195
+ outputSchema: z.object({
196
+ location: z.string(),
197
+ temperatureCelsius: z.number(),
198
+ conditions: z.string(),
185
199
  }),
186
- execute: async ({ city, units }) => {
200
+ execute: async ({ location }) => {
187
201
  return {
188
- city,
189
- units,
190
- forecast: 'sunny',
202
+ location,
203
+ temperatureCelsius: 21,
204
+ conditions: 'sunny',
191
205
  }
192
206
  },
193
207
  })
@@ -204,28 +218,31 @@ import { createTool } from '@mastra/core/tools'
204
218
  import { z } from 'zod'
205
219
 
206
220
  export const weatherTool = createTool({
207
- id: 'get-weather',
208
- description: 'Get weather for a city',
221
+ id: 'weather-tool',
222
+ description: 'Get the current weather for a location',
209
223
  inputSchema: z.object({
210
- city: z.string(),
224
+ location: z.string(),
211
225
  }),
212
226
  outputSchema: z.object({
213
- city: z.string(),
214
- temperature: z.number(),
215
- condition: z.string(),
227
+ location: z.string(),
228
+ temperatureCelsius: z.number(),
229
+ conditions: z.string(),
216
230
  radarImageUrl: z.string().url(),
217
231
  }),
218
- execute: async ({ city }) => ({
219
- city,
220
- temperature: 72,
221
- condition: 'sunny',
232
+ execute: async ({ location }) => ({
233
+ location,
234
+ temperatureCelsius: 21,
235
+ conditions: 'sunny',
222
236
  radarImageUrl: 'https://example.com/radar/seattle.png',
223
237
  }),
224
238
  toModelOutput: output => {
225
239
  return {
226
240
  type: 'content',
227
241
  value: [
228
- { type: 'text', text: `${output.city}: ${output.temperature}F and ${output.condition}` },
242
+ {
243
+ type: 'text',
244
+ text: `${output.location}: ${output.temperatureCelsius}°C and ${output.conditions}`,
245
+ },
229
246
  { type: 'image-url', url: output.radarImageUrl },
230
247
  ],
231
248
  }
@@ -294,11 +311,16 @@ import { createTool } from '@mastra/core/tools'
294
311
  import { z } from 'zod'
295
312
 
296
313
  export const weatherTool = createTool({
297
- id: 'get-weather',
298
- description: 'Get current weather for a location',
314
+ id: 'weather-tool',
315
+ description: 'Get the current weather for a location',
299
316
  inputSchema: z.object({
300
317
  location: z.string().describe('City name or coordinates'),
301
318
  }),
319
+ outputSchema: z.object({
320
+ location: z.string(),
321
+ temperatureCelsius: z.number(),
322
+ conditions: z.string(),
323
+ }),
302
324
  // MCP-specific properties
303
325
  mcp: {
304
326
  // Annotations for client behavior hints
@@ -315,9 +337,11 @@ export const weatherTool = createTool({
315
337
  category: 'weather',
316
338
  },
317
339
  },
318
- execute: async inputData => {
340
+ execute: async ({ location }) => {
319
341
  return {
320
- weather: `Current weather for ${inputData.location}: sunny`,
342
+ location,
343
+ temperatureCelsius: 21,
344
+ conditions: 'sunny',
321
345
  }
322
346
  },
323
347
  })
@@ -334,19 +358,21 @@ import { createTool } from '@mastra/core/tools'
334
358
  import { z } from 'zod'
335
359
 
336
360
  export const weatherTool = createTool({
337
- id: 'get-weather',
338
- description: 'Get weather for a city',
361
+ id: 'weather-tool',
362
+ description: 'Get the current weather for a location',
339
363
  inputSchema: z.object({
340
- city: z.string(),
364
+ location: z.string(),
341
365
  }),
342
366
  outputSchema: z.object({
343
- city: z.string(),
344
- forecast: z.string(),
367
+ location: z.string(),
368
+ temperatureCelsius: z.number(),
369
+ conditions: z.string(),
345
370
  }),
346
- execute: async ({ city }) => {
371
+ execute: async ({ location }) => {
347
372
  return {
348
- city,
349
- forecast: 'sunny',
373
+ location,
374
+ temperatureCelsius: 21,
375
+ conditions: 'sunny',
350
376
  }
351
377
  },
352
378
  onInputStart: ({ toolCallId }) => {
@@ -356,10 +382,10 @@ export const weatherTool = createTool({
356
382
  console.log(`Tool call ${toolCallId} received input chunk: ${inputTextDelta}`)
357
383
  },
358
384
  onInputAvailable: ({ input, toolCallId }) => {
359
- console.log(`Tool call ${toolCallId} received city: ${input.city}`)
385
+ console.log(`Tool call ${toolCallId} received location: ${input.location}`)
360
386
  },
361
387
  onOutput: ({ output, toolCallId, toolName }) => {
362
- console.log(`Tool ${toolName} call ${toolCallId} returned forecast: ${output.forecast}`)
388
+ console.log(`Tool ${toolName} call ${toolCallId} returned conditions: ${output.conditions}`)
363
389
  },
364
390
  })
365
391
  ```
@@ -403,7 +429,7 @@ export const tool = createTool({
403
429
  id: 'example-tool',
404
430
  description: 'Example tool with hooks',
405
431
  inputSchema: z.object({
406
- city: z.string(),
432
+ location: z.string(),
407
433
  }),
408
434
  onInputAvailable: ({ input, toolCallId, messages, abortSignal }) => {
409
435
  console.log(`Tool received complete input:`, input)
@@ -207,6 +207,34 @@ const instructionsByServer = mcp.getServerInstructions()
207
207
  console.log(instructionsByServer.db)
208
208
  ```
209
209
 
210
+ ### `authenticate()`
211
+
212
+ Runs the interactive OAuth authorization-code flow for a server configured with an `MCPOAuthClientProvider` whose redirect URL points at a loopback address. Starts a local callback server, delivers the authorization URL through the provider's `onRedirectToAuthorization` callback, waits for the browser to return the authorization code, exchanges it for tokens, and reconnects. See [Interactive browser authentication](#interactive-browser-authentication).
213
+
214
+ The optional `timeoutMs` bounds how long the flow waits for the browser to return the authorization code before rejecting, and defaults to 5 minutes.
215
+
216
+ ```typescript
217
+ async authenticate(serverName: string, options?: { timeoutMs?: number }): Promise<void>
218
+ ```
219
+
220
+ ### `getServerAuthState()`
221
+
222
+ Returns the OAuth authorization state of a configured server: `'needs-auth'` after a connection attempt was rejected with an authorization error, `'authorized'` once the server accepted the provider's credentials, or `undefined` for servers without an `authProvider` or that haven't attempted a connection yet.
223
+
224
+ ```typescript
225
+ getServerAuthState(serverName: string): 'needs-auth' | 'authorized' | undefined
226
+ ```
227
+
228
+ ### `cancelAuthentication()`
229
+
230
+ Cancels an in-progress `authenticate()` flow for a server, so an abandoned browser authorization does not leave the client waiting indefinitely. It aborts the flow (including its setup phase, before the callback server binds), closes the local callback server if one is listening, and the pending `authenticate()` call rejects. Returns `true` if a flow was cancelled, or `false` when no flow was in progress.
231
+
232
+ The resulting `getServerAuthState()` depends on how far the flow had progressed: a flow cancelled after the server rejected the connection with `401` stays at `'needs-auth'` and can be retried immediately, while a flow cancelled during the setup phase — before any connection was attempted — leaves the state unchanged (typically `undefined`).
233
+
234
+ ```typescript
235
+ async cancelAuthentication(serverName: string): Promise<boolean>
236
+ ```
237
+
210
238
  ### `disconnect()`
211
239
 
212
240
  Disconnects from all MCP servers and cleans up resources.
@@ -252,9 +280,7 @@ This is useful for connecting external MCP servers that implement the MCP Apps e
252
280
  The `MCPClient` instance has a `resources` property that provides access to resource-related operations.
253
281
 
254
282
  ```typescript
255
- const mcpClient = new MCPClient({
256
- /* ...servers configuration... */
257
- })
283
+ const mcpClient = new MCPClient({/* ...servers configuration... */})
258
284
 
259
285
  // Access resource methods via mcpClient.resources
260
286
  const allResourcesByServer = await mcpClient.resources.list()
@@ -383,9 +409,7 @@ mcpClient.resources.onListChanged('myWeatherServer', () => {
383
409
  The `MCPClient` instance has an `elicitation` property that provides access to elicitation-related operations. Elicitation allows MCP servers to request structured information from users.
384
410
 
385
411
  ```typescript
386
- const mcpClient = new MCPClient({
387
- /* ...servers configuration... */
388
- })
412
+ const mcpClient = new MCPClient({/* ...servers configuration... */})
389
413
 
390
414
  // Set up elicitation handler
391
415
  mcpClient.elicitation.onRequest('serverName', async request => {
@@ -524,9 +548,7 @@ await mcpClient.elicitation.onRequest('interactiveServer', async request => {
524
548
  The `MCPClient` instance has a `prompts` property that provides access to prompt-related operations.
525
549
 
526
550
  ```typescript
527
- const mcpClient = new MCPClient({
528
- /* ...servers configuration... */
529
- })
551
+ const mcpClient = new MCPClient({/* ...servers configuration... */})
530
552
 
531
553
  // Access prompt methods via mcpClient.prompts
532
554
  const allPromptsByServer = await mcpClient.prompts.list()
@@ -821,6 +843,74 @@ const client = new MCPClient({
821
843
  })
822
844
  ```
823
845
 
846
+ Give each server its own `MCPOAuthClientProvider` instance. A provider holds per-server session and credential state during authorization, so sharing one instance across multiple servers lets their flows overwrite each other. When configuring several protected servers, construct a separate provider for each.
847
+
848
+ ### Interactive browser authentication
849
+
850
+ When a server rejects a connection because authorization is required, the client records a `'needs-auth'` state instead of failing outright. Calling `authenticate()` then completes the flow end to end: it starts a one-shot callback server on the provider's loopback redirect URL (falling back to the next sequential ports when it is in use), lets the SDK run discovery and dynamic client registration, delivers the authorization URL through `onRedirectToAuthorization` — open it in the user's browser — and finishes the token exchange once the browser returns the authorization code:
851
+
852
+ ```typescript
853
+ import { MCPClient, MCPOAuthClientProvider } from '@mastra/mcp'
854
+
855
+ const oauthProvider = new MCPOAuthClientProvider({
856
+ redirectUrl: 'http://127.0.0.1:5533/oauth/callback',
857
+ clientMetadata: {
858
+ redirect_uris: ['http://127.0.0.1:5533/oauth/callback'],
859
+ client_name: 'My MCP Client',
860
+ grant_types: ['authorization_code', 'refresh_token'],
861
+ response_types: ['code'],
862
+ },
863
+ onRedirectToAuthorization: url => {
864
+ // Open the user's browser at the consent page
865
+ console.log(`Please visit: ${url}`)
866
+ },
867
+ })
868
+
869
+ const mcp = new MCPClient({
870
+ servers: {
871
+ protectedServer: {
872
+ url: new URL('https://mcp.example.com/mcp'),
873
+ authProvider: oauthProvider,
874
+ },
875
+ },
876
+ })
877
+
878
+ try {
879
+ await mcp.listTools()
880
+ } catch {
881
+ if (mcp.getServerAuthState('protectedServer') === 'needs-auth') {
882
+ await mcp.authenticate('protectedServer')
883
+ }
884
+ }
885
+ ```
886
+
887
+ Concurrent `authenticate()` calls for the same server join the pending flow; different servers authenticate independently. With valid stored tokens the call simply reconnects without opening a browser.
888
+
889
+ Hosts that drive the flow themselves can capture the authorization code with the exported `createOAuthCallbackServer` helper, which binds a one-shot loopback server, validates the OAuth `state` parameter, and resolves with the code. It creates a plain HTTP server, so it is only for local loopback redirects. Web applications that use an HTTPS redirect URL must host their own callback endpoint and drive the provider directly rather than using this helper:
890
+
891
+ ```typescript
892
+ import { createOAuthCallbackServer, getCallbackUrlCandidates } from '@mastra/mcp'
893
+
894
+ // getCallbackUrlCandidates() lists every URL the helper may bind, so register
895
+ // all of them as redirect_uris during client registration to cover port fallback.
896
+ const redirectUris = getCallbackUrlCandidates('http://127.0.0.1:5533/oauth/callback').map(url =>
897
+ url.toString(),
898
+ )
899
+
900
+ const server = await createOAuthCallbackServer({
901
+ redirectUrl: 'http://127.0.0.1:5533/oauth/callback',
902
+ state: expectedState,
903
+ })
904
+
905
+ // server.url reflects the port actually bound — use it as the redirect_uri.
906
+ try {
907
+ const { code } = await server.waitForCode()
908
+ // Exchange the code here.
909
+ } finally {
910
+ await server.close()
911
+ }
912
+ ```
913
+
824
914
  ### Quick Token Provider
825
915
 
826
916
  For testing or when you already have a valid access token:
@@ -884,9 +974,7 @@ class DatabaseOAuthStorage implements OAuthStorage {
884
974
 
885
975
  const provider = new MCPOAuthClientProvider({
886
976
  redirectUrl: 'http://localhost:3000/callback',
887
- clientMetadata: {
888
- /* ... */
889
- },
977
+ clientMetadata: {/* ... */},
890
978
  storage: new DatabaseOAuthStorage(db, 'user-123'),
891
979
  })
892
980
  ```
@@ -1021,33 +1109,25 @@ For example, if you try to create multiple instances with the same configuration
1021
1109
  ```typescript
1022
1110
  // First instance - OK
1023
1111
  const mcp1 = new MCPClient({
1024
- servers: {
1025
- /* ... */
1026
- },
1112
+ servers: {/* ... */},
1027
1113
  })
1028
1114
 
1029
1115
  // Second instance with same config - Will throw an error
1030
1116
  const mcp2 = new MCPClient({
1031
- servers: {
1032
- /* ... */
1033
- },
1117
+ servers: {/* ... */},
1034
1118
  })
1035
1119
 
1036
1120
  // To fix, either:
1037
1121
  // 1. Add unique IDs
1038
1122
  const mcp3 = new MCPClient({
1039
1123
  id: 'instance-1',
1040
- servers: {
1041
- /* ... */
1042
- },
1124
+ servers: {/* ... */},
1043
1125
  })
1044
1126
 
1045
1127
  // 2. Or disconnect before recreating
1046
1128
  await mcp1.disconnect()
1047
1129
  const mcp4 = new MCPClient({
1048
- servers: {
1049
- /* ... */
1050
- },
1130
+ servers: {/* ... */},
1051
1131
  })
1052
1132
  ```
1053
1133
 
@@ -187,9 +187,7 @@ const server = new MCPServer({
187
187
  id: 'my-server',
188
188
  name: 'My Server',
189
189
  version: '1.0.0',
190
- tools: {
191
- /* ... */
192
- },
190
+ tools: {/* ... */},
193
191
  })
194
192
  await server.startStdio()
195
193
  ```
@@ -353,9 +351,7 @@ const server = new MCPServer({
353
351
  id: 'my-serverless-mcp',
354
352
  name: 'My Serverless MCP',
355
353
  version: '1.0.0',
356
- tools: {
357
- /* your tools */
358
- },
354
+ tools: {/* your tools */},
359
355
  })
360
356
 
361
357
  serve(async req => {
@@ -637,9 +633,7 @@ const serverWithResources = new MCPServer({
637
633
  id: 'resourceful-server',
638
634
  name: 'Resourceful Server',
639
635
  version: '1.0.0',
640
- tools: {
641
- /* ... your tools ... */
642
- },
636
+ tools: {/* ... your tools ... */},
643
637
  resources: myResourceHandlers,
644
638
  })
645
639
  ```
@@ -768,9 +762,7 @@ const serverWithPrompts = new MCPServer({
768
762
  id: 'promptful-server',
769
763
  name: 'Promptful Server',
770
764
  version: '1.0.0',
771
- tools: {
772
- /* ... */
773
- },
765
+ tools: {/* ... */},
774
766
  prompts: myPromptHandlers,
775
767
  })
776
768
  ```
@@ -966,9 +958,7 @@ execute: async (inputData, context) => {
966
958
  // Use elicitation capabilities
967
959
  const result = await context.mcp.elicitation.sendRequest({
968
960
  message: 'Please provide information',
969
- requestedSchema: {
970
- /* schema */
971
- },
961
+ requestedSchema: {/* schema */},
972
962
  })
973
963
 
974
964
  return result
@@ -1159,9 +1149,7 @@ const mcpServer = new MCPServer({
1159
1149
  id: 'protected-server',
1160
1150
  name: 'Protected MCP Server',
1161
1151
  version: '1.0.0',
1162
- tools: {
1163
- /* your tools */
1164
- },
1152
+ tools: {/* your tools */},
1165
1153
  })
1166
1154
 
1167
1155
  // Create OAuth middleware
@@ -1342,21 +1330,48 @@ To pass data to your tools, populate `req.auth` on the Node.js request object in
1342
1330
  ```typescript
1343
1331
  import express from 'express'
1344
1332
 
1333
+ type MCPAuthenticatedRequest = express.Request & {
1334
+ auth?: {
1335
+ token: string
1336
+ clientId: string
1337
+ scopes: string[]
1338
+ expiresAt?: number
1339
+ extra?: Record<string, unknown>
1340
+ }
1341
+ }
1342
+
1345
1343
  const app = express()
1346
1344
 
1347
1345
  // Auth middleware - set req.auth before the MCP handler
1348
- app.use('/mcp', (req, res, next) => {
1349
- const token = req.headers.authorization?.replace('Bearer ', '')
1350
- const user = verifyToken(token)
1351
-
1352
- // This entire object becomes context.mcp.extra.authInfo
1353
- // @ts-ignore - req.auth is read by the MCP SDK
1354
- req.auth = {
1355
- token,
1356
- userId: user.userId,
1357
- email: user.email,
1346
+ app.use('/mcp', async (req, res, next) => {
1347
+ const authorization = req.headers.authorization
1348
+
1349
+ if (!authorization?.startsWith('Bearer ')) {
1350
+ res.status(401).json({ error: 'Missing bearer token' })
1351
+ return
1352
+ }
1353
+
1354
+ const token = authorization.slice('Bearer '.length)
1355
+
1356
+ try {
1357
+ const user = await verifyToken(token)
1358
+
1359
+ // This entire object becomes context.mcp.extra.authInfo
1360
+ const authenticatedRequest = req as MCPAuthenticatedRequest
1361
+ authenticatedRequest.auth = {
1362
+ token,
1363
+ clientId: user.clientId,
1364
+ scopes: user.scopes,
1365
+ expiresAt: user.expiresAt,
1366
+ extra: {
1367
+ userId: user.userId,
1368
+ email: user.email,
1369
+ },
1370
+ }
1371
+ next()
1372
+ } catch {
1373
+ res.status(401).json({ error: 'Invalid or expired token' })
1358
1374
  }
1359
- next()
1360
1375
  })
1361
1376
 
1362
1377
  app.all('/mcp', async (req, res) => {
@@ -1434,19 +1449,86 @@ The full `context.mcp.extra` object contains:
1434
1449
 
1435
1450
  ### Complete Example
1436
1451
 
1437
- Here's a complete example showing the data flow from middleware to tool:
1452
+ Install [`jose`](https://github.com/panva/jose) to verify JSON Web Tokens (JWTs) against your identity provider's JSON Web Key Set (JWKS):
1453
+
1454
+ **npm**:
1455
+
1456
+ ```shell
1457
+ npm install jose
1458
+ ```
1459
+
1460
+ **pnpm**:
1461
+
1462
+ ```shell
1463
+ pnpm add jose
1464
+ ```
1465
+
1466
+ **Yarn**:
1467
+
1468
+ ```shell
1469
+ yarn add jose
1470
+ ```
1471
+
1472
+ **Bun**:
1473
+
1474
+ ```shell
1475
+ bun add jose
1476
+ ```
1477
+
1478
+ The following example validates the token's signature, issuer, audience, algorithm, expiration, and required claims before passing its user data to the tool:
1438
1479
 
1439
1480
  ```typescript
1440
1481
  import express from 'express'
1482
+ import { createRemoteJWKSet, jwtVerify } from 'jose'
1441
1483
  import { MCPServer } from '@mastra/mcp'
1442
1484
  import { createTool } from '@mastra/core/tools'
1443
1485
  import { z } from 'zod'
1444
1486
 
1445
- const verifyToken = (token: string) => {
1446
- // TODO: Implement token verification
1487
+ type MCPAuthenticatedRequest = express.Request & {
1488
+ auth?: {
1489
+ token: string
1490
+ clientId: string
1491
+ scopes: string[]
1492
+ expiresAt?: number
1493
+ extra?: Record<string, unknown>
1494
+ }
1495
+ }
1496
+
1497
+ const issuer = process.env.JWT_ISSUER
1498
+ const audience = process.env.JWT_AUDIENCE
1499
+ const jwksUri = process.env.JWT_JWKS_URI
1500
+
1501
+ if (!issuer || !audience || !jwksUri) {
1502
+ throw new Error('JWT_ISSUER, JWT_AUDIENCE, and JWT_JWKS_URI are required')
1503
+ }
1504
+
1505
+ const jwks = createRemoteJWKSet(new URL(jwksUri))
1506
+
1507
+ const verifyToken = async (token: string) => {
1508
+ const { payload } = await jwtVerify(token, jwks, {
1509
+ issuer,
1510
+ audience,
1511
+ algorithms: ['RS256'],
1512
+ requiredClaims: ['exp'],
1513
+ })
1514
+
1515
+ const clientId =
1516
+ typeof payload.client_id === 'string'
1517
+ ? payload.client_id
1518
+ : typeof payload.azp === 'string'
1519
+ ? payload.azp
1520
+ : undefined
1521
+
1522
+ if (!payload.sub || typeof payload.email !== 'string' || !clientId || !payload.exp) {
1523
+ throw new Error('Token must contain sub, email, exp, and client_id or azp claims')
1524
+ }
1525
+
1447
1526
  return {
1448
- userId: '123',
1449
- email: 'test@test.com',
1527
+ userId: payload.sub,
1528
+ clientId,
1529
+ email: payload.email,
1530
+ expiresAt: payload.exp,
1531
+ scopes: typeof payload.scope === 'string' ? payload.scope.split(' ') : [],
1450
1532
  }
1451
1533
  }
1452
1534
 
@@ -1481,18 +1563,35 @@ const server = new MCPServer({
1481
1563
  // 3. Set up Express with auth middleware
1482
1564
  const app = express()
1483
1565
 
1484
- app.use('/mcp', (req, res, next) => {
1485
- const token = req.headers.authorization?.replace('Bearer ', '')
1486
- const user = verifyToken(token)
1566
+ app.use('/mcp', async (req, res, next) => {
1567
+ const authorization = req.headers.authorization
1568
+
1569
+ if (!authorization?.startsWith('Bearer ')) {
1570
+ res.status(401).json({ error: 'Missing bearer token' })
1571
+ return
1572
+ }
1487
1573
 
1488
- // This entire object becomes context.mcp.extra.authInfo
1489
- // @ts-ignore - req.auth is read by the MCP SDK
1490
- req.auth = {
1491
- token,
1492
- userId: user.userId,
1493
- email: user.email,
1574
+ const token = authorization.slice('Bearer '.length)
1575
+
1576
+ try {
1577
+ const user = await verifyToken(token)
1578
+
1579
+ // This entire object becomes context.mcp.extra.authInfo
1580
+ const authenticatedRequest = req as MCPAuthenticatedRequest
1581
+ authenticatedRequest.auth = {
1582
+ token,
1583
+ clientId: user.clientId,
1584
+ scopes: user.scopes,
1585
+ expiresAt: user.expiresAt,
1586
+ extra: {
1587
+ userId: user.userId,
1588
+ email: user.email,
1589
+ },
1590
+ }
1591
+ next()
1592
+ } catch {
1593
+ res.status(401).json({ error: 'Invalid or expired token' })
1494
1594
  }
1495
- next()
1496
1595
  })
1497
1596
 
1498
1597
  app.all('/mcp', async (req, res) => {