@mastra/mcp-docs-server 1.0.0-beta.5 → 1.0.0-beta.6
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.
- package/.docs/organized/changelogs/%40mastra%2Fagent-builder.md +9 -9
- package/.docs/organized/changelogs/%40mastra%2Fai-sdk.md +67 -67
- package/.docs/organized/changelogs/%40mastra%2Fclickhouse.md +26 -26
- package/.docs/organized/changelogs/%40mastra%2Fclient-js.md +53 -53
- package/.docs/organized/changelogs/%40mastra%2Fcloudflare-d1.md +26 -26
- package/.docs/organized/changelogs/%40mastra%2Fcloudflare.md +27 -27
- package/.docs/organized/changelogs/%40mastra%2Fconvex.md +29 -0
- package/.docs/organized/changelogs/%40mastra%2Fcore.md +274 -274
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-cloud.md +15 -15
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-netlify.md +12 -12
- package/.docs/organized/changelogs/%40mastra%2Fdeployer.md +65 -65
- package/.docs/organized/changelogs/%40mastra%2Fduckdb.md +42 -0
- package/.docs/organized/changelogs/%40mastra%2Fdynamodb.md +26 -26
- package/.docs/organized/changelogs/%40mastra%2Felasticsearch.md +52 -0
- package/.docs/organized/changelogs/%40mastra%2Fevals.md +12 -12
- package/.docs/organized/changelogs/%40mastra%2Flance.md +26 -26
- package/.docs/organized/changelogs/%40mastra%2Flibsql.md +24 -24
- package/.docs/organized/changelogs/%40mastra%2Fmcp-docs-server.md +9 -9
- package/.docs/organized/changelogs/%40mastra%2Fmcp.md +84 -84
- package/.docs/organized/changelogs/%40mastra%2Fmemory.md +36 -36
- package/.docs/organized/changelogs/%40mastra%2Fmongodb.md +26 -26
- package/.docs/organized/changelogs/%40mastra%2Fmssql.md +27 -27
- package/.docs/organized/changelogs/%40mastra%2Fpg.md +28 -28
- package/.docs/organized/changelogs/%40mastra%2Fplayground-ui.md +47 -47
- package/.docs/organized/changelogs/%40mastra%2Frag.md +43 -43
- package/.docs/organized/changelogs/%40mastra%2Freact.md +9 -0
- package/.docs/organized/changelogs/%40mastra%2Fschema-compat.md +6 -0
- package/.docs/organized/changelogs/%40mastra%2Fserver.md +56 -56
- package/.docs/organized/changelogs/%40mastra%2Fupstash.md +26 -26
- package/.docs/organized/changelogs/%40mastra%2Fvoice-google.md +19 -19
- package/.docs/organized/changelogs/create-mastra.md +9 -9
- package/.docs/organized/changelogs/mastra.md +17 -17
- package/.docs/organized/code-examples/agui.md +1 -0
- package/.docs/organized/code-examples/ai-sdk-v5.md +1 -0
- package/.docs/organized/code-examples/mcp-server-adapters.md +721 -0
- package/.docs/organized/code-examples/server-app-access.md +342 -0
- package/.docs/raw/agents/agent-approval.mdx +189 -0
- package/.docs/raw/agents/guardrails.mdx +13 -9
- package/.docs/raw/agents/networks.mdx +1 -0
- package/.docs/raw/agents/overview.mdx +23 -58
- package/.docs/raw/agents/processors.mdx +279 -0
- package/.docs/raw/deployment/cloud-providers/index.mdx +19 -26
- package/.docs/raw/deployment/cloud-providers/netlify-deployer.mdx +44 -13
- package/.docs/raw/evals/running-in-ci.mdx +0 -2
- package/.docs/raw/{guides/getting-started → getting-started}/manual-install.mdx +2 -2
- package/.docs/raw/getting-started/start.mdx +1 -1
- package/.docs/raw/guides/build-your-ui/ai-sdk-ui.mdx +8 -0
- package/.docs/raw/guides/getting-started/quickstart.mdx +1 -1
- package/.docs/raw/guides/guide/whatsapp-chat-bot.mdx +421 -0
- package/.docs/raw/guides/index.mdx +3 -35
- package/.docs/raw/guides/migrations/upgrade-to-v1/agent.mdx +11 -0
- package/.docs/raw/guides/migrations/upgrade-to-v1/workflows.mdx +29 -0
- package/.docs/raw/index.mdx +1 -1
- package/.docs/raw/memory/memory-processors.mdx +265 -79
- package/.docs/raw/memory/working-memory.mdx +10 -2
- package/.docs/raw/observability/overview.mdx +0 -1
- package/.docs/raw/observability/tracing/bridges/otel.mdx +176 -0
- package/.docs/raw/observability/tracing/exporters/arize.mdx +17 -0
- package/.docs/raw/observability/tracing/exporters/braintrust.mdx +19 -0
- package/.docs/raw/observability/tracing/exporters/langfuse.mdx +20 -0
- package/.docs/raw/observability/tracing/exporters/langsmith.mdx +12 -0
- package/.docs/raw/observability/tracing/exporters/otel.mdx +5 -4
- package/.docs/raw/observability/tracing/overview.mdx +71 -6
- package/.docs/raw/observability/tracing/processors/sensitive-data-filter.mdx +0 -1
- package/.docs/raw/rag/retrieval.mdx +23 -6
- package/.docs/raw/rag/vector-databases.mdx +93 -2
- package/.docs/raw/reference/agents/generate.mdx +55 -6
- package/.docs/raw/reference/agents/network.mdx +44 -0
- package/.docs/raw/reference/client-js/memory.mdx +43 -0
- package/.docs/raw/reference/client-js/workflows.mdx +92 -63
- package/.docs/raw/reference/deployer/netlify.mdx +1 -2
- package/.docs/raw/reference/evals/scorer-utils.mdx +362 -0
- package/.docs/raw/reference/index.mdx +1 -0
- package/.docs/raw/reference/observability/tracing/bridges/otel.mdx +150 -0
- package/.docs/raw/reference/observability/tracing/configuration.mdx +0 -4
- package/.docs/raw/reference/observability/tracing/exporters/arize.mdx +4 -0
- package/.docs/raw/reference/observability/tracing/exporters/langsmith.mdx +17 -1
- package/.docs/raw/reference/observability/tracing/exporters/otel.mdx +6 -0
- package/.docs/raw/reference/observability/tracing/instances.mdx +0 -4
- package/.docs/raw/reference/observability/tracing/interfaces.mdx +29 -4
- package/.docs/raw/reference/observability/tracing/spans.mdx +0 -4
- package/.docs/raw/reference/processors/language-detector.mdx +9 -2
- package/.docs/raw/reference/processors/message-history-processor.mdx +131 -0
- package/.docs/raw/reference/processors/moderation-processor.mdx +10 -3
- package/.docs/raw/reference/processors/pii-detector.mdx +10 -3
- package/.docs/raw/reference/processors/processor-interface.mdx +502 -0
- package/.docs/raw/reference/processors/prompt-injection-detector.mdx +9 -2
- package/.docs/raw/reference/processors/semantic-recall-processor.mdx +197 -0
- package/.docs/raw/reference/processors/system-prompt-scrubber.mdx +2 -2
- package/.docs/raw/reference/processors/tool-call-filter.mdx +125 -0
- package/.docs/raw/reference/processors/working-memory-processor.mdx +221 -0
- package/.docs/raw/reference/storage/cloudflare-d1.mdx +37 -0
- package/.docs/raw/reference/storage/convex.mdx +164 -0
- package/.docs/raw/reference/storage/lance.mdx +33 -0
- package/.docs/raw/reference/storage/libsql.mdx +37 -0
- package/.docs/raw/reference/storage/mongodb.mdx +39 -0
- package/.docs/raw/reference/storage/mssql.mdx +37 -0
- package/.docs/raw/reference/storage/postgresql.mdx +37 -0
- package/.docs/raw/reference/streaming/ChunkType.mdx +1 -1
- package/.docs/raw/reference/streaming/agents/stream.mdx +56 -1
- package/.docs/raw/reference/streaming/workflows/observeStream.mdx +7 -9
- package/.docs/raw/reference/streaming/workflows/{resumeStreamVNext.mdx → resumeStream.mdx} +51 -11
- package/.docs/raw/reference/streaming/workflows/stream.mdx +83 -24
- package/.docs/raw/reference/tools/mcp-client.mdx +74 -17
- package/.docs/raw/reference/vectors/convex.mdx +429 -0
- package/.docs/raw/reference/vectors/duckdb.mdx +462 -0
- package/.docs/raw/reference/vectors/elasticsearch.mdx +310 -0
- package/.docs/raw/reference/voice/google.mdx +159 -20
- package/.docs/raw/reference/workflows/run-methods/restart.mdx +142 -0
- package/.docs/raw/reference/workflows/run-methods/resume.mdx +44 -0
- package/.docs/raw/reference/workflows/run-methods/start.mdx +44 -0
- package/.docs/raw/reference/workflows/run.mdx +13 -5
- package/.docs/raw/reference/workflows/step.mdx +13 -0
- package/.docs/raw/reference/workflows/workflow.mdx +19 -0
- package/.docs/raw/server-db/mastra-server.mdx +30 -1
- package/.docs/raw/server-db/request-context.mdx +0 -1
- package/.docs/raw/server-db/storage.mdx +11 -0
- package/.docs/raw/streaming/overview.mdx +6 -6
- package/.docs/raw/streaming/tool-streaming.mdx +2 -2
- package/.docs/raw/streaming/workflow-streaming.mdx +5 -11
- package/.docs/raw/workflows/error-handling.mdx +1 -0
- package/.docs/raw/workflows/human-in-the-loop.mdx +4 -4
- package/.docs/raw/workflows/overview.mdx +56 -44
- package/.docs/raw/workflows/snapshots.mdx +1 -0
- package/.docs/raw/workflows/suspend-and-resume.mdx +85 -16
- package/.docs/raw/workflows/time-travel.mdx +313 -0
- package/.docs/raw/workflows/workflow-state.mdx +191 -0
- package/CHANGELOG.md +8 -0
- package/package.json +4 -4
- package/.docs/raw/agents/human-in-the-loop-with-tools.mdx +0 -91
- package/.docs/raw/reference/streaming/workflows/observeStreamVNext.mdx +0 -47
- package/.docs/raw/reference/streaming/workflows/streamVNext.mdx +0 -153
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
### package.json
|
|
2
|
+
```json
|
|
3
|
+
{
|
|
4
|
+
"name": "examples-server-app-access",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@mastra/core": "latest",
|
|
7
|
+
"@mastra/hono": "latest",
|
|
8
|
+
"hono": "^4.10.4",
|
|
9
|
+
"zod": "^3.25.76"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@types/node": "22.13.17",
|
|
13
|
+
"tsx": "^4.19.3",
|
|
14
|
+
"typescript": "^5.8.3"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### demo-cli-batch.ts
|
|
20
|
+
```typescript
|
|
21
|
+
/**
|
|
22
|
+
* Demo: Direct Server App Access
|
|
23
|
+
*
|
|
24
|
+
* This demonstrates how to use mastra.getServerApp() to call routes
|
|
25
|
+
* directly without running an HTTP server.
|
|
26
|
+
*
|
|
27
|
+
* Use case: CLI scripts, batch processing, testing, background jobs
|
|
28
|
+
*
|
|
29
|
+
* NOTE: The base URL (http://internal) is just a placeholder - Hono's app.fetch()
|
|
30
|
+
* processes requests in-memory and only uses the path. The hostname is ignored.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { MastraServer } from '@mastra/hono';
|
|
34
|
+
import { Hono } from 'hono';
|
|
35
|
+
import type { Hono as HonoType } from 'hono';
|
|
36
|
+
|
|
37
|
+
import { mastra } from './mastra';
|
|
38
|
+
|
|
39
|
+
// Base URL is a placeholder - only the path matters for in-memory routing
|
|
40
|
+
const BASE_URL = 'http://internal';
|
|
41
|
+
|
|
42
|
+
async function main() {
|
|
43
|
+
// Create and initialize server (no port binding)
|
|
44
|
+
const app = new Hono();
|
|
45
|
+
const adapter = new MastraServer({ app: app as any, mastra });
|
|
46
|
+
await adapter.init();
|
|
47
|
+
|
|
48
|
+
// Get the server app - works because MastraServer auto-registers with mastra
|
|
49
|
+
const serverApp = mastra.getServerApp<HonoType>();
|
|
50
|
+
if (!serverApp) throw new Error('Server app not initialized');
|
|
51
|
+
|
|
52
|
+
// List tools
|
|
53
|
+
console.log('--- List Tools ---');
|
|
54
|
+
const toolsResponse = await serverApp.fetch(new Request(`${BASE_URL}/api/tools`));
|
|
55
|
+
const tools = (await toolsResponse.json()) as Record<string, unknown>;
|
|
56
|
+
console.log(`Tools: ${Object.keys(tools).join(', ') || '(none registered by key)'}`);
|
|
57
|
+
|
|
58
|
+
// Execute a tool
|
|
59
|
+
console.log('\n--- Execute Tool ---');
|
|
60
|
+
const calcResponse = await serverApp.fetch(
|
|
61
|
+
new Request(`${BASE_URL}/api/tools/calculator/execute`, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: { 'Content-Type': 'application/json' },
|
|
64
|
+
body: JSON.stringify({ data: { operation: 'multiply', a: 7, b: 6 } }),
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
console.log(`Result: ${JSON.stringify(await calcResponse.json())}`);
|
|
68
|
+
|
|
69
|
+
// List workflows
|
|
70
|
+
console.log('\n--- List Workflows ---');
|
|
71
|
+
const workflowsResponse = await serverApp.fetch(new Request(`${BASE_URL}/api/workflows`));
|
|
72
|
+
const workflows = (await workflowsResponse.json()) as Record<string, unknown>;
|
|
73
|
+
console.log(`Workflows: ${Object.keys(workflows).join(', ')}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
main().catch(console.error);
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### mastra/index.ts
|
|
81
|
+
```typescript
|
|
82
|
+
import { Mastra } from '@mastra/core/mastra';
|
|
83
|
+
|
|
84
|
+
import { calculatorTool, timestampTool } from './tools';
|
|
85
|
+
import { dailyReportWorkflow, paymentProcessorWorkflow, processMessageWorkflow } from './workflows';
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Main Mastra instance configured with tools and workflows
|
|
89
|
+
* for demonstrating server app access patterns.
|
|
90
|
+
*/
|
|
91
|
+
export const mastra = new Mastra({
|
|
92
|
+
tools: {
|
|
93
|
+
calculatorTool,
|
|
94
|
+
timestampTool,
|
|
95
|
+
},
|
|
96
|
+
workflows: {
|
|
97
|
+
processMessageWorkflow,
|
|
98
|
+
dailyReportWorkflow,
|
|
99
|
+
paymentProcessorWorkflow,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### mastra/tools/index.ts
|
|
106
|
+
```typescript
|
|
107
|
+
import { createTool } from '@mastra/core/tools';
|
|
108
|
+
import { z } from 'zod';
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Calculator tool - performs basic arithmetic operations
|
|
112
|
+
*/
|
|
113
|
+
export const calculatorTool = createTool({
|
|
114
|
+
id: 'calculator',
|
|
115
|
+
description: 'Performs basic arithmetic operations',
|
|
116
|
+
inputSchema: z.object({
|
|
117
|
+
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
|
|
118
|
+
a: z.number(),
|
|
119
|
+
b: z.number(),
|
|
120
|
+
}),
|
|
121
|
+
execute: async ({ operation, a, b }) => {
|
|
122
|
+
if (operation === 'divide' && b === 0) {
|
|
123
|
+
throw new Error('Cannot divide by zero');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let result: number;
|
|
127
|
+
switch (operation) {
|
|
128
|
+
case 'add':
|
|
129
|
+
result = a + b;
|
|
130
|
+
break;
|
|
131
|
+
case 'subtract':
|
|
132
|
+
result = a - b;
|
|
133
|
+
break;
|
|
134
|
+
case 'multiply':
|
|
135
|
+
result = a * b;
|
|
136
|
+
break;
|
|
137
|
+
case 'divide':
|
|
138
|
+
result = a / b;
|
|
139
|
+
break;
|
|
140
|
+
default:
|
|
141
|
+
throw new Error(`Unsupported operation: ${operation}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { result, operation, operands: { a, b } };
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Timestamp tool - returns current timestamp in various formats
|
|
150
|
+
*/
|
|
151
|
+
export const timestampTool = createTool({
|
|
152
|
+
id: 'timestamp',
|
|
153
|
+
description: 'Returns the current timestamp in various formats',
|
|
154
|
+
inputSchema: z.object({
|
|
155
|
+
format: z.enum(['iso', 'unix', 'readable']).default('iso'),
|
|
156
|
+
}),
|
|
157
|
+
execute: async ({ format }) => {
|
|
158
|
+
const now = new Date();
|
|
159
|
+
|
|
160
|
+
let timestamp: string;
|
|
161
|
+
switch (format) {
|
|
162
|
+
case 'unix':
|
|
163
|
+
timestamp = Math.floor(now.getTime() / 1000).toString();
|
|
164
|
+
break;
|
|
165
|
+
case 'readable':
|
|
166
|
+
timestamp = now.toLocaleString();
|
|
167
|
+
break;
|
|
168
|
+
case 'iso':
|
|
169
|
+
default:
|
|
170
|
+
timestamp = now.toISOString();
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { timestamp, format };
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### mastra/workflows/index.ts
|
|
181
|
+
```typescript
|
|
182
|
+
import { createStep, createWorkflow } from '@mastra/core/workflows';
|
|
183
|
+
import { z } from 'zod';
|
|
184
|
+
|
|
185
|
+
// --- Process Message Workflow ---
|
|
186
|
+
|
|
187
|
+
const validateStep = createStep({
|
|
188
|
+
id: 'validate',
|
|
189
|
+
description: 'Validates the incoming message',
|
|
190
|
+
inputSchema: z.object({
|
|
191
|
+
message: z.string(),
|
|
192
|
+
priority: z.enum(['low', 'medium', 'high']).default('medium'),
|
|
193
|
+
}),
|
|
194
|
+
outputSchema: z.object({
|
|
195
|
+
valid: z.boolean(),
|
|
196
|
+
message: z.string(),
|
|
197
|
+
priority: z.enum(['low', 'medium', 'high']),
|
|
198
|
+
}),
|
|
199
|
+
execute: async ({ inputData }) => {
|
|
200
|
+
const valid = inputData.message.length > 0 && inputData.message.length < 1000;
|
|
201
|
+
return {
|
|
202
|
+
valid,
|
|
203
|
+
message: inputData.message,
|
|
204
|
+
priority: inputData.priority,
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const processStep = createStep({
|
|
210
|
+
id: 'process',
|
|
211
|
+
description: 'Processes the validated message',
|
|
212
|
+
inputSchema: z.object({
|
|
213
|
+
valid: z.boolean(),
|
|
214
|
+
message: z.string(),
|
|
215
|
+
priority: z.enum(['low', 'medium', 'high']),
|
|
216
|
+
}),
|
|
217
|
+
outputSchema: z.object({
|
|
218
|
+
processed: z.boolean(),
|
|
219
|
+
result: z.string(),
|
|
220
|
+
timestamp: z.string(),
|
|
221
|
+
}),
|
|
222
|
+
execute: async ({ inputData }) => {
|
|
223
|
+
if (!inputData.valid) {
|
|
224
|
+
return {
|
|
225
|
+
processed: false,
|
|
226
|
+
result: 'Invalid message',
|
|
227
|
+
timestamp: new Date().toISOString(),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const result = `Processed [${inputData.priority}]: ${inputData.message.substring(0, 50)}...`;
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
processed: true,
|
|
235
|
+
result,
|
|
236
|
+
timestamp: new Date().toISOString(),
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
export const processMessageWorkflow = createWorkflow({
|
|
242
|
+
id: 'process-message',
|
|
243
|
+
description: 'Validates and processes a message',
|
|
244
|
+
inputSchema: z.object({
|
|
245
|
+
message: z.string(),
|
|
246
|
+
priority: z.enum(['low', 'medium', 'high']).default('medium'),
|
|
247
|
+
}),
|
|
248
|
+
outputSchema: z.object({
|
|
249
|
+
processed: z.boolean(),
|
|
250
|
+
result: z.string(),
|
|
251
|
+
timestamp: z.string(),
|
|
252
|
+
}),
|
|
253
|
+
})
|
|
254
|
+
.then(validateStep)
|
|
255
|
+
.then(processStep)
|
|
256
|
+
.commit();
|
|
257
|
+
|
|
258
|
+
// --- Daily Report Workflow ---
|
|
259
|
+
|
|
260
|
+
const generateReportStep = createStep({
|
|
261
|
+
id: 'generate-report',
|
|
262
|
+
description: 'Generates a daily report',
|
|
263
|
+
inputSchema: z.object({
|
|
264
|
+
date: z.string(),
|
|
265
|
+
reportType: z.enum(['summary', 'detailed']).default('summary'),
|
|
266
|
+
}),
|
|
267
|
+
outputSchema: z.object({
|
|
268
|
+
reportId: z.string(),
|
|
269
|
+
status: z.string(),
|
|
270
|
+
generatedAt: z.string(),
|
|
271
|
+
}),
|
|
272
|
+
execute: async ({ inputData }) => {
|
|
273
|
+
const reportId = `RPT-${Date.now()}`;
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
reportId,
|
|
277
|
+
status: `Generated ${inputData.reportType} report for ${inputData.date}`,
|
|
278
|
+
generatedAt: new Date().toISOString(),
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
export const dailyReportWorkflow = createWorkflow({
|
|
284
|
+
id: 'daily-report',
|
|
285
|
+
description: 'Generates daily reports',
|
|
286
|
+
inputSchema: z.object({
|
|
287
|
+
date: z.string(),
|
|
288
|
+
reportType: z.enum(['summary', 'detailed']).default('summary'),
|
|
289
|
+
}),
|
|
290
|
+
outputSchema: z.object({
|
|
291
|
+
reportId: z.string(),
|
|
292
|
+
status: z.string(),
|
|
293
|
+
generatedAt: z.string(),
|
|
294
|
+
}),
|
|
295
|
+
})
|
|
296
|
+
.then(generateReportStep)
|
|
297
|
+
.commit();
|
|
298
|
+
|
|
299
|
+
// --- Payment Processor Workflow ---
|
|
300
|
+
|
|
301
|
+
const processPaymentStep = createStep({
|
|
302
|
+
id: 'process-payment',
|
|
303
|
+
description: 'Processes a payment transaction',
|
|
304
|
+
inputSchema: z.object({
|
|
305
|
+
amount: z.number(),
|
|
306
|
+
currency: z.string(),
|
|
307
|
+
customerId: z.string(),
|
|
308
|
+
}),
|
|
309
|
+
outputSchema: z.object({
|
|
310
|
+
transactionId: z.string(),
|
|
311
|
+
status: z.string(),
|
|
312
|
+
processedAt: z.string(),
|
|
313
|
+
}),
|
|
314
|
+
execute: async ({ inputData }) => {
|
|
315
|
+
const transactionId = `TXN-${Date.now()}`;
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
transactionId,
|
|
319
|
+
status: `Processed ${inputData.currency} ${inputData.amount} for customer ${inputData.customerId}`,
|
|
320
|
+
processedAt: new Date().toISOString(),
|
|
321
|
+
};
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
export const paymentProcessorWorkflow = createWorkflow({
|
|
326
|
+
id: 'payment-processor',
|
|
327
|
+
description: 'Processes payment transactions',
|
|
328
|
+
inputSchema: z.object({
|
|
329
|
+
amount: z.number(),
|
|
330
|
+
currency: z.string(),
|
|
331
|
+
customerId: z.string(),
|
|
332
|
+
}),
|
|
333
|
+
outputSchema: z.object({
|
|
334
|
+
transactionId: z.string(),
|
|
335
|
+
status: z.string(),
|
|
336
|
+
processedAt: z.string(),
|
|
337
|
+
}),
|
|
338
|
+
})
|
|
339
|
+
.then(processPaymentStep)
|
|
340
|
+
.commit();
|
|
341
|
+
|
|
342
|
+
```
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Agent Approval | Agents"
|
|
3
|
+
description: Learn how to require approvals and suspend tool execution while keeping humans in control of agent workflows.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Agent Approval
|
|
7
|
+
|
|
8
|
+
Agents sometimes require the same [human-in-the-loop](/docs/v1/workflows/human-in-the-loop) oversight used in workflows when calling tools that handle sensitive operations, like deleting resources or performing running long processes. With agent approval you can suspend a tool call and provide feedback to the user, or approve or decline a tool call based on targeted application conditions.
|
|
9
|
+
|
|
10
|
+
## Tool call approval
|
|
11
|
+
|
|
12
|
+
Tool call approval can be enabled at the agent level and apply to every tool the agent uses, or at the tool level providing more granular control over individual tool calls.
|
|
13
|
+
|
|
14
|
+
### Storage
|
|
15
|
+
|
|
16
|
+
Agent approval uses a snapshot to capture the state of the request. Ensure you've enabled a storage provider in your main Mastra instance. If storage isn't enabled you'll see an error relating to snapshot not found.
|
|
17
|
+
|
|
18
|
+
```typescript title="src/mastra/index.ts"
|
|
19
|
+
import { Mastra } from "@mastra/core/mastra";
|
|
20
|
+
import { LibSQLStore } from "@mastra/libsql";
|
|
21
|
+
|
|
22
|
+
export const mastra = new Mastra({
|
|
23
|
+
// ...
|
|
24
|
+
storage: new LibSQLStore({
|
|
25
|
+
id: "mastra-storage",
|
|
26
|
+
url: ":memory:"
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Agent-level approval
|
|
33
|
+
|
|
34
|
+
When calling an agent using `.stream()` set `requireToolApproval` to `true` which will prevent the agent from calling any of the tools defined in its configuration.
|
|
35
|
+
|
|
36
|
+
```typescript showLineNumbers
|
|
37
|
+
const stream = await agent.stream("What's the weather in London?", {
|
|
38
|
+
requireToolApproval: true
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Approving tool calls
|
|
43
|
+
|
|
44
|
+
To approve a tool call, access `approveToolCall` from the `agent`, passing in the `runId` of the stream. This will let the agent know its now OK to call its tools.
|
|
45
|
+
|
|
46
|
+
```typescript showLineNumbers
|
|
47
|
+
const handleApproval = async () => {
|
|
48
|
+
const approvedStream = await agent.approveToolCall({ runId: stream.runId });
|
|
49
|
+
|
|
50
|
+
for await (const chunk of approvedStream.textStream) {
|
|
51
|
+
process.stdout.write(chunk);
|
|
52
|
+
}
|
|
53
|
+
process.stdout.write("\n");
|
|
54
|
+
};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Declining tool calls
|
|
58
|
+
|
|
59
|
+
To decline a tool call, access the `declineToolCall` from the `agent`. You will see the streamed response from the agent, but it won't call its tools.
|
|
60
|
+
|
|
61
|
+
```typescript showLineNumbers
|
|
62
|
+
const handleDecline = async () => {
|
|
63
|
+
const declinedStream = await agent.declineToolCall({ runId: stream.runId });
|
|
64
|
+
|
|
65
|
+
for await (const chunk of declinedStream.textStream) {
|
|
66
|
+
process.stdout.write(chunk);
|
|
67
|
+
}
|
|
68
|
+
process.stdout.write("\n");
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Tool-level approval
|
|
73
|
+
|
|
74
|
+
There are two types of tool call approval. The first uses `requireApproval`, which is a property on the tool definition, while `requireToolApproval` is a parameter passed to `agent.stream()`. The second uses `suspend` and lets the agent provide context or confirmation prompts so the user can decide whether the tool call should continue.
|
|
75
|
+
|
|
76
|
+
### Tool approval using `requireToolApproval`
|
|
77
|
+
|
|
78
|
+
In this approach, `requireApproval` is configured on the tool definition (shown below) rather than on the agent.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
export const testTool = createTool({
|
|
82
|
+
id: "test-tool",
|
|
83
|
+
description: "Fetches weather for a location",
|
|
84
|
+
inputSchema: z.object({
|
|
85
|
+
location: z.string()
|
|
86
|
+
}),
|
|
87
|
+
outputSchema: z.object({
|
|
88
|
+
weather: z.string()
|
|
89
|
+
}),
|
|
90
|
+
resumeSchema: z.object({
|
|
91
|
+
approved: z.boolean()
|
|
92
|
+
}),
|
|
93
|
+
execute: async ({ location }) => {
|
|
94
|
+
const response = await fetch(`https://wttr.in/${location}?format=3`);
|
|
95
|
+
const weather = await response.text();
|
|
96
|
+
|
|
97
|
+
return { weather };
|
|
98
|
+
},
|
|
99
|
+
requireApproval: true
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
When `requireApproval` is true for a tool, the stream will include chunks of type `tool-call-approval` to indicate that the call is paused. To continue the call, invoke `resumeStream` with the required `resumeSchema` and the `runId`.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const stream = await agent.stream("What's the weather in London?");
|
|
107
|
+
|
|
108
|
+
for await (const chunk of stream.fullStream) {
|
|
109
|
+
if (chunk.type === "tool-call-approval") {
|
|
110
|
+
console.log("Approval required.");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const handleResume = async () => {
|
|
115
|
+
const resumedStream = await agent.resumeStream({ approved: true }, { runId: stream.runId });
|
|
116
|
+
|
|
117
|
+
for await (const chunk of resumedStream.textStream) {
|
|
118
|
+
process.stdout.write(chunk);
|
|
119
|
+
}
|
|
120
|
+
process.stdout.write("\n");
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
### Tool approval using `suspend`
|
|
126
|
+
|
|
127
|
+
With this approach, neither the agent nor the tool uses `requireApproval`. Instead, the tool implementation calls `suspend` to pause execution and return context or confirmation prompts to the user.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
|
|
131
|
+
export const testToolB = createTool({
|
|
132
|
+
id: "test-tool-b",
|
|
133
|
+
description: "Fetches weather for a location",
|
|
134
|
+
inputSchema: z.object({
|
|
135
|
+
location: z.string()
|
|
136
|
+
}),
|
|
137
|
+
outputSchema: z.object({
|
|
138
|
+
weather: z.string()
|
|
139
|
+
}),
|
|
140
|
+
resumeSchema: z.object({
|
|
141
|
+
approved: z.boolean()
|
|
142
|
+
}),
|
|
143
|
+
suspendSchema: z.object({
|
|
144
|
+
reason: z.string()
|
|
145
|
+
}),
|
|
146
|
+
execute: async ({ location }, { agent } = {}) => {
|
|
147
|
+
const { resumeData: { approved } = {}, suspend } = agent ?? {};
|
|
148
|
+
|
|
149
|
+
if (!approved) {
|
|
150
|
+
return suspend?.({ reason: "Approval required." });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const response = await fetch(`https://wttr.in/${location}?format=3`);
|
|
154
|
+
const weather = await response.text();
|
|
155
|
+
|
|
156
|
+
return { weather };
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
With this approach the stream will include a `tool-call-suspended` chunk, and the `suspendPayload` will contain the `reason` defined by the tool's `suspendSchema`. To continue the call, invoke `resumeStream` with the required `resumeSchema` and the `runId`.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const stream = await agent.stream("What's the weather in London?");
|
|
165
|
+
|
|
166
|
+
for await (const chunk of stream.fullStream) {
|
|
167
|
+
if (chunk.type === "tool-call-suspended") {
|
|
168
|
+
console.log(chunk.payload.suspendPayload);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const handleResume = async () => {
|
|
173
|
+
const resumedStream = await agent.resumeStream({ approved: true }, { runId: stream.runId });
|
|
174
|
+
|
|
175
|
+
for await (const chunk of resumedStream.textStream) {
|
|
176
|
+
process.stdout.write(chunk);
|
|
177
|
+
}
|
|
178
|
+
process.stdout.write("\n");
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Related
|
|
184
|
+
|
|
185
|
+
- [Using Tools](./using-tools)
|
|
186
|
+
- [Agent Overview](./overview)
|
|
187
|
+
- [Tools Overview](../mcp/overview)
|
|
188
|
+
- [Agent Memory](./agent-memory)
|
|
189
|
+
- [Request Context](/docs/v1/server-db/request-context)
|
|
@@ -33,7 +33,7 @@ export const moderatedAgent = new Agent({
|
|
|
33
33
|
model: "openai/gpt-5.1",
|
|
34
34
|
inputProcessors: [
|
|
35
35
|
new ModerationProcessor({
|
|
36
|
-
model: "openai/gpt-
|
|
36
|
+
model: "openrouter/openai/gpt-oss-safeguard-20b",
|
|
37
37
|
categories: ["hate", "harassment", "violence"],
|
|
38
38
|
threshold: 0.7,
|
|
39
39
|
strategy: "block",
|
|
@@ -82,7 +82,7 @@ export const secureAgent = new Agent({
|
|
|
82
82
|
// ...
|
|
83
83
|
inputProcessors: [
|
|
84
84
|
new PromptInjectionDetector({
|
|
85
|
-
model: "openai/gpt-
|
|
85
|
+
model: "openrouter/openai/gpt-oss-safeguard-20b",
|
|
86
86
|
threshold: 0.8,
|
|
87
87
|
strategy: "rewrite",
|
|
88
88
|
detectionTypes: ["injection", "jailbreak", "system-override"],
|
|
@@ -106,7 +106,7 @@ export const multilingualAgent = new Agent({
|
|
|
106
106
|
// ...
|
|
107
107
|
inputProcessors: [
|
|
108
108
|
new LanguageDetector({
|
|
109
|
-
model: "openai/gpt-
|
|
109
|
+
model: "openrouter/openai/gpt-oss-safeguard-20b",
|
|
110
110
|
targetLanguages: ["English", "en"],
|
|
111
111
|
strategy: "translate",
|
|
112
112
|
threshold: 0.8,
|
|
@@ -179,7 +179,7 @@ const scrubbedAgent = new Agent({
|
|
|
179
179
|
name: "Scrubbed Agent",
|
|
180
180
|
outputProcessors: [
|
|
181
181
|
new SystemPromptScrubber({
|
|
182
|
-
model: "openai/gpt-
|
|
182
|
+
model: "openrouter/openai/gpt-oss-safeguard-20b",
|
|
183
183
|
strategy: "redact",
|
|
184
184
|
customPatterns: ["system prompt", "internal instructions"],
|
|
185
185
|
includeDetections: true,
|
|
@@ -194,6 +194,10 @@ const scrubbedAgent = new Agent({
|
|
|
194
194
|
|
|
195
195
|
> See [SystemPromptScrubber](/reference/v1/processors/system-prompt-scrubber) for a full list of configuration options.
|
|
196
196
|
|
|
197
|
+
:::note
|
|
198
|
+
When streaming responses over HTTP, Mastra redacts sensitive request data (system prompts, tool definitions, API keys) from stream chunks at the server level by default. See [Stream data redaction](/docs/v1/server-db/mastra-server#stream-data-redaction) for details.
|
|
199
|
+
:::
|
|
200
|
+
|
|
197
201
|
## Hybrid processors
|
|
198
202
|
|
|
199
203
|
Hybrid processors can be applied either before messages are sent to the language model or before responses are returned to the user. They are useful for tasks like content moderation and PII redaction.
|
|
@@ -211,7 +215,7 @@ export const moderatedAgent = new Agent({
|
|
|
211
215
|
// ...
|
|
212
216
|
inputProcessors: [
|
|
213
217
|
new ModerationProcessor({
|
|
214
|
-
model: "openai/gpt-
|
|
218
|
+
model: "openrouter/openai/gpt-oss-safeguard-20b",
|
|
215
219
|
threshold: 0.7,
|
|
216
220
|
strategy: "block",
|
|
217
221
|
categories: ["hate", "harassment", "violence"],
|
|
@@ -240,7 +244,7 @@ export const privateAgent = new Agent({
|
|
|
240
244
|
// ...
|
|
241
245
|
inputProcessors: [
|
|
242
246
|
new PIIDetector({
|
|
243
|
-
model: "openai/gpt-
|
|
247
|
+
model: "openrouter/openai/gpt-oss-safeguard-20b",
|
|
244
248
|
threshold: 0.6,
|
|
245
249
|
strategy: "redact",
|
|
246
250
|
redactionMethod: "mask",
|
|
@@ -364,6 +368,6 @@ If the built-in processors don’t cover your needs, you can create your own by
|
|
|
364
368
|
|
|
365
369
|
Available examples:
|
|
366
370
|
|
|
367
|
-
- [Message Length Limiter](/examples/
|
|
368
|
-
- [Response Length Limiter](/examples/
|
|
369
|
-
- [Response Validator](/examples/
|
|
371
|
+
- [Message Length Limiter](https://github.com/mastra-ai/mastra/tree/main/examples/processors-message-length-limiter)
|
|
372
|
+
- [Response Length Limiter](https://github.com/mastra-ai/mastra/tree/main/examples/processors-response-length-limiter)
|
|
373
|
+
- [Response Validator](https://github.com/mastra-ai/mastra/tree/main/examples/processors-response-validator)
|
|
@@ -240,3 +240,4 @@ network-execution-event-step-finish
|
|
|
240
240
|
- [Agent Memory](./agent-memory)
|
|
241
241
|
- [Workflows Overview](../workflows/overview)
|
|
242
242
|
- [Request Context](/docs/v1/server-db/request-context)
|
|
243
|
+
- [Supervisor example](https://github.com/mastra-ai/mastra/tree/main/examples/supervisor-agent)
|