@mastra/mcp-docs-server 0.13.2-alpha.1 → 0.13.2-alpha.3
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 +21 -21
- 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 +17 -17
- 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 +27 -27
- 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/course/01-first-agent/04-project-structure.md +8 -3
- package/.docs/raw/course/01-first-agent/07-creating-your-agent.md +5 -3
- package/.docs/raw/course/01-first-agent/08-exporting-your-agent.md +20 -6
- package/.docs/raw/course/01-first-agent/11-creating-transactions-tool.md +5 -3
- package/.docs/raw/course/01-first-agent/12-connecting-tool-to-agent.md +2 -2
- package/.docs/raw/course/04-workflows/01-introduction-to-workflows.md +44 -0
- package/.docs/raw/course/04-workflows/02-understanding-steps.md +53 -0
- package/.docs/raw/course/04-workflows/03-creating-your-first-step.md +57 -0
- package/.docs/raw/course/04-workflows/04-creating-a-second-step.md +58 -0
- package/.docs/raw/course/04-workflows/05-chaining-steps-together.md +56 -0
- package/.docs/raw/course/04-workflows/06-registering-with-mastra.md +24 -0
- package/.docs/raw/course/04-workflows/07-using-playground.md +58 -0
- package/.docs/raw/course/04-workflows/08-running-workflows-programmatically.md +77 -0
- package/.docs/raw/course/04-workflows/09-adding-a-third-step.md +70 -0
- package/.docs/raw/course/04-workflows/10-updating-the-workflow.md +55 -0
- package/.docs/raw/course/04-workflows/11-creating-an-ai-agent.md +67 -0
- package/.docs/raw/course/04-workflows/12-using-agent-in-workflow.md +91 -0
- package/.docs/raw/course/04-workflows/13-creating-ai-enhanced-workflow.md +75 -0
- package/.docs/raw/course/04-workflows/14-understanding-parallel-execution.md +38 -0
- package/.docs/raw/course/04-workflows/15-creating-parallel-steps.md +115 -0
- package/.docs/raw/course/04-workflows/16-building-parallel-workflow.md +100 -0
- package/.docs/raw/course/04-workflows/17-testing-parallel-performance.md +40 -0
- package/.docs/raw/course/04-workflows/18-understanding-conditional-branching.md +58 -0
- package/.docs/raw/course/04-workflows/19-creating-conditional-steps.md +128 -0
- package/.docs/raw/course/04-workflows/20-building-conditional-workflow.md +60 -0
- package/.docs/raw/course/04-workflows/21-testing-conditional-logic.md +58 -0
- package/.docs/raw/course/04-workflows/22-conclusion.md +58 -0
- 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",
|
|
@@ -4,9 +4,14 @@ Let's check that your project has the correct structure. You should have:
|
|
|
4
4
|
|
|
5
5
|
1. A `src/mastra` directory that contains:
|
|
6
6
|
- `index.ts` - The main entry point for your Mastra project
|
|
7
|
-
- `agents
|
|
8
|
-
- `tools
|
|
7
|
+
- `agents/` - Directory containing individual agent files
|
|
8
|
+
- `tools/` - Directory containing individual tool files
|
|
9
|
+
- `workflows/` - Directory containing individual workflow files
|
|
9
10
|
|
|
10
|
-
If
|
|
11
|
+
If the CLI created your project, you should see files like:
|
|
12
|
+
|
|
13
|
+
- `agents/weather-agent.ts` - Example weather agent
|
|
14
|
+
- `tools/weather-tool.ts` - Example weather tool
|
|
15
|
+
- `workflows/weather-workflow.ts` - Example weather workflow
|
|
11
16
|
|
|
12
17
|
This structure is important because it follows the Mastra convention for organizing your code. The `index.ts` file is the main entry point for your Mastra project, while the `agents` and `tools` directories contain the definitions for your agents and tools, respectively.
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Creating Your Agent
|
|
2
2
|
|
|
3
|
-
Let's create a simple agent that will help users analyze financial transaction data. We'll
|
|
3
|
+
Let's create a simple agent that will help users analyze financial transaction data. We'll create a new file called `agents/financial-agent.ts`.
|
|
4
4
|
|
|
5
|
-
First,
|
|
5
|
+
First, create the new agent file at src/mastra/agents/financial-agent.ts
|
|
6
|
+
|
|
7
|
+
Now add the necessary imports at the top of your file:
|
|
6
8
|
|
|
7
9
|
```typescript
|
|
8
|
-
import { Agent } from "@mastra/core";
|
|
10
|
+
import { Agent } from "@mastra/core/agent";
|
|
9
11
|
import { openai } from "@ai-sdk/openai";
|
|
10
12
|
// We'll import our tool in a later step
|
|
11
13
|
```
|
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
# Exporting Your Agent
|
|
2
2
|
|
|
3
|
-
To make your agent available to the playground, you need to export it through the Mastra class in your `src/mastra/index.ts` file
|
|
3
|
+
To make your agent available to the playground, you need to export it through the Mastra class in your `src/mastra/index.ts` file.
|
|
4
|
+
|
|
5
|
+
First, import the necessary dependencies and your agent:
|
|
4
6
|
|
|
5
7
|
```typescript
|
|
6
|
-
import { Mastra } from "@mastra/core";
|
|
7
|
-
import {
|
|
8
|
+
import { Mastra } from "@mastra/core/mastra";
|
|
9
|
+
import { PinoLogger } from "@mastra/loggers";
|
|
10
|
+
import { LibSQLStore } from "@mastra/libsql";
|
|
11
|
+
import { financialAgent } from "./agents/financial-agent";
|
|
8
12
|
|
|
9
|
-
export const mastra
|
|
13
|
+
export const mastra = new Mastra({
|
|
10
14
|
agents: {
|
|
11
15
|
financialAgent,
|
|
12
16
|
},
|
|
17
|
+
storage: new LibSQLStore({
|
|
18
|
+
url: ":memory:",
|
|
19
|
+
}),
|
|
20
|
+
logger: new PinoLogger({
|
|
21
|
+
name: 'Mastra',
|
|
22
|
+
level: 'info',
|
|
23
|
+
}),
|
|
13
24
|
});
|
|
14
25
|
```
|
|
15
26
|
|
|
16
|
-
This creates a new Mastra instance that includes
|
|
27
|
+
This creates a new Mastra instance that includes:
|
|
28
|
+
- Your financial agent
|
|
29
|
+
- In-memory storage for development
|
|
30
|
+
- A logger for debugging and monitoring
|
|
17
31
|
|
|
18
|
-
The Mastra class is the main entry point for your Mastra project. It's responsible for registering your agents and
|
|
32
|
+
The Mastra class is the main entry point for your Mastra project. It's responsible for registering your agents and configuring the core services like storage and logging.
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Creating the getTransactions Tool
|
|
2
2
|
|
|
3
|
-
Let's create a tool that fetches transaction data from a Google Sheet. We'll
|
|
3
|
+
Let's create a tool that fetches transaction data from a Google Sheet. We'll create a new file called `tools/get-transactions-tool.ts`.
|
|
4
4
|
|
|
5
|
-
First,
|
|
5
|
+
First, create the new tool file at src/mastra/tools/get-transactions-tool.ts
|
|
6
|
+
|
|
7
|
+
Now add the necessary imports:
|
|
6
8
|
|
|
7
9
|
```typescript
|
|
8
|
-
import { createTool } from "@mastra/core";
|
|
10
|
+
import { createTool } from "@mastra/core/tools";
|
|
9
11
|
import { z } from "zod";
|
|
10
12
|
```
|
|
11
13
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Connecting the Tool to Your Agent
|
|
2
2
|
|
|
3
|
-
Now that we've created our tool, we need to connect it to our agent. Go back to your `agents/
|
|
3
|
+
Now that we've created our tool, we need to connect it to our agent. Go back to your `agents/financial-agent.ts` file and update it:
|
|
4
4
|
|
|
5
5
|
1. Import the tool:
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
|
-
import { getTransactionsTool } from "../tools";
|
|
8
|
+
import { getTransactionsTool } from "../tools/get-transactions-tool";
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
2. Add the tool to your agent:
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Introduction to Workflows
|
|
2
|
+
|
|
3
|
+
Welcome to the fourth lesson of the Mastra course! In this lesson, you'll learn about Mastra Workflows - a powerful way to orchestrate complex sequences of operations.
|
|
4
|
+
|
|
5
|
+
## What are Workflows?
|
|
6
|
+
|
|
7
|
+
Workflows in Mastra let you chain together multiple operations in a predictable, type-safe manner. Think of them as a recipe that breaks down complex tasks into smaller, manageable steps.
|
|
8
|
+
|
|
9
|
+
Instead of writing one big function that does everything, workflows let you:
|
|
10
|
+
|
|
11
|
+
- Break complex operations into smaller, reusable steps
|
|
12
|
+
- Define clear inputs and outputs for each step
|
|
13
|
+
- Chain steps together with automatic data validation
|
|
14
|
+
- Handle errors gracefully at each step
|
|
15
|
+
|
|
16
|
+
## Simple Example
|
|
17
|
+
|
|
18
|
+
Without workflows, you might write:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
async function processContent(text: string) {
|
|
22
|
+
// All logic in one function - hard to test and reuse
|
|
23
|
+
const validated = validateText(text);
|
|
24
|
+
const enhanced = enhanceText(validated);
|
|
25
|
+
const summarized = summarizeText(enhanced);
|
|
26
|
+
return summarized;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
With workflows, the same logic becomes modular and reusable with tracing built in at every step.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
export const contentWorkflow = createWorkflow({...})
|
|
34
|
+
.then(validateStep)
|
|
35
|
+
.then(enhanceStep)
|
|
36
|
+
.then(summarizeStep)
|
|
37
|
+
.commit();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## What You'll Build
|
|
41
|
+
|
|
42
|
+
In this lesson, you'll create a content processing workflow that validates, enhances, and summarizes text content using multiple connected steps.
|
|
43
|
+
|
|
44
|
+
Let's start by understanding the basic building blocks!
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Understanding Steps
|
|
2
|
+
|
|
3
|
+
Steps are the building blocks of workflows. Each step is a self-contained unit that takes some input, processes it, and produces an output.
|
|
4
|
+
|
|
5
|
+
## What is a Step?
|
|
6
|
+
|
|
7
|
+
A step has three main parts:
|
|
8
|
+
|
|
9
|
+
1. **Input Schema** - what data it expects to receive
|
|
10
|
+
2. **Output Schema** - what data it will produce
|
|
11
|
+
3. **Execute Function** - the logic that transforms input to output
|
|
12
|
+
|
|
13
|
+
## Step Structure
|
|
14
|
+
|
|
15
|
+
Every step follows this pattern:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
const myStep = createStep({
|
|
19
|
+
id: "unique-step-name",
|
|
20
|
+
description: "What this step does",
|
|
21
|
+
inputSchema: z.object({
|
|
22
|
+
// Define expected input structure
|
|
23
|
+
}),
|
|
24
|
+
outputSchema: z.object({
|
|
25
|
+
// Define output structure
|
|
26
|
+
}),
|
|
27
|
+
execute: async ({ inputData }) => {
|
|
28
|
+
// Your logic here
|
|
29
|
+
return {
|
|
30
|
+
// Return data matching output schema
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Why Use Schemas?
|
|
37
|
+
|
|
38
|
+
Schemas provide several benefits:
|
|
39
|
+
|
|
40
|
+
- **Type Safety**: TypeScript knows exactly what data flows between steps
|
|
41
|
+
- **Runtime Validation**: Invalid data is caught immediately with helpful error messages
|
|
42
|
+
- **Documentation**: Schemas serve as living documentation of your workflow
|
|
43
|
+
- **Debugging**: Clear contracts make it easy to identify issues
|
|
44
|
+
|
|
45
|
+
## Key Benefits
|
|
46
|
+
|
|
47
|
+
- **Reusable**: Steps can be used in multiple workflows
|
|
48
|
+
- **Testable**: Each step can be tested in isolation
|
|
49
|
+
- **Composable**: Steps can be combined in different ways
|
|
50
|
+
- **Reliable**: Schemas catch data flow issues early
|
|
51
|
+
- **Traceable**: Every step is traced so you can see the flow of data
|
|
52
|
+
|
|
53
|
+
Next, you'll create your first step!
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Creating Your First Step
|
|
2
|
+
|
|
3
|
+
Let's create your first workflow step! We'll build a step that validates text content.
|
|
4
|
+
|
|
5
|
+
## Setting Up
|
|
6
|
+
|
|
7
|
+
First, create a new file for your workflow in the `src/mastra/workflows` directory. Let's name this file `content-workflow.ts`
|
|
8
|
+
|
|
9
|
+
## Creating a Validation Step
|
|
10
|
+
|
|
11
|
+
Add this code to your workflow file:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createStep } from "@mastra/core/workflows";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
|
|
17
|
+
const validateContentStep = createStep({
|
|
18
|
+
id: "validate-content",
|
|
19
|
+
description: "Validates incoming text content",
|
|
20
|
+
inputSchema: z.object({
|
|
21
|
+
content: z.string().min(1, "Content cannot be empty"),
|
|
22
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
23
|
+
}),
|
|
24
|
+
outputSchema: z.object({
|
|
25
|
+
content: z.string(),
|
|
26
|
+
type: z.string(),
|
|
27
|
+
wordCount: z.number(),
|
|
28
|
+
isValid: z.boolean(),
|
|
29
|
+
}),
|
|
30
|
+
execute: async ({ inputData }) => {
|
|
31
|
+
const { content, type } = inputData;
|
|
32
|
+
|
|
33
|
+
const wordCount = content.trim().split(/\s+/).length;
|
|
34
|
+
const isValid = wordCount >= 5; // Minimum 5 words
|
|
35
|
+
|
|
36
|
+
if (!isValid) {
|
|
37
|
+
throw new Error(`Content too short: ${wordCount} words`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
content: content.trim(),
|
|
42
|
+
type,
|
|
43
|
+
wordCount,
|
|
44
|
+
isValid,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Understanding the Code
|
|
51
|
+
|
|
52
|
+
- **ID**: Unique identifier for this step
|
|
53
|
+
- **Input Schema**: Expects `content` (string) and optional `type`
|
|
54
|
+
- **Output Schema**: Returns content, type, word count, and validation status
|
|
55
|
+
- **Execute**: Contains the validation logic
|
|
56
|
+
|
|
57
|
+
Your first step is ready! Next, you'll test it to make sure it works correctly.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Creating a Second Step
|
|
2
|
+
|
|
3
|
+
Now let's create a second step that enhances the validated content with metadata.
|
|
4
|
+
|
|
5
|
+
## The Enhancement Step
|
|
6
|
+
|
|
7
|
+
Add this step to your workflow file:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const enhanceContentStep = createStep({
|
|
11
|
+
id: "enhance-content",
|
|
12
|
+
description: "Adds metadata to validated content",
|
|
13
|
+
inputSchema: z.object({
|
|
14
|
+
content: z.string(),
|
|
15
|
+
type: z.string(),
|
|
16
|
+
wordCount: z.number(),
|
|
17
|
+
isValid: z.boolean(),
|
|
18
|
+
}),
|
|
19
|
+
outputSchema: z.object({
|
|
20
|
+
content: z.string(),
|
|
21
|
+
type: z.string(),
|
|
22
|
+
wordCount: z.number(),
|
|
23
|
+
metadata: z.object({
|
|
24
|
+
readingTime: z.number(),
|
|
25
|
+
difficulty: z.enum(["easy", "medium", "hard"]),
|
|
26
|
+
processedAt: z.string(),
|
|
27
|
+
}),
|
|
28
|
+
}),
|
|
29
|
+
execute: async ({ inputData }) => {
|
|
30
|
+
const { content, type, wordCount } = inputData;
|
|
31
|
+
|
|
32
|
+
// Calculate reading time (200 words per minute)
|
|
33
|
+
const readingTime = Math.ceil(wordCount / 200);
|
|
34
|
+
|
|
35
|
+
// Determine difficulty based on word count
|
|
36
|
+
let difficulty: "easy" | "medium" | "hard" = "easy";
|
|
37
|
+
if (wordCount > 100) difficulty = "medium";
|
|
38
|
+
if (wordCount > 300) difficulty = "hard";
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
content,
|
|
42
|
+
type,
|
|
43
|
+
wordCount,
|
|
44
|
+
metadata: {
|
|
45
|
+
readingTime,
|
|
46
|
+
difficulty,
|
|
47
|
+
processedAt: new Date().toISOString(),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Notice the Input Schema
|
|
55
|
+
|
|
56
|
+
The input schema of this step matches the output schema of the previous step. This is important for chaining steps together!
|
|
57
|
+
|
|
58
|
+
Your second step is ready! Next, you'll learn how to chain these steps together into a complete workflow.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Chaining Steps Together
|
|
2
|
+
|
|
3
|
+
Now you'll learn how to chain your steps together to create a complete workflow.
|
|
4
|
+
|
|
5
|
+
## Creating the Workflow
|
|
6
|
+
|
|
7
|
+
Add this workflow definition to your file:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { createWorkflow } from "@mastra/core/workflows";
|
|
11
|
+
|
|
12
|
+
export const contentWorkflow = createWorkflow({
|
|
13
|
+
id: "content-processing-workflow",
|
|
14
|
+
description: "Validates and enhances content",
|
|
15
|
+
inputSchema: z.object({
|
|
16
|
+
content: z.string(),
|
|
17
|
+
type: z.enum(["article", "blog", "social"]).default("article")
|
|
18
|
+
}),
|
|
19
|
+
outputSchema: z.object({
|
|
20
|
+
content: z.string(),
|
|
21
|
+
type: z.string(),
|
|
22
|
+
wordCount: z.number(),
|
|
23
|
+
metadata: z.object({
|
|
24
|
+
readingTime: z.number(),
|
|
25
|
+
difficulty: z.enum(["easy", "medium", "hard"]),
|
|
26
|
+
processedAt: z.string()
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
.then(validateContentStep)
|
|
31
|
+
.then(enhanceContentStep)
|
|
32
|
+
.commit();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Understanding the Workflow
|
|
36
|
+
|
|
37
|
+
- **Input Schema**: Defines what data the workflow expects
|
|
38
|
+
- **Output Schema**: Defines what the workflow will return
|
|
39
|
+
- **Steps**: Chained using `.then()` in the order they should execute
|
|
40
|
+
- **Commit**: Finalizes the workflow definition
|
|
41
|
+
|
|
42
|
+
## How Data Flows
|
|
43
|
+
|
|
44
|
+
1. Workflow receives input matching the input schema
|
|
45
|
+
2. First step processes input and outputs validated data
|
|
46
|
+
3. Second step receives the first step's output as its input
|
|
47
|
+
4. Workflow returns the final step's output
|
|
48
|
+
|
|
49
|
+
## Schema Validation
|
|
50
|
+
|
|
51
|
+
The workflow automatically validates:
|
|
52
|
+
- Input data matches the workflow's input schema
|
|
53
|
+
- Each step's output matches the next step's input schema
|
|
54
|
+
- Final output matches the workflow's output schema
|
|
55
|
+
|
|
56
|
+
Your workflow is now ready to run! Next, you'll test the complete workflow.
|