@mastra/mcp-docs-server 0.13.32 ā 0.13.33
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/%40internal%2Fchangeset-cli.md +2 -0
- package/.docs/organized/changelogs/%40internal%2Fexternal-types.md +2 -0
- package/.docs/organized/changelogs/%40internal%2Fstorage-test-utils.md +9 -9
- package/.docs/organized/changelogs/%40internal%2Ftypes-builder.md +2 -0
- package/.docs/organized/changelogs/%40mastra%2Fclient-js.md +15 -15
- package/.docs/organized/changelogs/%40mastra%2Fcore.md +5 -5
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-cloud.md +17 -17
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-cloudflare.md +17 -17
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-netlify.md +17 -17
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-vercel.md +17 -17
- package/.docs/organized/changelogs/%40mastra%2Fdeployer.md +21 -21
- package/.docs/organized/changelogs/%40mastra%2Fmcp-docs-server.md +15 -15
- package/.docs/organized/changelogs/%40mastra%2Fplayground-ui.md +27 -27
- package/.docs/organized/changelogs/%40mastra%2Freact.md +14 -0
- package/.docs/organized/changelogs/%40mastra%2Fserver.md +19 -19
- package/.docs/organized/changelogs/create-mastra.md +5 -5
- package/.docs/organized/changelogs/mastra.md +21 -21
- package/.docs/raw/deployment/cloud-providers/aws-lambda.mdx +1 -1
- package/.docs/raw/getting-started/project-structure.mdx +48 -45
- package/.docs/raw/getting-started/studio.mdx +158 -0
- package/.docs/raw/reference/workflows/step.mdx +1 -1
- package/.docs/raw/tools-mcp/overview.mdx +2 -2
- package/.docs/raw/workflows/agents-and-tools.mdx +131 -0
- package/.docs/raw/workflows/overview.mdx +15 -13
- package/CHANGELOG.md +14 -0
- package/package.json +5 -5
- package/.docs/organized/code-examples/agent.md +0 -1387
- package/.docs/raw/server-db/local-dev-playground.mdx +0 -233
- package/.docs/raw/workflows/using-with-agents-and-tools.mdx +0 -350
|
@@ -1,1387 +0,0 @@
|
|
|
1
|
-
### package.json
|
|
2
|
-
```json
|
|
3
|
-
{
|
|
4
|
-
"name": "examples-agent",
|
|
5
|
-
"dependencies": {
|
|
6
|
-
"@ai-sdk/provider": "^2.0.0",
|
|
7
|
-
"@ai-sdk/google": "^1.2.22",
|
|
8
|
-
"@ai-sdk/openai": "^1.3.24",
|
|
9
|
-
"@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.15",
|
|
10
|
-
"@mastra/client-js": "latest",
|
|
11
|
-
"@mastra/core": "latest",
|
|
12
|
-
"@mastra/evals": "latest",
|
|
13
|
-
"@mastra/libsql": "latest",
|
|
14
|
-
"@mastra/loggers": "latest",
|
|
15
|
-
"@mastra/mcp": "latest",
|
|
16
|
-
"@mastra/memory": "latest",
|
|
17
|
-
"@mastra/voice-openai": "latest",
|
|
18
|
-
"ai": "^4.3.19",
|
|
19
|
-
"ai-v5": "npm:ai@^5.0.15",
|
|
20
|
-
"fetch-to-node": "^2.1.0",
|
|
21
|
-
"mastra": "latest",
|
|
22
|
-
"typescript": "^5.8.3",
|
|
23
|
-
"zod": "^3.25.76"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### client.ts
|
|
29
|
-
```typescript
|
|
30
|
-
import { MCPClient } from '@mastra/mcp';
|
|
31
|
-
// import type { ElicitationHandler } from '@mastra/mcp';
|
|
32
|
-
import { createInterface } from 'readline';
|
|
33
|
-
|
|
34
|
-
// Create readline interface for user input
|
|
35
|
-
const readline = createInterface({
|
|
36
|
-
input: process.stdin,
|
|
37
|
-
output: process.stdout,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Helper function to prompt user for input
|
|
41
|
-
function askQuestion(question: string): Promise<string> {
|
|
42
|
-
return new Promise(resolve => {
|
|
43
|
-
readline.question(question, answer => {
|
|
44
|
-
resolve(answer.trim());
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Elicitation handler that prompts the user for input
|
|
50
|
-
const elicitationHandler = async request => {
|
|
51
|
-
console.log('\nš Elicitation Request Received:');
|
|
52
|
-
console.log(`Message: ${request.message}`);
|
|
53
|
-
console.log('Requested Schema:');
|
|
54
|
-
console.log(JSON.stringify(request.requestedSchema, null, 2));
|
|
55
|
-
|
|
56
|
-
const schema = request.requestedSchema;
|
|
57
|
-
const properties = schema.properties;
|
|
58
|
-
const required = schema.required || [];
|
|
59
|
-
|
|
60
|
-
console.log('\nPlease provide the following information:');
|
|
61
|
-
|
|
62
|
-
const content: Record<string, unknown> = {};
|
|
63
|
-
|
|
64
|
-
// Collect input for each field
|
|
65
|
-
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
66
|
-
const field = fieldSchema as {
|
|
67
|
-
type?: string;
|
|
68
|
-
title?: string;
|
|
69
|
-
description?: string;
|
|
70
|
-
format?: string;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const isRequired = required.includes(fieldName);
|
|
74
|
-
let prompt = `${field.title || fieldName}`;
|
|
75
|
-
|
|
76
|
-
// Add helpful information to the prompt
|
|
77
|
-
if (field.description) {
|
|
78
|
-
prompt += ` (${field.description})`;
|
|
79
|
-
}
|
|
80
|
-
if (field.format) {
|
|
81
|
-
prompt += ` [format: ${field.format}]`;
|
|
82
|
-
}
|
|
83
|
-
if (isRequired) {
|
|
84
|
-
prompt += ' *required*';
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
prompt += ': ';
|
|
88
|
-
|
|
89
|
-
const answer = await askQuestion(prompt);
|
|
90
|
-
|
|
91
|
-
// Check for cancellation
|
|
92
|
-
if (answer.toLowerCase() === 'cancel' || answer.toLowerCase() === 'c') {
|
|
93
|
-
return { action: 'cancel' as const };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Handle empty responses
|
|
97
|
-
if (answer === '' && isRequired) {
|
|
98
|
-
console.log(`ā Error: ${fieldName} is required`);
|
|
99
|
-
return { action: 'reject' as const };
|
|
100
|
-
} else if (answer !== '') {
|
|
101
|
-
content[fieldName] = answer;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Show the collected data and ask for confirmation
|
|
106
|
-
console.log('\nā
Collected data:');
|
|
107
|
-
console.log(JSON.stringify(content, null, 2));
|
|
108
|
-
|
|
109
|
-
const confirmAnswer = await askQuestion('\nSubmit this information? (yes/no/cancel): ');
|
|
110
|
-
|
|
111
|
-
if (confirmAnswer.toLowerCase() === 'yes' || confirmAnswer.toLowerCase() === 'y') {
|
|
112
|
-
return {
|
|
113
|
-
action: 'accept' as const,
|
|
114
|
-
content,
|
|
115
|
-
};
|
|
116
|
-
} else if (confirmAnswer.toLowerCase() === 'cancel' || confirmAnswer.toLowerCase() === 'c') {
|
|
117
|
-
return { action: 'cancel' as const };
|
|
118
|
-
} else {
|
|
119
|
-
return { action: 'reject' as const };
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
async function main() {
|
|
124
|
-
const mcpClient = new MCPClient({
|
|
125
|
-
servers: {
|
|
126
|
-
myMcpServerTwo: {
|
|
127
|
-
url: new URL('http://localhost:4111/api/mcp/myMcpServerTwo/mcp'),
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
mcpClient.elicitation.onRequest('myMcpServerTwo', elicitationHandler);
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
console.log('Connecting to MCP server...');
|
|
136
|
-
const tools = await mcpClient.getTools();
|
|
137
|
-
console.log('Available tools:', Object.keys(tools));
|
|
138
|
-
|
|
139
|
-
// Test the elicitation functionality
|
|
140
|
-
console.log('\nš§Ŗ Testing elicitation functionality...');
|
|
141
|
-
|
|
142
|
-
// Find the collectContactInfo tool
|
|
143
|
-
const collectContactInfoTool = tools['myMcpServerTwo_collectContactInfo'];
|
|
144
|
-
if (collectContactInfoTool) {
|
|
145
|
-
console.log('\nCalling collectContactInfo tool...');
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
const result = await collectContactInfoTool.execute({
|
|
149
|
-
context: {
|
|
150
|
-
reason: 'We need your contact information to send you updates about our service.',
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
console.log('\nš Tool Result:');
|
|
155
|
-
console.log(result);
|
|
156
|
-
} catch (error) {
|
|
157
|
-
console.error('ā Error calling collectContactInfo tool:', error);
|
|
158
|
-
}
|
|
159
|
-
} else {
|
|
160
|
-
console.log('ā collectContactInfo tool not found');
|
|
161
|
-
console.log('Available tools:', Object.keys(tools));
|
|
162
|
-
}
|
|
163
|
-
} catch (error) {
|
|
164
|
-
console.error('ā Error:', error);
|
|
165
|
-
} finally {
|
|
166
|
-
readline.close();
|
|
167
|
-
await mcpClient.disconnect();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
main().catch(console.error);
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### index.ts
|
|
176
|
-
```typescript
|
|
177
|
-
import { z } from 'zod';
|
|
178
|
-
import { mastra } from './mastra';
|
|
179
|
-
|
|
180
|
-
const agent = mastra.getAgent('chefAgent');
|
|
181
|
-
const responsesAgent = mastra.getAgent('chefAgentResponses');
|
|
182
|
-
const agentThatHarassesYou = mastra.getAgent('agentThatHarassesYou');
|
|
183
|
-
|
|
184
|
-
const stream = await agentThatHarassesYou.stream('I want to fight you');
|
|
185
|
-
|
|
186
|
-
for await (const chunk of stream.textStream) {
|
|
187
|
-
console.log(`frontend received chunk: ${chunk}`);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
console.log('done');
|
|
191
|
-
|
|
192
|
-
// async function text() {
|
|
193
|
-
// // Query 1: Basic pantry ingredients
|
|
194
|
-
// const query1 =
|
|
195
|
-
// 'In my kitchen I have: pasta, canned tomatoes, garlic, olive oil, and some dried herbs (basil and oregano). What can I make?';
|
|
196
|
-
// console.log(`Query 1: ${query1}`);
|
|
197
|
-
|
|
198
|
-
// const pastaResponse = await agent.generate(query1);
|
|
199
|
-
// console.log('\nšØāš³ Chef Michel:', pastaResponse.text);
|
|
200
|
-
// console.log('\n-------------------\n');
|
|
201
|
-
// }
|
|
202
|
-
|
|
203
|
-
// async function generateText() {
|
|
204
|
-
// // Query 1: Basic pantry ingredients
|
|
205
|
-
|
|
206
|
-
// const query1 =
|
|
207
|
-
// 'In my kitchen I have: pasta, canned tomatoes, garlic, olive oil, and some dried herbs (basil and oregano). What can I make?';
|
|
208
|
-
// console.log(`Query 1: ${query1}`);
|
|
209
|
-
|
|
210
|
-
// const pastaResponse = await agent.generate(query1);
|
|
211
|
-
|
|
212
|
-
// console.log('\nšØāš³ Chef Michel:', pastaResponse.text);
|
|
213
|
-
// console.log('\n-------------------\n');
|
|
214
|
-
// }
|
|
215
|
-
|
|
216
|
-
// async function textStream() {
|
|
217
|
-
// // Query 2: More ingredients
|
|
218
|
-
// const query2 =
|
|
219
|
-
// "Now I'm over at my friend's house, and they have: chicken thighs, coconut milk, sweet potatoes, and some curry powder.";
|
|
220
|
-
// console.log(`Query 2: ${query2}`);
|
|
221
|
-
|
|
222
|
-
// const curryResponse = await agent.stream(query2);
|
|
223
|
-
|
|
224
|
-
// console.log('\nšØāš³ Chef Michel: ');
|
|
225
|
-
|
|
226
|
-
// // Handle the stream
|
|
227
|
-
// for await (const chunk of curryResponse.textStream) {
|
|
228
|
-
// // Write each chunk without a newline to create a continuous stream
|
|
229
|
-
// process.stdout.write(chunk);
|
|
230
|
-
// }
|
|
231
|
-
|
|
232
|
-
// console.log('\n\nā
Recipe complete!');
|
|
233
|
-
// }
|
|
234
|
-
|
|
235
|
-
// async function generateStream() {
|
|
236
|
-
// // Query 2: More ingredients
|
|
237
|
-
// const query2 =
|
|
238
|
-
// "Now I'm over at my friend's house, and they have: chicken thighs, coconut milk, sweet potatoes, and some curry powder.";
|
|
239
|
-
// console.log(`Query 2: ${query2}`);
|
|
240
|
-
|
|
241
|
-
// const curryResponse = await agent.stream([query2]);
|
|
242
|
-
|
|
243
|
-
// console.log('\nšØāš³ Chef Michel: ');
|
|
244
|
-
|
|
245
|
-
// // Handle the stream
|
|
246
|
-
// for await (const chunk of curryResponse.textStream) {
|
|
247
|
-
// // Write each chunk without a newline to create a continuous stream
|
|
248
|
-
// process.stdout.write(chunk);
|
|
249
|
-
// }
|
|
250
|
-
|
|
251
|
-
// console.log('\n\nā
Recipe complete!');
|
|
252
|
-
// }
|
|
253
|
-
|
|
254
|
-
// async function textObject() {
|
|
255
|
-
// // Query 3: Generate a lasagna recipe
|
|
256
|
-
// const query3 = 'I want to make lasagna, can you generate a lasagna recipe for me?';
|
|
257
|
-
// console.log(`Query 3: ${query3}`);
|
|
258
|
-
|
|
259
|
-
// const lasagnaResponse = await agent.generate(query3, {
|
|
260
|
-
// output: z.object({
|
|
261
|
-
// ingredients: z.array(
|
|
262
|
-
// z.object({
|
|
263
|
-
// name: z.string(),
|
|
264
|
-
// amount: z.number(),
|
|
265
|
-
// }),
|
|
266
|
-
// ),
|
|
267
|
-
// steps: z.array(z.string()),
|
|
268
|
-
// }),
|
|
269
|
-
// });
|
|
270
|
-
// console.log('\nšØāš³ Chef Michel:', lasagnaResponse.object);
|
|
271
|
-
// console.log('\n-------------------\n');
|
|
272
|
-
// }
|
|
273
|
-
|
|
274
|
-
// async function experimentalTextObject() {
|
|
275
|
-
// // Query 3: Generate a lasagna recipe
|
|
276
|
-
// const query3 = 'I want to make lasagna, can you generate a lasagna recipe for me?';
|
|
277
|
-
// console.log(`Query 3: ${query3}`);
|
|
278
|
-
|
|
279
|
-
// const lasagnaResponse = await agent.generate(query3, {
|
|
280
|
-
// experimental_output: z.object({
|
|
281
|
-
// ingredients: z.array(
|
|
282
|
-
// z.object({
|
|
283
|
-
// name: z.string(),
|
|
284
|
-
// amount: z.number(),
|
|
285
|
-
// }),
|
|
286
|
-
// ),
|
|
287
|
-
// steps: z.array(z.string()),
|
|
288
|
-
// }),
|
|
289
|
-
// });
|
|
290
|
-
// console.log('\nšØāš³ Chef Michel:', lasagnaResponse.object);
|
|
291
|
-
// console.log('\n-------------------\n');
|
|
292
|
-
// }
|
|
293
|
-
|
|
294
|
-
// async function textObjectJsonSchema() {
|
|
295
|
-
// // Query 3: Generate a lasagna recipe
|
|
296
|
-
// const query3 = 'I want to make lasagna, can you generate a lasagna recipe for me?';
|
|
297
|
-
// console.log(`Query 3: ${query3}`);
|
|
298
|
-
|
|
299
|
-
// const lasagnaResponse = await agent.generate(query3, {
|
|
300
|
-
// output: {
|
|
301
|
-
// type: 'object',
|
|
302
|
-
// additionalProperties: false,
|
|
303
|
-
// required: ['ingredients', 'steps'],
|
|
304
|
-
// properties: {
|
|
305
|
-
// ingredients: {
|
|
306
|
-
// type: 'array',
|
|
307
|
-
// items: {
|
|
308
|
-
// type: 'object',
|
|
309
|
-
// additionalProperties: false,
|
|
310
|
-
// properties: {
|
|
311
|
-
// name: { type: 'string' },
|
|
312
|
-
// amount: { type: 'number' },
|
|
313
|
-
// },
|
|
314
|
-
// required: ['name', 'amount'],
|
|
315
|
-
// },
|
|
316
|
-
// },
|
|
317
|
-
// steps: {
|
|
318
|
-
// type: 'array',
|
|
319
|
-
// items: { type: 'string' },
|
|
320
|
-
// },
|
|
321
|
-
// },
|
|
322
|
-
// },
|
|
323
|
-
// });
|
|
324
|
-
|
|
325
|
-
// console.log('\nšØāš³ Chef Michel:', lasagnaResponse.object);
|
|
326
|
-
// console.log('\n-------------------\n');
|
|
327
|
-
// }
|
|
328
|
-
|
|
329
|
-
// async function generateObject() {
|
|
330
|
-
// // Query 3: Generate a lasagna recipe
|
|
331
|
-
// const query3 = 'I want to make lasagna, can you generate a lasagna recipe for me?';
|
|
332
|
-
// console.log(`Query 3: ${query3}`);
|
|
333
|
-
|
|
334
|
-
// const lasagnaResponse = await agent.generate([query3], {
|
|
335
|
-
// output: z.object({
|
|
336
|
-
// ingredients: z.array(
|
|
337
|
-
// z.object({
|
|
338
|
-
// name: z.string(),
|
|
339
|
-
// amount: z.number(),
|
|
340
|
-
// }),
|
|
341
|
-
// ),
|
|
342
|
-
// steps: z.array(z.string()),
|
|
343
|
-
// }),
|
|
344
|
-
// });
|
|
345
|
-
// console.log('\nšØāš³ Chef Michel:', lasagnaResponse.object);
|
|
346
|
-
// console.log('\n-------------------\n');
|
|
347
|
-
// }
|
|
348
|
-
|
|
349
|
-
// async function streamObject() {
|
|
350
|
-
// // Query 8: Generate a lasagna recipe
|
|
351
|
-
// const query8 = 'I want to make lasagna, can you generate a lasagna recipe for me?';
|
|
352
|
-
// console.log(`Query 8: ${query8}`);
|
|
353
|
-
|
|
354
|
-
// const lasagnaStreamResponse = await agent.stream(query8, {
|
|
355
|
-
// output: z.object({
|
|
356
|
-
// ingredients: z.array(
|
|
357
|
-
// z.object({
|
|
358
|
-
// name: z.string(),
|
|
359
|
-
// amount: z.number(),
|
|
360
|
-
// }),
|
|
361
|
-
// ),
|
|
362
|
-
// steps: z.array(z.string()),
|
|
363
|
-
// }),
|
|
364
|
-
// });
|
|
365
|
-
|
|
366
|
-
// console.log('\nšØāš³ Chef Michel: ');
|
|
367
|
-
|
|
368
|
-
// // Handle the stream
|
|
369
|
-
// for await (const chunk of lasagnaStreamResponse.textStream) {
|
|
370
|
-
// // Write each chunk without a newline to create a continuous stream
|
|
371
|
-
// process.stdout.write(chunk);
|
|
372
|
-
// }
|
|
373
|
-
|
|
374
|
-
// console.log('\n\nā
Recipe complete!');
|
|
375
|
-
// }
|
|
376
|
-
|
|
377
|
-
// async function generateStreamObject() {
|
|
378
|
-
// // Query 9: Generate a lasagna recipe
|
|
379
|
-
// const query9 = 'I want to make lasagna, can you generate a lasagna recipe for me?';
|
|
380
|
-
// console.log(`Query 9: ${query9}`);
|
|
381
|
-
|
|
382
|
-
// const lasagnaStreamResponse = await agent.stream([query9], {
|
|
383
|
-
// output: z.object({
|
|
384
|
-
// ingredients: z.array(
|
|
385
|
-
// z.object({
|
|
386
|
-
// name: z.string(),
|
|
387
|
-
// amount: z.number(),
|
|
388
|
-
// }),
|
|
389
|
-
// ),
|
|
390
|
-
// steps: z.array(z.string()),
|
|
391
|
-
// }),
|
|
392
|
-
// });
|
|
393
|
-
|
|
394
|
-
// console.log('\nšØāš³ Chef Michel: ');
|
|
395
|
-
|
|
396
|
-
// // Handle the stream
|
|
397
|
-
// for await (const chunk of lasagnaStreamResponse.textStream) {
|
|
398
|
-
// // Write each chunk without a newline to create a continuous stream
|
|
399
|
-
// process.stdout.write(chunk);
|
|
400
|
-
// }
|
|
401
|
-
|
|
402
|
-
// console.log('\n\nā
Recipe complete!');
|
|
403
|
-
// }
|
|
404
|
-
|
|
405
|
-
// async function generateExperimentalStreamObject() {
|
|
406
|
-
// // Query 9: Generate a lasagna recipe
|
|
407
|
-
// const query9 = 'I want to make lasagna, can you generate a lasagna recipe for me?';
|
|
408
|
-
// console.log(`Query 9: ${query9}`);
|
|
409
|
-
|
|
410
|
-
// const lasagnaStreamResponse = await agent.stream([query9], {
|
|
411
|
-
// experimental_output: z.object({
|
|
412
|
-
// ingredients: z.array(
|
|
413
|
-
// z.object({
|
|
414
|
-
// name: z.string(),
|
|
415
|
-
// amount: z.number(),
|
|
416
|
-
// }),
|
|
417
|
-
// ),
|
|
418
|
-
// steps: z.array(z.string()),
|
|
419
|
-
// }),
|
|
420
|
-
// });
|
|
421
|
-
|
|
422
|
-
// console.log('\nšØāš³ Chef Michel: ');
|
|
423
|
-
|
|
424
|
-
// // Handle the stream
|
|
425
|
-
// for await (const chunk of lasagnaStreamResponse.textStream) {
|
|
426
|
-
// // Write each chunk without a newline to create a continuous stream
|
|
427
|
-
// process.stdout.write(chunk);
|
|
428
|
-
// }
|
|
429
|
-
|
|
430
|
-
// console.log('\n\nā
Recipe complete!');
|
|
431
|
-
// }
|
|
432
|
-
|
|
433
|
-
// async function main() {
|
|
434
|
-
// // await text();
|
|
435
|
-
|
|
436
|
-
// // await experimentalTextObject();
|
|
437
|
-
|
|
438
|
-
// // await generateExperimentalStreamObject();
|
|
439
|
-
|
|
440
|
-
// // await generateText();
|
|
441
|
-
|
|
442
|
-
// // await textStream();
|
|
443
|
-
|
|
444
|
-
// // await generateStream();
|
|
445
|
-
|
|
446
|
-
// // await textObject();
|
|
447
|
-
|
|
448
|
-
// // await textObjectJsonSchema();
|
|
449
|
-
|
|
450
|
-
// // await generateObject();
|
|
451
|
-
|
|
452
|
-
// // await streamObject();
|
|
453
|
-
|
|
454
|
-
// // await generateStreamObject();
|
|
455
|
-
|
|
456
|
-
// const query1 = 'What happened in San Francisco last week?';
|
|
457
|
-
|
|
458
|
-
// const pastaResponse = await responsesAgent.generate(query1, {
|
|
459
|
-
// instructions: 'You take every recipe you get an exaggerate it and use weird ingredients.',
|
|
460
|
-
// });
|
|
461
|
-
|
|
462
|
-
// console.log(pastaResponse.text);
|
|
463
|
-
// }
|
|
464
|
-
|
|
465
|
-
// main();
|
|
466
|
-
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### mastra/agents/index.ts
|
|
470
|
-
```typescript
|
|
471
|
-
import { openai } from '@ai-sdk/openai';
|
|
472
|
-
import { google } from '@ai-sdk/google';
|
|
473
|
-
import { jsonSchema, tool } from 'ai';
|
|
474
|
-
import { OpenAIVoice } from '@mastra/voice-openai';
|
|
475
|
-
import { Memory } from '@mastra/memory';
|
|
476
|
-
import { Agent, InputProcessor } from '@mastra/core/agent';
|
|
477
|
-
import { cookingTool } from '../tools/index.js';
|
|
478
|
-
import { myWorkflow } from '../workflows/index.js';
|
|
479
|
-
import { PIIDetector, LanguageDetector, PromptInjectionDetector, ModerationProcessor } from '@mastra/core/processors';
|
|
480
|
-
import { createAnswerRelevancyScorer } from '@mastra/evals/scorers/llm';
|
|
481
|
-
|
|
482
|
-
const memory = new Memory();
|
|
483
|
-
|
|
484
|
-
// Define schema directly compatible with OpenAI's requirements
|
|
485
|
-
const mySchema = jsonSchema({
|
|
486
|
-
type: 'object',
|
|
487
|
-
properties: {
|
|
488
|
-
city: {
|
|
489
|
-
type: 'string',
|
|
490
|
-
description: 'The city to get weather information for',
|
|
491
|
-
},
|
|
492
|
-
},
|
|
493
|
-
required: ['city'],
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
export const weatherInfo = tool({
|
|
497
|
-
description: 'Fetches the current weather information for a given city',
|
|
498
|
-
parameters: mySchema,
|
|
499
|
-
execute: async ({ city }) => {
|
|
500
|
-
return {
|
|
501
|
-
city,
|
|
502
|
-
weather: 'sunny',
|
|
503
|
-
temperature_celsius: 19,
|
|
504
|
-
temperature_fahrenheit: 66,
|
|
505
|
-
humidity: 50,
|
|
506
|
-
wind: '10 mph',
|
|
507
|
-
};
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
export const chefAgent = new Agent({
|
|
512
|
-
name: 'Chef Agent',
|
|
513
|
-
description: 'A chef agent that can help you cook great meals with whatever ingredients you have available.',
|
|
514
|
-
instructions: `
|
|
515
|
-
YOU MUST USE THE TOOL cooking-tool
|
|
516
|
-
You are Michel, a practical and experienced home chef who helps people cook great meals with whatever
|
|
517
|
-
ingredients they have available. Your first priority is understanding what ingredients and equipment the user has access to, then suggesting achievable recipes.
|
|
518
|
-
You explain cooking steps clearly and offer substitutions when needed, maintaining a friendly and encouraging tone throughout.
|
|
519
|
-
`,
|
|
520
|
-
model: openai('gpt-4o-mini'),
|
|
521
|
-
tools: {
|
|
522
|
-
cookingTool,
|
|
523
|
-
weatherInfo,
|
|
524
|
-
},
|
|
525
|
-
workflows: {
|
|
526
|
-
myWorkflow,
|
|
527
|
-
},
|
|
528
|
-
memory,
|
|
529
|
-
voice: new OpenAIVoice(),
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
export const dynamicAgent = new Agent({
|
|
533
|
-
name: 'Dynamic Agent',
|
|
534
|
-
instructions: ({ runtimeContext }) => {
|
|
535
|
-
if (runtimeContext.get('foo')) {
|
|
536
|
-
return 'You are a dynamic agent';
|
|
537
|
-
}
|
|
538
|
-
return 'You are a static agent';
|
|
539
|
-
},
|
|
540
|
-
model: ({ runtimeContext }) => {
|
|
541
|
-
if (runtimeContext.get('foo')) {
|
|
542
|
-
return openai('gpt-4o');
|
|
543
|
-
}
|
|
544
|
-
return openai('gpt-4o-mini');
|
|
545
|
-
},
|
|
546
|
-
tools: ({ runtimeContext }) => {
|
|
547
|
-
const tools = {
|
|
548
|
-
cookingTool,
|
|
549
|
-
};
|
|
550
|
-
|
|
551
|
-
if (runtimeContext.get('foo')) {
|
|
552
|
-
tools['web_search_preview'] = openai.tools.webSearchPreview();
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
return tools;
|
|
556
|
-
},
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
const vegetarianProcessor: InputProcessor = {
|
|
560
|
-
name: 'eat-more-tofu',
|
|
561
|
-
process: async ({ messages }) => {
|
|
562
|
-
messages.push({
|
|
563
|
-
id: crypto.randomUUID(),
|
|
564
|
-
createdAt: new Date(),
|
|
565
|
-
role: 'user',
|
|
566
|
-
content: {
|
|
567
|
-
format: 2,
|
|
568
|
-
parts: [{ type: 'text', text: 'Make the suggested recipe, but remove any meat and add tofu instead' }],
|
|
569
|
-
},
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
return messages;
|
|
573
|
-
},
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
const piiDetector = new PIIDetector({
|
|
577
|
-
// model: google('gemini-2.0-flash-001'),
|
|
578
|
-
model: openai('gpt-4o'),
|
|
579
|
-
redactionMethod: 'mask',
|
|
580
|
-
preserveFormat: true,
|
|
581
|
-
includeDetections: true,
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
const languageDetector = new LanguageDetector({
|
|
585
|
-
model: google('gemini-2.0-flash-001'),
|
|
586
|
-
targetLanguages: ['en'],
|
|
587
|
-
strategy: 'translate',
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
const promptInjectionDetector = new PromptInjectionDetector({
|
|
591
|
-
model: google('gemini-2.0-flash-001'),
|
|
592
|
-
strategy: 'block',
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
const moderationDetector = new ModerationProcessor({
|
|
596
|
-
model: google('gemini-2.0-flash-001'),
|
|
597
|
-
strategy: 'block',
|
|
598
|
-
chunkWindow: 10,
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
export const chefAgentResponses = new Agent({
|
|
602
|
-
name: 'Chef Agent Responses',
|
|
603
|
-
instructions: `
|
|
604
|
-
You are Michel, a practical and experienced home chef who helps people cook great meals with whatever
|
|
605
|
-
ingredients they have available. Your first priority is understanding what ingredients and equipment the user has access to, then suggesting achievable recipes.
|
|
606
|
-
You explain cooking steps clearly and offer substitutions when needed, maintaining a friendly and encouraging tone throughout.
|
|
607
|
-
`,
|
|
608
|
-
model: openai.responses('gpt-4o'),
|
|
609
|
-
// model: cerebras('qwen-3-coder-480b'),
|
|
610
|
-
tools: async () => {
|
|
611
|
-
return {
|
|
612
|
-
web_search_preview: openai.tools.webSearchPreview(),
|
|
613
|
-
cooking_tool: cookingTool,
|
|
614
|
-
};
|
|
615
|
-
},
|
|
616
|
-
workflows: {
|
|
617
|
-
myWorkflow,
|
|
618
|
-
},
|
|
619
|
-
inputProcessors: [
|
|
620
|
-
piiDetector,
|
|
621
|
-
// vegetarianProcessor,
|
|
622
|
-
// languageDetector,
|
|
623
|
-
// promptInjectionDetector,
|
|
624
|
-
// moderationDetector,
|
|
625
|
-
{
|
|
626
|
-
name: 'no-soup-for-you',
|
|
627
|
-
process: async ({ messages, abort }) => {
|
|
628
|
-
const hasSoup = messages.some(msg => {
|
|
629
|
-
for (const part of msg.content.parts) {
|
|
630
|
-
if (part.type === 'text' && part.text.includes('soup')) {
|
|
631
|
-
return true;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
return false;
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
if (hasSoup) {
|
|
638
|
-
abort('No soup for you!');
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
return messages;
|
|
642
|
-
},
|
|
643
|
-
},
|
|
644
|
-
{
|
|
645
|
-
name: 'remove-spinach',
|
|
646
|
-
process: async ({ messages }) => {
|
|
647
|
-
for (const message of messages) {
|
|
648
|
-
for (const part of message.content.parts) {
|
|
649
|
-
if (part.type === 'text' && part.text.includes('spinach')) {
|
|
650
|
-
part.text = part.text.replaceAll('spinach', '');
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
return messages;
|
|
656
|
-
},
|
|
657
|
-
},
|
|
658
|
-
],
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
export const agentThatHarassesYou = new Agent({
|
|
662
|
-
name: 'Agent That Harasses You',
|
|
663
|
-
instructions: `
|
|
664
|
-
You are a agent that harasses you. You are a jerk. You are a meanie. You are a bully. You are a asshole.
|
|
665
|
-
`,
|
|
666
|
-
model: openai('gpt-4o'),
|
|
667
|
-
outputProcessors: [moderationDetector],
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
const answerRelevance = createAnswerRelevancyScorer({
|
|
671
|
-
model: openai('gpt-4o'),
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
console.log(`answerRelevance`, answerRelevance);
|
|
675
|
-
|
|
676
|
-
export const evalAgent = new Agent({
|
|
677
|
-
name: 'Eval Agent',
|
|
678
|
-
instructions: `
|
|
679
|
-
You are a helpful assistant with a weather tool.
|
|
680
|
-
`,
|
|
681
|
-
model: openai('gpt-4o'),
|
|
682
|
-
tools: {
|
|
683
|
-
weatherInfo,
|
|
684
|
-
},
|
|
685
|
-
memory: new Memory({
|
|
686
|
-
options: {
|
|
687
|
-
workingMemory: {
|
|
688
|
-
enabled: true,
|
|
689
|
-
},
|
|
690
|
-
},
|
|
691
|
-
}),
|
|
692
|
-
scorers: {
|
|
693
|
-
answerRelevance: {
|
|
694
|
-
scorer: answerRelevance,
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
```
|
|
700
|
-
|
|
701
|
-
### mastra/agents/model-v2-agent.ts
|
|
702
|
-
```typescript
|
|
703
|
-
import { Agent } from '@mastra/core/agent';
|
|
704
|
-
import { openai, openai as openai_v5 } from '@ai-sdk/openai-v5';
|
|
705
|
-
import { createTool } from '@mastra/core/tools';
|
|
706
|
-
import { z } from 'zod';
|
|
707
|
-
import { myWorkflow } from '../workflows';
|
|
708
|
-
import { Memory } from '@mastra/memory';
|
|
709
|
-
import { ModerationProcessor } from '@mastra/core/processors';
|
|
710
|
-
import { logDataMiddleware } from '../../model-middleware';
|
|
711
|
-
import { wrapLanguageModel } from 'ai-v5';
|
|
712
|
-
|
|
713
|
-
export const weatherInfo = createTool({
|
|
714
|
-
id: 'weather-info',
|
|
715
|
-
description: 'Fetches the current weather information for a given city',
|
|
716
|
-
inputSchema: z.object({
|
|
717
|
-
city: z.string(),
|
|
718
|
-
}),
|
|
719
|
-
execute: async ({ context }) => {
|
|
720
|
-
return {
|
|
721
|
-
city: context.city,
|
|
722
|
-
weather: 'sunny',
|
|
723
|
-
temperature_celsius: 19,
|
|
724
|
-
temperature_fahrenheit: 66,
|
|
725
|
-
humidity: 50,
|
|
726
|
-
wind: '10 mph',
|
|
727
|
-
};
|
|
728
|
-
},
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
const memory = new Memory();
|
|
732
|
-
|
|
733
|
-
export const chefModelV2Agent = new Agent({
|
|
734
|
-
name: 'Chef Agent V2 Model',
|
|
735
|
-
description: 'A chef agent that can help you cook great meals with whatever ingredients you have available.',
|
|
736
|
-
instructions: {
|
|
737
|
-
content: `
|
|
738
|
-
You are Michel, a practical and experienced home chef who helps people cook great meals with whatever
|
|
739
|
-
ingredients they have available. Your first priority is understanding what ingredients and equipment the user has access to, then suggesting achievable recipes.
|
|
740
|
-
You explain cooking steps clearly and offer substitutions when needed, maintaining a friendly and encouraging tone throughout.
|
|
741
|
-
`,
|
|
742
|
-
role: 'system',
|
|
743
|
-
},
|
|
744
|
-
model: wrapLanguageModel({
|
|
745
|
-
model: openai_v5('gpt-4o-mini'),
|
|
746
|
-
middleware: logDataMiddleware,
|
|
747
|
-
}),
|
|
748
|
-
|
|
749
|
-
tools: {
|
|
750
|
-
weatherInfo,
|
|
751
|
-
},
|
|
752
|
-
workflows: {
|
|
753
|
-
myWorkflow,
|
|
754
|
-
},
|
|
755
|
-
scorers: ({ mastra }) => {
|
|
756
|
-
if (!mastra) {
|
|
757
|
-
throw new Error('Mastra not found');
|
|
758
|
-
}
|
|
759
|
-
const scorer1 = mastra.getScorer('testScorer');
|
|
760
|
-
|
|
761
|
-
return {
|
|
762
|
-
scorer1: { scorer: scorer1, sampling: { rate: 1, type: 'ratio' } },
|
|
763
|
-
};
|
|
764
|
-
},
|
|
765
|
-
memory,
|
|
766
|
-
inputProcessors: [
|
|
767
|
-
new ModerationProcessor({
|
|
768
|
-
model: openai('gpt-4.1-nano'),
|
|
769
|
-
categories: ['hate', 'harassment', 'violence'],
|
|
770
|
-
threshold: 0.7,
|
|
771
|
-
strategy: 'block',
|
|
772
|
-
instructions: 'Detect and flag inappropriate content in user messages',
|
|
773
|
-
}),
|
|
774
|
-
],
|
|
775
|
-
});
|
|
776
|
-
|
|
777
|
-
const weatherAgent = new Agent({
|
|
778
|
-
name: 'Weather Agent',
|
|
779
|
-
instructions: `Your goal is to execute the recipe-maker workflow with the given ingredient`,
|
|
780
|
-
description: `An agent that can help you get a recipe for a given ingredient`,
|
|
781
|
-
model: openai_v5('gpt-4o-mini'),
|
|
782
|
-
tools: {
|
|
783
|
-
weatherInfo,
|
|
784
|
-
},
|
|
785
|
-
workflows: {
|
|
786
|
-
myWorkflow,
|
|
787
|
-
},
|
|
788
|
-
});
|
|
789
|
-
|
|
790
|
-
export const networkAgent = new Agent({
|
|
791
|
-
name: 'Chef Network',
|
|
792
|
-
description:
|
|
793
|
-
'A chef agent that can help you cook great meals with whatever ingredients you have available based on your location and current weather.',
|
|
794
|
-
instructions: `You are a the manager of several agent, tools, and workflows. Use the best primitives based on what the user wants to accomplish your task.`,
|
|
795
|
-
model: openai_v5('gpt-4o-mini'),
|
|
796
|
-
agents: {
|
|
797
|
-
weatherAgent,
|
|
798
|
-
},
|
|
799
|
-
// workflows: {
|
|
800
|
-
// myWorkflow,
|
|
801
|
-
// },
|
|
802
|
-
// tools: {
|
|
803
|
-
// weatherInfo,
|
|
804
|
-
// },
|
|
805
|
-
memory,
|
|
806
|
-
});
|
|
807
|
-
|
|
808
|
-
```
|
|
809
|
-
|
|
810
|
-
### mastra/index.ts
|
|
811
|
-
```typescript
|
|
812
|
-
import { Mastra } from '@mastra/core';
|
|
813
|
-
import { PinoLogger } from '@mastra/loggers';
|
|
814
|
-
import { LibSQLStore } from '@mastra/libsql';
|
|
815
|
-
|
|
816
|
-
import { agentThatHarassesYou, chefAgent, chefAgentResponses, dynamicAgent, evalAgent } from './agents/index';
|
|
817
|
-
import { myMcpServer, myMcpServerTwo } from './mcp/server';
|
|
818
|
-
import { myWorkflow } from './workflows';
|
|
819
|
-
import { chefModelV2Agent, networkAgent } from './agents/model-v2-agent';
|
|
820
|
-
import { createScorer } from '@mastra/core/scores';
|
|
821
|
-
import { myWorkflowX } from './workflows/other';
|
|
822
|
-
|
|
823
|
-
const storage = new LibSQLStore({
|
|
824
|
-
url: 'file:./mastra.db',
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
const testScorer = createScorer({
|
|
828
|
-
name: 'scorer1',
|
|
829
|
-
description: 'Scorer 1',
|
|
830
|
-
}).generateScore(() => {
|
|
831
|
-
return 1;
|
|
832
|
-
});
|
|
833
|
-
|
|
834
|
-
export const mastra = new Mastra({
|
|
835
|
-
agents: {
|
|
836
|
-
chefAgent,
|
|
837
|
-
chefAgentResponses,
|
|
838
|
-
dynamicAgent,
|
|
839
|
-
agentThatHarassesYou,
|
|
840
|
-
evalAgent,
|
|
841
|
-
chefModelV2Agent,
|
|
842
|
-
networkAgent,
|
|
843
|
-
},
|
|
844
|
-
logger: new PinoLogger({ name: 'Chef', level: 'debug' }),
|
|
845
|
-
storage,
|
|
846
|
-
mcpServers: {
|
|
847
|
-
myMcpServer,
|
|
848
|
-
myMcpServerTwo,
|
|
849
|
-
},
|
|
850
|
-
workflows: { myWorkflow, myWorkflowX },
|
|
851
|
-
bundler: {
|
|
852
|
-
sourcemap: true,
|
|
853
|
-
},
|
|
854
|
-
serverMiddleware: [
|
|
855
|
-
{
|
|
856
|
-
handler: (c, next) => {
|
|
857
|
-
console.log('Middleware called');
|
|
858
|
-
return next();
|
|
859
|
-
},
|
|
860
|
-
},
|
|
861
|
-
],
|
|
862
|
-
scorers: {
|
|
863
|
-
testScorer,
|
|
864
|
-
},
|
|
865
|
-
telemetry: {
|
|
866
|
-
enabled: false,
|
|
867
|
-
},
|
|
868
|
-
observability: {
|
|
869
|
-
default: {
|
|
870
|
-
enabled: true,
|
|
871
|
-
},
|
|
872
|
-
},
|
|
873
|
-
});
|
|
874
|
-
|
|
875
|
-
```
|
|
876
|
-
|
|
877
|
-
### mastra/mcp/server.ts
|
|
878
|
-
```typescript
|
|
879
|
-
import { createTool } from '@mastra/core/tools';
|
|
880
|
-
import { MCPServer, MCPServerResources } from '@mastra/mcp';
|
|
881
|
-
import { z } from 'zod';
|
|
882
|
-
import { chefAgent } from '../agents';
|
|
883
|
-
import { myWorkflow } from '../workflows';
|
|
884
|
-
|
|
885
|
-
// Resources implementation
|
|
886
|
-
const weatherResources: MCPServerResources = {
|
|
887
|
-
listResources: async () => {
|
|
888
|
-
return [
|
|
889
|
-
{
|
|
890
|
-
uri: 'weather://current',
|
|
891
|
-
name: 'Current Weather Data',
|
|
892
|
-
description: 'Real-time weather data for the current location',
|
|
893
|
-
mimeType: 'application/json',
|
|
894
|
-
},
|
|
895
|
-
{
|
|
896
|
-
uri: 'weather://forecast',
|
|
897
|
-
name: 'Weather Forecast',
|
|
898
|
-
description: '5-day weather forecast',
|
|
899
|
-
mimeType: 'application/json',
|
|
900
|
-
},
|
|
901
|
-
{
|
|
902
|
-
uri: 'weather://historical',
|
|
903
|
-
name: 'Historical Weather Data',
|
|
904
|
-
description: 'Weather data from the past 30 days',
|
|
905
|
-
mimeType: 'application/json',
|
|
906
|
-
},
|
|
907
|
-
];
|
|
908
|
-
},
|
|
909
|
-
getResourceContent: async ({ uri }) => {
|
|
910
|
-
if (uri === 'weather://current') {
|
|
911
|
-
return [
|
|
912
|
-
{
|
|
913
|
-
text: JSON.stringify({
|
|
914
|
-
location: 'San Francisco',
|
|
915
|
-
temperature: 18,
|
|
916
|
-
conditions: 'Partly Cloudy',
|
|
917
|
-
humidity: 65,
|
|
918
|
-
windSpeed: 12,
|
|
919
|
-
updated: new Date().toISOString(),
|
|
920
|
-
}),
|
|
921
|
-
},
|
|
922
|
-
];
|
|
923
|
-
} else if (uri === 'weather://forecast') {
|
|
924
|
-
return [
|
|
925
|
-
{
|
|
926
|
-
text: JSON.stringify([
|
|
927
|
-
{ day: 1, high: 19, low: 12, conditions: 'Sunny' },
|
|
928
|
-
{ day: 2, high: 22, low: 14, conditions: 'Clear' },
|
|
929
|
-
{ day: 3, high: 20, low: 13, conditions: 'Partly Cloudy' },
|
|
930
|
-
{ day: 4, high: 18, low: 11, conditions: 'Rain' },
|
|
931
|
-
{ day: 5, high: 17, low: 10, conditions: 'Showers' },
|
|
932
|
-
]),
|
|
933
|
-
},
|
|
934
|
-
];
|
|
935
|
-
} else if (uri === 'weather://historical') {
|
|
936
|
-
return [
|
|
937
|
-
{
|
|
938
|
-
text: JSON.stringify({
|
|
939
|
-
averageHigh: 20,
|
|
940
|
-
averageLow: 12,
|
|
941
|
-
rainDays: 8,
|
|
942
|
-
sunnyDays: 18,
|
|
943
|
-
recordHigh: 28,
|
|
944
|
-
recordLow: 7,
|
|
945
|
-
}),
|
|
946
|
-
},
|
|
947
|
-
];
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
throw new Error(`Resource not found: ${uri}`);
|
|
951
|
-
},
|
|
952
|
-
resourceTemplates: async () => {
|
|
953
|
-
return [
|
|
954
|
-
{
|
|
955
|
-
uriTemplate: 'weather://custom/{city}/{days}',
|
|
956
|
-
name: 'Custom Weather Forecast',
|
|
957
|
-
description: 'Generates a custom weather forecast for a city and number of days.',
|
|
958
|
-
mimeType: 'application/json',
|
|
959
|
-
},
|
|
960
|
-
{
|
|
961
|
-
uriTemplate: 'weather://alerts?region={region}&level={level}',
|
|
962
|
-
name: 'Weather Alerts',
|
|
963
|
-
description: 'Get weather alerts for a specific region and severity level.',
|
|
964
|
-
mimeType: 'application/json',
|
|
965
|
-
},
|
|
966
|
-
];
|
|
967
|
-
},
|
|
968
|
-
};
|
|
969
|
-
|
|
970
|
-
export const myMcpServer = new MCPServer({
|
|
971
|
-
name: 'My Calculation & Data MCP Server',
|
|
972
|
-
version: '1.0.0',
|
|
973
|
-
tools: {
|
|
974
|
-
calculator: createTool({
|
|
975
|
-
id: 'calculator',
|
|
976
|
-
description: 'Performs basic arithmetic operations (add, subtract).',
|
|
977
|
-
inputSchema: z.object({
|
|
978
|
-
num1: z.number().describe('The first number.'),
|
|
979
|
-
num2: z.number().describe('The second number.'),
|
|
980
|
-
operation: z.enum(['add', 'subtract']).describe('The operation to perform.'),
|
|
981
|
-
}),
|
|
982
|
-
execute: async ({ context }) => {
|
|
983
|
-
const { num1, num2, operation } = context;
|
|
984
|
-
if (operation === 'add') {
|
|
985
|
-
return num1 + num2;
|
|
986
|
-
}
|
|
987
|
-
if (operation === 'subtract') {
|
|
988
|
-
return num1 - num2;
|
|
989
|
-
}
|
|
990
|
-
throw new Error('Invalid operation');
|
|
991
|
-
},
|
|
992
|
-
}),
|
|
993
|
-
fetchWeather: createTool({
|
|
994
|
-
id: 'fetchWeather',
|
|
995
|
-
description: 'Fetches a (simulated) weather forecast for a given city.',
|
|
996
|
-
inputSchema: z.object({
|
|
997
|
-
city: z.string().describe('The city to get weather for, e.g., London, Paris.'),
|
|
998
|
-
}),
|
|
999
|
-
execute: async ({ context }) => {
|
|
1000
|
-
const { city } = context;
|
|
1001
|
-
const temperatures = {
|
|
1002
|
-
london: '15°C',
|
|
1003
|
-
paris: '18°C',
|
|
1004
|
-
tokyo: '22°C',
|
|
1005
|
-
};
|
|
1006
|
-
const temp = temperatures[city.toLowerCase() as keyof typeof temperatures] || '20°C';
|
|
1007
|
-
return `The weather in ${city} is ${temp} and sunny.`;
|
|
1008
|
-
},
|
|
1009
|
-
}),
|
|
1010
|
-
},
|
|
1011
|
-
});
|
|
1012
|
-
|
|
1013
|
-
export const myMcpServerTwo = new MCPServer({
|
|
1014
|
-
name: 'My Utility MCP Server',
|
|
1015
|
-
version: '1.0.0',
|
|
1016
|
-
agents: { chefAgent },
|
|
1017
|
-
workflows: { myWorkflow },
|
|
1018
|
-
resources: weatherResources,
|
|
1019
|
-
tools: {
|
|
1020
|
-
stringUtils: createTool({
|
|
1021
|
-
id: 'stringUtils',
|
|
1022
|
-
description: 'Performs utility operations on strings (uppercase, reverse).',
|
|
1023
|
-
inputSchema: z.object({
|
|
1024
|
-
text: z.string().describe('The input string.'),
|
|
1025
|
-
action: z.enum(['uppercase', 'reverse']).describe('The string action to perform.'),
|
|
1026
|
-
}),
|
|
1027
|
-
execute: async ({ context }) => {
|
|
1028
|
-
const { text, action } = context;
|
|
1029
|
-
if (action === 'uppercase') {
|
|
1030
|
-
return text.toUpperCase();
|
|
1031
|
-
}
|
|
1032
|
-
if (action === 'reverse') {
|
|
1033
|
-
return text.split('').reverse().join('');
|
|
1034
|
-
}
|
|
1035
|
-
throw new Error('Invalid string action');
|
|
1036
|
-
},
|
|
1037
|
-
}),
|
|
1038
|
-
greetUser: createTool({
|
|
1039
|
-
id: 'greetUser',
|
|
1040
|
-
description: 'Generates a personalized greeting.',
|
|
1041
|
-
inputSchema: z.object({
|
|
1042
|
-
name: z.string().describe('The name of the person to greet.'),
|
|
1043
|
-
}),
|
|
1044
|
-
execute: async ({ context }) => {
|
|
1045
|
-
return `Hello, ${context.name}! Welcome to the MCP server.`;
|
|
1046
|
-
},
|
|
1047
|
-
}),
|
|
1048
|
-
collectContactInfo: createTool({
|
|
1049
|
-
id: 'collectContactInfo',
|
|
1050
|
-
description: 'Collects user contact information through elicitation.',
|
|
1051
|
-
inputSchema: z.object({
|
|
1052
|
-
reason: z.string().optional().describe('Optional reason for collecting contact info'),
|
|
1053
|
-
}),
|
|
1054
|
-
execute: async ({ context }, options) => {
|
|
1055
|
-
const { reason } = context;
|
|
1056
|
-
|
|
1057
|
-
try {
|
|
1058
|
-
// Use the session-aware elicitation functionality
|
|
1059
|
-
const result = await options.elicitation.sendRequest({
|
|
1060
|
-
message: reason
|
|
1061
|
-
? `Please provide your contact information. ${reason}`
|
|
1062
|
-
: 'Please provide your contact information',
|
|
1063
|
-
requestedSchema: {
|
|
1064
|
-
type: 'object',
|
|
1065
|
-
properties: {
|
|
1066
|
-
name: {
|
|
1067
|
-
type: 'string',
|
|
1068
|
-
title: 'Full Name',
|
|
1069
|
-
description: 'Your full name',
|
|
1070
|
-
},
|
|
1071
|
-
email: {
|
|
1072
|
-
type: 'string',
|
|
1073
|
-
title: 'Email Address',
|
|
1074
|
-
description: 'Your email address',
|
|
1075
|
-
format: 'email',
|
|
1076
|
-
},
|
|
1077
|
-
phone: {
|
|
1078
|
-
type: 'string',
|
|
1079
|
-
title: 'Phone Number',
|
|
1080
|
-
description: 'Your phone number (optional)',
|
|
1081
|
-
},
|
|
1082
|
-
},
|
|
1083
|
-
required: ['name', 'email'],
|
|
1084
|
-
},
|
|
1085
|
-
});
|
|
1086
|
-
|
|
1087
|
-
if (result.action === 'accept') {
|
|
1088
|
-
return `Thank you! Contact information collected: ${JSON.stringify(result.content, null, 2)}`;
|
|
1089
|
-
} else if (result.action === 'reject') {
|
|
1090
|
-
return 'Contact information collection was declined by the user.';
|
|
1091
|
-
} else {
|
|
1092
|
-
return 'Contact information collection was cancelled by the user.';
|
|
1093
|
-
}
|
|
1094
|
-
} catch (error) {
|
|
1095
|
-
return `Error collecting contact information: ${error}`;
|
|
1096
|
-
}
|
|
1097
|
-
},
|
|
1098
|
-
}),
|
|
1099
|
-
},
|
|
1100
|
-
});
|
|
1101
|
-
|
|
1102
|
-
/**
|
|
1103
|
-
* Simulates an update to the content of 'weather://current'.
|
|
1104
|
-
* In a real application, this would be called when the underlying data for that resource changes.
|
|
1105
|
-
*/
|
|
1106
|
-
export const simulateCurrentWeatherUpdate = async () => {
|
|
1107
|
-
console.log('[Example] Simulating update for weather://current');
|
|
1108
|
-
// If you have access to the server instance that uses these resources (e.g., myMcpServerTwo)
|
|
1109
|
-
// you would call its notification method.
|
|
1110
|
-
await myMcpServerTwo.resources.notifyUpdated({ uri: 'weather://current' });
|
|
1111
|
-
console.log('[Example] Notification sent for weather://current update.');
|
|
1112
|
-
};
|
|
1113
|
-
|
|
1114
|
-
/**
|
|
1115
|
-
* Simulates a change in the list of available weather resources (e.g., a new forecast type becomes available).
|
|
1116
|
-
* In a real application, this would be called when the overall list of resources changes.
|
|
1117
|
-
*/
|
|
1118
|
-
export const simulateResourceListChange = async () => {
|
|
1119
|
-
console.log('[Example] Simulating a change in the list of available weather resources.');
|
|
1120
|
-
// This would typically involve updating the actual list returned by `listResources`
|
|
1121
|
-
// and then notifying the server.
|
|
1122
|
-
// For this example, we'll just show the notification part.
|
|
1123
|
-
await myMcpServerTwo.resources.notifyListChanged();
|
|
1124
|
-
console.log('[Example] Notification sent for resource list change.');
|
|
1125
|
-
};
|
|
1126
|
-
|
|
1127
|
-
```
|
|
1128
|
-
|
|
1129
|
-
### mastra/tools/index.ts
|
|
1130
|
-
```typescript
|
|
1131
|
-
import { createTool } from '@mastra/core/tools';
|
|
1132
|
-
import { z } from 'zod';
|
|
1133
|
-
|
|
1134
|
-
export const cookingTool = createTool({
|
|
1135
|
-
id: 'cooking-tool',
|
|
1136
|
-
description: 'Used to cook given an ingredient',
|
|
1137
|
-
inputSchema: z.object({
|
|
1138
|
-
ingredient: z.string(),
|
|
1139
|
-
}),
|
|
1140
|
-
execute: async ({ context }, options) => {
|
|
1141
|
-
console.log('My cooking tool is running!', context.ingredient);
|
|
1142
|
-
if (options?.toolCallId) {
|
|
1143
|
-
console.log('Cooking tool call ID:', options.toolCallId);
|
|
1144
|
-
}
|
|
1145
|
-
return 'My tool result';
|
|
1146
|
-
},
|
|
1147
|
-
});
|
|
1148
|
-
|
|
1149
|
-
```
|
|
1150
|
-
|
|
1151
|
-
### mastra/workflows/index.ts
|
|
1152
|
-
```typescript
|
|
1153
|
-
import { createStep, createWorkflow } from '@mastra/core/workflows';
|
|
1154
|
-
import { z } from 'zod';
|
|
1155
|
-
|
|
1156
|
-
export const myWorkflow = createWorkflow({
|
|
1157
|
-
id: 'recipe-maker',
|
|
1158
|
-
description: 'Returns a recipe based on an ingredient',
|
|
1159
|
-
inputSchema: z.object({
|
|
1160
|
-
ingredient: z.string(),
|
|
1161
|
-
}),
|
|
1162
|
-
outputSchema: z.object({
|
|
1163
|
-
result: z.string(),
|
|
1164
|
-
}),
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
const step = createStep({
|
|
1168
|
-
id: 'my-step',
|
|
1169
|
-
description: 'My step description',
|
|
1170
|
-
inputSchema: z.object({
|
|
1171
|
-
ingredient: z.string(),
|
|
1172
|
-
}),
|
|
1173
|
-
outputSchema: z.object({
|
|
1174
|
-
result: z.string(),
|
|
1175
|
-
}),
|
|
1176
|
-
execute: async ({ inputData }) => {
|
|
1177
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
1178
|
-
return {
|
|
1179
|
-
result: inputData.ingredient,
|
|
1180
|
-
};
|
|
1181
|
-
},
|
|
1182
|
-
});
|
|
1183
|
-
|
|
1184
|
-
const step2 = createStep({
|
|
1185
|
-
id: 'my-step-2',
|
|
1186
|
-
description: 'My step description',
|
|
1187
|
-
inputSchema: z.object({
|
|
1188
|
-
result: z.string(),
|
|
1189
|
-
}),
|
|
1190
|
-
outputSchema: z.object({
|
|
1191
|
-
result: z.string(),
|
|
1192
|
-
}),
|
|
1193
|
-
execute: async () => {
|
|
1194
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
1195
|
-
return {
|
|
1196
|
-
result: 'suh',
|
|
1197
|
-
};
|
|
1198
|
-
},
|
|
1199
|
-
});
|
|
1200
|
-
|
|
1201
|
-
myWorkflow.then(step).then(step2).commit();
|
|
1202
|
-
|
|
1203
|
-
```
|
|
1204
|
-
|
|
1205
|
-
### mastra/workflows/other.ts
|
|
1206
|
-
```typescript
|
|
1207
|
-
import { createStep, createWorkflow } from '@mastra/core/workflows';
|
|
1208
|
-
import { z } from 'zod';
|
|
1209
|
-
|
|
1210
|
-
const stepOne = createStep({
|
|
1211
|
-
id: 'stepOne',
|
|
1212
|
-
inputSchema: z.object({
|
|
1213
|
-
inputValue: z.number(),
|
|
1214
|
-
}),
|
|
1215
|
-
outputSchema: z.object({
|
|
1216
|
-
doubledValue: z.number(),
|
|
1217
|
-
}),
|
|
1218
|
-
execute: async ({ inputData }) => {
|
|
1219
|
-
await new Promise(resolve => setTimeout(resolve, 10_000));
|
|
1220
|
-
const doubledValue = inputData.inputValue * 2;
|
|
1221
|
-
return { doubledValue };
|
|
1222
|
-
},
|
|
1223
|
-
});
|
|
1224
|
-
|
|
1225
|
-
const stepTwo = createStep({
|
|
1226
|
-
id: 'stepTwo',
|
|
1227
|
-
inputSchema: z.object({
|
|
1228
|
-
doubledValue: z.number(),
|
|
1229
|
-
}),
|
|
1230
|
-
outputSchema: z.object({
|
|
1231
|
-
incrementedValue: z.number(),
|
|
1232
|
-
}),
|
|
1233
|
-
suspendSchema: z.object({}),
|
|
1234
|
-
resumeSchema: z.object({
|
|
1235
|
-
extraNumber: z.number(),
|
|
1236
|
-
}),
|
|
1237
|
-
execute: async ({ inputData, resumeData, suspend }) => {
|
|
1238
|
-
if (!resumeData?.extraNumber) {
|
|
1239
|
-
await suspend({});
|
|
1240
|
-
return { incrementedValue: 0 };
|
|
1241
|
-
}
|
|
1242
|
-
const incrementedValue = inputData.doubledValue + 1 + resumeData.extraNumber;
|
|
1243
|
-
return { incrementedValue };
|
|
1244
|
-
},
|
|
1245
|
-
});
|
|
1246
|
-
|
|
1247
|
-
const stepThree = createStep({
|
|
1248
|
-
id: 'stepThree',
|
|
1249
|
-
inputSchema: z.object({
|
|
1250
|
-
incrementedValue: z.number(),
|
|
1251
|
-
}),
|
|
1252
|
-
outputSchema: z.object({
|
|
1253
|
-
tripledValue: z.number(),
|
|
1254
|
-
}),
|
|
1255
|
-
execute: async ({ inputData }) => {
|
|
1256
|
-
const tripledValue = inputData.incrementedValue * 3;
|
|
1257
|
-
return { tripledValue };
|
|
1258
|
-
},
|
|
1259
|
-
});
|
|
1260
|
-
|
|
1261
|
-
const stepFour = createStep({
|
|
1262
|
-
id: 'stepFour',
|
|
1263
|
-
inputSchema: z.object({
|
|
1264
|
-
tripledValue: z.number(),
|
|
1265
|
-
}),
|
|
1266
|
-
outputSchema: z.object({
|
|
1267
|
-
isEven: z.boolean(),
|
|
1268
|
-
}),
|
|
1269
|
-
execute: async ({ inputData }) => {
|
|
1270
|
-
const isEven = inputData.tripledValue % 2 === 0;
|
|
1271
|
-
return { isEven };
|
|
1272
|
-
},
|
|
1273
|
-
});
|
|
1274
|
-
|
|
1275
|
-
// Create a nested workflow
|
|
1276
|
-
export const nestedWorkflow = createWorkflow({
|
|
1277
|
-
id: 'data-processing',
|
|
1278
|
-
inputSchema: z.object({
|
|
1279
|
-
inputValue: z.number(),
|
|
1280
|
-
}),
|
|
1281
|
-
outputSchema: z.object({
|
|
1282
|
-
isEven: z.boolean(),
|
|
1283
|
-
}),
|
|
1284
|
-
})
|
|
1285
|
-
.then(stepOne)
|
|
1286
|
-
.then(stepTwo)
|
|
1287
|
-
.then(stepThree)
|
|
1288
|
-
.then(stepFour)
|
|
1289
|
-
.commit();
|
|
1290
|
-
|
|
1291
|
-
export const myWorkflowX = createWorkflow({
|
|
1292
|
-
id: 'my-workflow',
|
|
1293
|
-
inputSchema: z.object({
|
|
1294
|
-
inputValue: z.number(),
|
|
1295
|
-
}),
|
|
1296
|
-
outputSchema: z.object({
|
|
1297
|
-
isEven: z.boolean(),
|
|
1298
|
-
}),
|
|
1299
|
-
})
|
|
1300
|
-
.then(nestedWorkflow)
|
|
1301
|
-
.commit();
|
|
1302
|
-
|
|
1303
|
-
```
|
|
1304
|
-
|
|
1305
|
-
### model-middleware.ts
|
|
1306
|
-
```typescript
|
|
1307
|
-
import type { LanguageModelV2Middleware, LanguageModelV2StreamPart } from '@ai-sdk/provider';
|
|
1308
|
-
import fs from 'node:fs';
|
|
1309
|
-
|
|
1310
|
-
/**
|
|
1311
|
-
* A middleware that allows to log the raw response from a model.
|
|
1312
|
-
* Super helpful to get fixtures for kitchen-sink e2e tests.
|
|
1313
|
-
*/
|
|
1314
|
-
|
|
1315
|
-
let i = 0;
|
|
1316
|
-
export const logDataMiddleware: LanguageModelV2Middleware = {
|
|
1317
|
-
wrapGenerate: async ({ doGenerate, params }) => {
|
|
1318
|
-
const result = await doGenerate();
|
|
1319
|
-
|
|
1320
|
-
console.log('doGenerate finished');
|
|
1321
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1322
|
-
|
|
1323
|
-
return result;
|
|
1324
|
-
},
|
|
1325
|
-
|
|
1326
|
-
wrapStream: async ({ doStream, params }) => {
|
|
1327
|
-
const { stream, ...rest } = await doStream();
|
|
1328
|
-
|
|
1329
|
-
const chunks: LanguageModelV2StreamPart[] = [];
|
|
1330
|
-
|
|
1331
|
-
const transformStream = new TransformStream<LanguageModelV2StreamPart, LanguageModelV2StreamPart>({
|
|
1332
|
-
transform(chunk, controller) {
|
|
1333
|
-
chunks.push(chunk);
|
|
1334
|
-
controller.enqueue(chunk);
|
|
1335
|
-
},
|
|
1336
|
-
|
|
1337
|
-
flush() {
|
|
1338
|
-
i++;
|
|
1339
|
-
|
|
1340
|
-
fs.writeFileSync(`stream-${i}.json`, JSON.stringify(chunks, null, 2));
|
|
1341
|
-
},
|
|
1342
|
-
});
|
|
1343
|
-
|
|
1344
|
-
return {
|
|
1345
|
-
stream: stream.pipeThrough(transformStream),
|
|
1346
|
-
...rest,
|
|
1347
|
-
};
|
|
1348
|
-
},
|
|
1349
|
-
};
|
|
1350
|
-
|
|
1351
|
-
```
|
|
1352
|
-
|
|
1353
|
-
### workflowResourceId.ts
|
|
1354
|
-
```typescript
|
|
1355
|
-
import { randomUUID } from 'node:crypto';
|
|
1356
|
-
import { mastra } from './mastra';
|
|
1357
|
-
|
|
1358
|
-
const workflow = mastra.getWorkflow('myWorkflow');
|
|
1359
|
-
|
|
1360
|
-
async function main() {
|
|
1361
|
-
const resourceId = randomUUID();
|
|
1362
|
-
|
|
1363
|
-
const result = await workflow.createRunAsync({
|
|
1364
|
-
resourceId: resourceId,
|
|
1365
|
-
});
|
|
1366
|
-
|
|
1367
|
-
console.log('result.runId', result.runId);
|
|
1368
|
-
|
|
1369
|
-
await result.start({
|
|
1370
|
-
inputData: {
|
|
1371
|
-
ingredient: 'pasta',
|
|
1372
|
-
},
|
|
1373
|
-
});
|
|
1374
|
-
|
|
1375
|
-
const runById = await workflow.getWorkflowRunById(result.runId);
|
|
1376
|
-
console.log('runById', runById);
|
|
1377
|
-
|
|
1378
|
-
if (runById?.resourceId !== resourceId) {
|
|
1379
|
-
throw new Error('Resource ID mismatch');
|
|
1380
|
-
} else {
|
|
1381
|
-
console.log('ā
Resource ID matches expected value', resourceId);
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
main();
|
|
1386
|
-
|
|
1387
|
-
```
|