@mastra/mcp-docs-server 1.2.8-alpha.2 → 1.2.8-alpha.5

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.
@@ -197,9 +197,9 @@ If your tools aren't being called when structured output is enabled, or you rece
197
197
 
198
198
  When your model doesn't support tools and structured output together, you have three options:
199
199
 
200
- 1. **Use `jsonPromptInjection: true`** - Injects the schema into the prompt instead of using the API's `response_format` parameter
201
- 2. **Use a separate structuring model** - Pass a `model` to `structuredOutput` to use a second LLM for structuring
202
- 3. **Use `prepareStep`** - Handle tools and structured output in separate steps
200
+ 1. **Use `jsonPromptInjection`**: Set it to `'auto'` to select native structured output when supported and inline prompt injection otherwise, or choose an explicit injection mode
201
+ 2. **Use a separate structuring model**: Pass a `model` to `structuredOutput` to use a second LLM for structuring
202
+ 3. **Use `prepareStep`**: Handle tools and structured output in separate steps
203
203
 
204
204
  Each approach is detailed in the sections below.
205
205
 
@@ -209,9 +209,7 @@ Structured output support varies across LLMs due to differences in their APIs. T
209
209
 
210
210
  ### `jsonPromptInjection`
211
211
 
212
- By default, Mastra passes the schema to the model provider using the `response_format` API parameter. Most model providers have built-in support for this, which reliably enforces the schema.
213
-
214
- If your model provider doesn't support `response_format`, you'll get an error from the API. When this happens, set `jsonPromptInjection: true`. This adds the schema to the system prompt instead, instructing the model to output JSON. This is less reliable than the API parameter approach.
212
+ By default, Mastra passes the schema to the model provider using the `response_format` API parameter. Set `jsonPromptInjection: 'auto'` to let Mastra choose the mode from its model capability data. Mastra uses native structured output for supported models and inline prompt injection for unsupported models or models without capability data.
215
213
 
216
214
  ```typescript
217
215
  import { z } from 'zod'
@@ -224,13 +222,20 @@ const response = await testAgent.generate('Help me plan my day.', {
224
222
  activities: z.array(z.string()),
225
223
  }),
226
224
  ),
227
- jsonPromptInjection: true,
225
+ jsonPromptInjection: 'auto',
228
226
  },
229
227
  })
230
228
 
231
229
  console.log(response.object)
232
230
  ```
233
231
 
232
+ Use an explicit mode when you need to override the capability-based choice:
233
+
234
+ - `false` or omitted: Use the provider's native structured output.
235
+ - `'inline'`: Add the schema instructions to the latest user message.
236
+ - `true` or `'system'`: Add the schema instructions to the system message.
237
+ - `'auto'`: Use native structured output when the model supports it. Otherwise, use inline prompt injection.
238
+
234
239
  > **Gemini 2.5 with tools:** Gemini 2.5 models don't support combining `response_format` (structured output) with function calling (tools) in the same API call. If your agent has tools and you're using `structuredOutput` with a Gemini 2.5 model, you must set `jsonPromptInjection: true` to avoid the error `Function calling with a response mime type: 'application/json' is unsupported`.
235
240
  >
236
241
  > ```typescript
@@ -0,0 +1,178 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
3
+ # Multi-turn evals
4
+
5
+ Multi-turn evals test agent behavior across a conversation. Instead of a single `input`, you provide an `inputs` array. Each entry is sent sequentially to the agent on the same thread, and scorers see the accumulated output from all turns.
6
+
7
+ ## When to use multi-turn evals
8
+
9
+ - The agent uses memory and must recall context from earlier turns
10
+ - The agent handles follow-up questions that depend on prior responses
11
+ - You need to verify tool-call sequences across a conversation
12
+ - The agent runs a multi-step workflow (search, confirm, execute)
13
+
14
+ ## Quickstart
15
+
16
+ ```typescript
17
+ import { runEvals } from '@mastra/core/evals'
18
+ import { checks } from '@mastra/evals/checks'
19
+ import { weatherAgent } from '../agents'
20
+
21
+ const result = await runEvals({
22
+ data: [
23
+ {
24
+ inputs: [
25
+ 'What is the weather in Brooklyn?',
26
+ 'What about tomorrow?',
27
+ 'Compare the two forecasts.',
28
+ ],
29
+ },
30
+ ],
31
+ target: weatherAgent,
32
+ scorers: [checks.calledTool('get_weather', { times: 2 }), checks.includes('Brooklyn')],
33
+ })
34
+ ```
35
+
36
+ Each turn runs `agent.generate()` with the same thread ID, so the agent sees the full conversation history. Scorers receive the accumulated output messages from all turns.
37
+
38
+ ## Memory is required for cross-turn recall
39
+
40
+ Multi-turn recall depends on the agent having a **memory store configured**. The shared thread ID is what lets each turn see the earlier ones — but a thread only persists history when the agent has memory. If the agent has no memory configured, the turns still run sequentially and their outputs still accumulate for scoring, but the agent will not recall earlier turns (each input runs in isolation). `runEvals` logs a warning when you use `inputs` on an agent without memory.
41
+
42
+ `runEvals` manages the conversation identity for you: it generates the shared `threadId` and injects a `resourceId` (Mastra memory scopes messages by resource + thread, so both are required for recall to work). By default the resource is derived from the generated thread so each conversation is isolated. To pin a specific resource — for example to reuse an existing user's memory — pass `targetOptions.memory.resource`; `runEvals` still owns the thread, so you don't provide one:
43
+
44
+ ```typescript
45
+ await runEvals({
46
+ target: weatherAgent,
47
+ data: [{ inputs: ['What is the weather in Brooklyn?', 'What about tomorrow?'] }],
48
+ scorers: [checks.similarity('weather forecast')],
49
+ targetOptions: { memory: { resource: 'user-42' } },
50
+ })
51
+ ```
52
+
53
+ See [Memory](https://mastra.ai/docs/memory/overview) for how to configure a memory store.
54
+
55
+ ## How it works
56
+
57
+ When a data item has an `inputs` array, `runEvals`:
58
+
59
+ 1. Creates a fresh thread (unique `threadId`) and resource for the conversation (a caller-provided `targetOptions.memory.resource` is preserved)
60
+ 2. Sends each input sequentially via `agent.generate()` on that thread
61
+ 3. Accumulates all output messages across turns
62
+ 4. Passes the full accumulated output to scorers for evaluation
63
+
64
+ Scorers see the complete conversation output, not just the last turn.
65
+
66
+ ## Scoring semantics
67
+
68
+ Two details matter when writing scorers for multi-turn items:
69
+
70
+ - **`run.output` is the accumulated output from every turn.** Output-based scorers — `checks.includes`, `checks.calledTool`, `checks.similarity`, and similar — evaluate the whole conversation. For example, `checks.calledTool('get_weather', { times: 2 })` counts calls across all turns.
71
+ - **`run.input` is only the first turn's input.** Scorers that compare input against output (faithfulness, answer relevancy, and other input-relative LLM scorers) only see the first user message, not the full conversation. Prefer output-based checks for multi-turn, or build scorers that read the accumulated `run.output` directly.
72
+
73
+ ## Per-turn assertions with `turns`
74
+
75
+ The `inputs` form scores the **accumulated** output holistically — a single score over every turn's output. That can hide per-turn failures: an output-based check like `checks.includes('Brooklyn')` passes if _any_ turn mentions Brooklyn, even when the follow-up turn is broken.
76
+
77
+ When you need to assert that a **specific turn** behaved correctly, use `turns` instead. Each turn is an object with its own `input` and optional `gates`/`scorers` that evaluate **only that turn's** input and output:
78
+
79
+ ```typescript
80
+ import { runEvals } from '@mastra/core/evals'
81
+ import { checks } from '@mastra/evals/checks'
82
+ import { weatherAgent } from '../agents'
83
+
84
+ const result = await runEvals({
85
+ data: [
86
+ {
87
+ turns: [
88
+ {
89
+ input: 'What is the weather in Brooklyn?',
90
+ gates: [checks.calledTool('get_weather')],
91
+ },
92
+ {
93
+ // The follow-up must call the tool again — it can't be satisfied
94
+ // by the first turn's tool call.
95
+ input: 'What about tomorrow?',
96
+ gates: [checks.calledTool('get_weather')],
97
+ scorers: [{ scorer: checks.similarity('tomorrow forecast'), threshold: 0.5 }],
98
+ },
99
+ ],
100
+ },
101
+ ],
102
+ target: weatherAgent,
103
+ })
104
+
105
+ result.verdict // 'passed' | 'scored' | 'failed'
106
+ result.turnResults // per-turn gate/threshold/scorer outcomes
107
+ ```
108
+
109
+ Semantics:
110
+
111
+ - A per-turn gate or scorer sees **only that turn's** `run.input` and `run.output` — never the accumulated conversation. This fixes both blind spots of `inputs`: the wrong turn can't satisfy a check, and `run.input` is correct for each turn.
112
+ - Per-turn outcomes fold into the overall [verdict](https://mastra.ai/docs/evals/gates-and-verdicts): a failing turn gate makes the verdict `failed`; a missed turn threshold (with gates passing) makes it `scored`.
113
+ - `result.turnResults[i]` reports each turn's `gateResults`, `thresholdResults`, and `scores`, so a failure points at the exact turn. Across multiple conversations, turn results are averaged by turn index.
114
+ - A turn with no `gates`/`scorers` just advances the conversation.
115
+ - Top-level `scorers`/`gates` still run holistically over the accumulated output, so you can combine "this turn must call the tool" with "the overall answer mentions Brooklyn."
116
+ - When the agent has storage configured, each per-turn scorer/gate result is persisted like top-level scores, so per-turn outcomes appear in your scores store.
117
+
118
+ Use `inputs` when a single holistic score over the whole conversation is enough; use `turns` when correctness depends on individual turns. `turns` cannot be combined with `input` or `inputs` in the same data item.
119
+
120
+ ## Combining with gates and thresholds
121
+
122
+ Multi-turn data items work with [gates and verdicts](https://mastra.ai/docs/evals/gates-and-verdicts). Use output-based scorers so gating reflects the full conversation:
123
+
124
+ ```typescript
125
+ import { runEvals } from '@mastra/core/evals'
126
+ import { checks } from '@mastra/evals/checks'
127
+
128
+ const result = await runEvals({
129
+ data: [
130
+ {
131
+ inputs: ['My favorite city is Brooklyn.', 'What is the weather in my favorite city?'],
132
+ },
133
+ ],
134
+ target: weatherAgent,
135
+ gates: [checks.calledTool('get_weather')],
136
+ scorers: [{ scorer: checks.similarity('Brooklyn weather forecast'), threshold: 0.5 }],
137
+ })
138
+
139
+ result.verdict // 'passed' | 'scored' | 'failed'
140
+ ```
141
+
142
+ ## Mixing single-turn and multi-turn
143
+
144
+ A single `runEvals` call can include both single-turn and multi-turn data items:
145
+
146
+ ```typescript
147
+ const result = await runEvals({
148
+ data: [
149
+ { input: 'What is the weather in Brooklyn?' },
150
+ {
151
+ inputs: ['My favorite city is Brooklyn.', 'What is the weather in my favorite city?'],
152
+ },
153
+ ],
154
+ target: weatherAgent,
155
+ scorers: [checks.includes('Brooklyn')],
156
+ })
157
+ ```
158
+
159
+ Single-turn items use `input` as usual. Multi-turn items use `inputs` — `input` can be omitted entirely.
160
+
161
+ ## Validation
162
+
163
+ `runEvals` throws a `MastraError` if `inputs` is present but empty:
164
+
165
+ ```typescript
166
+ // Throws: 'inputs' must be a non-empty array
167
+ await runEvals({
168
+ data: [{ inputs: [] }],
169
+ target: myAgent,
170
+ scorers: [myScorer],
171
+ })
172
+ ```
173
+
174
+ ## Related
175
+
176
+ - [`runEvals()` reference](https://mastra.ai/reference/evals/run-evals): Full API for `runEvals` parameters and returns
177
+ - [Gates and verdicts](https://mastra.ai/docs/evals/gates-and-verdicts): Enforce hard requirements and quality thresholds
178
+ - [Quick Checks](https://mastra.ai/docs/evals/quick-checks): Zero-LLM composable micro-scorers
@@ -142,6 +142,7 @@ Once registered, you can score traces interactively within Studio under the **Ob
142
142
 
143
143
  - Use [Quick Checks](https://mastra.ai/docs/evals/quick-checks) for fast, deterministic assertions on text and tool usage
144
144
  - Add [gates and verdicts](https://mastra.ai/docs/evals/gates-and-verdicts) to enforce hard requirements and quality thresholds
145
+ - Test [multi-turn conversations](https://mastra.ai/docs/evals/multi-turn) where behavior emerges across sequential turns
145
146
  - Learn how to create your own scorers in the [Creating Custom Scorers](https://mastra.ai/docs/evals/custom-scorers) guide
146
147
  - Explore built-in scorers in the [Built-in Scorers](https://mastra.ai/docs/evals/built-in-scorers) section
147
148
  - Test scorers with [Studio](https://mastra.ai/docs/studio/overview)
@@ -64,7 +64,7 @@ export const testMcpClient = new MCPClient({
64
64
 
65
65
  Visit [MCPClient](https://mastra.ai/reference/tools/mcp-client) for a full list of configuration options.
66
66
 
67
- > **Authentication:** For connecting to OAuth-protected MCP servers, see the [OAuth Authentication](https://mastra.ai/reference/tools/mcp-client) section.
67
+ > **Authentication:** For connecting to OAuth-protected MCP servers — including completing the browser-based authorization flow with `authenticate()` — see the [OAuth Authentication](https://mastra.ai/reference/tools/mcp-client) section.
68
68
 
69
69
  ## Using `MCPClient` with an agent
70
70
 
@@ -247,6 +247,8 @@ auth: {
247
247
  }
248
248
  ```
249
249
 
250
+ When the resource ID is derived this way, clients can omit `memory.resource` from agent generate and stream request bodies — the server-derived value is used instead (and always takes precedence over any client-provided value). If a request uses memory and neither the body nor the request context provides a resource ID, the server responds with a 400 error.
251
+
250
252
  You can also set these keys manually in middleware:
251
253
 
252
254
  ```typescript
@@ -2,7 +2,7 @@
2
2
 
3
3
  # ![OpenRouter logo](https://models.dev/logos/openrouter.svg)OpenRouter
4
4
 
5
- OpenRouter aggregates models from multiple providers with enhanced features like rate limiting and failover. Access 342 models through Mastra's model router.
5
+ OpenRouter aggregates models from multiple providers with enhanced features like rate limiting and failover. Access 341 models through Mastra's model router.
6
6
 
7
7
  Learn more in the [OpenRouter documentation](https://openrouter.ai/models).
8
8
 
@@ -73,7 +73,6 @@ ANTHROPIC_API_KEY=ant-...
73
73
  | `anthropic/claude-sonnet-4.5` |
74
74
  | `anthropic/claude-sonnet-4.6` |
75
75
  | `anthropic/claude-sonnet-5` |
76
- | `arcee-ai/coder-large` |
77
76
  | `arcee-ai/trinity-large-thinking` |
78
77
  | `arcee-ai/virtuoso-large` |
79
78
  | `baidu/ernie-4.5-vl-424b-a47b` |
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Model Providers
4
4
 
5
- Mastra provides a unified interface for working with LLMs across multiple providers, giving you access to 4867 models from 155 providers through a single API.
5
+ Mastra provides a unified interface for working with LLMs across multiple providers, giving you access to 4844 models from 155 providers through a single API.
6
6
 
7
7
  ## Features
8
8
 
@@ -49,8 +49,8 @@ for await (const chunk of stream) {
49
49
  | `google/gemini-3.1-pro-preview-customtools` | 1.0M | | | | | | $2 | $12 |
50
50
  | `google/gemini-3.5-flash` | 1.0M | | | | | | $2 | $9 |
51
51
  | `google/gemini-embedding-001` | 2K | | | | | | $0.15 | — |
52
- | `google/gemini-flash-latest` | 1.0M | | | | | | $0.30 | $3 |
53
- | `google/gemini-flash-lite-latest` | 1.0M | | | | | | $0.10 | $0.40 |
52
+ | `google/gemini-flash-latest` | 1.0M | | | | | | $2 | $9 |
53
+ | `google/gemini-flash-lite-latest` | 1.0M | | | | | | $0.25 | $2 |
54
54
  | `google/gemini-omni-flash-preview` | 131K | | | | | | $2 | $18 |
55
55
  | `google/gemma-4-26b-a4b-it` | 262K | | | | | | — | — |
56
56
  | `google/gemma-4-31b-it` | 262K | | | | | | — | — |
@@ -39,7 +39,7 @@ for await (const chunk of stream) {
39
39
  | `kilo/~anthropic/claude-haiku-latest` | 200K | | | | | | $1 | $5 |
40
40
  | `kilo/~anthropic/claude-opus-latest` | 1.0M | | | | | | $5 | $25 |
41
41
  | `kilo/~anthropic/claude-sonnet-latest` | 1.0M | | | | | | $3 | $15 |
42
- | `kilo/~google/gemini-flash-latest` | 1.0M | | | | | | $0.50 | $3 |
42
+ | `kilo/~google/gemini-flash-latest` | 1.0M | | | | | | $2 | $9 |
43
43
  | `kilo/~google/gemini-pro-latest` | 1.0M | | | | | | $2 | $12 |
44
44
  | `kilo/~moonshotai/kimi-latest` | 262K | | | | | | $0.74 | $3 |
45
45
  | `kilo/~openai/gpt-latest` | 1.1M | | | | | | $5 | $30 |
@@ -2,7 +2,7 @@
2
2
 
3
3
  # ![Kimi For Coding logo](https://models.dev/logos/kimi-for-coding.svg)Kimi For Coding
4
4
 
5
- Access 4 Kimi For Coding models through Mastra's model router. Authentication is handled automatically using the `KIMI_API_KEY` environment variable.
5
+ Access 5 Kimi For Coding models through Mastra's model router. Authentication is handled automatically using the `KIMI_API_KEY` environment variable.
6
6
 
7
7
  Learn more in the [Kimi For Coding documentation](https://www.kimi.com/code/docs/en/third-party-tools/other-coding-agents.html).
8
8
 
@@ -34,12 +34,13 @@ for await (const chunk of stream) {
34
34
 
35
35
  ## Models
36
36
 
37
- | Model | Context | Tools | Reasoning | Image | Audio | Video | Input $/1M | Output $/1M |
38
- | ---------------------------------- | ------- | ----- | --------- | ----- | ----- | ----- | ---------- | ----------- |
39
- | `kimi-for-coding/k2p5` | 262K | | | | | | — | — |
40
- | `kimi-for-coding/k2p6` | 262K | | | | | | — | — |
41
- | `kimi-for-coding/k2p7` | 262K | | | | | | — | — |
42
- | `kimi-for-coding/kimi-k2-thinking` | 262K | | | | | | — | — |
37
+ | Model | Context | Tools | Reasoning | Image | Audio | Video | Input $/1M | Output $/1M |
38
+ | ------------------------------------------- | ------- | ----- | --------- | ----- | ----- | ----- | ---------- | ----------- |
39
+ | `kimi-for-coding/k2p5` | 262K | | | | | | — | — |
40
+ | `kimi-for-coding/k2p6` | 262K | | | | | | — | — |
41
+ | `kimi-for-coding/k2p7` | 262K | | | | | | — | — |
42
+ | `kimi-for-coding/kimi-for-coding-highspeed` | 262K | | | | | | — | — |
43
+ | `kimi-for-coding/kimi-k2-thinking` | 262K | | | | | | — | — |
43
44
 
44
45
  ## Advanced configuration
45
46
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # ![LLM Gateway logo](https://models.dev/logos/llmgateway.svg)LLM Gateway
4
4
 
5
- Access 176 LLM Gateway models through Mastra's model router. Authentication is handled automatically using the `LLMGATEWAY_API_KEY` environment variable.
5
+ Access 170 LLM Gateway models through Mastra's model router. Authentication is handled automatically using the `LLMGATEWAY_API_KEY` environment variable.
6
6
 
7
7
  Learn more in the [LLM Gateway documentation](https://llmgateway.io/docs).
8
8
 
@@ -53,7 +53,6 @@ for await (const chunk of stream) {
53
53
  | `llmgateway/claude-sonnet-5` | 1.0M | | | | | | $2 | $10 |
54
54
  | `llmgateway/codestral-2508` | 256K | | | | | | $0.30 | $0.90 |
55
55
  | `llmgateway/custom` | 128K | | | | | | — | — |
56
- | `llmgateway/deepseek-v3.1` | 128K | | | | | | $0.56 | $2 |
57
56
  | `llmgateway/deepseek-v3.2` | 164K | | | | | | $0.26 | $0.38 |
58
57
  | `llmgateway/deepseek-v4-flash` | 1.1M | | | | | | $0.14 | $0.28 |
59
58
  | `llmgateway/deepseek-v4-pro` | 1.1M | | | | | | $0.43 | $0.87 |
@@ -135,7 +134,6 @@ for await (const chunk of stream) {
135
134
  | `llmgateway/kimi-k2.7-code` | 262K | | | | | | $0.95 | $4 |
136
135
  | `llmgateway/kimi-k2.7-code-highspeed` | 262K | | | | | | $2 | $8 |
137
136
  | `llmgateway/llama-3-70b-instruct` | 8K | | | | | | $0.51 | $0.74 |
138
- | `llmgateway/llama-3-8b-instruct` | 8K | | | | | | $0.04 | $0.04 |
139
137
  | `llmgateway/llama-3.1-70b-instruct` | 128K | | | | | | $0.72 | $0.72 |
140
138
  | `llmgateway/llama-3.1-nemotron-ultra-253b` | 128K | | | | | | $0.60 | $2 |
141
139
  | `llmgateway/llama-3.2-11b-instruct` | 128K | | | | | | $0.07 | $0.33 |
@@ -152,7 +150,7 @@ for await (const chunk of stream) {
152
150
  | `llmgateway/minimax-m2.5-highspeed` | 205K | | | | | | $0.60 | $2 |
153
151
  | `llmgateway/minimax-m2.7` | 205K | | | | | | $0.30 | $1 |
154
152
  | `llmgateway/minimax-m2.7-highspeed` | 205K | | | | | | $0.60 | $2 |
155
- | `llmgateway/minimax-m3` | 512K | | | | | | $0.60 | $2 |
153
+ | `llmgateway/minimax-m3` | 524K | | | | | | $0.30 | $1 |
156
154
  | `llmgateway/minimax-text-01` | 1.0M | | | | | | $0.20 | $1 |
157
155
  | `llmgateway/ministral-14b-2512` | 262K | | | | | | $0.20 | $0.20 |
158
156
  | `llmgateway/ministral-3b-2512` | 131K | | | | | | $0.10 | $0.10 |
@@ -166,7 +164,6 @@ for await (const chunk of stream) {
166
164
  | `llmgateway/o3` | 200K | | | | | | $2 | $8 |
167
165
  | `llmgateway/o3-mini` | 200K | | | | | | $1 | $4 |
168
166
  | `llmgateway/o4-mini` | 200K | | | | | | $1 | $4 |
169
- | `llmgateway/pixtral-large-latest` | 128K | | | | | | $4 | $12 |
170
167
  | `llmgateway/qwen-coder-plus` | 131K | | | | | | $0.50 | $1 |
171
168
  | `llmgateway/qwen-flash` | 1.0M | | | | | | $0.05 | $0.40 |
172
169
  | `llmgateway/qwen-max` | 33K | | | | | | $2 | $6 |
@@ -181,7 +178,6 @@ for await (const chunk of stream) {
181
178
  | `llmgateway/qwen3-235b-a22b-thinking-2507` | 262K | | | | | | $0.20 | $0.60 |
182
179
  | `llmgateway/qwen3-30b-a3b-instruct-2507` | 262K | | | | | | $0.10 | $0.30 |
183
180
  | `llmgateway/qwen3-32b` | 33K | | | | | | $0.10 | $0.30 |
184
- | `llmgateway/qwen3-4b-fp8` | 128K | | | | | | $0.03 | $0.03 |
185
181
  | `llmgateway/qwen3-coder-30b-a3b-instruct` | 262K | | | | | | $0.07 | $0.27 |
186
182
  | `llmgateway/qwen3-coder-480b-a35b-instruct` | 262K | | | | | | $0.30 | $1 |
187
183
  | `llmgateway/qwen3-coder-flash` | 1.0M | | | | | | $0.30 | $2 |
@@ -193,8 +189,6 @@ for await (const chunk of stream) {
193
189
  | `llmgateway/qwen3-vl-235b-a22b-instruct` | 131K | | | | | | $0.30 | $2 |
194
190
  | `llmgateway/qwen3-vl-235b-a22b-thinking` | 131K | | | | | | $0.98 | $4 |
195
191
  | `llmgateway/qwen3-vl-30b-a3b-instruct` | 131K | | | | | | $0.20 | $0.70 |
196
- | `llmgateway/qwen3-vl-30b-a3b-thinking` | 131K | | | | | | $0.20 | $1 |
197
- | `llmgateway/qwen3-vl-8b-instruct` | 131K | | | | | | $0.08 | $0.50 |
198
192
  | `llmgateway/qwen3-vl-flash` | 262K | | | | | | $0.05 | $0.40 |
199
193
  | `llmgateway/qwen3-vl-plus` | 262K | | | | | | $0.20 | $2 |
200
194
  | `llmgateway/qwen3.5-9b` | 262K | | | | | | $0.10 | $0.15 |
@@ -2,7 +2,7 @@
2
2
 
3
3
  # ![Ollama Cloud logo](https://models.dev/logos/ollama-cloud.svg)Ollama Cloud
4
4
 
5
- Access 35 Ollama Cloud models through Mastra's model router. Authentication is handled automatically using the `OLLAMA_API_KEY` environment variable.
5
+ Access 18 Ollama Cloud models through Mastra's model router. Authentication is handled automatically using the `OLLAMA_API_KEY` environment variable.
6
6
 
7
7
  Learn more in the [Ollama Cloud documentation](https://docs.ollama.com/cloud).
8
8
 
@@ -17,7 +17,7 @@ const agent = new Agent({
17
17
  id: "my-agent",
18
18
  name: "My Agent",
19
19
  instructions: "You are a helpful assistant",
20
- model: "ollama-cloud/deepseek-v3.1:671b"
20
+ model: "ollama-cloud/deepseek-v4-flash"
21
21
  });
22
22
 
23
23
  // Generate a response
@@ -34,43 +34,26 @@ for await (const chunk of stream) {
34
34
 
35
35
  ## Models
36
36
 
37
- | Model | Context | Tools | Reasoning | Image | Audio | Video | Input $/1M | Output $/1M |
38
- | ------------------------------------- | ------- | ----- | --------- | ----- | ----- | ----- | ---------- | ----------- |
39
- | `ollama-cloud/deepseek-v3.1:671b` | 164K | | | | | | — | — |
40
- | `ollama-cloud/deepseek-v3.2` | 164K | | | | | | — | — |
41
- | `ollama-cloud/deepseek-v4-flash` | 1.0M | | | | | | — | — |
42
- | `ollama-cloud/deepseek-v4-pro` | 1.0M | | | | | | — | — |
43
- | `ollama-cloud/devstral-2:123b` | 262K | | | | | | — | — |
44
- | `ollama-cloud/devstral-small-2:24b` | 262K | | | | | | — | — |
45
- | `ollama-cloud/gemini-3-flash-preview` | 1.0M | | | | | | — | — |
46
- | `ollama-cloud/gemma3:12b` | 131K | | | | | | — | — |
47
- | `ollama-cloud/gemma3:27b` | 131K | | | | | | — | — |
48
- | `ollama-cloud/gemma3:4b` | 131K | | | | | | — | — |
49
- | `ollama-cloud/gemma4:31b` | 262K | | | | | | — | — |
50
- | `ollama-cloud/glm-4.7` | 203K | | | | | | — | — |
51
- | `ollama-cloud/glm-5` | 203K | | | | | | — | — |
52
- | `ollama-cloud/glm-5.1` | 203K | | | | | | — | — |
53
- | `ollama-cloud/glm-5.2` | 976K | | | | | | — | — |
54
- | `ollama-cloud/gpt-oss:120b` | 131K | | | | | | — | — |
55
- | `ollama-cloud/gpt-oss:20b` | 131K | | | | | | — | — |
56
- | `ollama-cloud/kimi-k2.5` | 262K | | | | | | — | — |
57
- | `ollama-cloud/kimi-k2.6` | 262K | | | | | | — | — |
58
- | `ollama-cloud/kimi-k2.7-code` | 262K | | | | | | — | — |
59
- | `ollama-cloud/minimax-m2.1` | 205K | | | | | | — | — |
60
- | `ollama-cloud/minimax-m2.5` | 205K | | | | | | — | — |
61
- | `ollama-cloud/minimax-m2.7` | 197K | | | | | | — | — |
62
- | `ollama-cloud/minimax-m3` | 512K | | | | | | — | — |
63
- | `ollama-cloud/ministral-3:14b` | 262K | | | | | | — | — |
64
- | `ollama-cloud/ministral-3:3b` | 262K | | | | | | — | — |
65
- | `ollama-cloud/ministral-3:8b` | 262K | | | | | | — | — |
66
- | `ollama-cloud/mistral-large-3:675b` | 262K | | | | | | — | — |
67
- | `ollama-cloud/nemotron-3-nano:30b` | 1.0M | | | | | | — | — |
68
- | `ollama-cloud/nemotron-3-super` | 262K | | | | | | — | — |
69
- | `ollama-cloud/nemotron-3-ultra` | 262K | | | | | | — | — |
70
- | `ollama-cloud/qwen3-coder-next` | 262K | | | | | | — | — |
71
- | `ollama-cloud/qwen3-coder:480b` | 262K | | | | | | — | — |
72
- | `ollama-cloud/qwen3.5:397b` | 262K | | | | | | — | — |
73
- | `ollama-cloud/rnj-1:8b` | 33K | | | | | | — | — |
37
+ | Model | Context | Tools | Reasoning | Image | Audio | Video | Input $/1M | Output $/1M |
38
+ | ----------------------------------- | ------- | ----- | --------- | ----- | ----- | ----- | ---------- | ----------- |
39
+ | `ollama-cloud/deepseek-v4-flash` | 1.0M | | | | | | — | — |
40
+ | `ollama-cloud/deepseek-v4-pro` | 1.0M | | | | | | — | — |
41
+ | `ollama-cloud/gemma4:31b` | 262K | | | | | | — | — |
42
+ | `ollama-cloud/glm-5.1` | 203K | | | | | | — | — |
43
+ | `ollama-cloud/glm-5.2` | 976K | | | | | | — | — |
44
+ | `ollama-cloud/gpt-oss:120b` | 131K | | | | | | — | — |
45
+ | `ollama-cloud/gpt-oss:20b` | 131K | | | | | | — | — |
46
+ | `ollama-cloud/kimi-k2.5` | 262K | | | | | | — | — |
47
+ | `ollama-cloud/kimi-k2.6` | 262K | | | | | | — | — |
48
+ | `ollama-cloud/kimi-k2.7-code` | 262K | | | | | | — | — |
49
+ | `ollama-cloud/minimax-m2.5` | 205K | | | | | | — | — |
50
+ | `ollama-cloud/minimax-m2.7` | 197K | | | | | | — | — |
51
+ | `ollama-cloud/minimax-m3` | 512K | | | | | | — | — |
52
+ | `ollama-cloud/mistral-large-3:675b` | 262K | | | | | | — | — |
53
+ | `ollama-cloud/nemotron-3-nano:30b` | 1.0M | | | | | | — | — |
54
+ | `ollama-cloud/nemotron-3-super` | 262K | | | | | | — | — |
55
+ | `ollama-cloud/nemotron-3-ultra` | 262K | | | | | | — | — |
56
+ | `ollama-cloud/qwen3.5:397b` | 262K | | | | | | — | — |
74
57
 
75
58
  ## Advanced configuration
76
59
 
@@ -82,7 +65,7 @@ const agent = new Agent({
82
65
  name: "custom-agent",
83
66
  model: {
84
67
  url: "https://ollama.com/v1",
85
- id: "ollama-cloud/deepseek-v3.1:671b",
68
+ id: "ollama-cloud/deepseek-v4-flash",
86
69
  apiKey: process.env.OLLAMA_API_KEY,
87
70
  headers: {
88
71
  "X-Custom-Header": "value"
@@ -100,8 +83,8 @@ const agent = new Agent({
100
83
  model: ({ requestContext }) => {
101
84
  const useAdvanced = requestContext.task === "complex";
102
85
  return useAdvanced
103
- ? "ollama-cloud/rnj-1:8b"
104
- : "ollama-cloud/deepseek-v3.1:671b";
86
+ ? "ollama-cloud/qwen3.5:397b"
87
+ : "ollama-cloud/deepseek-v4-flash";
105
88
  }
106
89
  });
107
90
  ```
@@ -57,7 +57,7 @@ for await (const chunk of stream) {
57
57
  | `orcarouter/google/gemini-3.1-flash-lite-preview` | 1.0M | | | | | | $0.25 | $2 |
58
58
  | `orcarouter/google/gemini-3.1-pro-preview` | 1.0M | | | | | | $4 | $18 |
59
59
  | `orcarouter/google/gemini-3.1-pro-preview-customtools` | 1.0M | | | | | | $4 | $18 |
60
- | `orcarouter/google/gemini-flash-latest` | 1.0M | | | | | | $0.50 | $3 |
60
+ | `orcarouter/google/gemini-flash-latest` | 1.0M | | | | | | $2 | $9 |
61
61
  | `orcarouter/google/gemini-flash-lite-latest` | 1.0M | | | | | | $0.25 | $2 |
62
62
  | `orcarouter/google/gemma-4-26b-a4b-it` | 262K | | | | | | $0.06 | $0.33 |
63
63
  | `orcarouter/google/gemma-4-31b-it` | 262K | | | | | | $0.13 | $0.38 |
@@ -17,7 +17,7 @@ const agent = new Agent({
17
17
  id: "my-agent",
18
18
  name: "My Agent",
19
19
  instructions: "You are a helpful assistant",
20
- model: "privatemode-ai/gemma-3-27b"
20
+ model: "privatemode-ai/gpt-oss-120b"
21
21
  });
22
22
 
23
23
  // Generate a response
@@ -34,13 +34,13 @@ for await (const chunk of stream) {
34
34
 
35
35
  ## Models
36
36
 
37
- | Model | Context | Tools | Reasoning | Image | Audio | Video | Input $/1M | Output $/1M |
38
- | ------------------------------------ | ------- | ----- | --------- | ----- | ----- | ----- | ---------- | ----------- |
39
- | `privatemode-ai/gemma-3-27b` | 128K | | | | | | — | — |
40
- | `privatemode-ai/gpt-oss-120b` | 128K | | | | | | — | — |
41
- | `privatemode-ai/qwen3-coder-30b-a3b` | 128K | | | | | | — | — |
42
- | `privatemode-ai/qwen3-embedding-4b` | 32K | | | | | | — | — |
43
- | `privatemode-ai/whisper-large-v3` | — | | | | | | — | — |
37
+ | Model | Context | Tools | Reasoning | Image | Audio | Video | Input $/1M | Output $/1M |
38
+ | ----------------------------------- | ------- | ----- | --------- | ----- | ----- | ----- | ---------- | ----------- |
39
+ | `privatemode-ai/gpt-oss-120b` | 128K | | | | | | — | — |
40
+ | `privatemode-ai/kimi-k2.6` | 262K | | | | | | — | — |
41
+ | `privatemode-ai/qwen3-embedding-4b` | 32K | | | | | | — | — |
42
+ | `privatemode-ai/voxtral-mini-3b` | 32K | | | | | | — | — |
43
+ | `privatemode-ai/whisper-large-v3` | — | | | | | | — | — |
44
44
 
45
45
  ## Advanced configuration
46
46
 
@@ -52,7 +52,7 @@ const agent = new Agent({
52
52
  name: "custom-agent",
53
53
  model: {
54
54
  url: "http://localhost:8080/v1",
55
- id: "privatemode-ai/gemma-3-27b",
55
+ id: "privatemode-ai/gpt-oss-120b",
56
56
  apiKey: process.env.PRIVATEMODE_API_KEY,
57
57
  headers: {
58
58
  "X-Custom-Header": "value"
@@ -71,7 +71,7 @@ const agent = new Agent({
71
71
  const useAdvanced = requestContext.task === "complex";
72
72
  return useAdvanced
73
73
  ? "privatemode-ai/whisper-large-v3"
74
- : "privatemode-ai/gemma-3-27b";
74
+ : "privatemode-ai/gpt-oss-120b";
75
75
  }
76
76
  });
77
77
  ```
@@ -106,7 +106,7 @@ const result = await agent.generate('message for agent')
106
106
 
107
107
  **options.structuredOutput.instructions** (`string`): Additional instructions for the structured output model.
108
108
 
109
- **options.structuredOutput.jsonPromptInjection** (`boolean`): Injects system prompt into the main agent instructing it to return structured output, useful for when a model does not natively support structured outputs.
109
+ **options.structuredOutput.jsonPromptInjection** (`boolean | 'system' | 'inline' | 'auto'`): Controls how the JSON schema reaches the model. Set to 'auto' to use native structured output when supported and inline prompt injection otherwise.
110
110
 
111
111
  **options.structuredOutput.logger** (`IMastraLogger`): Optional logger instance for structured logging during output generation.
112
112
 
@@ -51,7 +51,7 @@ const scorer = createScorer({
51
51
 
52
52
  **judge.instructions** (`string`): System prompt/instructions for the LLM.
53
53
 
54
- **judge.jsonPromptInjection** (`boolean`): When true, inject the JSON schema into the prompt instead of using the provider's native response\_format API. Set this for models that don't support native structured output (e.g. some Groq Llama models) to avoid a wasted 400 call.
54
+ **judge.jsonPromptInjection** (`boolean | 'system' | 'inline' | 'auto'`): Controls how the judge's structured output schema reaches the model. Defaults to 'auto', which uses native structured output when supported and inline prompt injection otherwise. Explicit values override automatic routing.
55
55
 
56
56
  **judge.inputProcessors** (`Processor[]`): Input processors applied to the internal judge agent before its messages reach the model (e.g. redaction, validation).
57
57
 
@@ -31,6 +31,28 @@ console.log(`Average scores:`, result.scores)
31
31
  console.log(`Processed ${result.summary.totalItems} items`)
32
32
  ```
33
33
 
34
+ ### Multi-turn evaluation
35
+
36
+ ```typescript
37
+ import { runEvals } from '@mastra/core/evals'
38
+ import { checks } from '@mastra/evals/checks'
39
+ import { weatherAgent } from './agents/weather-agent'
40
+
41
+ const result = await runEvals({
42
+ target: weatherAgent,
43
+ data: [
44
+ {
45
+ inputs: [
46
+ 'What is the weather in Brooklyn?',
47
+ 'What about tomorrow?',
48
+ 'Compare the two forecasts.',
49
+ ],
50
+ },
51
+ ],
52
+ scorers: [checks.calledTool('get_weather', { times: 2 }), checks.includes('Brooklyn')],
53
+ })
54
+ ```
55
+
34
56
  ### With gates and thresholds
35
57
 
36
58
  ```typescript
@@ -60,7 +82,7 @@ result.thresholdResults // [{ id, passed, averageScore, threshold }]
60
82
 
61
83
  **gates** (`MastraScorer[]`): Scorers that must score 1.0 for the run to pass. If any gate averages below 1.0 across data items, the verdict is failed. Gates run before regular scorers on each data item. When provided, scorers may be omitted.
62
84
 
63
- **targetOptions** (`AgentExecutionOptions | WorkflowRunOptions`): Options forwarded to the target during execution. For agents: options passed to agent.generate() (e.g. maxSteps, modelSettings, instructions). For workflows: options passed to run.start() (e.g. perStep, outputOptions, initialState).
85
+ **targetOptions** (`AgentExecutionOptions | WorkflowRunOptions`): Options forwarded to the target during execution. For agents: options passed to agent.generate() (e.g. maxSteps, modelSettings, instructions). For workflows: options passed to run.start() (e.g. perStep, outputOptions, initialState). For multi-turn agent runs (inputs/turns), runEvals generates and injects the shared thread and a resource, so memory.thread is optional; provide memory.resource to reuse a specific resource.
64
86
 
65
87
  **concurrency** (`number`): Number of test cases to run concurrently. (Default: `1`)
66
88
 
@@ -68,7 +90,11 @@ result.thresholdResults // [{ id, passed, averageScore, threshold }]
68
90
 
69
91
  ## Data item structure
70
92
 
71
- **input** (`string | string[] | CoreMessage[] | any`): Input data for the target. For agents: messages or strings. For workflows: workflow input data.
93
+ **input** (`string | string[] | CoreMessage[] | any`): Input data for the target. For agents: messages or strings. For workflows: workflow input data. Optional when inputs is provided.
94
+
95
+ **inputs** (`(string | string[] | CoreMessage[] | any)[]`): Multi-turn inputs. Each entry is one turn (same shape as input) sent sequentially to the agent on the same thread. Scorers see the accumulated output from all turns. Only supported for Agent targets. When provided, input can be omitted. Mutually exclusive with turns.
96
+
97
+ **turns** (`EvalTurn[]`): Multi-turn conversation with per-turn assertions. Each turn is an object { input, gates?, scorers? } sent sequentially on the same thread; its gates/scorers evaluate only that turn's input and output. Per-turn outcomes are reported in turnResults and folded into the overall verdict. Only supported for Agent targets. Mutually exclusive with input and inputs.
72
98
 
73
99
  **groundTruth** (`any`): Expected or reference output for comparison during scoring.
74
100
 
@@ -112,6 +138,18 @@ For workflows, use `WorkflowScorerConfig` to specify scorers at different levels
112
138
 
113
139
  **thresholdResults** (`ThresholdResult[]`): Per-threshold-scorer results averaged across all data items. Each entry has id, passed, averageScore, and threshold.
114
140
 
141
+ **turnResults** (`TurnResult[]`): Present when any data item uses turns. Each entry has index (zero-based turn), optional gateResults, thresholdResults, and scores (bare-scorer averages keyed by scorer id), aggregated by turn index across data items.
142
+
143
+ ## EvalTurn
144
+
145
+ A single turn in a `turns` array. Its `gates`/`scorers` evaluate only that turn's input and output:
146
+
147
+ **input** (`string | string[] | CoreMessage[] | any`): The input sent to the agent for this turn.
148
+
149
+ **gates** (`MastraScorer[]`): Gates that must score 1.0 for this turn. A failing turn gate makes the overall verdict failed.
150
+
151
+ **scorers** (`ScorerEntry[]`): Scorers (optionally with thresholds) evaluated against this turn only. A missed per-turn threshold (with gates passing) makes the verdict scored.
152
+
115
153
  ## ScorerEntry
116
154
 
117
155
  A scorer entry in the `scorers` array can be either a bare scorer or a scorer with a threshold:
@@ -314,8 +352,62 @@ const result = await runEvals({
314
352
  })
315
353
  ```
316
354
 
355
+ ### Multi-turn conversation evaluation
356
+
357
+ Use `inputs` to send sequential turns on a shared thread. Scorers see the accumulated output from all turns:
358
+
359
+ ```typescript
360
+ const result = await runEvals({
361
+ target: chatAgent,
362
+ data: [
363
+ {
364
+ inputs: ['My favorite city is Brooklyn.', 'What is the weather in my favorite city?'],
365
+ },
366
+ ],
367
+ gates: [checks.calledTool('get_weather')],
368
+ scorers: [{ scorer: checks.similarity('Brooklyn weather forecast'), threshold: 0.5 }],
369
+ })
370
+
371
+ // result.verdict: 'passed' | 'scored' | 'failed'
372
+ ```
373
+
374
+ Each turn runs `agent.generate()` with the same `threadId`, so the agent sees the full conversation history. `runEvals` also injects a `resourceId` (Mastra memory scopes messages by resource + thread), defaulting it to the generated thread; pass `targetOptions.memory.resource` to pin a specific one. Cross-turn recall requires the agent to have a memory store configured; otherwise turns run in isolation. Mix single-turn (`input`) and multi-turn (`inputs`) items in the same `data` array. When using `inputs`, `input` can be omitted.
375
+
376
+ Scoring uses the accumulated output from all turns as `run.output`, but only the first turn as `run.input`. Prefer output-based scorers (`checks.includes`, `checks.calledTool`, `checks.similarity`) for multi-turn; input-relative scorers (e.g. faithfulness) only see the first turn's input. Trajectory scorers that read from the trace (`AgentScorerConfig.trajectory`) resolve against the last turn's span; tool-call checks that read `run.output` (like `checks.calledTool`) still see every turn.
377
+
378
+ ### Per-turn assertions
379
+
380
+ Use `turns` to attach `gates`/`scorers` to individual turns. Each per-turn assertion sees only that turn's input and output, so a later-turn regression can't be hidden by an earlier turn:
381
+
382
+ ```typescript
383
+ const result = await runEvals({
384
+ target: chatAgent,
385
+ data: [
386
+ {
387
+ turns: [
388
+ {
389
+ input: 'What is the weather in Brooklyn?',
390
+ gates: [checks.calledTool('get_weather')],
391
+ },
392
+ {
393
+ input: 'What about tomorrow?',
394
+ gates: [checks.calledTool('get_weather')], // must call again this turn
395
+ scorers: [{ scorer: checks.similarity('tomorrow forecast'), threshold: 0.5 }],
396
+ },
397
+ ],
398
+ },
399
+ ],
400
+ })
401
+
402
+ result.verdict // folds in per-turn gate/threshold outcomes
403
+ result.turnResults // [{ index, gateResults, thresholdResults, scores }]
404
+ ```
405
+
406
+ Per-turn gates/scorers evaluate only that turn (`run.input`/`run.output` are that turn's). A failing turn gate makes the verdict `failed`; a missed turn threshold (gates passing) makes it `scored`. Top-level `scorers`/`gates` still score the accumulated conversation holistically. `turns` is Agent-only and cannot be combined with `input` or `inputs`.
407
+
317
408
  ## Related
318
409
 
410
+ - [Multi-turn evals](https://mastra.ai/docs/evals/multi-turn): Conceptual guide to multi-turn evaluation
319
411
  - [Gates and Verdicts](https://mastra.ai/docs/evals/gates-and-verdicts): Conceptual guide to severity semantics
320
412
  - [Quick Checks](https://mastra.ai/reference/evals/checks): Zero-LLM composable micro-scorers
321
413
  - [createScorer()](https://mastra.ai/reference/evals/create-scorer): Create custom scorers for experiments
@@ -100,7 +100,7 @@ const stream = await agent.stream('message for agent')
100
100
 
101
101
  **options.structuredOutput.instructions** (`string`): Additional instructions for the structured output model.
102
102
 
103
- **options.structuredOutput.jsonPromptInjection** (`boolean`): Injects system prompt into the main agent instructing it to return structured output, useful for when a model does not natively support structured outputs.
103
+ **options.structuredOutput.jsonPromptInjection** (`boolean | 'system' | 'inline' | 'auto'`): Controls how the JSON schema reaches the model. Set to 'auto' to use native structured output when supported and inline prompt injection otherwise.
104
104
 
105
105
  **options.structuredOutput.providerOptions** (`ProviderOptions`): Provider-specific options passed to the internal structuring agent. Use this to control model behavior like reasoning effort for thinking models (e.g., { openai: { reasoningEffort: 'low' } }).
106
106
 
@@ -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.
@@ -821,6 +849,74 @@ const client = new MCPClient({
821
849
  })
822
850
  ```
823
851
 
852
+ 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.
853
+
854
+ ### Interactive browser authentication
855
+
856
+ 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:
857
+
858
+ ```typescript
859
+ import { MCPClient, MCPOAuthClientProvider } from '@mastra/mcp'
860
+
861
+ const oauthProvider = new MCPOAuthClientProvider({
862
+ redirectUrl: 'http://127.0.0.1:5533/oauth/callback',
863
+ clientMetadata: {
864
+ redirect_uris: ['http://127.0.0.1:5533/oauth/callback'],
865
+ client_name: 'My MCP Client',
866
+ grant_types: ['authorization_code', 'refresh_token'],
867
+ response_types: ['code'],
868
+ },
869
+ onRedirectToAuthorization: url => {
870
+ // Open the user's browser at the consent page
871
+ console.log(`Please visit: ${url}`)
872
+ },
873
+ })
874
+
875
+ const mcp = new MCPClient({
876
+ servers: {
877
+ protectedServer: {
878
+ url: new URL('https://mcp.example.com/mcp'),
879
+ authProvider: oauthProvider,
880
+ },
881
+ },
882
+ })
883
+
884
+ try {
885
+ await mcp.listTools()
886
+ } catch {
887
+ if (mcp.getServerAuthState('protectedServer') === 'needs-auth') {
888
+ await mcp.authenticate('protectedServer')
889
+ }
890
+ }
891
+ ```
892
+
893
+ 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.
894
+
895
+ 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:
896
+
897
+ ```typescript
898
+ import { createOAuthCallbackServer, getCallbackUrlCandidates } from '@mastra/mcp'
899
+
900
+ // getCallbackUrlCandidates() lists every URL the helper may bind, so register
901
+ // all of them as redirect_uris during client registration to cover port fallback.
902
+ const redirectUris = getCallbackUrlCandidates('http://127.0.0.1:5533/oauth/callback').map(url =>
903
+ url.toString(),
904
+ )
905
+
906
+ const server = await createOAuthCallbackServer({
907
+ redirectUrl: 'http://127.0.0.1:5533/oauth/callback',
908
+ state: expectedState,
909
+ })
910
+
911
+ // server.url reflects the port actually bound — use it as the redirect_uri.
912
+ try {
913
+ const { code } = await server.waitForCode()
914
+ // Exchange the code here.
915
+ } finally {
916
+ await server.close()
917
+ }
918
+ ```
919
+
824
920
  ### Quick Token Provider
825
921
 
826
922
  For testing or when you already have a valid access token:
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @mastra/mcp-docs-server
2
2
 
3
+ ## 1.2.8-alpha.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`8b20926`](https://github.com/mastra-ai/mastra/commit/8b20926cd59e2ba3d66458e062fa0e6e2ada3e68), [`74faf8b`](https://github.com/mastra-ai/mastra/commit/74faf8bd9c1018f2492653c06b1e25fc8300e9e6), [`1fadac4`](https://github.com/mastra-ai/mastra/commit/1fadac44537caeefe81f9f775ae2f2f3d94e9069), [`970c032`](https://github.com/mastra-ai/mastra/commit/970c032502751ee5dd4d0b603331d9838cb538fc), [`792ec9a`](https://github.com/mastra-ai/mastra/commit/792ec9a0869bab8274cf5e0ed2840738737a1607), [`712b864`](https://github.com/mastra-ai/mastra/commit/712b864aa1ed12b14c54390ec17b69de163c37f7), [`8f7a5de`](https://github.com/mastra-ai/mastra/commit/8f7a5dedc246cdc938bb65516703cf9b27b03756), [`c0bec73`](https://github.com/mastra-ai/mastra/commit/c0bec732c93d1a22ae5e51ed66cf8cacca8bd6a6)]:
8
+ - @mastra/core@1.52.0-alpha.2
9
+ - @mastra/mcp@1.15.0-alpha.0
10
+
11
+ ## 1.2.8-alpha.3
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies:
16
+ - @mastra/core@1.51.1-alpha.1
17
+
3
18
  ## 1.2.8-alpha.1
4
19
 
5
20
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/mcp-docs-server",
3
- "version": "1.2.8-alpha.2",
3
+ "version": "1.2.8-alpha.5",
4
4
  "description": "MCP server for accessing Mastra.ai documentation, changelogs, and news.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,8 +28,8 @@
28
28
  "jsdom": "^26.1.0",
29
29
  "local-pkg": "^1.1.2",
30
30
  "zod": "^4.4.3",
31
- "@mastra/core": "1.51.1-alpha.0",
32
- "@mastra/mcp": "^1.14.0"
31
+ "@mastra/core": "1.52.0-alpha.2",
32
+ "@mastra/mcp": "^1.15.0-alpha.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@hono/node-server": "^1.19.11",
@@ -47,7 +47,7 @@
47
47
  "vitest": "4.1.10",
48
48
  "@internal/lint": "0.0.114",
49
49
  "@internal/types-builder": "0.0.89",
50
- "@mastra/core": "1.51.1-alpha.0"
50
+ "@mastra/core": "1.52.0-alpha.2"
51
51
  },
52
52
  "homepage": "https://mastra.ai",
53
53
  "repository": {