@mastra/mcp-docs-server 0.13.2-alpha.1 ā 0.13.2-alpha.2
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%2Fastra.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fchroma.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fclickhouse.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fclient-js.md +18 -18
- package/.docs/organized/changelogs/%40mastra%2Fcloudflare-d1.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fcloudflare.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fcore.md +15 -15
- package/.docs/organized/changelogs/%40mastra%2Fcouchbase.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-cloudflare.md +20 -20
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-netlify.md +24 -24
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-vercel.md +24 -24
- package/.docs/organized/changelogs/%40mastra%2Fdeployer.md +22 -22
- package/.docs/organized/changelogs/%40mastra%2Fdynamodb.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Flance.md +13 -0
- package/.docs/organized/changelogs/%40mastra%2Flibsql.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fmcp-docs-server.md +10 -10
- package/.docs/organized/changelogs/%40mastra%2Fmcp.md +9 -9
- package/.docs/organized/changelogs/%40mastra%2Fmemory.md +22 -22
- package/.docs/organized/changelogs/%40mastra%2Fmongodb.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fopensearch.md +13 -0
- package/.docs/organized/changelogs/%40mastra%2Fpg.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fpinecone.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fplayground-ui.md +20 -20
- package/.docs/organized/changelogs/%40mastra%2Fqdrant.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fserver.md +20 -20
- package/.docs/organized/changelogs/%40mastra%2Fturbopuffer.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fupstash.md +14 -14
- package/.docs/organized/changelogs/%40mastra%2Fvectorize.md +14 -14
- package/.docs/organized/changelogs/mastra.md +22 -22
- package/.docs/organized/code-examples/agent.md +182 -5
- package/.docs/organized/code-examples/assistant-ui.md +1 -1
- package/.docs/organized/code-examples/bird-checker-with-nextjs-and-eval.md +1 -1
- package/.docs/organized/code-examples/bird-checker-with-nextjs.md +1 -1
- package/.docs/organized/code-examples/crypto-chatbot.md +2 -2
- package/.docs/organized/code-examples/openapi-spec-writer.md +1 -1
- package/.docs/raw/agents/using-tools-and-mcp.mdx +3 -2
- package/.docs/raw/deployment/cloud-providers/digital-ocean.mdx +111 -0
- package/.docs/raw/deployment/cloud-providers/index.mdx +15 -0
- package/.docs/raw/memory/working-memory.mdx +56 -0
- package/.docs/raw/networks-vnext/complex-task-execution.mdx +137 -0
- package/.docs/raw/networks-vnext/overview.mdx +85 -0
- package/.docs/raw/networks-vnext/single-task-execution.mdx +131 -0
- package/.docs/raw/reference/client-js/agents.mdx +41 -0
- package/.docs/raw/reference/deployer/netlify.mdx +22 -68
- package/.docs/raw/reference/deployer/vercel.mdx +7 -77
- package/.docs/raw/reference/tools/mcp-client.mdx +244 -0
- package/.docs/raw/reference/tools/mcp-server.mdx +186 -0
- package/.docs/raw/reference/workflows/create-run.mdx +1 -1
- package/.docs/raw/reference/workflows/resume.mdx +1 -1
- package/.docs/raw/reference/workflows/start.mdx +1 -1
- package/.docs/raw/reference/workflows/stream.mdx +1 -1
- package/.docs/raw/reference/workflows/watch.mdx +1 -1
- package/.docs/raw/reference/workflows/workflow.mdx +6 -2
- package/.docs/raw/workflows/control-flow.mdx +42 -1
- package/.docs/raw/workflows/overview.mdx +73 -5
- package/.docs/raw/workflows/pausing-execution.mdx +1 -1
- package/.docs/raw/workflows/suspend-and-resume.mdx +68 -23
- package/.docs/raw/workflows/using-with-agents-and-tools.mdx +1 -1
- package/package.json +3 -3
|
@@ -50,21 +50,147 @@
|
|
|
50
50
|
### client.ts
|
|
51
51
|
```typescript
|
|
52
52
|
import { MCPClient } from '@mastra/mcp';
|
|
53
|
+
// import type { ElicitationHandler } from '@mastra/mcp';
|
|
54
|
+
import { createInterface } from 'readline';
|
|
55
|
+
|
|
56
|
+
// Create readline interface for user input
|
|
57
|
+
const readline = createInterface({
|
|
58
|
+
input: process.stdin,
|
|
59
|
+
output: process.stdout,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Helper function to prompt user for input
|
|
63
|
+
function askQuestion(question: string): Promise<string> {
|
|
64
|
+
return new Promise(resolve => {
|
|
65
|
+
readline.question(question, answer => {
|
|
66
|
+
resolve(answer.trim());
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Elicitation handler that prompts the user for input
|
|
72
|
+
const elicitationHandler = async request => {
|
|
73
|
+
console.log('\nš Elicitation Request Received:');
|
|
74
|
+
console.log(`Message: ${request.message}`);
|
|
75
|
+
console.log('Requested Schema:');
|
|
76
|
+
console.log(JSON.stringify(request.requestedSchema, null, 2));
|
|
77
|
+
|
|
78
|
+
const schema = request.requestedSchema;
|
|
79
|
+
const properties = schema.properties;
|
|
80
|
+
const required = schema.required || [];
|
|
81
|
+
|
|
82
|
+
console.log('\nPlease provide the following information:');
|
|
83
|
+
|
|
84
|
+
const content: Record<string, unknown> = {};
|
|
85
|
+
|
|
86
|
+
// Collect input for each field
|
|
87
|
+
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
88
|
+
const field = fieldSchema as {
|
|
89
|
+
type?: string;
|
|
90
|
+
title?: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
format?: string;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const isRequired = required.includes(fieldName);
|
|
96
|
+
let prompt = `${field.title || fieldName}`;
|
|
97
|
+
|
|
98
|
+
// Add helpful information to the prompt
|
|
99
|
+
if (field.description) {
|
|
100
|
+
prompt += ` (${field.description})`;
|
|
101
|
+
}
|
|
102
|
+
if (field.format) {
|
|
103
|
+
prompt += ` [format: ${field.format}]`;
|
|
104
|
+
}
|
|
105
|
+
if (isRequired) {
|
|
106
|
+
prompt += ' *required*';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
prompt += ': ';
|
|
110
|
+
|
|
111
|
+
const answer = await askQuestion(prompt);
|
|
112
|
+
|
|
113
|
+
// Check for cancellation
|
|
114
|
+
if (answer.toLowerCase() === 'cancel' || answer.toLowerCase() === 'c') {
|
|
115
|
+
return { action: 'cancel' as const };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Handle empty responses
|
|
119
|
+
if (answer === '' && isRequired) {
|
|
120
|
+
console.log(`ā Error: ${fieldName} is required`);
|
|
121
|
+
return { action: 'reject' as const };
|
|
122
|
+
} else if (answer !== '') {
|
|
123
|
+
content[fieldName] = answer;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Show the collected data and ask for confirmation
|
|
128
|
+
console.log('\nā
Collected data:');
|
|
129
|
+
console.log(JSON.stringify(content, null, 2));
|
|
130
|
+
|
|
131
|
+
const confirmAnswer = await askQuestion('\nSubmit this information? (yes/no/cancel): ');
|
|
132
|
+
|
|
133
|
+
if (confirmAnswer.toLowerCase() === 'yes' || confirmAnswer.toLowerCase() === 'y') {
|
|
134
|
+
return {
|
|
135
|
+
action: 'accept' as const,
|
|
136
|
+
content,
|
|
137
|
+
};
|
|
138
|
+
} else if (confirmAnswer.toLowerCase() === 'cancel' || confirmAnswer.toLowerCase() === 'c') {
|
|
139
|
+
return { action: 'cancel' as const };
|
|
140
|
+
} else {
|
|
141
|
+
return { action: 'reject' as const };
|
|
142
|
+
}
|
|
143
|
+
};
|
|
53
144
|
|
|
54
145
|
async function main() {
|
|
55
146
|
const mcpClient = new MCPClient({
|
|
56
147
|
servers: {
|
|
57
|
-
|
|
58
|
-
url: new URL('http://localhost:4111/api/mcp/
|
|
148
|
+
myMcpServerTwo: {
|
|
149
|
+
url: new URL('http://localhost:4111/api/mcp/myMcpServerTwo/mcp'),
|
|
59
150
|
},
|
|
60
151
|
},
|
|
61
152
|
});
|
|
62
153
|
|
|
63
|
-
|
|
64
|
-
|
|
154
|
+
mcpClient.elicitation.onRequest('myMcpServerTwo', elicitationHandler);
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
console.log('Connecting to MCP server...');
|
|
158
|
+
const tools = await mcpClient.getTools();
|
|
159
|
+
console.log('Available tools:', Object.keys(tools));
|
|
160
|
+
|
|
161
|
+
// Test the elicitation functionality
|
|
162
|
+
console.log('\nš§Ŗ Testing elicitation functionality...');
|
|
163
|
+
|
|
164
|
+
// Find the collectContactInfo tool
|
|
165
|
+
const collectContactInfoTool = tools['myMcpServerTwo_collectContactInfo'];
|
|
166
|
+
if (collectContactInfoTool) {
|
|
167
|
+
console.log('\nCalling collectContactInfo tool...');
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const result = await collectContactInfoTool.execute({
|
|
171
|
+
context: {
|
|
172
|
+
reason: 'We need your contact information to send you updates about our service.',
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
console.log('\nš Tool Result:');
|
|
177
|
+
console.log(result);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error('ā Error calling collectContactInfo tool:', error);
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
console.log('ā collectContactInfo tool not found');
|
|
183
|
+
console.log('Available tools:', Object.keys(tools));
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error('ā Error:', error);
|
|
187
|
+
} finally {
|
|
188
|
+
readline.close();
|
|
189
|
+
await mcpClient.disconnect();
|
|
190
|
+
}
|
|
65
191
|
}
|
|
66
192
|
|
|
67
|
-
main();
|
|
193
|
+
main().catch(console.error);
|
|
68
194
|
|
|
69
195
|
```
|
|
70
196
|
|
|
@@ -664,6 +790,57 @@ export const myMcpServerTwo = new MCPServer({
|
|
|
664
790
|
return `Hello, ${context.name}! Welcome to the MCP server.`;
|
|
665
791
|
},
|
|
666
792
|
}),
|
|
793
|
+
collectContactInfo: createTool({
|
|
794
|
+
id: 'collectContactInfo',
|
|
795
|
+
description: 'Collects user contact information through elicitation.',
|
|
796
|
+
inputSchema: z.object({
|
|
797
|
+
reason: z.string().optional().describe('Optional reason for collecting contact info'),
|
|
798
|
+
}),
|
|
799
|
+
execute: async ({ context }, options) => {
|
|
800
|
+
const { reason } = context;
|
|
801
|
+
|
|
802
|
+
try {
|
|
803
|
+
// Use the session-aware elicitation functionality
|
|
804
|
+
const result = await options.elicitation.sendRequest({
|
|
805
|
+
message: reason
|
|
806
|
+
? `Please provide your contact information. ${reason}`
|
|
807
|
+
: 'Please provide your contact information',
|
|
808
|
+
requestedSchema: {
|
|
809
|
+
type: 'object',
|
|
810
|
+
properties: {
|
|
811
|
+
name: {
|
|
812
|
+
type: 'string',
|
|
813
|
+
title: 'Full Name',
|
|
814
|
+
description: 'Your full name',
|
|
815
|
+
},
|
|
816
|
+
email: {
|
|
817
|
+
type: 'string',
|
|
818
|
+
title: 'Email Address',
|
|
819
|
+
description: 'Your email address',
|
|
820
|
+
format: 'email',
|
|
821
|
+
},
|
|
822
|
+
phone: {
|
|
823
|
+
type: 'string',
|
|
824
|
+
title: 'Phone Number',
|
|
825
|
+
description: 'Your phone number (optional)',
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
required: ['name', 'email'],
|
|
829
|
+
},
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
if (result.action === 'accept') {
|
|
833
|
+
return `Thank you! Contact information collected: ${JSON.stringify(result.content, null, 2)}`;
|
|
834
|
+
} else if (result.action === 'reject') {
|
|
835
|
+
return 'Contact information collection was declined by the user.';
|
|
836
|
+
} else {
|
|
837
|
+
return 'Contact information collection was cancelled by the user.';
|
|
838
|
+
}
|
|
839
|
+
} catch (error) {
|
|
840
|
+
return `Error collecting contact information: ${error}`;
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
}),
|
|
667
844
|
},
|
|
668
845
|
});
|
|
669
846
|
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
"drizzle-kit": "^0.31.0",
|
|
92
92
|
"eslint": "^9.29.0",
|
|
93
93
|
"eslint-config-next": "15.3.3",
|
|
94
|
-
"eslint-config-prettier": "^
|
|
95
|
-
"eslint-import-resolver-typescript": "^
|
|
94
|
+
"eslint-config-prettier": "^10.1.5",
|
|
95
|
+
"eslint-import-resolver-typescript": "^4.4.3",
|
|
96
96
|
"eslint-plugin-import": "^2.31.0",
|
|
97
97
|
"eslint-plugin-tailwindcss": "^3.18.0",
|
|
98
98
|
"postcss": "^8.5.3",
|
|
@@ -75,8 +75,6 @@ Once you have a server you want to use with your agent, import the Mastra `MCPCl
|
|
|
75
75
|
|
|
76
76
|
```typescript filename="src/mastra/mcp.ts" {1,7-16}
|
|
77
77
|
import { MCPClient } from "@mastra/mcp";
|
|
78
|
-
import { Agent } from "@mastra/core/agent";
|
|
79
|
-
import { openai } from "@ai-sdk/openai";
|
|
80
78
|
|
|
81
79
|
// Configure MCPClient to connect to your server(s)
|
|
82
80
|
export const mcp = new MCPClient({
|
|
@@ -96,7 +94,10 @@ export const mcp = new MCPClient({
|
|
|
96
94
|
Then connect your agent to the server tools:
|
|
97
95
|
|
|
98
96
|
```typescript filename="src/mastra/agents/mcpAgent.ts" {7}
|
|
97
|
+
import { Agent } from "@mastra/core/agent";
|
|
98
|
+
import { openai } from "@ai-sdk/openai";
|
|
99
99
|
import { mcp } from "../mcp";
|
|
100
|
+
|
|
100
101
|
// Create an agent and add tools from the MCP client
|
|
101
102
|
const agent = new Agent({
|
|
102
103
|
name: "Agent with MCP Tools",
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Digital Ocean"
|
|
3
|
+
description: "Deploy your Mastra applications to Digital Ocean."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
import { Callout, Steps, Tabs } from "nextra/components";
|
|
7
|
+
import ServerConfig from "@/components/content-blocks/server-config.mdx";
|
|
8
|
+
|
|
9
|
+
## Digital Ocean
|
|
10
|
+
|
|
11
|
+
Deploy your Mastra applications to Digital Ocean's App Platform and Droplets.
|
|
12
|
+
|
|
13
|
+
<Callout>
|
|
14
|
+
This guide assumes your Mastra application has been created using the default
|
|
15
|
+
`npx create-mastra@latest` command.
|
|
16
|
+
For more information on how to create a new Mastra application,
|
|
17
|
+
refer to our [getting started guide](./../../getting-started/installation.mdx)
|
|
18
|
+
</Callout>
|
|
19
|
+
|
|
20
|
+
<Tabs items={["App Platform", "Droplets"]}>
|
|
21
|
+
|
|
22
|
+
<Tabs.Tab>
|
|
23
|
+
|
|
24
|
+
### App Platform
|
|
25
|
+
|
|
26
|
+
#### Prerequisites [#app-platform-prerequisites]
|
|
27
|
+
|
|
28
|
+
- A Git repository containing your Mastra application. This can be a GitHub repository, GitLab repository, or any other compatible source provider.
|
|
29
|
+
- A Digital Ocean account
|
|
30
|
+
|
|
31
|
+
#### Deployment Steps
|
|
32
|
+
|
|
33
|
+
<Steps>
|
|
34
|
+
|
|
35
|
+
#### Create a new App
|
|
36
|
+
|
|
37
|
+
- Log in to your Digital Ocean dashboard.
|
|
38
|
+
- Navigate to the App Platform service.
|
|
39
|
+
- Select your source provider and create a new app.
|
|
40
|
+
|
|
41
|
+
#### Configure Deployment Source
|
|
42
|
+
|
|
43
|
+
- Connect and select your repository. You may also choose a container image or a sample app.
|
|
44
|
+
- Select the branch you want to deploy from.
|
|
45
|
+
- Configure the source directory if necessary. If your Mastra application uses the default directory structure, no action is required here.
|
|
46
|
+
- Head to the next step.
|
|
47
|
+
|
|
48
|
+
#### Configure Resource Settings and Environment Variables
|
|
49
|
+
|
|
50
|
+
- A Node.js build should be detected automatically.
|
|
51
|
+
- Add any required environment variables for your Mastra application. This includes API keys, database URLs, and other configuration values.
|
|
52
|
+
- You may choose to configure the size of your resource here.
|
|
53
|
+
- Other things you may optionally configure include, the region of your resource, the unique app name, and what project the resource belongs to.
|
|
54
|
+
- Once you're done, you may create the app after reviewing your configuration and pricing estimates.
|
|
55
|
+
|
|
56
|
+
#### Deployment
|
|
57
|
+
|
|
58
|
+
- Your app will be built and deployed automatically.
|
|
59
|
+
- Digital Ocean will provide you with a URL to access your deployed application.
|
|
60
|
+
|
|
61
|
+
</Steps>
|
|
62
|
+
|
|
63
|
+
You can now access your deployed application at the URL provided by Digital Ocean.
|
|
64
|
+
|
|
65
|
+
<Callout>
|
|
66
|
+
The Digital Ocean App Platform uses an ephemeral file system,
|
|
67
|
+
meaning that any files written to the file system are short-lived and may be lost.
|
|
68
|
+
Avoid using a Mastra storage provider that uses the file system,
|
|
69
|
+
such as `LibSQLStore` with a file URL.
|
|
70
|
+
</Callout>
|
|
71
|
+
|
|
72
|
+
</Tabs.Tab>
|
|
73
|
+
|
|
74
|
+
<Tabs.Tab>
|
|
75
|
+
|
|
76
|
+
### Droplets
|
|
77
|
+
|
|
78
|
+
Deploy your Mastra application to Digital Ocean's Droplets.
|
|
79
|
+
This guide will cover setting up a droplet, a reverse proxy using Nginx, and running your Mastra application.
|
|
80
|
+
|
|
81
|
+
<Callout>
|
|
82
|
+
The guide assumes your droplet runs Ubuntu 24+.
|
|
83
|
+
</Callout>
|
|
84
|
+
|
|
85
|
+
#### Prerequisites [#droplets-prerequisites]
|
|
86
|
+
|
|
87
|
+
- A Digital Ocean account
|
|
88
|
+
- A droplet running Ubuntu 24+
|
|
89
|
+
- A domain name with an A record pointing to your droplet
|
|
90
|
+
|
|
91
|
+
#### Setting up the droplet
|
|
92
|
+
|
|
93
|
+
<ServerConfig />
|
|
94
|
+
|
|
95
|
+
</Tabs.Tab>
|
|
96
|
+
|
|
97
|
+
</Tabs>
|
|
98
|
+
|
|
99
|
+
### Connect to your Mastra server
|
|
100
|
+
|
|
101
|
+
You can now connect to your Mastra server from your client application using a `MastraClient` from the `@mastra/client-js` package.
|
|
102
|
+
|
|
103
|
+
Refer to the [`MastraClient` documentation](../../client-js/overview.mdx) for more information.
|
|
104
|
+
|
|
105
|
+
```typescript copy showLineNumbers
|
|
106
|
+
import { MastraClient } from "@mastra/client-js";
|
|
107
|
+
|
|
108
|
+
const mastraClient = new MastraClient({
|
|
109
|
+
baseUrl: "https://<your-domain-name>",
|
|
110
|
+
});
|
|
111
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Cloud Providers"
|
|
3
|
+
description: "Deploy your Mastra applications to popular cloud providers."
|
|
4
|
+
asIndexPage: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
import { CardGrid, CardGridItem } from "@/components/cards/card-grid";
|
|
8
|
+
|
|
9
|
+
## Cloud Providers
|
|
10
|
+
|
|
11
|
+
Deploy your Mastra applicaitons to popular cloud providers.
|
|
12
|
+
|
|
13
|
+
<CardGrid>
|
|
14
|
+
<CardGridItem title="Digital Ocean" description="Deploy your Mastra applications to Digital Ocean" href="./cloud-providers/digital-ocean" />
|
|
15
|
+
</CardGrid>
|
|
@@ -208,6 +208,61 @@ const paragraphMemory = new Memory({
|
|
|
208
208
|
});
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
+
## Structured Working Memory
|
|
212
|
+
|
|
213
|
+
Working memory can also be defined using a structured schema instead of a Markdown template. This allows you to specify the exact fields and types that should be tracked, using a [Zod](https://zod.dev/) schema. When using a schema, the agent will see and update working memory as a JSON object matching your schema.
|
|
214
|
+
|
|
215
|
+
**Important:** You must specify either `template` or `schema`, but not both.
|
|
216
|
+
|
|
217
|
+
### Example: Schema-Based Working Memory
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import { z } from 'zod';
|
|
221
|
+
import { Memory } from '@mastra/memory';
|
|
222
|
+
|
|
223
|
+
const userProfileSchema = z.object({
|
|
224
|
+
name: z.string().optional(),
|
|
225
|
+
location: z.string().optional(),
|
|
226
|
+
timezone: z.string().optional(),
|
|
227
|
+
preferences: z.object({
|
|
228
|
+
communicationStyle: z.string().optional(),
|
|
229
|
+
projectGoal: z.string().optional(),
|
|
230
|
+
deadlines: z.array(z.string()).optional(),
|
|
231
|
+
}).optional(),
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const memory = new Memory({
|
|
235
|
+
options: {
|
|
236
|
+
workingMemory: {
|
|
237
|
+
enabled: true,
|
|
238
|
+
schema: userProfileSchema,
|
|
239
|
+
// template: ... (do not set)
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
When a schema is provided, the agent receives the working memory as a JSON object. For example:
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"name": "Sam",
|
|
250
|
+
"location": "Berlin",
|
|
251
|
+
"timezone": "CET",
|
|
252
|
+
"preferences": {
|
|
253
|
+
"communicationStyle": "Formal",
|
|
254
|
+
"projectGoal": "Launch MVP",
|
|
255
|
+
"deadlines": ["2025-07-01"]
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Choosing Between Template and Schema
|
|
261
|
+
|
|
262
|
+
- Use a **template** (Markdown) if you want the agent to maintain memory as a free-form text block, such as a user profile or scratchpad.
|
|
263
|
+
- Use a **schema** if you need structured, type-safe data that can be validated and programmatically accessed as JSON.
|
|
264
|
+
- Only one mode can be active at a time: setting both `template` and `schema` is not supported.
|
|
265
|
+
|
|
211
266
|
## Example: Multi-step Retention
|
|
212
267
|
|
|
213
268
|
Below is a simplified view of how the `User Profile` template updates across a short user
|
|
@@ -247,4 +302,5 @@ instructions on _how_ and _when_ to use this template in your agent's `instructi
|
|
|
247
302
|
|
|
248
303
|
- [Streaming working memory](/examples/memory/streaming-working-memory)
|
|
249
304
|
- [Using a working memory template](/examples/memory/streaming-working-memory-advanced)
|
|
305
|
+
- [Using a working memory schema](/examples/memory/streaming-working-memory-structured)
|
|
250
306
|
- [Per-resource working memory](https://github.com/mastra-ai/mastra/tree/main/examples/memory-per-resource-example) - Complete example showing resource-scoped memory persistence
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
## Complex tasks requiring multiple primitives
|
|
2
|
+
|
|
3
|
+
As an example, we have an AgentNetwork with 3 primitives at its disposal:
|
|
4
|
+
|
|
5
|
+
- `agent1`: A general research agent that can do research on a given topic.
|
|
6
|
+
- `agent2`: A general writing agent that can write a full report based on the researched material.
|
|
7
|
+
- `workflow1`: A workflow that can research a given city and write a full report based on the researched material (using both agent1 and agent2).
|
|
8
|
+
|
|
9
|
+
We use the `loop` method to create a task that requires multiple primitives. The AgentNetwork will, using memory, figure out which primitives to call and in which order, as well as when the task is complete.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { NewAgentNetwork } from '@mastra/core/network/vNext';
|
|
13
|
+
import { Agent } from '@mastra/core/agent';
|
|
14
|
+
import { createStep, createWorkflow } from '@mastra/core/workflows';
|
|
15
|
+
import { Memory } from '@mastra/memory';
|
|
16
|
+
import { openai } from '@ai-sdk/openai';
|
|
17
|
+
import { LibSQLStore } from '@mastra/libsql';
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { RuntimeContext } from '@mastra/core/runtime-context';
|
|
20
|
+
|
|
21
|
+
const memory = new Memory({
|
|
22
|
+
storage: new LibSQLStore({
|
|
23
|
+
url: 'file:../mastra.db', // Or your database URL
|
|
24
|
+
}),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const agentStep1 = createStep({
|
|
28
|
+
id: 'agent-step',
|
|
29
|
+
description: 'This step is used to do research and text synthesis.',
|
|
30
|
+
inputSchema: z.object({
|
|
31
|
+
city: z.string().describe('The city to research'),
|
|
32
|
+
}),
|
|
33
|
+
outputSchema: z.object({
|
|
34
|
+
text: z.string(),
|
|
35
|
+
}),
|
|
36
|
+
execute: async ({ inputData }) => {
|
|
37
|
+
const resp = await agent1.generate(inputData.city, {
|
|
38
|
+
output: z.object({
|
|
39
|
+
text: z.string(),
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return { text: resp.object.text };
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const agentStep2 = createStep({
|
|
48
|
+
id: 'agent-step-two',
|
|
49
|
+
description: 'This step is used to do research and text synthesis.',
|
|
50
|
+
inputSchema: z.object({
|
|
51
|
+
text: z.string().describe('The city to research'),
|
|
52
|
+
}),
|
|
53
|
+
outputSchema: z.object({
|
|
54
|
+
text: z.string(),
|
|
55
|
+
}),
|
|
56
|
+
execute: async ({ inputData }) => {
|
|
57
|
+
const resp = await agent2.generate(inputData.text, {
|
|
58
|
+
output: z.object({
|
|
59
|
+
text: z.string(),
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return { text: resp.object.text };
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const workflow1 = createWorkflow({
|
|
68
|
+
id: 'workflow1',
|
|
69
|
+
description:
|
|
70
|
+
'This workflow is perfect for researching a specific city. It should be used when you have a city in mind to research.',
|
|
71
|
+
steps: [],
|
|
72
|
+
inputSchema: z.object({
|
|
73
|
+
city: z.string(),
|
|
74
|
+
}),
|
|
75
|
+
outputSchema: z.object({
|
|
76
|
+
text: z.string(),
|
|
77
|
+
}),
|
|
78
|
+
})
|
|
79
|
+
.then(agentStep1)
|
|
80
|
+
.then(agentStep2)
|
|
81
|
+
.commit();
|
|
82
|
+
|
|
83
|
+
const agent1 = new Agent({
|
|
84
|
+
name: 'agent1',
|
|
85
|
+
instructions:
|
|
86
|
+
'This agent is used to do research, but not create full responses. Answer in bullet points only and be concise.',
|
|
87
|
+
description:
|
|
88
|
+
'This agent is used to do research, but not create full responses. Answer in bullet points only and be concise.',
|
|
89
|
+
model: openai('gpt-4o'),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const agent2 = new Agent({
|
|
93
|
+
name: 'agent2',
|
|
94
|
+
description:
|
|
95
|
+
'This agent is used to do text synthesis on researched material. Write a full report based on the researched material. Writes reports in full paragraphs. Should be used to synthesize text from different sources together as a final report.',
|
|
96
|
+
instructions:
|
|
97
|
+
'This agent is used to do text synthesis on researched material. Write a full report based on the researched material. Do not use bullet points. Write full paragraphs. There should not be a single bullet point in the final report.',
|
|
98
|
+
model: openai('gpt-4o'),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const network = new NewAgentNetwork({
|
|
102
|
+
id: 'test-network',
|
|
103
|
+
name: 'Test Network',
|
|
104
|
+
instructions:
|
|
105
|
+
'You are a network of writers and researchers. The user will ask you to research a topic. You always need to answer with a full report. Bullet points are NOT a full report. WRITE FULL PARAGRAPHS like this is a blog post or something similar. You should not rely on partial information.',
|
|
106
|
+
model: openai('gpt-4o'),
|
|
107
|
+
agents: {
|
|
108
|
+
agent1,
|
|
109
|
+
agent2,
|
|
110
|
+
},
|
|
111
|
+
workflows: {
|
|
112
|
+
workflow1,
|
|
113
|
+
},
|
|
114
|
+
memory: memory,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const runtimeContext = new RuntimeContext();
|
|
118
|
+
|
|
119
|
+
console.log(
|
|
120
|
+
// specifying the task, note that there is a mention here about using an agent for synthesis. This is because the routing agent can actually do some synthesis on results on its own, so this will force it to use agent2 instead
|
|
121
|
+
await network.loop(
|
|
122
|
+
'What are the biggest cities in France? Give me 3. How are they like? Find cities, then do thorough research on each city, and give me a final full report synthesizing all that information. Make sure to use an agent for synthesis.',
|
|
123
|
+
{ runtimeContext },
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
For the given task (research 3 biggest cities in France and write a full report), the AgentNetwork will call the following primitives:
|
|
129
|
+
|
|
130
|
+
1. `agent1` to find the 3 biggest cities in France.
|
|
131
|
+
2. `workflow1` to research each city one by one. The workflow uses `memory` to figure out which cities have already been researched and makes sure it has researched all of them before proceeding.
|
|
132
|
+
3. `agent2` to synthesize the final report.
|
|
133
|
+
|
|
134
|
+
### How It Works
|
|
135
|
+
|
|
136
|
+
- The underlying engine is a Mastra workflow that wraps the single call `generate` workflow.
|
|
137
|
+
- The workflow will repeatedly call the network execution workflow with a `dountil` structure, until the routing model determines the task is complete. This check is used as the `dountil` condition.
|