@mastra/mcp-docs-server 1.2.1-alpha.2 → 1.2.1-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.
Files changed (45) hide show
  1. package/.docs/docs/agent-builder/overview.md +2 -0
  2. package/.docs/docs/agents/background-tasks.md +89 -14
  3. package/.docs/docs/agents/durable-agents.md +231 -0
  4. package/.docs/docs/agents/skills.md +186 -0
  5. package/.docs/docs/agents/using-tools.md +52 -0
  6. package/.docs/docs/evals/datasets/running-experiments.md +76 -0
  7. package/.docs/docs/getting-started/build-with-ai.md +273 -8
  8. package/.docs/docs/server/pubsub.md +2 -2
  9. package/.docs/docs/workflows/overview.md +71 -0
  10. package/.docs/docs/workspace/skills.md +9 -1
  11. package/.docs/guides/build-your-ui/ai-sdk-ui.md +3 -3
  12. package/.docs/guides/concepts/streaming.md +317 -0
  13. package/.docs/guides/getting-started/quickstart.md +1 -1
  14. package/.docs/models/gateways/openrouter.md +2 -2
  15. package/.docs/models/gateways/vercel.md +10 -1
  16. package/.docs/models/index.md +1 -1
  17. package/.docs/models/providers/baseten.md +1 -1
  18. package/.docs/models/providers/friendli.md +3 -2
  19. package/.docs/models/providers/lilac.md +7 -7
  20. package/.docs/models/providers/opencode.md +2 -1
  21. package/.docs/models/providers/siliconflow-cn.md +2 -1
  22. package/.docs/models/providers/wafer.ai.md +2 -1
  23. package/.docs/reference/agents/createSkill.md +78 -0
  24. package/.docs/reference/agents/durable-agent.md +30 -1
  25. package/.docs/reference/agents/getDefaultOptions.md +1 -1
  26. package/.docs/reference/agents/getDefaultStreamOptions.md +1 -1
  27. package/.docs/reference/agents/getSkill.md +58 -0
  28. package/.docs/reference/agents/inngest-agent.md +339 -0
  29. package/.docs/reference/agents/listSkills.md +53 -0
  30. package/.docs/reference/ai-sdk/handle-workflow-stream.md +1 -1
  31. package/.docs/reference/ai-sdk/workflow-route.md +1 -1
  32. package/.docs/reference/index.md +4 -0
  33. package/.docs/reference/processors/stream-error-retry-processor.md +32 -0
  34. package/.docs/reference/streaming/workflows/timeTravelStream.md +1 -1
  35. package/.docs/reference/tools/create-tool.md +1 -1
  36. package/.docs/reference/workflows/run.md +1 -1
  37. package/CHANGELOG.md +14 -0
  38. package/package.json +4 -4
  39. package/.docs/docs/build-with-ai/mcp-docs-server.md +0 -238
  40. package/.docs/docs/build-with-ai/skills.md +0 -63
  41. package/.docs/docs/streaming/background-task-streaming.md +0 -80
  42. package/.docs/docs/streaming/events.md +0 -148
  43. package/.docs/docs/streaming/overview.md +0 -136
  44. package/.docs/docs/streaming/tool-streaming.md +0 -189
  45. package/.docs/docs/streaming/workflow-streaming.md +0 -109
@@ -2,6 +2,8 @@
2
2
 
3
3
  > **Note:** The Agent Builder is part of the Mastra Enterprise Edition. Production deployments require a valid EE license. [Contact sales](https://mastra.ai/contact) for more information.
4
4
 
5
+ [YouTube video player](https://www.youtube-nocookie.com/embed/AbdgIu4Z07I)
6
+
5
7
  The Agent Builder lets you build, configure, and operate Mastra agents all within the UI. It runs inside your Mastra server, persists everything to `Mastra.storage`, and supports multi-tenant agent workflows with RBAC and channel integrations.
6
8
 
7
9
  - [**Configuration**](https://mastra.ai/docs/agent-builder/configuration): Toggle UI sections and pin admin-controlled defaults for every new agent.
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Added in:** `@mastra/core@1.29.0`
4
4
 
5
- Background tasks let an agent dispatch a long-running tool call without blocking the agentic loop. The tool returns an immediate acknowledgement, the LLM continues responding, and the task runs to completion in the background. When it finishes, its result is written to memory and if you use [`streamUntilIdle()`](https://mastra.ai/reference/streaming/agents/streamUntilIdle) the agent is re-invoked automatically so the result is processed in the same call.
5
+ Background tasks let an agent dispatch a long-running tool call without blocking the agentic loop. The tool returns an immediate acknowledgement, the LLM continues responding, and the task runs to completion in the background. When it finishes, its result is written to memory and if you use `stream()` with the [`untilIdle`](https://mastra.ai/reference/agents/agent) option the agent is re-invoked automatically so the result is processed in the same call.
6
6
 
7
7
  ## When to use background tasks
8
8
 
@@ -119,7 +119,7 @@ If the agent has `backgroundTasks.disabled: true`, every tool call runs synchron
119
119
 
120
120
  ## Background tasks related stream chunks
121
121
 
122
- When a tool call dispatches as a background task, two streams may surface lifecycle events for it: the agent's own stream and the [`backgroundTaskManager.stream()`](https://mastra.ai/docs/streaming/background-task-streaming) SSE stream. Each stream covers a different set of chunk types:
122
+ When a tool call dispatches as a background task, two streams may surface lifecycle events for it: the agent's own stream and the [`backgroundTaskManager.stream()`](https://mastra.ai/docs/agents/background-tasks) SSE stream. Each stream covers a different set of chunk types:
123
123
 
124
124
  | Chunk type | When it fires | Emitted by |
125
125
  | --------------------------- | -------------------------------------------------------------------------------------- | -------------- |
@@ -133,20 +133,20 @@ When a tool call dispatches as a background task, two streams may surface lifecy
133
133
  | `background-task-suspended` | The tool called `suspend()` from inside its execute. | Manager stream |
134
134
  | `background-task-resumed` | A suspended task was resumed via `manager.resume(taskId, resumeData)`. | Manager stream |
135
135
 
136
- `agent.stream().fullStream` only emits the agent-loop chunks (`background-task-started`, `background-task-progress`) on its own. `agent.streamUntilIdle()` emits the same two chunks and additionally subscribes to the manager pubsub for the run's memory scope and pipes the seven manager chunks (`background-task-running`, `background-task-output`, `background-task-completed`, `background-task-failed`, `background-task-cancelled`, `background-task-suspended`, `background-task-resumed`) into the same `fullStream`.
136
+ `agent.stream().fullStream` only emits the agent-loop chunks (`background-task-started`, `background-task-progress`) on its own. `agent.stream()` with `untilIdle: true` emits the same two chunks and additionally subscribes to the manager pubsub for the run's memory scope and pipes the seven manager chunks (`background-task-running`, `background-task-output`, `background-task-completed`, `background-task-failed`, `background-task-cancelled`, `background-task-suspended`, `background-task-resumed`) into the same `fullStream`.
137
137
 
138
138
  `backgroundTaskManager.stream()` only emits the seven manager chunks.
139
139
 
140
140
  The full payload shapes are documented in the [background task chunks reference](https://mastra.ai/reference/streaming/ChunkType).
141
141
 
142
- ## Keep the agent stream open with `streamUntilIdle()`
142
+ ## Keep the agent stream open with `untilIdle`
143
143
 
144
- `agent.stream()` returns once the LLM emits a final response even if a background task is still running. Use `agent.streamUntilIdle()` when you want the stream to stay open until every dispatched background task has completed and the LLM has had a chance to respond to the result:
144
+ `agent.stream()` returns once the LLM emits a final response even if a background task is still running. Pass `untilIdle: true` when you want the stream to stay open until every dispatched background task has completed and the LLM has had a chance to respond to the result:
145
145
 
146
146
  ```typescript
147
- const stream = await agent.streamUntilIdle('Research solana for me', {
147
+ const stream = await agent.stream('Research solana for me', {
148
148
  memory: { thread: 't1', resource: 'u1' },
149
- maxIdleMs: 5 * 60_000,
149
+ untilIdle: true,
150
150
  })
151
151
 
152
152
  for await (const chunk of stream.fullStream) {
@@ -155,15 +155,22 @@ for await (const chunk of stream.fullStream) {
155
155
  }
156
156
  ```
157
157
 
158
- When a background task completes, the result is injected into the agent memory, `streamUntilIdle()` re-enters the agentic loop so the LLM can react to it. The stream closes when no tasks are running and no completions are queued.
158
+ When a background task completes, the result is injected into the agent memory, `stream()` re-enters the agentic loop so the LLM can react to it. The stream closes when no tasks are running and no completions are queued.
159
159
 
160
- `maxIdleMs` caps how long the stream waits between turns. The timer only runs while the wrapper is between turns, so a slow first token won't close the stream. The default is 5 minutes.
160
+ Customize the idle timeout by passing an object instead of `true`. The timer only runs while the wrapper is between turns, so a slow first token won't close the stream. The default is 5 minutes:
161
161
 
162
- > **Note:** Visit [`Agent.streamUntilIdle()`](https://mastra.ai/reference/streaming/agents/streamUntilIdle) for the full API.
162
+ ```typescript
163
+ const stream = await agent.stream('Research solana for me', {
164
+ memory: { thread: 't1', resource: 'u1' },
165
+ untilIdle: { maxIdleMs: 30_000 },
166
+ })
167
+ ```
168
+
169
+ > **Note:** Visit [`Agent.stream()`](https://mastra.ai/reference/agents/agent) for the full API.
163
170
 
164
171
  ### Aggregate properties
165
172
 
166
- `streamUntilIdle()` returns a `MastraModelOutput` that looks like the one from `stream()`, but only `fullStream` spans the initial turn **and** any auto-continuations. Aggregate properties (`text`, `toolCalls`, `toolResults`, `finishReason`, `messageList`, `getFullOutput()`) still resolve against the **first turn's** internal buffer. If you need an aggregate view across continuations, consume `fullStream` yourself and accumulate.
173
+ `stream()` with `untilIdle` returns a `MastraModelOutput` that looks like the one from a regular `stream()` call, but only `fullStream` spans the initial turn **and** any auto-continuations. Aggregate properties (`text`, `toolCalls`, `toolResults`, `finishReason`, `messageList`, `getFullOutput()`) still resolve against the **first turn's** internal buffer. If you need an aggregate view across continuations, consume `fullStream` yourself and accumulate.
167
174
 
168
175
  ## Subagents in the background
169
176
 
@@ -185,8 +192,9 @@ const supervisor = new Agent({
185
192
  },
186
193
  })
187
194
 
188
- const stream = await supervisor.streamUntilIdle('Research AI in education and write an article', {
195
+ const stream = await supervisor.stream('Research AI in education and write an article', {
189
196
  memory: { thread: 't1', resource: 'u1' },
197
+ untilIdle: true,
190
198
  })
191
199
  ```
192
200
 
@@ -263,7 +271,7 @@ await mastra.backgroundTaskManager?.resume(taskId, {
263
271
 
264
272
  ### What happens to the agent loop
265
273
 
266
- When a task suspends mid-`streamUntilIdle()`, the wrapper treats it as terminal for the current iteration and closes. To continue the agent immediately when the resume payload is in hand, call `agent.resumeStreamUntilIdle(resumeData, { runId, toolCallId, memory })`: the resumed bg task runs to completion, its result lands in the message list, and the agent runs a follow-up turn — all on the same SSE connection. If you'd rather drive the resume out-of-band, call `mastra.backgroundTaskManager.resume(taskId, resumeData)` directly and the result still writes into the thread for the next user turn to pick up.
274
+ When a task suspends mid-`stream()` with `untilIdle`, the wrapper treats it as terminal for the current iteration and closes. To continue the agent immediately when the resume payload is in hand, call `agent.resumeStream(resumeData, { runId, toolCallId, memory, untilIdle: true })`: the resumed bg task runs to completion, its result lands in the message list, and the agent runs a follow-up turn — all on the same SSE connection. If you'd rather drive the resume out-of-band, call `mastra.backgroundTaskManager.resume(taskId, resumeData)` directly and the result still writes into the thread for the next user turn to pick up.
267
275
 
268
276
  ### Re-registering the executor on resume
269
277
 
@@ -296,10 +304,77 @@ export const mastra = new Mastra({
296
304
  })
297
305
  ```
298
306
 
307
+ ## Streaming
308
+
309
+ ### Subscribe to all task events
310
+
311
+ Calling `stream()` with no filter returns a stream of every task event in the system. On connection, the stream emits a snapshot of all currently running tasks, then forwards live events as they happen.
312
+
313
+ ```typescript
314
+ const bgManager = mastra.backgroundTaskManager
315
+ if (!bgManager) throw new Error('Background tasks are not enabled')
316
+
317
+ const controller = new AbortController()
318
+ const stream = bgManager.stream({ abortSignal: controller.signal })
319
+
320
+ for await (const chunk of stream) {
321
+ switch (chunk.type) {
322
+ case 'background-task-running':
323
+ console.log('started', chunk.payload.taskId, chunk.payload.toolName)
324
+ break
325
+ case 'background-task-completed':
326
+ console.log('done', chunk.payload.taskId, chunk.payload.result)
327
+ break
328
+ case 'background-task-failed':
329
+ console.error('failed', chunk.payload.taskId, chunk.payload.error)
330
+ break
331
+ }
332
+ }
333
+ ```
334
+
335
+ The stream stays open until the caller's `AbortSignal` fires. Always pass an `abortSignal` so you can disconnect cleanly.
336
+
337
+ ### Filter the stream
338
+
339
+ Pass any combination of filter options to narrow the events you receive. Filters apply to both the initial snapshot and the live event subscription.
340
+
341
+ ```typescript
342
+ const stream = bgManager.stream({
343
+ agentId: 'researcher',
344
+ threadId: 't1',
345
+ resourceId: 'u1',
346
+ abortSignal: controller.signal,
347
+ })
348
+ ```
349
+
350
+ | Filter | Description |
351
+ | ------------- | --------------------------------------------------- |
352
+ | `agentId` | Only events from tasks dispatched by this agent |
353
+ | `runId` | Only events from this specific agent run |
354
+ | `threadId` | Only events from tasks scoped to this memory thread |
355
+ | `resourceId` | Only events from tasks scoped to this resource |
356
+ | `taskId` | Only events for a single task |
357
+ | `abortSignal` | Closes the stream when the signal aborts |
358
+
359
+ ### Look up task state directly
360
+
361
+ For one-off lookups instead of a live stream, use `getTask` and `listTasks`:
362
+
363
+ ```typescript
364
+ const task = await mastra.backgroundTaskManager?.getTask(taskId)
365
+ const { tasks, total } = await mastra.backgroundTaskManager?.listTasks({
366
+ status: 'running',
367
+ agentId: 'researcher',
368
+ })
369
+ ```
370
+
371
+ These read from storage rather than the pubsub stream, so they're suitable for paginated lists and detail views.
372
+
299
373
  ## Related
300
374
 
301
- - [`Agent.streamUntilIdle()` reference](https://mastra.ai/reference/streaming/agents/streamUntilIdle)
375
+ - [`Agent.stream()` reference](https://mastra.ai/reference/agents/agent)
302
376
  - [backgroundTasks configuration reference](https://mastra.ai/reference/configuration)
377
+ - [Durable agents](https://mastra.ai/docs/agents/durable-agents)
303
378
  - [Supervisor agents](https://mastra.ai/docs/agents/supervisor-agents)
304
379
  - [Stream chunk types](https://mastra.ai/reference/streaming/ChunkType)
305
380
  - [Storage](https://mastra.ai/docs/memory/storage)
@@ -0,0 +1,231 @@
1
+ # Durable agents
2
+
3
+ **Added in:** `@mastra/core@1.45.0`
4
+
5
+ > **Warning:** Durable agents are currently in **beta**. APIs may change in future releases.
6
+
7
+ A durable agent wraps a regular [`Agent`](https://mastra.ai/docs/agents/overview) so the agentic loop runs inside a workflow. Events flow through [PubSub](https://mastra.ai/docs/server/pubsub), which means a client can disconnect and reconnect without missing chunks. The run state is persisted, so it survives process restarts.
8
+
9
+ ## When to use durable agents
10
+
11
+ Use a durable agent when any of the following apply:
12
+
13
+ - The client may drop and reconnect mid-stream (mobile, spotty networks, long-running calls).
14
+ - The agentic loop may outlive a single HTTP request (background research, multi-step tool use).
15
+ - You need an observe/reconnect API where a second client picks up a stream that a first client started.
16
+ - You want [Inngest-powered execution](https://mastra.ai/guides/deployment/inngest) with step memoization, retries, and monitoring.
17
+
18
+ For short-lived, request-scoped calls where the client stays connected, a regular `Agent` with `stream()` or `generate()` is simpler.
19
+
20
+ ## Quickstart
21
+
22
+ Wrap an existing agent with `createDurableAgent()` from `@mastra/core/agent/durable`:
23
+
24
+ ```typescript
25
+ import { Agent } from '@mastra/core/agent'
26
+ import { createDurableAgent } from '@mastra/core/agent/durable'
27
+
28
+ const agent = new Agent({
29
+ id: 'researcher',
30
+ name: 'Researcher',
31
+ instructions: 'You research topics thoroughly.',
32
+ model: 'openai/gpt-5.5',
33
+ })
34
+
35
+ export const durableResearcher = createDurableAgent({ agent })
36
+ ```
37
+
38
+ Register the durable agent with Mastra and call `stream()`:
39
+
40
+ ```typescript
41
+ import { Mastra } from '@mastra/core'
42
+ import { durableResearcher } from './agents/researcher'
43
+
44
+ const mastra = new Mastra({
45
+ agents: { durableResearcher },
46
+ })
47
+
48
+ const { output, runId, cleanup } = await durableResearcher.stream(
49
+ 'Research quantum computing advances in 2025',
50
+ )
51
+
52
+ for await (const chunk of output.fullStream) {
53
+ // Process each chunk as it arrives
54
+ }
55
+
56
+ // Release PubSub subscriptions and clear the run from the registry.
57
+ // If you skip this, an automatic cleanup timer fires after the stream ends.
58
+ cleanup()
59
+ ```
60
+
61
+ The returned `runId` identifies the execution. Pass it to `observe()` to reconnect from a different client.
62
+
63
+ > **Note:** Visit the [DurableAgent reference](https://mastra.ai/reference/agents/durable-agent) for the full configuration and method API.
64
+
65
+ ## How it works
66
+
67
+ A durable agent adds three layers on top of a regular agent:
68
+
69
+ 1. **Workflow execution**: `stream()` serializes the messages and options into a workflow input, then triggers the agentic loop inside a durable workflow. The workflow runs the same loop as `Agent.stream()` but each step can be memoized and replayed.
70
+
71
+ 2. **PubSub streaming**: As the loop runs, chunks are published to a PubSub topic keyed by the run ID. The caller subscribes to this topic and pipes chunks into a `ReadableStream`. If the caller disconnects and reconnects, missed chunks are replayed from the cache.
72
+
73
+ 3. **Cache layer**: An optional cache (in-memory by default, Redis or another backend in production) stores published events so that a late subscriber can catch up.
74
+
75
+ ## Execution variants
76
+
77
+ Mastra provides three factory functions that produce durable agents. They differ in how the workflow is executed:
78
+
79
+ | Factory | Package | Best for |
80
+ | ---------------------- | ----------------- | -------------------------------------------------------------------------------------------------- |
81
+ | `createDurableAgent()` | `@mastra/core` | Local development and single-process servers. You get a stream you can await directly. |
82
+ | `createEventedAgent()` | `@mastra/core` | Background execution. The workflow starts without blocking, and you consume chunks through PubSub. |
83
+ | `createInngestAgent()` | `@mastra/inngest` | Production deployments. Inngest adds step memoization, retries, and a monitoring dashboard. |
84
+
85
+ All three return an object you register with `Mastra` the same way as a regular agent. `createDurableAgent()` and `createEventedAgent()` return class instances that extend `Agent`. `createInngestAgent()` returns a Proxy-backed object that forwards `Agent` methods to the underlying agent.
86
+
87
+ ### In-process with `createDurableAgent()`
88
+
89
+ Wrap your agent and call `stream()`. You get a `DurableAgentStreamResult` back in the same process. No external infrastructure is required, so this is the fastest way to get started:
90
+
91
+ ```typescript
92
+ import { Agent } from '@mastra/core/agent'
93
+ import { createDurableAgent } from '@mastra/core/agent/durable'
94
+
95
+ const agent = new Agent({
96
+ id: 'helper',
97
+ instructions: 'You are a helpful assistant.',
98
+ model: 'openai/gpt-5.5',
99
+ })
100
+
101
+ export const durableHelper = createDurableAgent({ agent })
102
+ ```
103
+
104
+ ### Fire-and-forget with `createEventedAgent()`
105
+
106
+ The workflow starts in the background without blocking the caller. You still receive chunks through PubSub, so `stream()` returns a result you can consume. The HTTP handler that triggered the run does not need to wait for the workflow to finish:
107
+
108
+ ```typescript
109
+ import { Agent } from '@mastra/core/agent'
110
+ import { createEventedAgent } from '@mastra/core/agent/durable'
111
+
112
+ const agent = new Agent({
113
+ id: 'writer',
114
+ instructions: 'You write articles.',
115
+ model: 'openai/gpt-5.5',
116
+ })
117
+
118
+ export const eventedWriter = createEventedAgent({ agent })
119
+ ```
120
+
121
+ ### Inngest-powered with `createInngestAgent()`
122
+
123
+ Run the workflow on the [Inngest](https://www.inngest.com/docs) platform. Each tool call becomes a memoized step that Inngest can retry independently, and you get a dashboard for monitoring runs:
124
+
125
+ ```typescript
126
+ import { Agent } from '@mastra/core/agent'
127
+ import { createInngestAgent } from '@mastra/inngest'
128
+ import { Inngest } from 'inngest'
129
+
130
+ const inngest = new Inngest({ id: 'my-app' })
131
+
132
+ const agent = new Agent({
133
+ id: 'analyst',
134
+ instructions: 'You analyze data.',
135
+ model: 'openai/gpt-5.5',
136
+ })
137
+
138
+ export const inngestAnalyst = createInngestAgent({ agent, inngest })
139
+ ```
140
+
141
+ > **Note:** Visit the [`createInngestAgent()` reference](https://mastra.ai/reference/agents/inngest-agent) for the full API, including Inngest-specific options like PubSub and cache configuration.
142
+
143
+ ## Resumable streams
144
+
145
+ Durable agents support resumable streams through PubSub and an event cache. When a client disconnects mid-stream, the cache continues storing events. The same client can reconnect by calling `observe()` with the `runId`:
146
+
147
+ ```typescript
148
+ const { output, cleanup } = await durableResearcher.observe(runId)
149
+
150
+ for await (const chunk of output.fullStream) {
151
+ // Chunks from the run, including any missed while disconnected
152
+ }
153
+
154
+ cleanup()
155
+ ```
156
+
157
+ `createDurableAgent()` and `createEventedAgent()` use an in-memory cache by default, which means resumable streams work within a single process. For production, provide a persistent cache backend (e.g., Redis) so cached events survive process restarts:
158
+
159
+ ```typescript
160
+ import { createDurableAgent } from '@mastra/core/agent/durable'
161
+ import { RedisServerCache } from '@mastra/redis'
162
+
163
+ const cache = new RedisServerCache({ url: 'redis://localhost:6379' })
164
+
165
+ export const durableAgent = createDurableAgent({
166
+ agent,
167
+ cache,
168
+ })
169
+ ```
170
+
171
+ `createInngestAgent()` does not enable caching by default. Pass a `cache` option or register the agent with a `Mastra` instance that has a `serverCache` configured to enable resumable streams.
172
+
173
+ ## Streaming with background tasks
174
+
175
+ Durable agents support the same [`untilIdle`](https://mastra.ai/reference/agents/durable-agent) option as regular agents. When `untilIdle` is set, `stream()` keeps the connection open across background-task continuations until the agent is idle:
176
+
177
+ ```typescript
178
+ const { output, cleanup } = await durableAgent.stream('Research and summarize the topic', {
179
+ untilIdle: true,
180
+ memory: { thread: 'thread-1', resource: 'user-1' },
181
+ })
182
+
183
+ for await (const chunk of output.fullStream) {
184
+ // Chunks from the initial turn AND any follow-up turns triggered by
185
+ // background task completions
186
+ }
187
+
188
+ cleanup()
189
+ ```
190
+
191
+ Pass `{ maxIdleMs }` to customize the idle timeout (defaults to 5 minutes):
192
+
193
+ ```typescript
194
+ await durableAgent.stream('Research topic', {
195
+ untilIdle: { maxIdleMs: 30_000 },
196
+ memory: { thread: 'thread-1', resource: 'user-1' },
197
+ })
198
+ ```
199
+
200
+ > **Note:** Visit [Background tasks](https://mastra.ai/docs/agents/background-tasks) for the full background task guide, including configuration, subagents, and suspend/resume.
201
+
202
+ ## Cleanup
203
+
204
+ Every `stream()` and `observe()` call returns a `cleanup` function. Calling it unsubscribes from PubSub and removes the run from the internal registry. If you forget to call it, an automatic timer fires after the stream ends, but calling `cleanup()` yourself frees resources immediately.
205
+
206
+ ## Tool approval
207
+
208
+ Durable agents support tool approval (human-in-the-loop). When a tool call requires approval, the workflow suspends, emits an `onSuspended` callback, and waits for the caller to resume with `resume()`:
209
+
210
+ ```typescript
211
+ const { output, runId, cleanup } = await durableAgent.stream('Delete the old records', {
212
+ requireToolApproval: true,
213
+ onSuspended: ({ toolCallId, toolName, args }) => {
214
+ // Notify the user and ask for approval
215
+ },
216
+ })
217
+ ```
218
+
219
+ Resume the suspended run after approval:
220
+
221
+ ```typescript
222
+ await durableAgent.resume(runId, { approved: true })
223
+ ```
224
+
225
+ ## Related
226
+
227
+ - [DurableAgent reference](https://mastra.ai/reference/agents/durable-agent)
228
+ - [`createInngestAgent()` reference](https://mastra.ai/reference/agents/inngest-agent)
229
+ - [Background tasks](https://mastra.ai/docs/agents/background-tasks)
230
+ - [Inngest deployment guide](https://mastra.ai/guides/deployment/inngest)
231
+ - [Agent overview](https://mastra.ai/docs/agents/overview)
@@ -0,0 +1,186 @@
1
+ # Agent skills
2
+
3
+ Skills are reusable instructions that teach agents how to perform specific tasks. They follow the [Agent Skills specification](https://agentskills.io).
4
+
5
+ You can attach skills directly to an agent without setting up a workspace, filesystem, or sandbox. This is useful when you want portable, code-defined capabilities that travel with your agent definition.
6
+
7
+ ## When to use agent-level skills
8
+
9
+ Use agent-level skills when:
10
+
11
+ - You want self-contained agents that don't depend on a workspace
12
+ - Skills are defined in code and don't need filesystem discovery
13
+ - You're building packages or libraries that ship agent capabilities
14
+ - You need per-request skill resolution based on context
15
+
16
+ For filesystem-based skill discovery across a project, use [workspace skills](https://mastra.ai/docs/workspace/skills) instead.
17
+
18
+ ## Quickstart
19
+
20
+ Define a skill inline and attach it to an agent:
21
+
22
+ ```typescript
23
+ import { Agent } from '@mastra/core/agent'
24
+ import { createSkill } from '@mastra/core/skills'
25
+
26
+ const codeReview = createSkill({
27
+ name: 'code-review',
28
+ description: 'Use when reviewing code changes.',
29
+ instructions: `
30
+ When reviewing code:
31
+ 1. Check for correctness and edge cases
32
+ 2. Verify style consistency
33
+ 3. Look for potential bugs
34
+ `,
35
+ })
36
+
37
+ export const reviewer = new Agent({
38
+ id: 'reviewer',
39
+ model: 'openai/gpt-5.5',
40
+ instructions: 'You are a code review assistant.',
41
+ skills: [codeReview],
42
+ })
43
+ ```
44
+
45
+ The agent automatically gets `skill`, `skill_read`, and `skill_search` tools so it can discover and load skills during conversations.
46
+
47
+ ## Defining inline skills
48
+
49
+ Use `createSkill()` to create skills entirely in code:
50
+
51
+ ```typescript
52
+ import { createSkill } from '@mastra/core/skills'
53
+
54
+ export const releaseChecklist = createSkill({
55
+ name: 'release-checklist',
56
+ description: 'Use when preparing a release.',
57
+ instructions: `
58
+ ## Release Checklist
59
+ 1. Run the full test suite
60
+ 2. Update CHANGELOG.md
61
+ 3. Bump version numbers
62
+ 4. Create a git tag
63
+ `,
64
+ references: {
65
+ 'changelog-format.md': '# Changelog Format\nUse Keep a Changelog...',
66
+ },
67
+ })
68
+ ```
69
+
70
+ The `references` field bundles supporting documents that the agent can read with the `skill_read` tool, just like `references/` files in a filesystem skill.
71
+
72
+ > **Note:** Visit [`createSkill()` reference](https://mastra.ai/reference/agents/createSkill) for the full API.
73
+
74
+ ## Filesystem path skills
75
+
76
+ Point to skill directories on disk without a workspace:
77
+
78
+ ```typescript
79
+ import { Agent } from '@mastra/core/agent'
80
+ import { createSkill } from '@mastra/core/skills'
81
+
82
+ export const agent = new Agent({
83
+ id: 'my-agent',
84
+ model: 'openai/gpt-5.5',
85
+ skills: [
86
+ './skills/code-review', // path to a SKILL.md directory
87
+ './skills/testing', // another filesystem skill
88
+ createSkill({
89
+ /* ... */
90
+ }), // inline skill
91
+ ],
92
+ })
93
+ ```
94
+
95
+ Filesystem paths use `LocalSkillSource` under the hood, which reads `SKILL.md` files following the same format as [workspace skills](https://mastra.ai/docs/workspace/skills).
96
+
97
+ ## Dynamic skills
98
+
99
+ For per-request skill resolution, pass a function:
100
+
101
+ ```typescript
102
+ import { Agent } from '@mastra/core/agent'
103
+ import { createSkill } from '@mastra/core/skills'
104
+
105
+ const devSkill = createSkill({
106
+ name: 'dev-tools',
107
+ description: 'Developer productivity tools.',
108
+ instructions: '...',
109
+ })
110
+
111
+ const supportSkill = createSkill({
112
+ name: 'support-guide',
113
+ description: 'Customer support guidelines.',
114
+ instructions: '...',
115
+ })
116
+
117
+ export const agent = new Agent({
118
+ id: 'dynamic-agent',
119
+ model: 'openai/gpt-5.5',
120
+ skills: ({ requestContext }) => {
121
+ const role = requestContext.get('userRole')
122
+ if (role === 'developer') return [devSkill]
123
+ return [supportSkill]
124
+ },
125
+ })
126
+ ```
127
+
128
+ The resolver function receives `{ requestContext }` and returns a `SkillInput[]` array or a `Promise<SkillInput[]>`.
129
+
130
+ ## Merging with workspace skills
131
+
132
+ When an agent has both `skills` and a workspace with skills configured, they merge. Agent-level skills take precedence on name conflicts:
133
+
134
+ ```typescript
135
+ import { Agent } from '@mastra/core/agent'
136
+ import { Workspace, LocalFilesystem } from '@mastra/core/workspace'
137
+ import { createSkill } from '@mastra/core/skills'
138
+
139
+ const workspace = new Workspace({
140
+ filesystem: new LocalFilesystem({ basePath: './workspace' }),
141
+ skills: ['skills'], // provides "code-review" skill
142
+ })
143
+
144
+ const customReview = createSkill({
145
+ name: 'code-review', // same name as workspace skill
146
+ description: 'Custom review process.',
147
+ instructions: '...',
148
+ })
149
+
150
+ export const agent = new Agent({
151
+ id: 'reviewer',
152
+ model: 'openai/gpt-5.5',
153
+ workspace,
154
+ skills: [customReview], // agent-level "code-review" wins
155
+ })
156
+ ```
157
+
158
+ ## Programmatic skill access
159
+
160
+ Use `agent.getSkill()` and `agent.listSkills()` to access skills from application code (e.g., in workflows or API routes):
161
+
162
+ ```typescript
163
+ import { reviewer } from '../mastra/agents'
164
+
165
+ // Get a specific skill by name
166
+ const skill = await reviewer.getSkill('code-review')
167
+ if (skill) {
168
+ console.log(skill.instructions)
169
+ }
170
+
171
+ // List all available skills
172
+ const allSkills = await reviewer.listSkills()
173
+ for (const meta of allSkills) {
174
+ console.log(`${meta.name}: ${meta.description}`)
175
+ }
176
+ ```
177
+
178
+ > **Note:** Visit [`.getSkill()` reference](https://mastra.ai/reference/agents/getSkill) and [`.listSkills()` reference](https://mastra.ai/reference/agents/listSkills) for the full API.
179
+
180
+ ## Related
181
+
182
+ - [Workspace skills](https://mastra.ai/docs/workspace/skills)
183
+ - [`createSkill()` reference](https://mastra.ai/reference/agents/createSkill)
184
+ - [`.getSkill()` reference](https://mastra.ai/reference/agents/getSkill)
185
+ - [`.listSkills()` reference](https://mastra.ai/reference/agents/listSkills)
186
+ - [Agent skills specification](https://agentskills.io)
@@ -292,6 +292,58 @@ await supportAgent.generate('Look up the order status', {
292
292
 
293
293
  Agent-level and per-execution hooks merge per key: passing only `beforeToolCall` at execution time keeps the agent-level `afterToolCall`.
294
294
 
295
+ ## Streaming
296
+
297
+ Tools support lifecycle hooks that allow you to monitor different stages of tool execution during streaming. These hooks are particularly useful for logging or analytics.
298
+
299
+ For generic `writer` API usage, see [Streaming](https://mastra.ai/guides/concepts/streaming).
300
+
301
+ ### Available Hooks
302
+
303
+ - **onInputStart**: Called when tool call input streaming begins
304
+ - **onInputDelta**: Called for each chunk of input as it streams in
305
+ - **onInputAvailable**: Called when complete input is parsed and validated
306
+ - **onOutput**: Called after the tool successfully executes with the output
307
+
308
+ For detailed documentation on all lifecycle hooks, see the [createTool() reference](https://mastra.ai/reference/tools/create-tool).
309
+
310
+ ### Example: Using `onInputAvailable` and `onOutput`
311
+
312
+ ```typescript
313
+ import { createTool } from '@mastra/core/tools'
314
+ import { z } from 'zod'
315
+
316
+ export const weatherTool = createTool({
317
+ id: 'weather-tool',
318
+ description: 'Get weather information',
319
+ inputSchema: z.object({
320
+ city: z.string(),
321
+ }),
322
+ outputSchema: z.object({
323
+ temperature: z.number(),
324
+ conditions: z.string(),
325
+ }),
326
+ // Called when the complete input is available
327
+ onInputAvailable: ({ input, toolCallId }) => {
328
+ console.log(`Weather requested for: ${input.city}`)
329
+ },
330
+ execute: async input => {
331
+ const weather = await fetchWeather(input.city)
332
+ return weather
333
+ },
334
+ // Called after successful execution
335
+ onOutput: ({ output, toolName }) => {
336
+ console.log(`${toolName} result: ${output.temperature}°F, ${output.conditions}`)
337
+ },
338
+ })
339
+ ```
340
+
341
+ ### Streaming tool input in UIs
342
+
343
+ When a model generates a tool call, the arguments arrive incrementally as `tool-call-delta` stream chunks before the final `tool-call` chunk. UIs can listen for the corresponding `tool_input_start`, `tool_input_delta`, and `tool_input_end` events to render tool arguments as they stream in — for example, showing a file path or command immediately rather than waiting for the complete tool call.
344
+
345
+ Using a partial JSON parser on the accumulated `argsTextDelta` fragments lets you extract usable argument values before the JSON is complete. This enables features like live diff previews for edit tools, streaming file content for write tools, and instant display of search patterns or file paths.
346
+
295
347
  ## Control tool selection
296
348
 
297
349
  Pass `toolChoice` or `activeTools` to `.generate()` or `.stream()` to control which tools the agent uses at runtime.