@mastra/mcp-docs-server 1.1.46-alpha.2 → 1.1.46-alpha.4
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/docs/agent-builder/browser.md +1 -1
- package/.docs/docs/agent-builder/channels.md +1 -1
- package/.docs/docs/agent-builder/integrations.md +73 -0
- package/.docs/docs/agent-builder/overview.md +1 -0
- package/.docs/docs/agents/adding-voice.md +1 -1
- package/.docs/docs/agents/agent-approval.md +2 -2
- package/.docs/docs/agents/background-tasks.md +1 -1
- package/.docs/docs/agents/channels.md +2 -2
- package/.docs/docs/agents/code-mode.md +20 -56
- package/.docs/docs/agents/overview.md +1 -0
- package/.docs/docs/agents/sdk-agents.md +165 -1
- package/.docs/docs/agents/supervisor-agents.md +40 -2
- package/.docs/docs/agents/using-tools.md +59 -1
- package/.docs/docs/browser/agent-browser.md +1 -1
- package/.docs/docs/browser/browser-viewer.md +22 -15
- package/.docs/docs/browser/overview.md +1 -1
- package/.docs/docs/browser/stagehand.md +1 -1
- package/.docs/docs/editor/overview.md +6 -6
- package/.docs/docs/editor/prompts.md +3 -3
- package/.docs/docs/editor/tools.md +2 -2
- package/.docs/docs/evals/evals-with-memory.md +8 -8
- package/.docs/docs/mastra-platform/observability.md +1 -1
- package/.docs/docs/mastra-platform/server.md +1 -1
- package/.docs/docs/mcp/mcp-apps.md +4 -4
- package/.docs/docs/memory/observational-memory.md +1 -1
- package/.docs/docs/memory/working-memory.md +2 -2
- package/.docs/docs/observability/integrations/bridges/datadog.md +1 -1
- package/.docs/docs/observability/integrations/bridges/otel.md +1 -1
- package/.docs/docs/observability/integrations/exporters/laminar.md +1 -1
- package/.docs/docs/observability/integrations/exporters/langfuse.md +26 -1
- package/.docs/docs/observability/integrations/exporters/mastra-platform.md +1 -1
- package/.docs/docs/observability/integrations/exporters/mastra-storage.md +4 -4
- package/.docs/docs/observability/integrations/exporters/otel.md +1 -1
- package/.docs/docs/observability/integrations/overview.md +1 -1
- package/.docs/docs/observability/logging.md +1 -1
- package/.docs/docs/observability/metrics/overview.md +3 -3
- package/.docs/docs/observability/metrics/querying.md +2 -2
- package/.docs/docs/observability/storage.md +2 -2
- package/.docs/docs/observability/tracing/overview.md +1 -1
- package/.docs/docs/server/auth/fga.md +15 -15
- package/.docs/docs/server/auth/okta.md +2 -2
- package/.docs/docs/server/auth/workos.md +1 -1
- package/.docs/docs/server/custom-api-routes.md +1 -1
- package/.docs/docs/server/pubsub.md +4 -4
- package/.docs/docs/studio/auth.md +1 -1
- package/.docs/docs/studio/observability.md +3 -1
- package/.docs/docs/workflows/scheduled-workflows.md +13 -13
- package/.docs/docs/workspace/filesystem.md +1 -1
- package/.docs/docs/workspace/lsp.md +1 -1
- package/.docs/docs/workspace/overview.md +35 -1
- package/.docs/docs/workspace/sandbox.md +4 -3
- package/.docs/guides/build-your-ui/ai-sdk-ui.md +2 -2
- package/.docs/guides/deployment/aws-bedrock-agentcore.md +3 -3
- package/.docs/guides/deployment/inngest.md +5 -5
- package/.docs/guides/deployment/temporal.md +3 -3
- package/.docs/guides/getting-started/nestjs.md +1 -1
- package/.docs/guides/migrations/mastra-cloud.md +3 -3
- package/.docs/guides/migrations/upgrade-to-v1/overview.md +1 -1
- package/.docs/guides/migrations/upgrade-to-v1/tracing.md +1 -1
- package/.docs/models/gateways/custom-gateways.md +57 -7
- package/.docs/reference/acp/acp-agent.md +2 -2
- package/.docs/reference/agents/agent.md +44 -0
- package/.docs/reference/agents/channels.md +1 -1
- package/.docs/reference/agents/durable-agent.md +2 -2
- package/.docs/reference/agents/generate.md +2 -0
- package/.docs/reference/agents/generateLegacy.md +2 -0
- package/.docs/reference/ai-sdk/handle-chat-stream.md +1 -1
- package/.docs/reference/ai-sdk/to-ai-sdk-stream.md +1 -1
- package/.docs/reference/auth/okta.md +1 -1
- package/.docs/reference/auth/workos.md +1 -1
- package/.docs/reference/browser/agent-browser.md +1 -1
- package/.docs/reference/browser/browser-viewer.md +11 -9
- package/.docs/reference/browser/stagehand-browser.md +1 -1
- package/.docs/reference/cli/mastra.md +3 -1
- package/.docs/reference/client-js/responses.md +2 -2
- package/.docs/reference/client-js/workflows.md +1 -1
- package/.docs/reference/configuration.md +1 -1
- package/.docs/reference/core/mastra-model-gateway.md +15 -1
- package/.docs/reference/core/removeWorkspace.md +26 -0
- package/.docs/reference/editor/browser-provider.md +1 -1
- package/.docs/reference/editor/storage-browser-ref.md +3 -3
- package/.docs/reference/editor/storage-workspace-ref.md +1 -1
- package/.docs/reference/evals/rubric.md +113 -0
- package/.docs/reference/evals/trajectory-accuracy.md +3 -3
- package/.docs/reference/harness/harness-class.md +81 -62
- package/.docs/reference/index.md +5 -0
- package/.docs/reference/memory/serialized-memory-config.md +1 -1
- package/.docs/reference/observability/metrics/automatic-metrics.md +3 -3
- package/.docs/reference/observability/tracing/bridges/datadog.md +1 -1
- package/.docs/reference/observability/tracing/exporters/cloud-exporter.md +3 -3
- package/.docs/reference/observability/tracing/exporters/default-exporter.md +1 -1
- package/.docs/reference/observability/tracing/exporters/mastra-platform-exporter.md +5 -5
- package/.docs/reference/observability/tracing/exporters/mastra-storage-exporter.md +1 -1
- package/.docs/reference/observability/tracing/exporters/otel.md +1 -1
- package/.docs/reference/observability/tracing/processors/sensitive-data-filter.md +2 -2
- package/.docs/reference/processors/cost-guard-processor.md +2 -2
- package/.docs/reference/processors/processor-interface.md +4 -4
- package/.docs/reference/processors/response-cache.md +2 -2
- package/.docs/reference/processors/skill-search-processor.md +3 -3
- package/.docs/reference/processors/tool-search-processor.md +108 -4
- package/.docs/reference/pubsub/base.md +1 -1
- package/.docs/reference/pubsub/caching-pubsub.md +2 -2
- package/.docs/reference/pubsub/event-emitter.md +3 -3
- package/.docs/reference/pubsub/google-cloud-pubsub.md +1 -1
- package/.docs/reference/pubsub/redis-streams.md +1 -1
- package/.docs/reference/pubsub/unix-socket-pubsub.md +1 -1
- package/.docs/reference/server/nestjs-adapter.md +2 -2
- package/.docs/reference/signals/task-signal-provider.md +62 -0
- package/.docs/reference/storage/clickhouse.md +49 -4
- package/.docs/reference/storage/composite.md +33 -1
- package/.docs/reference/storage/convex.md +2 -2
- package/.docs/reference/storage/dsql.md +7 -7
- package/.docs/reference/storage/duckdb.md +3 -3
- package/.docs/reference/storage/redis.md +3 -3
- package/.docs/reference/storage/spanner.md +7 -7
- package/.docs/reference/streaming/agents/stream.md +2 -0
- package/.docs/reference/streaming/agents/streamLegacy.md +2 -0
- package/.docs/reference/streaming/agents/streamUntilIdle.md +1 -1
- package/.docs/reference/tools/brightdata.md +3 -3
- package/.docs/reference/tools/create-code-mode.md +124 -0
- package/.docs/reference/tools/create-tool.md +1 -1
- package/.docs/reference/tools/mcp-client.md +5 -5
- package/.docs/reference/tools/mcp-server.md +45 -0
- package/.docs/reference/tools/perplexity.md +4 -4
- package/.docs/reference/tools/tavily.md +3 -3
- package/.docs/reference/voice/aws-nova-sonic.md +1 -1
- package/.docs/reference/voice/google-gemini-live.md +1 -1
- package/.docs/reference/voice/inworld-realtime.md +5 -5
- package/.docs/reference/voice/inworld.md +1 -1
- package/.docs/reference/voice/sarvam.md +1 -1
- package/.docs/reference/workspace/agentcore-runtime-sandbox.md +7 -7
- package/.docs/reference/workspace/azure-blob-filesystem.md +2 -2
- package/.docs/reference/workspace/files-sdk-filesystem.md +3 -3
- package/.docs/reference/workspace/google-drive-filesystem.md +7 -7
- package/.docs/reference/workspace/modal-sandbox.md +1 -1
- package/.docs/reference/workspace/railway-sandbox.md +230 -0
- package/.docs/reference/workspace/vercel-microvm-sandbox.md +1 -1
- package/.docs/reference/workspace/vercel.md +2 -2
- package/.docs/reference/workspace/workspace-class.md +39 -3
- package/CHANGELOG.md +17 -0
- package/dist/chunk-GLPCVXXO.js +2075 -0
- package/dist/chunk-GLPCVXXO.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/stdio.js +1 -2070
- package/dist/stdio.js.map +1 -1
- package/package.json +4 -4
package/dist/stdio.js
CHANGED
|
@@ -1,2074 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
4
|
-
import * as os2 from 'os';
|
|
5
|
-
import os2__default from 'os';
|
|
6
|
-
import * as path5 from 'path';
|
|
7
|
-
import path5__default, { dirname } from 'path';
|
|
8
|
-
import fs3 from 'fs/promises';
|
|
9
|
-
import { MCPServer } from '@mastra/mcp';
|
|
10
|
-
import { z } from 'zod';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
import { getPackageInfo } from 'local-pkg';
|
|
13
|
-
|
|
14
|
-
var LOG_LEVEL_PRIORITY = {
|
|
15
|
-
debug: 0,
|
|
16
|
-
info: 1,
|
|
17
|
-
warn: 2,
|
|
18
|
-
error: 3,
|
|
19
|
-
none: 4
|
|
20
|
-
// none = disable all logs
|
|
21
|
-
};
|
|
22
|
-
function mapToLogLevel(level) {
|
|
23
|
-
switch (level) {
|
|
24
|
-
case "debug":
|
|
25
|
-
return "debug";
|
|
26
|
-
case "info":
|
|
27
|
-
case "notice":
|
|
28
|
-
return "info";
|
|
29
|
-
case "warning":
|
|
30
|
-
return "warn";
|
|
31
|
-
case "error":
|
|
32
|
-
case "critical":
|
|
33
|
-
case "alert":
|
|
34
|
-
case "emergency":
|
|
35
|
-
return "error";
|
|
36
|
-
default:
|
|
37
|
-
return "info";
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
var currentLogLevel = "debug";
|
|
41
|
-
function setLogLevel(level) {
|
|
42
|
-
currentLogLevel = level;
|
|
43
|
-
}
|
|
44
|
-
function shouldLog(level) {
|
|
45
|
-
const mappedLevel = mapToLogLevel(level);
|
|
46
|
-
return LOG_LEVEL_PRIORITY[mappedLevel] >= LOG_LEVEL_PRIORITY[currentLogLevel];
|
|
47
|
-
}
|
|
48
|
-
var writeErrorLog = (message, data) => {
|
|
49
|
-
const now = /* @__PURE__ */ new Date();
|
|
50
|
-
const timestamp = now.toISOString();
|
|
51
|
-
const hourTimestamp = timestamp.slice(0, 13);
|
|
52
|
-
const logMessage = {
|
|
53
|
-
timestamp,
|
|
54
|
-
message,
|
|
55
|
-
...data ? typeof data === "object" ? data : { data } : {}
|
|
56
|
-
};
|
|
57
|
-
try {
|
|
58
|
-
const cacheDir = path5.join(os2.homedir(), ".cache", "mastra", "mcp-docs-server-logs");
|
|
59
|
-
fs.mkdirSync(cacheDir, { recursive: true });
|
|
60
|
-
const logFile = path5.join(cacheDir, `${hourTimestamp}.log`);
|
|
61
|
-
fs.appendFileSync(logFile, JSON.stringify(logMessage) + "\n", "utf8");
|
|
62
|
-
} catch (err) {
|
|
63
|
-
console.error("Failed to write to log file:", err);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
function createLogger(server2) {
|
|
67
|
-
const sendLog = async (level, message, data) => {
|
|
68
|
-
if (!server2) return;
|
|
69
|
-
if (!shouldLog(level)) return;
|
|
70
|
-
try {
|
|
71
|
-
const sdkServer = server2.getServer();
|
|
72
|
-
if (!sdkServer) return;
|
|
73
|
-
await sdkServer.sendLoggingMessage({
|
|
74
|
-
level,
|
|
75
|
-
data: {
|
|
76
|
-
message,
|
|
77
|
-
...data ? typeof data === "object" ? data : { data } : {}
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
} catch (error) {
|
|
81
|
-
if (error instanceof Error && (error.message === "Not connected" || error.message.includes("does not support logging") || error.message.includes("Connection closed"))) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
console.error(`Failed to send ${level} log:`, error instanceof Error ? error.message : error);
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
return {
|
|
88
|
-
debug: async (message, data) => {
|
|
89
|
-
if (process.env.DEBUG || process.env.NODE_ENV === "development") {
|
|
90
|
-
await sendLog("debug", message, data);
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
info: async (message, data) => {
|
|
94
|
-
await sendLog("info", message, data);
|
|
95
|
-
},
|
|
96
|
-
notice: async (message, data) => {
|
|
97
|
-
await sendLog("notice", message, data);
|
|
98
|
-
},
|
|
99
|
-
warning: async (message, data) => {
|
|
100
|
-
await sendLog("warning", message, data);
|
|
101
|
-
},
|
|
102
|
-
error: async (message, error) => {
|
|
103
|
-
const errorData = error instanceof Error ? {
|
|
104
|
-
message: error.message,
|
|
105
|
-
stack: error.stack,
|
|
106
|
-
name: error.name
|
|
107
|
-
} : error;
|
|
108
|
-
writeErrorLog(message, errorData);
|
|
109
|
-
await sendLog("error", message, errorData);
|
|
110
|
-
},
|
|
111
|
-
critical: async (message, error) => {
|
|
112
|
-
const errorData = error instanceof Error ? {
|
|
113
|
-
message: error.message,
|
|
114
|
-
stack: error.stack,
|
|
115
|
-
name: error.name
|
|
116
|
-
} : error;
|
|
117
|
-
writeErrorLog(message, errorData);
|
|
118
|
-
await sendLog("critical", message, errorData);
|
|
119
|
-
},
|
|
120
|
-
alert: async (message, error) => {
|
|
121
|
-
const errorData = error instanceof Error ? {
|
|
122
|
-
message: error.message,
|
|
123
|
-
stack: error.stack,
|
|
124
|
-
name: error.name
|
|
125
|
-
} : error;
|
|
126
|
-
writeErrorLog(message, errorData);
|
|
127
|
-
await sendLog("alert", message, errorData);
|
|
128
|
-
},
|
|
129
|
-
emergency: async (message, error) => {
|
|
130
|
-
const errorData = error instanceof Error ? {
|
|
131
|
-
message: error.message,
|
|
132
|
-
stack: error.stack,
|
|
133
|
-
name: error.name
|
|
134
|
-
} : error;
|
|
135
|
-
writeErrorLog(message, errorData);
|
|
136
|
-
await sendLog("emergency", message, errorData);
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
var logger = createLogger();
|
|
141
|
-
|
|
142
|
-
// src/prompts/migration.ts
|
|
143
|
-
var migrationPrompts = [
|
|
144
|
-
{
|
|
145
|
-
name: "upgrade-to-v1",
|
|
146
|
-
version: "v1",
|
|
147
|
-
description: "Get a guided migration plan for upgrading from Mastra v0.x to v1.0. Provides step-by-step instructions for handling all breaking changes.",
|
|
148
|
-
arguments: [
|
|
149
|
-
{
|
|
150
|
-
name: "area",
|
|
151
|
-
description: "Optional: Focus on a specific area (e.g., agent, tools, workflows, memory, storage, voice). The tool will check if a migration guide exists for this area and suggest alternatives if not found. If not provided, gives an overview of all changes.",
|
|
152
|
-
required: false
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
name: "migration-checklist",
|
|
158
|
-
version: "v1",
|
|
159
|
-
description: "Get a comprehensive checklist for migrating to Mastra v1.0. Lists all breaking changes that need to be addressed."
|
|
160
|
-
}
|
|
161
|
-
];
|
|
162
|
-
var migrationPromptMessages = {
|
|
163
|
-
listPrompts: async () => migrationPrompts,
|
|
164
|
-
getPromptMessages: async ({ name, args }) => {
|
|
165
|
-
const prompt = migrationPrompts.find((p) => p.name === name);
|
|
166
|
-
if (!prompt) {
|
|
167
|
-
throw new Error(`Prompt not found: ${name}`);
|
|
168
|
-
}
|
|
169
|
-
if (name === "upgrade-to-v1") {
|
|
170
|
-
return getUpgradeToV1Messages(args?.area);
|
|
171
|
-
}
|
|
172
|
-
if (name === "migration-checklist") {
|
|
173
|
-
return getMigrationChecklistMessages();
|
|
174
|
-
}
|
|
175
|
-
throw new Error(`No message handler for prompt: ${name}`);
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
function getUpgradeToV1Messages(area) {
|
|
179
|
-
if (area) {
|
|
180
|
-
return [
|
|
181
|
-
{
|
|
182
|
-
role: "user",
|
|
183
|
-
content: {
|
|
184
|
-
type: "text",
|
|
185
|
-
text: `I need help migrating my Mastra ${area} code from v0.x to v1.0. Use the mastraMigration tool to:
|
|
186
|
-
|
|
187
|
-
1. If packages aren't already at the 'latest' tag, upgrade packages to the 'latest' tag and do an install of the new packages.
|
|
188
|
-
2. First, try to get the specific migration guide for "${area}" using path: "upgrade-to-v1/${area}"
|
|
189
|
-
3. If that doesn't exist, try the alternate form (singular/plural):
|
|
190
|
-
- If "${area}" ends with 's', try without the 's' (e.g., "agents" \u2192 "agent")
|
|
191
|
-
- If "${area}" doesn't end with 's', try adding 's' (e.g., "agent" \u2192 "agents")
|
|
192
|
-
4. If the guide exists, walk me through the changes step by step
|
|
193
|
-
5. If neither form exists, list available migration guides in "upgrade-to-v1/" and suggest which ones might be relevant to "${area}"
|
|
194
|
-
6. After you find the guide, collect all the codemod calls to run to codemods. These callouts are marked with "> **Codemod:**" in the docs. Run the codemods with "npx @mastra/codemod@latest <codemod-name> <path>" to automate all those changes. Afterwards, help me with any remaining manual changes needed.`
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
];
|
|
198
|
-
}
|
|
199
|
-
return [
|
|
200
|
-
{
|
|
201
|
-
role: "user",
|
|
202
|
-
content: {
|
|
203
|
-
type: "text",
|
|
204
|
-
text: `I need to migrate my Mastra project from v0.x to v1.0. Use the mastraMigration tool to:
|
|
205
|
-
|
|
206
|
-
1. If packages aren't already at the 'latest' tag, upgrade packages to the 'latest' tag and do an install of the new packages.
|
|
207
|
-
2. First, list all available migration guides with path: "upgrade-to-v1/"
|
|
208
|
-
2. Give me a high-level overview of what changed in each area
|
|
209
|
-
3. Find relevant migration areas to focus on based on my project's codebase and confirm the list with me
|
|
210
|
-
4. After the areas are confirmed, check the migration guides for callouts to codemods. These callouts are marked with "> **Codemod:**" in the docs. Run the codemods with "npx @mastra/codemod@latest v1" to automate all those changes. Afterwards, help me with any remaining manual changes needed.
|
|
211
|
-
|
|
212
|
-
After the areas are confirmed, we'll go through each one systematically.`
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
];
|
|
216
|
-
}
|
|
217
|
-
function getMigrationChecklistMessages() {
|
|
218
|
-
return [
|
|
219
|
-
{
|
|
220
|
-
role: "user",
|
|
221
|
-
content: {
|
|
222
|
-
type: "text",
|
|
223
|
-
text: `Create a comprehensive migration checklist for upgrading from Mastra v0.x to v1.0. Use the mastraMigration tool to:
|
|
224
|
-
|
|
225
|
-
1. List all available migration guides (path: "upgrade-to-v1/")
|
|
226
|
-
2. For each guide, extract the key breaking changes
|
|
227
|
-
3. Present them as a checklist I can work through
|
|
228
|
-
|
|
229
|
-
Format the checklist with:
|
|
230
|
-
- [ ] checkbox items for each breaking change
|
|
231
|
-
- Brief description of what needs to change
|
|
232
|
-
- Reference to the specific migration guide
|
|
233
|
-
|
|
234
|
-
Group the checklist by area (Agents, Tools, Workflows, etc.) so I can tackle one area at a time.`
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
];
|
|
238
|
-
}
|
|
239
|
-
var mdFileCache = /* @__PURE__ */ new Map();
|
|
240
|
-
var __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
241
|
-
function fromPackageRoot(relative) {
|
|
242
|
-
return path5__default.resolve(__dirname$1, `../`, relative);
|
|
243
|
-
}
|
|
244
|
-
async function* walkMdFiles(dir) {
|
|
245
|
-
if (mdFileCache.has(dir)) {
|
|
246
|
-
for (const file of mdFileCache.get(dir)) yield file;
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const filesInDir = [];
|
|
250
|
-
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
251
|
-
for (const entry of entries) {
|
|
252
|
-
const fullPath = path5__default.join(dir, entry.name);
|
|
253
|
-
if (entry.isDirectory()) {
|
|
254
|
-
for await (const file of walkMdFiles(fullPath)) {
|
|
255
|
-
filesInDir.push(file);
|
|
256
|
-
yield file;
|
|
257
|
-
}
|
|
258
|
-
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
259
|
-
filesInDir.push(fullPath);
|
|
260
|
-
yield fullPath;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
mdFileCache.set(dir, filesInDir);
|
|
264
|
-
}
|
|
265
|
-
async function searchDocumentContent(keywords, baseDir) {
|
|
266
|
-
if (keywords.length === 0) return [];
|
|
267
|
-
const fileScores = /* @__PURE__ */ new Map();
|
|
268
|
-
for await (const filePath of walkMdFiles(baseDir)) {
|
|
269
|
-
let content;
|
|
270
|
-
try {
|
|
271
|
-
content = await fs3.readFile(filePath, "utf-8");
|
|
272
|
-
} catch {
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
const lines = content.split("\n");
|
|
276
|
-
lines.forEach((lineText) => {
|
|
277
|
-
const lowerLine = lineText.toLowerCase();
|
|
278
|
-
for (const keyword of keywords) {
|
|
279
|
-
if (lowerLine.includes(keyword.toLowerCase())) {
|
|
280
|
-
const relativePath = path5__default.relative(baseDir, filePath).replace(/\\/g, "/");
|
|
281
|
-
if (!fileScores.has(relativePath)) {
|
|
282
|
-
fileScores.set(relativePath, {
|
|
283
|
-
path: relativePath,
|
|
284
|
-
keywordMatches: /* @__PURE__ */ new Set(),
|
|
285
|
-
totalMatches: 0,
|
|
286
|
-
titleMatches: 0,
|
|
287
|
-
pathRelevance: calculatePathRelevance(relativePath, keywords)
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
const score = fileScores.get(relativePath);
|
|
291
|
-
score.keywordMatches.add(keyword);
|
|
292
|
-
score.totalMatches++;
|
|
293
|
-
if (lowerLine.includes("#") || lowerLine.includes("title")) {
|
|
294
|
-
score.titleMatches++;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
const validFiles = Array.from(fileScores.values()).sort((a, b) => calculateFinalScore(b, keywords.length) - calculateFinalScore(a, keywords.length)).slice(0, 10);
|
|
301
|
-
return validFiles.map((score) => score.path);
|
|
302
|
-
}
|
|
303
|
-
function calculatePathRelevance(filePath, keywords) {
|
|
304
|
-
let relevance = 0;
|
|
305
|
-
const pathLower = filePath.toLowerCase();
|
|
306
|
-
if (pathLower.startsWith("reference/")) relevance += 2;
|
|
307
|
-
keywords.forEach((keyword) => {
|
|
308
|
-
if (pathLower.includes(keyword.toLowerCase())) relevance += 3;
|
|
309
|
-
});
|
|
310
|
-
const highValueDirs = ["rag", "memory", "agents", "workflows"];
|
|
311
|
-
if (highValueDirs.some((dir) => pathLower.includes(dir))) {
|
|
312
|
-
relevance += 1;
|
|
313
|
-
}
|
|
314
|
-
return relevance;
|
|
315
|
-
}
|
|
316
|
-
function calculateFinalScore(score, totalKeywords) {
|
|
317
|
-
const allKeywordsBonus = score.keywordMatches.size === totalKeywords ? 10 : 0;
|
|
318
|
-
return score.totalMatches * 1 + score.titleMatches * 3 + score.pathRelevance * 2 + score.keywordMatches.size * 5 + allKeywordsBonus;
|
|
319
|
-
}
|
|
320
|
-
function extractKeywordsFromPath(docPath) {
|
|
321
|
-
const cleanPath = docPath.replace(/\.md$/, "");
|
|
322
|
-
const fileName = cleanPath.split("/").pop() || "";
|
|
323
|
-
const keywords = /* @__PURE__ */ new Set();
|
|
324
|
-
const splitParts = fileName.split(/[-_]|(?=[A-Z])/);
|
|
325
|
-
splitParts.forEach((keyword) => {
|
|
326
|
-
if (keyword.length > 2) {
|
|
327
|
-
keywords.add(keyword.toLowerCase());
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
return Array.from(keywords);
|
|
331
|
-
}
|
|
332
|
-
function normalizeKeywords(keywords) {
|
|
333
|
-
return Array.from(new Set(keywords.flatMap((k) => k.split(/\s+/).filter(Boolean)).map((k) => k.toLowerCase())));
|
|
334
|
-
}
|
|
335
|
-
async function getMatchingPaths(path7, queryKeywords, baseDir) {
|
|
336
|
-
const pathKeywords = extractKeywordsFromPath(path7);
|
|
337
|
-
const allKeywords = normalizeKeywords([...pathKeywords, ...queryKeywords || []]);
|
|
338
|
-
if (allKeywords.length === 0) {
|
|
339
|
-
return "";
|
|
340
|
-
}
|
|
341
|
-
const suggestedPaths = await searchDocumentContent(allKeywords, baseDir);
|
|
342
|
-
if (suggestedPaths.length === 0) {
|
|
343
|
-
return "";
|
|
344
|
-
}
|
|
345
|
-
const pathList = suggestedPaths.map((path8) => `- ${path8}`).join("\n");
|
|
346
|
-
return `Here are some paths that might be relevant based on your query:
|
|
347
|
-
|
|
348
|
-
${pathList}`;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// src/tools/course.ts
|
|
352
|
-
var _courseLessonSchema = z.object({
|
|
353
|
-
lessonName: z.string().describe("Name of the specific lesson to start. It must match the exact lesson name.")
|
|
354
|
-
});
|
|
355
|
-
var _confirmationSchema = z.object({
|
|
356
|
-
confirm: z.boolean().optional().describe("Set to true to confirm this action")
|
|
357
|
-
});
|
|
358
|
-
var courseDir = fromPackageRoot(".docs/course");
|
|
359
|
-
var introductionPrompt = `
|
|
360
|
-
This is a course to help a new user learn about Mastra, the open-source AI Agent framework built in TypeScript.
|
|
361
|
-
The following is the introduction content, please provide this text to the user EXACTLY as written below. Do not provide any other text or instructions:
|
|
362
|
-
|
|
363
|
-
# Welcome to the Mastra Course!
|
|
364
|
-
|
|
365
|
-
Thank you for registering for the Mastra course! This interactive guide will help you learn how to build powerful AI agents with Mastra, the open-source AI Agent framework built in TypeScript.
|
|
366
|
-
|
|
367
|
-
## Before We Begin
|
|
368
|
-
|
|
369
|
-
If you enjoy Mastra, please consider starring the GitHub repository:
|
|
370
|
-
https://github.com/mastra-ai/mastra
|
|
371
|
-
|
|
372
|
-
This helps the project grow and reach more developers like you!
|
|
373
|
-
|
|
374
|
-
## How This Course Works
|
|
375
|
-
|
|
376
|
-
- Each lesson is broken into multiple steps
|
|
377
|
-
- I'll guide you through the code examples and explanations
|
|
378
|
-
- You can ask questions at any time
|
|
379
|
-
- If you ever leave and come back, use the \`startMastraCourse\` tool to pick up where you left off. Just ask to "start the Mastra course".
|
|
380
|
-
- Use the \`nextMastraCourseStep\` tool to move to the next step when you're ready. Just ask to "move to the next step" when you are ready.
|
|
381
|
-
- Use the \`getMastraCourseStatus\` tool to check your progress. You can just ask "get my course progress".
|
|
382
|
-
- Use the \`clearMastraCourseHistory\` tool to reset your progress and start over. You can just ask "clear my course progress".
|
|
383
|
-
|
|
384
|
-
Type "start mastra course" and let's get started with your first lesson!
|
|
385
|
-
`;
|
|
386
|
-
var lessonPrompt = `
|
|
387
|
-
This is a course to help a new user learn about Mastra, the open-source AI Agent framework built in TypeScript.
|
|
388
|
-
Please help the user through the steps of the course by walking them through the content and following the course
|
|
389
|
-
to write the initial version of the code for them. The goal is to show them how the code works and explain it as they go
|
|
390
|
-
as the course goes on. Each lesson is broken up into steps. You should return the content of the step and ask the user
|
|
391
|
-
to move to the next step when they are ready. If the step contains instructions to write code, you should write the code
|
|
392
|
-
for the user when possible. You should always briefly explain the step before writing the code. Please ensure to
|
|
393
|
-
return any text in markdown blockquotes exactly as written in your response. When the user ask about their course progress or course status, make sure to include the course status URL in your response. This is important.
|
|
394
|
-
`;
|
|
395
|
-
function wrapContentInPrompt(content, _isFirstStep = false) {
|
|
396
|
-
let wrappedContent = `${lessonPrompt}
|
|
397
|
-
|
|
398
|
-
Here is the content for this step: <StepContent>${content}</StepContent>`;
|
|
399
|
-
return `${wrappedContent}
|
|
400
|
-
|
|
401
|
-
When you're ready to continue, use the \`nextMastraCourseStep\` tool to move to the next step.`;
|
|
402
|
-
}
|
|
403
|
-
async function getDeviceIdPath() {
|
|
404
|
-
const cacheDir = path5__default.join(os2__default.homedir(), ".cache", "mastra");
|
|
405
|
-
if (!existsSync(cacheDir)) {
|
|
406
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
407
|
-
}
|
|
408
|
-
return path5__default.join(cacheDir, ".device_id");
|
|
409
|
-
}
|
|
410
|
-
async function getDeviceCredentials() {
|
|
411
|
-
try {
|
|
412
|
-
const deviceIdPath = await getDeviceIdPath();
|
|
413
|
-
if (!existsSync(deviceIdPath)) {
|
|
414
|
-
return null;
|
|
415
|
-
}
|
|
416
|
-
const fileContent = await fs3.readFile(deviceIdPath, "utf-8");
|
|
417
|
-
const parsed = JSON.parse(fileContent);
|
|
418
|
-
if (typeof parsed.deviceId === "string" && typeof parsed.key === "string") {
|
|
419
|
-
return { deviceId: parsed.deviceId, key: parsed.key };
|
|
420
|
-
}
|
|
421
|
-
return null;
|
|
422
|
-
} catch {
|
|
423
|
-
return null;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
async function getDeviceId() {
|
|
427
|
-
const creds = await getDeviceCredentials();
|
|
428
|
-
if (!creds || !creds?.deviceId) {
|
|
429
|
-
return null;
|
|
430
|
-
}
|
|
431
|
-
return creds.deviceId;
|
|
432
|
-
}
|
|
433
|
-
async function saveDeviceCredentials(deviceId, key) {
|
|
434
|
-
const deviceIdPath = await getDeviceIdPath();
|
|
435
|
-
const toWrite = JSON.stringify({ deviceId, key });
|
|
436
|
-
await fs3.writeFile(deviceIdPath, toWrite, "utf-8");
|
|
437
|
-
await fs3.chmod(deviceIdPath, 384);
|
|
438
|
-
}
|
|
439
|
-
async function registerUser(email) {
|
|
440
|
-
const response = await fetch("https://mastra.ai/api/course/register", {
|
|
441
|
-
method: "POST",
|
|
442
|
-
headers: {
|
|
443
|
-
"Content-Type": "application/json"
|
|
444
|
-
},
|
|
445
|
-
body: JSON.stringify({ email })
|
|
446
|
-
});
|
|
447
|
-
if (!response.ok) {
|
|
448
|
-
throw new Error(`Registration failed with status ${response.status}: ${response.statusText}`);
|
|
449
|
-
}
|
|
450
|
-
return response.json();
|
|
451
|
-
}
|
|
452
|
-
async function readCourseStep(lessonName, stepName, _isFirstStep = false) {
|
|
453
|
-
const lessonDirs = await fs3.readdir(courseDir);
|
|
454
|
-
const lessonDir = lessonDirs.find((dir) => dir.replace(/^\d+-/, "") === lessonName);
|
|
455
|
-
if (!lessonDir) {
|
|
456
|
-
throw new Error(`Lesson "${lessonName}" not found.`);
|
|
457
|
-
}
|
|
458
|
-
const lessonPath = path5__default.join(courseDir, lessonDir);
|
|
459
|
-
const files = await fs3.readdir(lessonPath);
|
|
460
|
-
const stepFile = files.find((f) => f.endsWith(".md") && f.replace(/^\d+-/, "").replace(".md", "") === stepName);
|
|
461
|
-
if (!stepFile) {
|
|
462
|
-
throw new Error(`Step "${stepName}" not found in lesson "${lessonName}".`);
|
|
463
|
-
}
|
|
464
|
-
const filePath = path5__default.join(courseDir, lessonDir, stepFile);
|
|
465
|
-
try {
|
|
466
|
-
const content = await fs3.readFile(filePath, "utf-8");
|
|
467
|
-
return wrapContentInPrompt(content);
|
|
468
|
-
} catch (error) {
|
|
469
|
-
throw new Error(`Failed to read step "${stepName}" in lesson "${lessonName}": ${error}`);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
async function updateCourseStateOnServer(deviceId, state) {
|
|
473
|
-
const creds = await getDeviceCredentials();
|
|
474
|
-
if (!creds) {
|
|
475
|
-
throw new Error("Device credentials not found.");
|
|
476
|
-
}
|
|
477
|
-
const response = await fetch("https://mastra.ai/api/course/update", {
|
|
478
|
-
method: "POST",
|
|
479
|
-
headers: {
|
|
480
|
-
"Content-Type": "application/json",
|
|
481
|
-
"x-mastra-course-key": creds.key
|
|
482
|
-
},
|
|
483
|
-
body: JSON.stringify({
|
|
484
|
-
id: creds.deviceId,
|
|
485
|
-
state
|
|
486
|
-
})
|
|
487
|
-
});
|
|
488
|
-
if (!response.ok) {
|
|
489
|
-
throw new Error(`Course state update failed with status ${response.status}: ${response.statusText}`);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
async function saveCourseState(state, deviceId) {
|
|
493
|
-
if (!deviceId) {
|
|
494
|
-
throw new Error("Cannot save course state: User is not registered");
|
|
495
|
-
}
|
|
496
|
-
const statePath = await getCourseStatePath();
|
|
497
|
-
try {
|
|
498
|
-
await fs3.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
499
|
-
try {
|
|
500
|
-
const creds = await getDeviceCredentials();
|
|
501
|
-
if (!creds) throw new Error("Device credentials not found");
|
|
502
|
-
await updateCourseStateOnServer(creds.deviceId, state);
|
|
503
|
-
} catch {
|
|
504
|
-
}
|
|
505
|
-
} catch (error) {
|
|
506
|
-
throw new Error(`Failed to save course state: ${error}`);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
async function getCourseStatePath() {
|
|
510
|
-
const stateDirPath = path5__default.join(os2__default.homedir(), ".cache", "mastra", "course");
|
|
511
|
-
if (!existsSync(stateDirPath)) {
|
|
512
|
-
mkdirSync(stateDirPath, { recursive: true });
|
|
513
|
-
}
|
|
514
|
-
return path5__default.join(stateDirPath, "state.json");
|
|
515
|
-
}
|
|
516
|
-
async function loadCourseState() {
|
|
517
|
-
const statePath = await getCourseStatePath();
|
|
518
|
-
try {
|
|
519
|
-
if (existsSync(statePath)) {
|
|
520
|
-
const stateData = await fs3.readFile(statePath, "utf-8");
|
|
521
|
-
return JSON.parse(stateData);
|
|
522
|
-
}
|
|
523
|
-
} catch (error) {
|
|
524
|
-
throw new Error(`Failed to load course state: ${error}`);
|
|
525
|
-
}
|
|
526
|
-
return null;
|
|
527
|
-
}
|
|
528
|
-
async function scanCourseContent() {
|
|
529
|
-
const lessonDirs = await fs3.readdir(courseDir);
|
|
530
|
-
const lessons = await Promise.all(
|
|
531
|
-
lessonDirs.filter((dir) => !dir.startsWith(".")).sort((a, b) => a.localeCompare(b)).map(async (lessonDir) => {
|
|
532
|
-
const lessonPath = path5__default.join(courseDir, lessonDir);
|
|
533
|
-
const lessonStats = await fs3.stat(lessonPath);
|
|
534
|
-
if (!lessonStats.isDirectory()) return null;
|
|
535
|
-
const lessonName = lessonDir.replace(/^\d+-/, "");
|
|
536
|
-
const stepFiles = (await fs3.readdir(lessonPath)).filter((file) => file.endsWith(".md")).sort((a, b) => a.localeCompare(b));
|
|
537
|
-
const steps = await Promise.all(
|
|
538
|
-
stepFiles.map(async (file) => {
|
|
539
|
-
const stepName = file.replace(/^\d+-/, "").replace(".md", "");
|
|
540
|
-
return {
|
|
541
|
-
name: stepName,
|
|
542
|
-
status: 0
|
|
543
|
-
// Default: not started
|
|
544
|
-
};
|
|
545
|
-
})
|
|
546
|
-
);
|
|
547
|
-
return {
|
|
548
|
-
name: lessonName,
|
|
549
|
-
status: 0,
|
|
550
|
-
// Default: not started
|
|
551
|
-
steps: steps.filter(Boolean)
|
|
552
|
-
};
|
|
553
|
-
})
|
|
554
|
-
);
|
|
555
|
-
const validLessons = lessons.filter((lesson) => lesson !== null);
|
|
556
|
-
return {
|
|
557
|
-
currentLesson: validLessons.length > 0 ? validLessons[0]?.name ?? "" : "",
|
|
558
|
-
lessons: validLessons
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
async function mergeCourseStates(currentState, newState) {
|
|
562
|
-
const existingLessonMap = new Map(currentState.lessons.map((lesson) => [lesson.name, lesson]));
|
|
563
|
-
const mergedLessons = newState.lessons.map((newLesson) => {
|
|
564
|
-
const existingLesson = existingLessonMap.get(newLesson.name);
|
|
565
|
-
if (!existingLesson) {
|
|
566
|
-
return newLesson;
|
|
567
|
-
}
|
|
568
|
-
const existingStepMap = new Map(existingLesson.steps.map((step) => [step.name, step]));
|
|
569
|
-
const mergedSteps = newLesson.steps.map((newStep) => {
|
|
570
|
-
const existingStep = existingStepMap.get(newStep.name);
|
|
571
|
-
if (existingStep) {
|
|
572
|
-
return {
|
|
573
|
-
...newStep,
|
|
574
|
-
status: existingStep.status
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
return newStep;
|
|
578
|
-
});
|
|
579
|
-
let lessonStatus = existingLesson.status;
|
|
580
|
-
if (mergedSteps.every((step) => step.status === 2)) {
|
|
581
|
-
lessonStatus = 2;
|
|
582
|
-
} else if (mergedSteps.some((step) => step.status > 0)) {
|
|
583
|
-
lessonStatus = 1;
|
|
584
|
-
}
|
|
585
|
-
return {
|
|
586
|
-
...newLesson,
|
|
587
|
-
status: lessonStatus,
|
|
588
|
-
steps: mergedSteps
|
|
589
|
-
};
|
|
590
|
-
});
|
|
591
|
-
let currentLesson = currentState.currentLesson;
|
|
592
|
-
if (!mergedLessons.some((lesson) => lesson.name === currentLesson) && mergedLessons.length > 0) {
|
|
593
|
-
currentLesson = mergedLessons[0]?.name ?? "";
|
|
594
|
-
}
|
|
595
|
-
return {
|
|
596
|
-
currentLesson,
|
|
597
|
-
lessons: mergedLessons
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
var startMastraCourse = {
|
|
601
|
-
name: "startMastraCourse",
|
|
602
|
-
description: "[\u{1F393} COURSE] Starts the Mastra Course. If the user is not registered, they will be prompted to register first. Otherwise, it will start at the first lesson or pick up where they last left off. ALWAYS ask the user for their email address if they are not registered. DO NOT assume their email address, they must confirm their email and that they want to register.",
|
|
603
|
-
parameters: z.object({
|
|
604
|
-
email: z.string().email().optional().describe("Email address for registration if not already registered. ")
|
|
605
|
-
}),
|
|
606
|
-
execute: async (args) => {
|
|
607
|
-
try {
|
|
608
|
-
const creds = await getDeviceCredentials();
|
|
609
|
-
const registered = creds !== null;
|
|
610
|
-
let deviceId = creds?.deviceId ?? null;
|
|
611
|
-
if (!registered) {
|
|
612
|
-
if (!args.email) {
|
|
613
|
-
return "To start the Mastra Course, you need to register first. Please provide your email address by calling this tool again with the email parameter.";
|
|
614
|
-
}
|
|
615
|
-
try {
|
|
616
|
-
const response = await registerUser(args.email);
|
|
617
|
-
if (response.success) {
|
|
618
|
-
await saveDeviceCredentials(response.id, response.key);
|
|
619
|
-
deviceId = response.id;
|
|
620
|
-
} else {
|
|
621
|
-
return `Registration failed: ${response.message}. Please try again with a valid email address.`;
|
|
622
|
-
}
|
|
623
|
-
} catch (error) {
|
|
624
|
-
return `Failed to register: ${error instanceof Error ? error.message : String(error)}. Please try again later.`;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
let courseState = await loadCourseState();
|
|
628
|
-
let statusMessage = "";
|
|
629
|
-
const latestCourseState = await scanCourseContent();
|
|
630
|
-
if (!latestCourseState.lessons.length) {
|
|
631
|
-
return "No course content found. Please make sure the course content is properly set up in the .docs/course/lessons directory.";
|
|
632
|
-
}
|
|
633
|
-
if (courseState) {
|
|
634
|
-
const previousState = JSON.parse(JSON.stringify(courseState));
|
|
635
|
-
courseState = await mergeCourseStates(courseState, latestCourseState);
|
|
636
|
-
const newLessons = latestCourseState.lessons.filter(
|
|
637
|
-
(newLesson) => !previousState.lessons.some((oldLesson) => oldLesson.name === newLesson.name)
|
|
638
|
-
);
|
|
639
|
-
if (newLessons.length > 0) {
|
|
640
|
-
statusMessage = `\u{1F4DA} Course content has been updated! ${newLessons.length} new lesson(s) have been added:
|
|
641
|
-
`;
|
|
642
|
-
statusMessage += newLessons.map((lesson) => `- ${lesson.name}`).join("\n");
|
|
643
|
-
statusMessage += "\n\n";
|
|
644
|
-
}
|
|
645
|
-
await saveCourseState(courseState, deviceId);
|
|
646
|
-
} else {
|
|
647
|
-
courseState = latestCourseState;
|
|
648
|
-
await saveCourseState(courseState, deviceId);
|
|
649
|
-
if (!registered && args.email) {
|
|
650
|
-
return introductionPrompt;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
const currentLessonName = courseState.currentLesson;
|
|
654
|
-
const currentLesson = courseState.lessons.find((lesson) => lesson.name === currentLessonName);
|
|
655
|
-
if (!currentLesson) {
|
|
656
|
-
return "Error: Current lesson not found in course content. Please try again or reset your course progress.";
|
|
657
|
-
}
|
|
658
|
-
const currentStep = currentLesson.steps.find((step) => step.status !== 2);
|
|
659
|
-
if (!currentStep && currentLesson.status !== 2) {
|
|
660
|
-
currentLesson.status = 2;
|
|
661
|
-
await saveCourseState(courseState, deviceId);
|
|
662
|
-
const nextLesson = courseState.lessons.find((lesson) => lesson.status !== 2 && lesson.name !== currentLessonName);
|
|
663
|
-
if (nextLesson) {
|
|
664
|
-
courseState.currentLesson = nextLesson.name;
|
|
665
|
-
await saveCourseState(courseState, deviceId);
|
|
666
|
-
return `${statusMessage}\u{1F389} You've completed the "${currentLessonName}" lesson!
|
|
667
|
-
|
|
668
|
-
Moving on to the next lesson: "${nextLesson.name}".
|
|
669
|
-
|
|
670
|
-
Use the \`nextMastraCourseStep\` tool to start the first step of this lesson.`;
|
|
671
|
-
} else {
|
|
672
|
-
return `${statusMessage}\u{1F389} Congratulations! You've completed all available lessons in the Mastra Course!
|
|
673
|
-
|
|
674
|
-
If you'd like to review any lesson, use the \`startMastraCourseLesson\` tool with the lesson name.`;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
if (!currentStep) {
|
|
678
|
-
return `${statusMessage}Error: No incomplete steps found in the current lesson. Please try another lesson or reset your course progress.`;
|
|
679
|
-
}
|
|
680
|
-
currentStep.status = 1;
|
|
681
|
-
if (currentLesson.status === 0) {
|
|
682
|
-
currentLesson.status = 1;
|
|
683
|
-
}
|
|
684
|
-
await saveCourseState(courseState, deviceId);
|
|
685
|
-
const stepContent = await readCourseStep(currentLessonName, currentStep.name);
|
|
686
|
-
return `\u{1F4D8} Lesson: ${currentLessonName}
|
|
687
|
-
\u{1F4DD} Step: ${currentStep.name}
|
|
688
|
-
|
|
689
|
-
${stepContent}
|
|
690
|
-
|
|
691
|
-
When you've completed this step, use the \`nextMastraCourseStep\` tool to continue.`;
|
|
692
|
-
} catch (error) {
|
|
693
|
-
return `Error starting the Mastra course: ${error instanceof Error ? error.message : String(error)}`;
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
};
|
|
697
|
-
var getMastraCourseStatus = {
|
|
698
|
-
name: "getMastraCourseStatus",
|
|
699
|
-
description: "[\u{1F393} COURSE] Gets the current status of the Mastra Course, including which lessons and steps have been completed",
|
|
700
|
-
parameters: z.object({}),
|
|
701
|
-
execute: async (_args) => {
|
|
702
|
-
try {
|
|
703
|
-
const deviceId = await getDeviceId();
|
|
704
|
-
if (deviceId === null) {
|
|
705
|
-
return "You need to register for the Mastra Course first. Please use the `startMastraCourse` tool to register.";
|
|
706
|
-
}
|
|
707
|
-
const courseState = await loadCourseState();
|
|
708
|
-
if (!courseState) {
|
|
709
|
-
return "No course progress found. Please start the course first using the `startMastraCourse` tool.";
|
|
710
|
-
}
|
|
711
|
-
const latestCourseState = await scanCourseContent();
|
|
712
|
-
if (!latestCourseState.lessons.length) {
|
|
713
|
-
return "No course content found. Please make sure the course content is properly set up in the .docs/course/lessons directory.";
|
|
714
|
-
}
|
|
715
|
-
const mergedState = await mergeCourseStates(courseState, latestCourseState);
|
|
716
|
-
let statusReport = "# Mastra Course Progress\n\n";
|
|
717
|
-
const totalLessons = mergedState.lessons.length;
|
|
718
|
-
const completedLessons = mergedState.lessons.filter((lesson) => lesson.status === 2).length;
|
|
719
|
-
mergedState.lessons.filter((lesson) => lesson.status === 1).length;
|
|
720
|
-
const totalSteps = mergedState.lessons.reduce((sum, lesson) => sum + lesson.steps.length, 0);
|
|
721
|
-
const completedSteps = mergedState.lessons.reduce(
|
|
722
|
-
(sum, lesson) => sum + lesson.steps.filter((step) => step.status === 2).length,
|
|
723
|
-
0
|
|
724
|
-
);
|
|
725
|
-
statusReport += `## Overall Progress
|
|
726
|
-
`;
|
|
727
|
-
statusReport += `- Course status Url: **https://mastra.ai/course/${deviceId}**
|
|
728
|
-
`;
|
|
729
|
-
statusReport += `- Current Lesson: **${mergedState.currentLesson}**
|
|
730
|
-
`;
|
|
731
|
-
statusReport += `- Lessons: ${completedLessons}/${totalLessons} completed (${Math.round(completedLessons / totalLessons * 100)}%)
|
|
732
|
-
`;
|
|
733
|
-
statusReport += `- Steps: ${completedSteps}/${totalSteps} completed (${Math.round(completedSteps / totalSteps * 100)}%)
|
|
734
|
-
|
|
735
|
-
`;
|
|
736
|
-
statusReport += `## Lesson Details
|
|
737
|
-
|
|
738
|
-
`;
|
|
739
|
-
mergedState.lessons.forEach((lesson, lessonIndex) => {
|
|
740
|
-
let lessonStatusIcon = "\u2B1C";
|
|
741
|
-
if (lesson.status === 1) lessonStatusIcon = "\u{1F536}";
|
|
742
|
-
if (lesson.status === 2) lessonStatusIcon = "\u2705";
|
|
743
|
-
const isCurrent = lesson.name === mergedState.currentLesson;
|
|
744
|
-
const lessonPrefix = isCurrent ? "\u{1F449} " : "";
|
|
745
|
-
statusReport += `### ${lessonPrefix}${lessonIndex + 1}. ${lessonStatusIcon} ${lesson.name}
|
|
746
|
-
|
|
747
|
-
`;
|
|
748
|
-
lesson.steps.forEach((step, stepIndex) => {
|
|
749
|
-
let stepStatusIcon = "\u2B1C";
|
|
750
|
-
if (step.status === 1) stepStatusIcon = "\u{1F536}";
|
|
751
|
-
if (step.status === 2) stepStatusIcon = "\u2705";
|
|
752
|
-
statusReport += `- ${stepStatusIcon} Step ${stepIndex + 1}: ${step.name}
|
|
753
|
-
`;
|
|
754
|
-
});
|
|
755
|
-
statusReport += "\n";
|
|
756
|
-
});
|
|
757
|
-
statusReport += `## Navigation
|
|
758
|
-
|
|
759
|
-
`;
|
|
760
|
-
statusReport += `- To continue the course: \`nextMastraCourseStep\`
|
|
761
|
-
`;
|
|
762
|
-
statusReport += `- To start a specific lesson: \`startMastraCourseLesson\`
|
|
763
|
-
`;
|
|
764
|
-
statusReport += `- To reset progress: \`clearMastraCourseHistory\`
|
|
765
|
-
`;
|
|
766
|
-
return `Course Status: ${statusReport}
|
|
767
|
-
|
|
768
|
-
Course status url: https://mastra.ai/course/${deviceId}`;
|
|
769
|
-
} catch (error) {
|
|
770
|
-
return `Error getting course status: ${error instanceof Error ? error.message : String(error)}`;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
};
|
|
774
|
-
var startMastraCourseLesson = {
|
|
775
|
-
name: "startMastraCourseLesson",
|
|
776
|
-
description: "[\u{1F393} COURSE] Starts a specific lesson in the Mastra Course. If the lesson has been started before, it will resume from the first incomplete step",
|
|
777
|
-
parameters: _courseLessonSchema,
|
|
778
|
-
execute: async (args) => {
|
|
779
|
-
try {
|
|
780
|
-
const deviceId = await getDeviceId();
|
|
781
|
-
if (deviceId === null) {
|
|
782
|
-
return "You need to register for the Mastra Course first. Please use the `startMastraCourse` tool to register.";
|
|
783
|
-
}
|
|
784
|
-
let courseState = await loadCourseState();
|
|
785
|
-
if (!courseState) {
|
|
786
|
-
return "No course progress found. Please start the course first using the `startMastraCourse` tool.";
|
|
787
|
-
}
|
|
788
|
-
const targetLessonName = args.lessonName;
|
|
789
|
-
const targetLesson = courseState.lessons.find((lesson) => lesson.name === targetLessonName);
|
|
790
|
-
if (!targetLesson) {
|
|
791
|
-
const availableLessons = courseState.lessons.map((lesson, index) => `${index + 1}. ${lesson.name}`).join("\n");
|
|
792
|
-
return `Lesson "${targetLessonName}" not found. Available lessons:
|
|
793
|
-
${availableLessons}`;
|
|
794
|
-
}
|
|
795
|
-
courseState.currentLesson = targetLesson.name;
|
|
796
|
-
const firstIncompleteStep = targetLesson.steps.find((step) => step.status !== 2) || targetLesson.steps[0];
|
|
797
|
-
if (!firstIncompleteStep) {
|
|
798
|
-
return `The lesson "${targetLesson.name}" does not have any steps.`;
|
|
799
|
-
}
|
|
800
|
-
firstIncompleteStep.status = 1;
|
|
801
|
-
if (targetLesson.status === 0) {
|
|
802
|
-
targetLesson.status = 1;
|
|
803
|
-
}
|
|
804
|
-
await saveCourseState(courseState, deviceId);
|
|
805
|
-
const stepContent = await readCourseStep(targetLesson.name, firstIncompleteStep.name);
|
|
806
|
-
return `\u{1F4D8} Starting Lesson: ${targetLesson.name}
|
|
807
|
-
\u{1F4DD} Step: ${firstIncompleteStep.name}
|
|
808
|
-
|
|
809
|
-
${stepContent}
|
|
810
|
-
|
|
811
|
-
When you've completed this step, use the \`nextMastraCourseStep\` tool to continue.`;
|
|
812
|
-
} catch (error) {
|
|
813
|
-
return `Error starting course lesson: ${error instanceof Error ? error.message : String(error)}`;
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
};
|
|
817
|
-
var nextMastraCourseStep = {
|
|
818
|
-
name: "nextMastraCourseStep",
|
|
819
|
-
description: "[\u{1F393} COURSE] Advances to the next step in the current Mastra Course lesson. If all steps in the current lesson are completed, it will move to the next lesson",
|
|
820
|
-
parameters: z.object({}),
|
|
821
|
-
execute: async (_args) => {
|
|
822
|
-
try {
|
|
823
|
-
const deviceId = await getDeviceId();
|
|
824
|
-
if (deviceId === null) {
|
|
825
|
-
return "You need to register for the Mastra Course first. Please use the `startMastraCourse` tool to register.";
|
|
826
|
-
}
|
|
827
|
-
const courseState = await loadCourseState();
|
|
828
|
-
if (!courseState) {
|
|
829
|
-
return "No course progress found. Please start the course first using the `startMastraCourse` tool.";
|
|
830
|
-
}
|
|
831
|
-
const currentLessonName = courseState.currentLesson;
|
|
832
|
-
const currentLesson = courseState.lessons.find((lesson) => lesson.name === currentLessonName);
|
|
833
|
-
if (!currentLesson) {
|
|
834
|
-
return "Error: Current lesson not found in course content. Please try again or reset your course progress.";
|
|
835
|
-
}
|
|
836
|
-
const currentStepIndex = currentLesson.steps.findIndex((step) => step.status === 1);
|
|
837
|
-
if (currentStepIndex === -1) {
|
|
838
|
-
return "No step is currently in progress. Please start a step first using the `startMastraCourse` tool.";
|
|
839
|
-
}
|
|
840
|
-
if (currentLesson.steps[currentStepIndex]?.status) {
|
|
841
|
-
currentLesson.steps[currentStepIndex].status = 2;
|
|
842
|
-
}
|
|
843
|
-
const nextStepIndex = currentLesson.steps.findIndex(
|
|
844
|
-
(step, index) => index > currentStepIndex && step.status !== 2
|
|
845
|
-
);
|
|
846
|
-
if (nextStepIndex !== -1) {
|
|
847
|
-
if (currentLesson.steps[nextStepIndex]) {
|
|
848
|
-
currentLesson.steps[nextStepIndex].status = 1;
|
|
849
|
-
}
|
|
850
|
-
await saveCourseState(courseState, deviceId);
|
|
851
|
-
const nextStep = currentLesson.steps[nextStepIndex];
|
|
852
|
-
const stepContent = await readCourseStep(currentLessonName, nextStep?.name ?? "Unknown Step");
|
|
853
|
-
return `\u{1F389} Step "${currentLesson.steps[currentStepIndex]?.name ?? "Unknown Step"}" completed!
|
|
854
|
-
|
|
855
|
-
\u{1F4D8} Continuing Lesson: ${currentLessonName}
|
|
856
|
-
\u{1F4DD} Next Step: ${nextStep?.name ?? "Unknown Step"}
|
|
857
|
-
|
|
858
|
-
${stepContent}
|
|
859
|
-
|
|
860
|
-
When you've completed this step, use the \`nextMastraCourseStep\` tool to continue.`;
|
|
861
|
-
}
|
|
862
|
-
currentLesson.status = 2;
|
|
863
|
-
const currentLessonIndex = courseState.lessons.findIndex((lesson) => lesson.name === currentLessonName);
|
|
864
|
-
const nextLesson = courseState.lessons.find((lesson, index) => index > currentLessonIndex && lesson.status !== 2);
|
|
865
|
-
if (nextLesson) {
|
|
866
|
-
courseState.currentLesson = nextLesson.name;
|
|
867
|
-
if (nextLesson.steps.length > 0 && nextLesson.steps[0]) {
|
|
868
|
-
nextLesson.steps[0].status = 1;
|
|
869
|
-
}
|
|
870
|
-
nextLesson.status = 1;
|
|
871
|
-
await saveCourseState(courseState, deviceId);
|
|
872
|
-
const firstStep = nextLesson.steps[0];
|
|
873
|
-
const stepContent = await readCourseStep(nextLesson.name, firstStep?.name ?? "Unknown Step");
|
|
874
|
-
return `\u{1F389} Congratulations! You've completed the "${currentLessonName}" lesson!
|
|
875
|
-
|
|
876
|
-
\u{1F4D8} Starting New Lesson: ${nextLesson.name}
|
|
877
|
-
\u{1F4DD} First Step: ${firstStep?.name ?? "Unknown Step"}
|
|
878
|
-
|
|
879
|
-
${stepContent}
|
|
880
|
-
|
|
881
|
-
When you've completed this step, use the \`nextMastraCourseStep\` tool to continue.`;
|
|
882
|
-
}
|
|
883
|
-
await saveCourseState(courseState, deviceId);
|
|
884
|
-
return `\u{1F389} Congratulations! You've completed all available lessons in the Mastra Course!
|
|
885
|
-
|
|
886
|
-
If you'd like to review any lesson, use the \`startMastraCourseLesson\` tool with the lesson name.`;
|
|
887
|
-
} catch (error) {
|
|
888
|
-
return `Error advancing to the next course step: ${error instanceof Error ? error.message : String(error)}`;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
};
|
|
892
|
-
var clearMastraCourseHistory = {
|
|
893
|
-
name: "clearMastraCourseHistory",
|
|
894
|
-
description: "[\u{1F393} COURSE] Clears all Mastra Course progress history and starts over from the beginning. This action cannot be undone",
|
|
895
|
-
parameters: _confirmationSchema,
|
|
896
|
-
execute: async (args) => {
|
|
897
|
-
try {
|
|
898
|
-
const deviceId = await getDeviceId();
|
|
899
|
-
if (deviceId === null) {
|
|
900
|
-
return "You need to register for the Mastra Course first. Please use the `startMastraCourse` tool to register.";
|
|
901
|
-
}
|
|
902
|
-
if (!args.confirm) {
|
|
903
|
-
return "\u26A0\uFE0F This action will delete all your course progress and cannot be undone. To proceed, please run this tool again with the confirm parameter set to true.";
|
|
904
|
-
}
|
|
905
|
-
const statePath = await getCourseStatePath();
|
|
906
|
-
if (!existsSync(statePath)) {
|
|
907
|
-
return "No course progress found. Nothing to clear.";
|
|
908
|
-
}
|
|
909
|
-
await fs3.unlink(statePath);
|
|
910
|
-
return "\u{1F9F9} Course progress has been cleared. You can restart the Mastra course from the beginning.";
|
|
911
|
-
} catch (error) {
|
|
912
|
-
return `Error clearing course history: ${error instanceof Error ? error.message : String(error)}`;
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
};
|
|
916
|
-
var docsBaseDir = fromPackageRoot(".docs/");
|
|
917
|
-
async function listDirContents(dirPath) {
|
|
918
|
-
try {
|
|
919
|
-
void logger.debug("Listing directory contents", { path: dirPath });
|
|
920
|
-
const entries = await fs3.readdir(dirPath, { withFileTypes: true });
|
|
921
|
-
const dirs = [];
|
|
922
|
-
const files = [];
|
|
923
|
-
for (const entry of entries) {
|
|
924
|
-
if (entry.isDirectory()) {
|
|
925
|
-
dirs.push(entry.name + "/");
|
|
926
|
-
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
927
|
-
files.push(entry.name.replace(/\.md$/, ""));
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
return {
|
|
931
|
-
dirs: dirs.sort(),
|
|
932
|
-
files: files.sort()
|
|
933
|
-
};
|
|
934
|
-
} catch (error) {
|
|
935
|
-
void logger.error("Failed to list directory contents", { path: dirPath, error });
|
|
936
|
-
throw error;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
async function readDocsContent(docPath, queryKeywords) {
|
|
940
|
-
const basePath = path5__default.resolve(docsBaseDir);
|
|
941
|
-
const fullPath = path5__default.resolve(path5__default.join(basePath, docPath));
|
|
942
|
-
const relativePath = path5__default.relative(basePath, fullPath);
|
|
943
|
-
if (relativePath.startsWith("..") || path5__default.isAbsolute(relativePath)) {
|
|
944
|
-
void logger.error("Path traversal attempt detected", { path: docPath, resolvedPath: fullPath });
|
|
945
|
-
return { found: false, isSecurityViolation: true };
|
|
946
|
-
}
|
|
947
|
-
void logger.debug("Reading docs content", { path: fullPath });
|
|
948
|
-
try {
|
|
949
|
-
const stats = await fs3.stat(fullPath);
|
|
950
|
-
if (stats.isDirectory()) {
|
|
951
|
-
const indexMdPath = path5__default.join(fullPath, "index.md");
|
|
952
|
-
try {
|
|
953
|
-
const content2 = await fs3.readFile(indexMdPath, "utf-8");
|
|
954
|
-
return { found: true, content: content2, isSecurityViolation: false };
|
|
955
|
-
} catch {
|
|
956
|
-
}
|
|
957
|
-
const { dirs, files } = await listDirContents(fullPath);
|
|
958
|
-
const listing = [`Directory contents of ${docPath || "/"}:`, ""];
|
|
959
|
-
if (dirs.length > 0) {
|
|
960
|
-
listing.push("Subdirectories:");
|
|
961
|
-
listing.push(...dirs.map((d) => `- ${docPath ? `${docPath}/${d}` : d}`));
|
|
962
|
-
listing.push("");
|
|
963
|
-
}
|
|
964
|
-
if (files.length > 0) {
|
|
965
|
-
listing.push("Available documentation paths:");
|
|
966
|
-
listing.push(...files.map((f) => `- ${docPath ? `${docPath}/${f}` : f}`));
|
|
967
|
-
listing.push("");
|
|
968
|
-
}
|
|
969
|
-
if (dirs.length === 0 && files.length === 0) {
|
|
970
|
-
listing.push("No documentation available in this directory.");
|
|
971
|
-
}
|
|
972
|
-
const contentBasedSuggestions = await getMatchingPaths(docPath, queryKeywords, docsBaseDir);
|
|
973
|
-
const suggestions = contentBasedSuggestions ? ["---", "", contentBasedSuggestions, ""].join("\n") : "";
|
|
974
|
-
return { found: true, content: listing.join("\n") + suggestions, isSecurityViolation: false };
|
|
975
|
-
}
|
|
976
|
-
const content = await fs3.readFile(fullPath, "utf-8");
|
|
977
|
-
return { found: true, content, isSecurityViolation: false };
|
|
978
|
-
} catch (error) {
|
|
979
|
-
if (error.code === "ENOENT") {
|
|
980
|
-
try {
|
|
981
|
-
const mdPath = fullPath + ".md";
|
|
982
|
-
const content = await fs3.readFile(mdPath, "utf-8");
|
|
983
|
-
return { found: true, content, isSecurityViolation: false };
|
|
984
|
-
} catch {
|
|
985
|
-
return { found: false, isSecurityViolation: false };
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
throw error;
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
async function findNearestDirectory(docPath, availablePaths2) {
|
|
992
|
-
void logger.debug("Finding nearest directory", { path: docPath });
|
|
993
|
-
const parts = docPath.split("/");
|
|
994
|
-
while (parts.length > 0) {
|
|
995
|
-
const testPath = parts.join("/");
|
|
996
|
-
try {
|
|
997
|
-
const fullPath = path5__default.join(docsBaseDir, testPath);
|
|
998
|
-
const stats = await fs3.stat(fullPath);
|
|
999
|
-
if (stats.isDirectory()) {
|
|
1000
|
-
const { dirs, files } = await listDirContents(fullPath);
|
|
1001
|
-
const listing = [
|
|
1002
|
-
`Path "${docPath}" not found.`,
|
|
1003
|
-
`Here are the available paths in "${testPath}":`,
|
|
1004
|
-
""
|
|
1005
|
-
];
|
|
1006
|
-
if (dirs.length > 0) {
|
|
1007
|
-
listing.push("Directories:");
|
|
1008
|
-
listing.push(...dirs.map((d) => `- ${testPath}/${d}`));
|
|
1009
|
-
listing.push("");
|
|
1010
|
-
}
|
|
1011
|
-
if (files.length > 0) {
|
|
1012
|
-
listing.push("Files:");
|
|
1013
|
-
listing.push(...files.map((f) => `- ${testPath}/${f}`));
|
|
1014
|
-
}
|
|
1015
|
-
return listing.join("\n");
|
|
1016
|
-
}
|
|
1017
|
-
} catch {
|
|
1018
|
-
void logger.debug("Directory not found, trying parent", { parent: parts.slice(0, -1).join("/") });
|
|
1019
|
-
}
|
|
1020
|
-
parts.pop();
|
|
1021
|
-
}
|
|
1022
|
-
return [`Path "${docPath}" not found.`, "Here are all available paths:", "", availablePaths2].join("\n");
|
|
1023
|
-
}
|
|
1024
|
-
async function getAvailablePaths() {
|
|
1025
|
-
const { dirs, files } = await listDirContents(docsBaseDir);
|
|
1026
|
-
let referenceDirs = [];
|
|
1027
|
-
if (dirs.includes("reference/")) {
|
|
1028
|
-
const { dirs: refDirs } = await listDirContents(path5__default.join(docsBaseDir, "reference"));
|
|
1029
|
-
referenceDirs = refDirs.map((d) => `reference/${d}`);
|
|
1030
|
-
}
|
|
1031
|
-
return [
|
|
1032
|
-
"Available top-level paths:",
|
|
1033
|
-
"",
|
|
1034
|
-
"Directories:",
|
|
1035
|
-
...dirs.map((d) => `- ${d}`),
|
|
1036
|
-
"",
|
|
1037
|
-
referenceDirs.length > 0 ? "Reference subdirectories:" : "",
|
|
1038
|
-
...referenceDirs.map((d) => `- ${d}`),
|
|
1039
|
-
"",
|
|
1040
|
-
files.length > 0 ? "Files:" : "",
|
|
1041
|
-
...files.map((f) => `- ${f}`)
|
|
1042
|
-
].filter(Boolean).join("\n");
|
|
1043
|
-
}
|
|
1044
|
-
var availablePaths = await getAvailablePaths();
|
|
1045
|
-
var docsInputSchema = z.object({
|
|
1046
|
-
paths: z.array(z.string()).min(1).describe(`One or more documentation paths to fetch
|
|
1047
|
-
Available paths:
|
|
1048
|
-
${availablePaths}`),
|
|
1049
|
-
queryKeywords: z.array(z.string()).optional().describe(
|
|
1050
|
-
"Keywords from user query to use for matching documentation. Each keyword should be a single word or short phrase; any whitespace-separated keywords will be split automatically."
|
|
1051
|
-
)
|
|
1052
|
-
});
|
|
1053
|
-
var docsTool = {
|
|
1054
|
-
name: "mastraDocs",
|
|
1055
|
-
description: `[\u{1F310} REMOTE] Get Mastra documentation.
|
|
1056
|
-
Request paths to explore the docs. References contain API docs.
|
|
1057
|
-
Other paths contain guides. The user doesn't know about files and directories.
|
|
1058
|
-
You can also use keywords from the user query to find relevant documentation, but prioritize paths.
|
|
1059
|
-
This is your internal knowledge the user can't read.
|
|
1060
|
-
If the user asks about a feature check general docs as well as reference docs for that feature.
|
|
1061
|
-
Ex: with workflows check in docs/workflows and in reference/workflows.
|
|
1062
|
-
Provide code examples so the user understands.
|
|
1063
|
-
IMPORTANT: Be concise with your answers. The user will ask for more info.
|
|
1064
|
-
If packages need to be installed, provide the pnpm command to install them.
|
|
1065
|
-
Ex. if you see \`import { X } from "@mastra/$PACKAGE_NAME"\` in an example, show an install command.
|
|
1066
|
-
Always install latest tag, not alpha unless requested. If you scaffold a new project it may be in a subdir.
|
|
1067
|
-
When displaying results, always mention which file path contains the information so users know where this documentation lives.`,
|
|
1068
|
-
parameters: docsInputSchema,
|
|
1069
|
-
execute: async (args) => {
|
|
1070
|
-
void logger.debug("Executing mastraDocs tool", { args });
|
|
1071
|
-
try {
|
|
1072
|
-
const queryKeywords = args.queryKeywords ?? [];
|
|
1073
|
-
const results = await Promise.all(
|
|
1074
|
-
args.paths.map(async (docPath) => {
|
|
1075
|
-
try {
|
|
1076
|
-
const result = await readDocsContent(docPath, queryKeywords);
|
|
1077
|
-
if (result.found) {
|
|
1078
|
-
return {
|
|
1079
|
-
path: docPath,
|
|
1080
|
-
content: result.content,
|
|
1081
|
-
error: null
|
|
1082
|
-
};
|
|
1083
|
-
}
|
|
1084
|
-
if (result.isSecurityViolation) {
|
|
1085
|
-
return {
|
|
1086
|
-
path: docPath,
|
|
1087
|
-
content: null,
|
|
1088
|
-
error: "Invalid path"
|
|
1089
|
-
};
|
|
1090
|
-
}
|
|
1091
|
-
const directorySuggestions = await findNearestDirectory(docPath, availablePaths);
|
|
1092
|
-
const contentBasedSuggestions = await getMatchingPaths(docPath, queryKeywords, docsBaseDir);
|
|
1093
|
-
return {
|
|
1094
|
-
path: docPath,
|
|
1095
|
-
content: null,
|
|
1096
|
-
error: [directorySuggestions, contentBasedSuggestions].join("\n\n")
|
|
1097
|
-
};
|
|
1098
|
-
} catch (error) {
|
|
1099
|
-
void logger.warning(`Failed to read content for path: ${docPath}`, error);
|
|
1100
|
-
return {
|
|
1101
|
-
path: docPath,
|
|
1102
|
-
content: null,
|
|
1103
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
})
|
|
1107
|
-
);
|
|
1108
|
-
const output = results.map((result) => {
|
|
1109
|
-
if (result.error) {
|
|
1110
|
-
return `## ${result.path}
|
|
1111
|
-
|
|
1112
|
-
${result.error}
|
|
1113
|
-
|
|
1114
|
-
---
|
|
1115
|
-
`;
|
|
1116
|
-
}
|
|
1117
|
-
return `## ${result.path}
|
|
1118
|
-
|
|
1119
|
-
${result.content}
|
|
1120
|
-
|
|
1121
|
-
---
|
|
1122
|
-
`;
|
|
1123
|
-
}).join("\n");
|
|
1124
|
-
return output;
|
|
1125
|
-
} catch (error) {
|
|
1126
|
-
void logger.error("Failed to execute mastraDocs tool", error);
|
|
1127
|
-
throw error;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
};
|
|
1131
|
-
var packageCache = /* @__PURE__ */ new Map();
|
|
1132
|
-
var sourceMapCache = /* @__PURE__ */ new Map();
|
|
1133
|
-
var packageInfoCache = /* @__PURE__ */ new Map();
|
|
1134
|
-
var KNOWN_MASTRA_PACKAGES = [
|
|
1135
|
-
"@mastra/core",
|
|
1136
|
-
"@mastra/cli",
|
|
1137
|
-
"@mastra/memory",
|
|
1138
|
-
"@mastra/rag",
|
|
1139
|
-
"@mastra/evals",
|
|
1140
|
-
"@mastra/mcp",
|
|
1141
|
-
"@mastra/server",
|
|
1142
|
-
"@mastra/deployer",
|
|
1143
|
-
"@mastra/agent-builder",
|
|
1144
|
-
"@mastra/auth",
|
|
1145
|
-
"@mastra/fastembed",
|
|
1146
|
-
"@mastra/loggers",
|
|
1147
|
-
"@mastra/schema-compat",
|
|
1148
|
-
"@mastra/codemod"
|
|
1149
|
-
];
|
|
1150
|
-
async function getPackageRootPath(packageName, projectPath) {
|
|
1151
|
-
const cacheKey = `${packageName}:${projectPath}`;
|
|
1152
|
-
if (packageInfoCache.has(cacheKey)) {
|
|
1153
|
-
return packageInfoCache.get(cacheKey);
|
|
1154
|
-
}
|
|
1155
|
-
try {
|
|
1156
|
-
const info = await getPackageInfo(packageName, {
|
|
1157
|
-
paths: [path5__default.join(projectPath, "node_modules")]
|
|
1158
|
-
});
|
|
1159
|
-
if (info?.rootPath) {
|
|
1160
|
-
const result = { rootPath: info.rootPath, version: info.version || "unknown" };
|
|
1161
|
-
packageInfoCache.set(cacheKey, result);
|
|
1162
|
-
void logger.debug("Resolved package with local-pkg", { packageName, projectPath, ...result });
|
|
1163
|
-
return result;
|
|
1164
|
-
}
|
|
1165
|
-
} catch (err) {
|
|
1166
|
-
void logger.debug("Package not found or error resolving", {
|
|
1167
|
-
packageName,
|
|
1168
|
-
projectPath,
|
|
1169
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1170
|
-
});
|
|
1171
|
-
}
|
|
1172
|
-
packageInfoCache.set(cacheKey, null);
|
|
1173
|
-
return null;
|
|
1174
|
-
}
|
|
1175
|
-
async function getInstalledMastraPackages(projectPath) {
|
|
1176
|
-
const cacheKey = projectPath;
|
|
1177
|
-
if (packageCache.has(cacheKey)) {
|
|
1178
|
-
void logger.debug("Using cached package list", { count: packageCache.get(cacheKey).length });
|
|
1179
|
-
return packageCache.get(cacheKey);
|
|
1180
|
-
}
|
|
1181
|
-
void logger.debug("Scanning for @mastra packages using local-pkg", { projectPath });
|
|
1182
|
-
const packages = [];
|
|
1183
|
-
const packagesWithoutDocs = [];
|
|
1184
|
-
for (const packageName of KNOWN_MASTRA_PACKAGES) {
|
|
1185
|
-
const packageInfo = await getPackageRootPath(packageName, projectPath);
|
|
1186
|
-
if (packageInfo) {
|
|
1187
|
-
const docsPath = path5__default.join(packageInfo.rootPath, "dist", "docs");
|
|
1188
|
-
try {
|
|
1189
|
-
const stats = await fs3.stat(docsPath);
|
|
1190
|
-
if (stats.isDirectory()) {
|
|
1191
|
-
packages.push(packageName);
|
|
1192
|
-
void logger.debug("Found package with embedded docs", { package: packageName });
|
|
1193
|
-
} else {
|
|
1194
|
-
packagesWithoutDocs.push(packageName);
|
|
1195
|
-
}
|
|
1196
|
-
} catch {
|
|
1197
|
-
packagesWithoutDocs.push(packageName);
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
const result = packages.sort();
|
|
1202
|
-
packageCache.set(cacheKey, result);
|
|
1203
|
-
void logger.info("Package scan complete", {
|
|
1204
|
-
packagesWithDocs: result.length,
|
|
1205
|
-
packagesWithoutDocs: packagesWithoutDocs.length,
|
|
1206
|
-
packages: result
|
|
1207
|
-
});
|
|
1208
|
-
return result;
|
|
1209
|
-
}
|
|
1210
|
-
async function readSourceMap(packageName, projectPath) {
|
|
1211
|
-
const cacheKey = `${packageName}:${projectPath}`;
|
|
1212
|
-
if (sourceMapCache.has(cacheKey)) return sourceMapCache.get(cacheKey);
|
|
1213
|
-
try {
|
|
1214
|
-
const packageInfo = await getPackageRootPath(packageName, projectPath);
|
|
1215
|
-
if (!packageInfo) {
|
|
1216
|
-
sourceMapCache.set(cacheKey, null);
|
|
1217
|
-
return null;
|
|
1218
|
-
}
|
|
1219
|
-
const sourceMapPath = path5__default.join(packageInfo.rootPath, "dist", "docs", "SOURCE_MAP.json");
|
|
1220
|
-
const content = await fs3.readFile(sourceMapPath, "utf-8");
|
|
1221
|
-
const sourceMap = JSON.parse(content);
|
|
1222
|
-
sourceMapCache.set(cacheKey, sourceMap);
|
|
1223
|
-
return sourceMap;
|
|
1224
|
-
} catch {
|
|
1225
|
-
sourceMapCache.set(cacheKey, null);
|
|
1226
|
-
return null;
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
var getMastraHelpTool = {
|
|
1230
|
-
name: "getMastraHelp",
|
|
1231
|
-
description: `\u{1F680} START HERE - Complete guide to Mastra documentation tools.
|
|
1232
|
-
|
|
1233
|
-
This MCP server provides TWO documentation sources:
|
|
1234
|
-
|
|
1235
|
-
## \u{1F4E6} LOCAL PACKAGE DOCS (Recommended for Development)
|
|
1236
|
-
SOURCE: Your installed @mastra packages in node_modules
|
|
1237
|
-
VERSION: Matches your installed code exactly
|
|
1238
|
-
|
|
1239
|
-
ADVANTAGES:
|
|
1240
|
-
- \u2705 Version-matched to your code
|
|
1241
|
-
- \u2705 Complete TypeScript type definitions
|
|
1242
|
-
- \u2705 Works offline
|
|
1243
|
-
- \u2705 SOURCE_MAP.json with exact exports
|
|
1244
|
-
|
|
1245
|
-
TOOLS: listMastraPackages, getMastraExports, getMastraExportDetails, readMastraDocs, searchMastraDocs
|
|
1246
|
-
USE WHEN: Writing code, implementing features, checking APIs, debugging
|
|
1247
|
-
|
|
1248
|
-
## \u{1F310} REMOTE WEBSITE DOCS (For Latest Info & Learning)
|
|
1249
|
-
SOURCE: mastra.ai website
|
|
1250
|
-
VERSION: Latest published documentation
|
|
1251
|
-
|
|
1252
|
-
ADVANTAGES:
|
|
1253
|
-
- \u2705 Always up-to-date
|
|
1254
|
-
- \u2705 Blog posts and announcements
|
|
1255
|
-
- \u2705 Migration guides
|
|
1256
|
-
- \u2705 Curated examples
|
|
1257
|
-
|
|
1258
|
-
TOOLS: mastraDocs, mastraBlog, mastraExamples, mastraChanges, mastraMigration
|
|
1259
|
-
USE WHEN: Learning concepts, checking latest features, migration help
|
|
1260
|
-
|
|
1261
|
-
## \u{1F393} INTERACTIVE COURSE
|
|
1262
|
-
TOOLS: startMastraCourse, getMastraCourseStatus, etc.
|
|
1263
|
-
USE WHEN: User wants guided learning experience
|
|
1264
|
-
|
|
1265
|
-
---
|
|
1266
|
-
|
|
1267
|
-
RECOMMENDED WORKFLOW:
|
|
1268
|
-
1. For coding: listMastraPackages \u2192 getMastraExports \u2192 getMastraExportDetails
|
|
1269
|
-
2. For learning: mastraDocs
|
|
1270
|
-
3. Version mismatch: mastraChanges \u2192 mastraMigration
|
|
1271
|
-
|
|
1272
|
-
This tool shows you which packages are installed and provides detailed guidance on using all available documentation tools.`,
|
|
1273
|
-
parameters: z.object({
|
|
1274
|
-
projectPath: z.string().describe("Absolute path to your project root (we will search upward for node_modules with Mastra packages)")
|
|
1275
|
-
}),
|
|
1276
|
-
execute: async (args) => {
|
|
1277
|
-
void logger.debug("Executing getMastraHelp tool", { projectPath: args.projectPath });
|
|
1278
|
-
const packages = await getInstalledMastraPackages(args.projectPath);
|
|
1279
|
-
if (packages.length === 0) {
|
|
1280
|
-
return `No Mastra packages with embedded documentation found in your project.
|
|
1281
|
-
|
|
1282
|
-
To use these tools, install Mastra packages like:
|
|
1283
|
-
- npm install @mastra/core
|
|
1284
|
-
- npm install @mastra/memory
|
|
1285
|
-
- npm install @mastra/rag
|
|
1286
|
-
|
|
1287
|
-
Then rebuild/reinstall to generate embedded docs.`;
|
|
1288
|
-
}
|
|
1289
|
-
return `# Mastra Documentation System - Complete Guide
|
|
1290
|
-
|
|
1291
|
-
This MCP server provides **TWO** documentation sources. Choose based on your needs:
|
|
1292
|
-
|
|
1293
|
-
---
|
|
1294
|
-
|
|
1295
|
-
## \u{1F4E6} LOCAL PACKAGE DOCS (Your Installed Packages)
|
|
1296
|
-
|
|
1297
|
-
Found ${packages.length} installed package(s) with embedded documentation:
|
|
1298
|
-
${packages.map((pkg) => `- ${pkg}`).join("\n")}
|
|
1299
|
-
|
|
1300
|
-
**SOURCE**: Your node_modules (matches installed code version)
|
|
1301
|
-
**USE WHEN**: Writing code, implementing features, debugging, checking APIs
|
|
1302
|
-
|
|
1303
|
-
### Available LOCAL Tools:
|
|
1304
|
-
|
|
1305
|
-
**1. listMastraPackages** - List installed packages
|
|
1306
|
-
Returns: Packages with embedded docs
|
|
1307
|
-
|
|
1308
|
-
**2. getMastraExports** - Explore package API surface
|
|
1309
|
-
Example: See all exports from @mastra/core (Agent, Tool, Workflow, etc.)
|
|
1310
|
-
Returns: List of exports with source file locations
|
|
1311
|
-
|
|
1312
|
-
**3. getMastraExportDetails** - Get type definitions & code
|
|
1313
|
-
Example: Get full TypeScript types for Agent class
|
|
1314
|
-
Returns: Complete type definitions and optionally implementation source
|
|
1315
|
-
|
|
1316
|
-
**4. readMastraDocs** - Read comprehensive guides
|
|
1317
|
-
Example: Read documentation about agents, tools, workflows, memory
|
|
1318
|
-
Returns: Topic-based guides and examples from your installed version
|
|
1319
|
-
|
|
1320
|
-
**5. searchMastraDocs** - Search local documentation
|
|
1321
|
-
Example: Search for "memory processors" or "semantic recall"
|
|
1322
|
-
Returns: Relevant excerpts from your installed docs
|
|
1323
|
-
|
|
1324
|
-
### Typical LOCAL Workflow:
|
|
1325
|
-
1. listMastraPackages \u2192 see what's installed
|
|
1326
|
-
2. getMastraExports \u2192 explore package API
|
|
1327
|
-
3. getMastraExportDetails \u2192 get type definitions
|
|
1328
|
-
4. readMastraDocs \u2192 learn concepts
|
|
1329
|
-
5. searchMastraDocs \u2192 find specific info
|
|
1330
|
-
|
|
1331
|
-
---
|
|
1332
|
-
|
|
1333
|
-
## \u{1F310} REMOTE WEBSITE DOCS (mastra.ai)
|
|
1334
|
-
|
|
1335
|
-
**SOURCE**: https://mastra.ai (latest published documentation)
|
|
1336
|
-
**USE WHEN**: Learning new concepts, checking latest features, migration guides
|
|
1337
|
-
|
|
1338
|
-
### Available REMOTE Tools:
|
|
1339
|
-
|
|
1340
|
-
**mastraDocs** - Browse official documentation
|
|
1341
|
-
Latest guides, references, and tutorials
|
|
1342
|
-
|
|
1343
|
-
**mastraBlog** - Read blog posts and announcements
|
|
1344
|
-
News, features, changelogs
|
|
1345
|
-
|
|
1346
|
-
**mastraExamples** - Get curated code examples
|
|
1347
|
-
Full example applications
|
|
1348
|
-
|
|
1349
|
-
**mastraChanges** - View package changelogs
|
|
1350
|
-
See what's new in each version
|
|
1351
|
-
|
|
1352
|
-
**mastraMigration** - Get migration guides
|
|
1353
|
-
Upgrade between versions
|
|
1354
|
-
|
|
1355
|
-
\u26A0\uFE0F **Version Note**: Remote docs show latest published version. For API reference matching YOUR code, use LOCAL tools above.
|
|
1356
|
-
|
|
1357
|
-
---
|
|
1358
|
-
|
|
1359
|
-
## \u{1F393} INTERACTIVE COURSE
|
|
1360
|
-
|
|
1361
|
-
**startMastraCourse**, **getMastraCourseStatus**, **startMastraCourseLesson**, **nextMastraCourseStep**, **clearMastraCourseHistory**
|
|
1362
|
-
|
|
1363
|
-
Guided learning experience with hands-on exercises.
|
|
1364
|
-
|
|
1365
|
-
---
|
|
1366
|
-
|
|
1367
|
-
## Quick Start Recommendations
|
|
1368
|
-
|
|
1369
|
-
**If you're writing code**: Use LOCAL tools
|
|
1370
|
-
\u2192 Start with listMastraPackages
|
|
1371
|
-
|
|
1372
|
-
**If you're learning**: Use REMOTE tools
|
|
1373
|
-
\u2192 Start with mastraDocs
|
|
1374
|
-
|
|
1375
|
-
**If version differs**: Check changes
|
|
1376
|
-
\u2192 mastraChanges \u2192 mastraMigration`;
|
|
1377
|
-
}
|
|
1378
|
-
};
|
|
1379
|
-
var listInstalledPackagesTool = {
|
|
1380
|
-
name: "listMastraPackages",
|
|
1381
|
-
description: `[\u{1F4E6} LOCAL PACKAGES] Discover which Mastra packages are installed and have documentation available.
|
|
1382
|
-
|
|
1383
|
-
Use this when you need to:
|
|
1384
|
-
- See what Mastra packages you can work with
|
|
1385
|
-
- Start exploring Mastra documentation
|
|
1386
|
-
- Check if a specific package is available
|
|
1387
|
-
|
|
1388
|
-
Returns: List of @mastra/* packages (core, memory, rag, etc.) with embedded docs.
|
|
1389
|
-
Next step: Use getMastraExports to explore a specific package's API.`,
|
|
1390
|
-
parameters: z.object({
|
|
1391
|
-
projectPath: z.string().describe("Absolute path to your project root (we will search upward for node_modules with Mastra packages)")
|
|
1392
|
-
}),
|
|
1393
|
-
execute: async (args) => {
|
|
1394
|
-
void logger.debug("Executing listInstalledMastraPackages tool", {
|
|
1395
|
-
projectPath: args.projectPath,
|
|
1396
|
-
cwd: process.cwd(),
|
|
1397
|
-
env: {
|
|
1398
|
-
PWD: process.env.PWD,
|
|
1399
|
-
HOME: process.env.HOME
|
|
1400
|
-
}
|
|
1401
|
-
});
|
|
1402
|
-
const packages = await getInstalledMastraPackages(args.projectPath);
|
|
1403
|
-
if (packages.length === 0) {
|
|
1404
|
-
return `No @mastra/* packages with embedded docs found in your project.
|
|
1405
|
-
|
|
1406
|
-
Install Mastra packages to get started:
|
|
1407
|
-
- npm install @mastra/core
|
|
1408
|
-
- npm install @mastra/memory
|
|
1409
|
-
- npm install @mastra/rag`;
|
|
1410
|
-
}
|
|
1411
|
-
return [
|
|
1412
|
-
`# Installed Mastra Packages`,
|
|
1413
|
-
"",
|
|
1414
|
-
`Found ${packages.length} package(s) with embedded documentation:`,
|
|
1415
|
-
"",
|
|
1416
|
-
...packages.map((pkg) => `- ${pkg}`),
|
|
1417
|
-
"",
|
|
1418
|
-
"## Next Steps",
|
|
1419
|
-
"",
|
|
1420
|
-
"1. Use **getMastraExports** with a package name to see all available APIs",
|
|
1421
|
-
"2. Use **readMastraDocs** with a package name to browse topic guides",
|
|
1422
|
-
"3. Use **searchMastraDocs** to find specific information"
|
|
1423
|
-
].join("\n");
|
|
1424
|
-
}
|
|
1425
|
-
};
|
|
1426
|
-
var readSourceMapTool = {
|
|
1427
|
-
name: "getMastraExports",
|
|
1428
|
-
description: `[\u{1F4E6} LOCAL PACKAGES] Explore the complete API surface of a Mastra package - see all classes, functions, types, and constants.
|
|
1429
|
-
|
|
1430
|
-
Use this when you need to:
|
|
1431
|
-
- Discover what APIs a Mastra package provides (Agent, Tool, Workflow, etc.)
|
|
1432
|
-
- See all available classes and functions before implementing
|
|
1433
|
-
- Find the right export for your use case
|
|
1434
|
-
- Understand package structure and organization
|
|
1435
|
-
|
|
1436
|
-
Returns: List of all exports with their source file locations.
|
|
1437
|
-
Next step: Use getMastraExportDetails to get full type definitions and code for a specific export.`,
|
|
1438
|
-
parameters: z.object({
|
|
1439
|
-
package: z.string().describe('Package name to explore (e.g., "@mastra/core", "@mastra/memory", "@mastra/rag")'),
|
|
1440
|
-
projectPath: z.string().describe("Absolute path to your project root (we will search upward for node_modules)"),
|
|
1441
|
-
filter: z.string().optional().describe('Optional: filter exports by name (case-insensitive, e.g., "Agent", "create", "Tool")')
|
|
1442
|
-
}),
|
|
1443
|
-
execute: async (args) => {
|
|
1444
|
-
void logger.debug("Executing readMastraSourceMap tool", { args });
|
|
1445
|
-
const sourceMap = await readSourceMap(args.package, args.projectPath);
|
|
1446
|
-
if (!sourceMap) return `No SOURCE_MAP.json found for ${args.package}.`;
|
|
1447
|
-
let exports = Object.entries(sourceMap.exports);
|
|
1448
|
-
if (args.filter) {
|
|
1449
|
-
const filterLower = args.filter.toLowerCase();
|
|
1450
|
-
exports = exports.filter(([name]) => name.toLowerCase().includes(filterLower));
|
|
1451
|
-
}
|
|
1452
|
-
if (exports.length === 0) {
|
|
1453
|
-
return args.filter ? `No exports matching "${args.filter}" in ${args.package}.
|
|
1454
|
-
|
|
1455
|
-
Try running without a filter to see all available exports.` : `No exports found in ${args.package}.`;
|
|
1456
|
-
}
|
|
1457
|
-
return [
|
|
1458
|
-
`# ${sourceMap.package} v${sourceMap.version} - API Exports`,
|
|
1459
|
-
"",
|
|
1460
|
-
`Found ${exports.length} export(s)${args.filter ? ` matching "${args.filter}"` : ""}:`,
|
|
1461
|
-
"",
|
|
1462
|
-
...exports.map(([name, info]) => {
|
|
1463
|
-
const line = info.line ? `:${info.line}` : "";
|
|
1464
|
-
return `- **${name}**: \`${info.implementation}${line}\``;
|
|
1465
|
-
}),
|
|
1466
|
-
"",
|
|
1467
|
-
"## Next Steps",
|
|
1468
|
-
"",
|
|
1469
|
-
"- Use **getMastraExportDetails** with an export name to see full type definitions and code",
|
|
1470
|
-
"- Use **readMastraDocs** to read conceptual guides and examples",
|
|
1471
|
-
"- Use **searchMastraDocs** to find specific topics or patterns"
|
|
1472
|
-
].join("\n");
|
|
1473
|
-
}
|
|
1474
|
-
};
|
|
1475
|
-
var findExportTool = {
|
|
1476
|
-
name: "getMastraExportDetails",
|
|
1477
|
-
description: `[\u{1F4E6} LOCAL PACKAGES] Get complete API reference for a specific Mastra export - type definitions, interfaces, and optionally source code.
|
|
1478
|
-
|
|
1479
|
-
Use this when you need to:
|
|
1480
|
-
- Understand how to use a specific Mastra class or function (Agent, Tool, Workflow, etc.)
|
|
1481
|
-
- See TypeScript type definitions and interfaces
|
|
1482
|
-
- Look up method signatures and parameters
|
|
1483
|
-
- Read implementation code and examples
|
|
1484
|
-
- Understand constructor options and configuration
|
|
1485
|
-
|
|
1486
|
-
Returns: Full TypeScript type definitions and optionally implementation source code.
|
|
1487
|
-
Example: Get details on the Agent class to see how to create and configure agents.`,
|
|
1488
|
-
parameters: z.object({
|
|
1489
|
-
package: z.string().describe('Package name (e.g., "@mastra/core", "@mastra/memory")'),
|
|
1490
|
-
exportName: z.string().describe('Exact export name to look up (e.g., "Agent", "createTool", "Workflow")'),
|
|
1491
|
-
includeTypes: z.boolean().optional().default(true).describe("Include TypeScript type definitions (recommended: true)"),
|
|
1492
|
-
includeImplementation: z.boolean().optional().default(false).describe("Include source code implementation (useful for understanding internals)"),
|
|
1493
|
-
implementationLines: z.number().optional().default(50).describe("Number of lines of implementation code to show (default: 50)"),
|
|
1494
|
-
projectPath: z.string().describe("Absolute path to your project root (we will search upward for node_modules)")
|
|
1495
|
-
}),
|
|
1496
|
-
execute: async (args) => {
|
|
1497
|
-
void logger.debug("Executing findMastraExport tool", { args });
|
|
1498
|
-
const sourceMap = await readSourceMap(args.package, args.projectPath);
|
|
1499
|
-
if (!sourceMap) return `No SOURCE_MAP.json found for ${args.package}.`;
|
|
1500
|
-
const exportInfo = sourceMap.exports[args.exportName];
|
|
1501
|
-
if (!exportInfo) {
|
|
1502
|
-
const match = Object.entries(sourceMap.exports).find(
|
|
1503
|
-
([name]) => name.toLowerCase() === args.exportName.toLowerCase()
|
|
1504
|
-
);
|
|
1505
|
-
if (match) {
|
|
1506
|
-
return `Export "${args.exportName}" not found. Did you mean "${match[0]}"?
|
|
1507
|
-
|
|
1508
|
-
Run getMastraExports with package="${args.package}" to see all available exports.`;
|
|
1509
|
-
}
|
|
1510
|
-
return `Export "${args.exportName}" not found in ${args.package}.
|
|
1511
|
-
|
|
1512
|
-
Run getMastraExports with package="${args.package}" to see all available exports.`;
|
|
1513
|
-
}
|
|
1514
|
-
const packageInfo = await getPackageRootPath(args.package, args.projectPath);
|
|
1515
|
-
if (!packageInfo) {
|
|
1516
|
-
return `Package ${args.package} not found. Make sure it's installed.`;
|
|
1517
|
-
}
|
|
1518
|
-
const output = [`# ${args.exportName} (${args.package})`, ""];
|
|
1519
|
-
if (args.includeTypes !== false) {
|
|
1520
|
-
try {
|
|
1521
|
-
const typesPath = path5__default.join(packageInfo.rootPath, exportInfo.types);
|
|
1522
|
-
const typesContent = await fs3.readFile(typesPath, "utf-8");
|
|
1523
|
-
output.push("## Type Definition", "", `\`${exportInfo.types}\``, "", "```typescript");
|
|
1524
|
-
const lines = typesContent.split("\n");
|
|
1525
|
-
let startLine = lines.findIndex((line) => line.includes(args.exportName));
|
|
1526
|
-
if (startLine === -1) {
|
|
1527
|
-
output.push(typesContent.slice(0, 2e3));
|
|
1528
|
-
} else {
|
|
1529
|
-
startLine = Math.max(0, startLine - 2);
|
|
1530
|
-
let endLine = Math.min(lines.length, startLine + 50);
|
|
1531
|
-
output.push(lines.slice(startLine, endLine).join("\n"));
|
|
1532
|
-
}
|
|
1533
|
-
output.push("```", "");
|
|
1534
|
-
} catch {
|
|
1535
|
-
output.push("## Type Definition", "", `Could not read: ${exportInfo.types}`, "");
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
if (args.includeImplementation) {
|
|
1539
|
-
try {
|
|
1540
|
-
const implPath = path5__default.join(packageInfo.rootPath, exportInfo.implementation);
|
|
1541
|
-
const implContent = await fs3.readFile(implPath, "utf-8");
|
|
1542
|
-
const lines = implContent.split("\n");
|
|
1543
|
-
const numLines = args.implementationLines || 50;
|
|
1544
|
-
output.push("## Implementation", "");
|
|
1545
|
-
output.push(`\`${exportInfo.implementation}\`${exportInfo.line ? ` (line ${exportInfo.line})` : ""}`);
|
|
1546
|
-
output.push("", "```javascript");
|
|
1547
|
-
const startLine = exportInfo.line ? Math.max(0, exportInfo.line - 1) : 0;
|
|
1548
|
-
const endLine = Math.min(lines.length, startLine + numLines);
|
|
1549
|
-
output.push(lines.slice(startLine, endLine).join("\n"));
|
|
1550
|
-
if (endLine < lines.length) output.push(`// ... ${lines.length - endLine} more lines`);
|
|
1551
|
-
output.push("```", "");
|
|
1552
|
-
} catch {
|
|
1553
|
-
output.push("## Implementation", "", `Could not read: ${exportInfo.implementation}`, "");
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
output.push(
|
|
1557
|
-
"## Next Steps",
|
|
1558
|
-
"",
|
|
1559
|
-
"- Use **readMastraDocs** to see practical guides and examples",
|
|
1560
|
-
"- Use **searchMastraDocs** to find usage patterns and best practices",
|
|
1561
|
-
"- Use **getMastraExports** to explore related APIs"
|
|
1562
|
-
);
|
|
1563
|
-
return output.join("\n");
|
|
1564
|
-
}
|
|
1565
|
-
};
|
|
1566
|
-
var readEmbeddedDocsTool = {
|
|
1567
|
-
name: "readMastraDocs",
|
|
1568
|
-
description: `[\u{1F4E6} LOCAL PACKAGES] Read comprehensive guides and documentation on Mastra concepts, patterns, and implementation examples.
|
|
1569
|
-
|
|
1570
|
-
Use this when you need to:
|
|
1571
|
-
- Learn how to implement Mastra features (agents, tools, workflows, memory, RAG, etc.)
|
|
1572
|
-
- Understand Mastra architecture and design patterns
|
|
1573
|
-
- See practical code examples and tutorials
|
|
1574
|
-
- Read getting started guides and best practices
|
|
1575
|
-
- Understand how different components work together
|
|
1576
|
-
|
|
1577
|
-
Returns: Topic-based documentation with explanations, examples, and usage patterns.
|
|
1578
|
-
Available topics: agents, tools, workflows, memory, rag, integrations, deployment, and more.`,
|
|
1579
|
-
parameters: z.object({
|
|
1580
|
-
package: z.string().describe('Package name to read docs from (e.g., "@mastra/core", "@mastra/memory")'),
|
|
1581
|
-
topic: z.string().optional().describe(
|
|
1582
|
-
'Optional: topic folder to read (e.g., "agents", "tools", "workflows"). Omit to list all available topics.'
|
|
1583
|
-
),
|
|
1584
|
-
file: z.string().optional().describe('Optional: specific documentation file within the topic (e.g., "01-overview.md")'),
|
|
1585
|
-
projectPath: z.string().describe("Absolute path to your project root (we will search upward for node_modules)")
|
|
1586
|
-
}),
|
|
1587
|
-
execute: async (args) => {
|
|
1588
|
-
void logger.debug("Executing readMastraEmbeddedDocs tool", { args });
|
|
1589
|
-
const packageInfo = await getPackageRootPath(args.package, args.projectPath);
|
|
1590
|
-
if (!packageInfo) {
|
|
1591
|
-
return `Package ${args.package} not found. Make sure it's installed.`;
|
|
1592
|
-
}
|
|
1593
|
-
const docsPath = path5__default.join(packageInfo.rootPath, "dist", "docs");
|
|
1594
|
-
try {
|
|
1595
|
-
await fs3.stat(docsPath);
|
|
1596
|
-
} catch {
|
|
1597
|
-
return `No embedded docs found for ${args.package}.
|
|
1598
|
-
|
|
1599
|
-
Make sure the package is installed and has documentation generated.`;
|
|
1600
|
-
}
|
|
1601
|
-
if (!args.topic) {
|
|
1602
|
-
const entries = await fs3.readdir(docsPath, { withFileTypes: true });
|
|
1603
|
-
const topics = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1604
|
-
const files = entries.filter((e) => e.isFile()).map((e) => e.name);
|
|
1605
|
-
return [
|
|
1606
|
-
`# ${args.package} - Available Documentation`,
|
|
1607
|
-
"",
|
|
1608
|
-
"## Root Files",
|
|
1609
|
-
...files.map((f) => `- ${f}`),
|
|
1610
|
-
"",
|
|
1611
|
-
"## Documentation Topics",
|
|
1612
|
-
...topics.map((t) => `- **${t}/** - Run readMastraDocs with topic="${t}" to read`),
|
|
1613
|
-
"",
|
|
1614
|
-
"## Next Steps",
|
|
1615
|
-
"",
|
|
1616
|
-
"- Choose a topic and run **readMastraDocs** with the topic parameter",
|
|
1617
|
-
"- Use **searchMastraDocs** to search for specific information",
|
|
1618
|
-
"- Use **getMastraExports** to see available APIs"
|
|
1619
|
-
].join("\n");
|
|
1620
|
-
}
|
|
1621
|
-
const topicPath = path5__default.join(docsPath, args.topic);
|
|
1622
|
-
if (args.file) {
|
|
1623
|
-
try {
|
|
1624
|
-
const content = await fs3.readFile(path5__default.join(topicPath, args.file), "utf-8");
|
|
1625
|
-
return `# ${args.package}/${args.topic}/${args.file}
|
|
1626
|
-
|
|
1627
|
-
${content}
|
|
1628
|
-
|
|
1629
|
-
## Next Steps
|
|
1630
|
-
|
|
1631
|
-
- Use **getMastraExportDetails** to see API references for specific classes/functions mentioned
|
|
1632
|
-
- Use **searchMastraDocs** to find related topics
|
|
1633
|
-
- Use **getMastraExports** to explore available APIs`;
|
|
1634
|
-
} catch {
|
|
1635
|
-
return `File not found: ${args.topic}/${args.file}
|
|
1636
|
-
|
|
1637
|
-
Run readMastraDocs with package="${args.package}" and topic="${args.topic}" (without file parameter) to see available files.`;
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
try {
|
|
1641
|
-
const entries = await fs3.readdir(topicPath, { withFileTypes: true });
|
|
1642
|
-
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".md")).sort();
|
|
1643
|
-
if (files.length === 0) {
|
|
1644
|
-
return `No markdown files in ${args.topic}/
|
|
1645
|
-
|
|
1646
|
-
Run readMastraDocs with package="${args.package}" (without topic parameter) to see available topics.`;
|
|
1647
|
-
}
|
|
1648
|
-
const contents = [`# ${args.package} - ${args.topic}`, ""];
|
|
1649
|
-
for (const file of files) {
|
|
1650
|
-
const content = await fs3.readFile(path5__default.join(topicPath, file.name), "utf-8");
|
|
1651
|
-
contents.push(`## ${file.name}`, "", content, "", "---", "");
|
|
1652
|
-
}
|
|
1653
|
-
contents.push(
|
|
1654
|
-
"",
|
|
1655
|
-
"## Next Steps",
|
|
1656
|
-
"",
|
|
1657
|
-
"- Use **getMastraExportDetails** to see API references for specific classes/functions mentioned above",
|
|
1658
|
-
"- Use **searchMastraDocs** to find related information",
|
|
1659
|
-
"- Use **getMastraExports** to explore the complete API surface"
|
|
1660
|
-
);
|
|
1661
|
-
return contents.join("\n");
|
|
1662
|
-
} catch {
|
|
1663
|
-
return `Topic not found: ${args.topic}
|
|
1664
|
-
|
|
1665
|
-
Run readMastraDocs with package="${args.package}" (without topic parameter) to see available topics.`;
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
};
|
|
1669
|
-
var searchEmbeddedDocsTool = {
|
|
1670
|
-
name: "searchMastraDocs",
|
|
1671
|
-
description: `[\u{1F4E6} LOCAL PACKAGES] Search across all Mastra documentation to find specific information, patterns, or examples.
|
|
1672
|
-
|
|
1673
|
-
Use this when you need to:
|
|
1674
|
-
- Find specific topics or concepts quickly (e.g., "memory processors", "tool composition")
|
|
1675
|
-
- Locate examples of specific features or patterns
|
|
1676
|
-
- Search for error messages or troubleshooting info
|
|
1677
|
-
- Find mentions of specific APIs or configuration options
|
|
1678
|
-
- Discover where a feature is documented
|
|
1679
|
-
|
|
1680
|
-
Returns: Relevant documentation excerpts with file paths, ranked by relevance.
|
|
1681
|
-
Tip: Use specific terms for better results (e.g., "agent memory" vs "memory").`,
|
|
1682
|
-
parameters: z.object({
|
|
1683
|
-
query: z.string().describe('What to search for (case-insensitive, e.g., "workflow steps", "vector store", "authentication")'),
|
|
1684
|
-
package: z.string().optional().describe('Optional: limit search to a specific package (e.g., "@mastra/core"). Omit to search all packages.'),
|
|
1685
|
-
maxResults: z.number().optional().default(10).describe("Optional: maximum number of results to return (default: 10)"),
|
|
1686
|
-
projectPath: z.string().describe("Absolute path to your project root (we will search upward for node_modules)")
|
|
1687
|
-
}),
|
|
1688
|
-
execute: async (args) => {
|
|
1689
|
-
void logger.debug("Executing searchMastraEmbeddedDocs tool", { args });
|
|
1690
|
-
const packages = args.package ? [args.package] : await getInstalledMastraPackages(args.projectPath);
|
|
1691
|
-
if (packages.length === 0) return "No Mastra packages found.";
|
|
1692
|
-
const queryLower = args.query.toLowerCase();
|
|
1693
|
-
const results = [];
|
|
1694
|
-
for (const pkg of packages) {
|
|
1695
|
-
const packageInfo = await getPackageRootPath(pkg, args.projectPath);
|
|
1696
|
-
if (!packageInfo) continue;
|
|
1697
|
-
const docsPath = path5__default.join(packageInfo.rootPath, "dist", "docs");
|
|
1698
|
-
try {
|
|
1699
|
-
const findFiles = async (dir) => {
|
|
1700
|
-
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
1701
|
-
const files = [];
|
|
1702
|
-
for (const entry of entries) {
|
|
1703
|
-
const fullPath = path5__default.join(dir, entry.name);
|
|
1704
|
-
if (entry.isDirectory()) files.push(...await findFiles(fullPath));
|
|
1705
|
-
else if (entry.name.endsWith(".md")) files.push(fullPath);
|
|
1706
|
-
}
|
|
1707
|
-
return files;
|
|
1708
|
-
};
|
|
1709
|
-
for (const file of await findFiles(docsPath)) {
|
|
1710
|
-
const content = await fs3.readFile(file, "utf-8");
|
|
1711
|
-
if (!content.toLowerCase().includes(queryLower)) continue;
|
|
1712
|
-
const lines = content.split("\n");
|
|
1713
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1714
|
-
if (lines[i]?.toLowerCase().includes(queryLower)) {
|
|
1715
|
-
const start = Math.max(0, i - 1);
|
|
1716
|
-
const end = Math.min(lines.length, i + 3);
|
|
1717
|
-
const excerpt = lines.slice(start, end).join("\n").slice(0, 300);
|
|
1718
|
-
const contentLower = content.toLowerCase();
|
|
1719
|
-
const occurrences = contentLower.split(queryLower).length - 1;
|
|
1720
|
-
results.push({
|
|
1721
|
-
pkg,
|
|
1722
|
-
file: path5__default.relative(docsPath, file),
|
|
1723
|
-
excerpt,
|
|
1724
|
-
score: occurrences
|
|
1725
|
-
});
|
|
1726
|
-
break;
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
} catch {
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
results.sort((a, b) => b.score - a.score);
|
|
1734
|
-
const topResults = results.slice(0, args.maxResults || 10);
|
|
1735
|
-
if (topResults.length === 0) {
|
|
1736
|
-
return `No results found for "${args.query}".
|
|
1737
|
-
|
|
1738
|
-
Try:
|
|
1739
|
-
- Using different search terms
|
|
1740
|
-
- Searching for broader topics
|
|
1741
|
-
- Using **listMastraPackages** to see available packages
|
|
1742
|
-
- Using **readMastraDocs** to browse documentation by topic`;
|
|
1743
|
-
}
|
|
1744
|
-
return [
|
|
1745
|
-
`# Search Results: "${args.query}"`,
|
|
1746
|
-
"",
|
|
1747
|
-
`Found ${results.length} result(s), showing top ${topResults.length}:`,
|
|
1748
|
-
"",
|
|
1749
|
-
...topResults.map((r, i) => `## ${i + 1}. ${r.pkg} - ${r.file}
|
|
1750
|
-
|
|
1751
|
-
\`\`\`
|
|
1752
|
-
${r.excerpt}
|
|
1753
|
-
\`\`\`
|
|
1754
|
-
`),
|
|
1755
|
-
"",
|
|
1756
|
-
"## Next Steps",
|
|
1757
|
-
"",
|
|
1758
|
-
"- Use **readMastraDocs** with a package and topic to read full documentation",
|
|
1759
|
-
"- Use **getMastraExportDetails** to see API details for mentioned classes/functions",
|
|
1760
|
-
"- Refine your search with more specific terms if needed"
|
|
1761
|
-
].join("\n");
|
|
1762
|
-
}
|
|
1763
|
-
};
|
|
1764
|
-
var embeddedDocsTools = {
|
|
1765
|
-
getMastraHelp: getMastraHelpTool,
|
|
1766
|
-
listMastraPackages: listInstalledPackagesTool,
|
|
1767
|
-
getMastraExports: readSourceMapTool,
|
|
1768
|
-
getMastraExportDetails: findExportTool,
|
|
1769
|
-
readMastraDocs: readEmbeddedDocsTool,
|
|
1770
|
-
searchMastraDocs: searchEmbeddedDocsTool
|
|
1771
|
-
};
|
|
1772
|
-
var migrationsBaseDir = fromPackageRoot(".docs/guides/migrations");
|
|
1773
|
-
function parseSections(content) {
|
|
1774
|
-
const lines = content.split("\n");
|
|
1775
|
-
const sections = [];
|
|
1776
|
-
let currentSection = null;
|
|
1777
|
-
let inFrontmatter = false;
|
|
1778
|
-
let contentStarted = false;
|
|
1779
|
-
for (let index = 0; index < lines.length; index++) {
|
|
1780
|
-
const line = lines[index];
|
|
1781
|
-
if (index === 0 && line === "---") {
|
|
1782
|
-
inFrontmatter = true;
|
|
1783
|
-
continue;
|
|
1784
|
-
}
|
|
1785
|
-
if (inFrontmatter && line === "---") {
|
|
1786
|
-
inFrontmatter = false;
|
|
1787
|
-
continue;
|
|
1788
|
-
}
|
|
1789
|
-
if (inFrontmatter) continue;
|
|
1790
|
-
contentStarted = true;
|
|
1791
|
-
const headingMatch = line?.match(/^(#{2,3})\s+(.+)$/);
|
|
1792
|
-
if (headingMatch && contentStarted) {
|
|
1793
|
-
if (currentSection) {
|
|
1794
|
-
currentSection.endLine = index - 1;
|
|
1795
|
-
sections.push(currentSection);
|
|
1796
|
-
}
|
|
1797
|
-
const level = headingMatch[1]?.length ?? 0;
|
|
1798
|
-
currentSection = {
|
|
1799
|
-
title: headingMatch[2] || "Untitled",
|
|
1800
|
-
level,
|
|
1801
|
-
content: line + "\n",
|
|
1802
|
-
startLine: index,
|
|
1803
|
-
endLine: index
|
|
1804
|
-
};
|
|
1805
|
-
} else if (currentSection) {
|
|
1806
|
-
currentSection.content += line + "\n";
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
if (currentSection) {
|
|
1810
|
-
currentSection.endLine = lines.length - 1;
|
|
1811
|
-
sections.push(currentSection);
|
|
1812
|
-
}
|
|
1813
|
-
return sections;
|
|
1814
|
-
}
|
|
1815
|
-
async function discoverMigrations(baseDir, relativePath = "") {
|
|
1816
|
-
const migrations = [];
|
|
1817
|
-
const fullPath = path5__default.join(baseDir, relativePath);
|
|
1818
|
-
try {
|
|
1819
|
-
const entries = await fs3.readdir(fullPath, { withFileTypes: true });
|
|
1820
|
-
for (const entry of entries) {
|
|
1821
|
-
const entryRelativePath = path5__default.join(relativePath, entry.name);
|
|
1822
|
-
if (entry.isDirectory()) {
|
|
1823
|
-
migrations.push({
|
|
1824
|
-
path: entryRelativePath,
|
|
1825
|
-
type: "directory"
|
|
1826
|
-
});
|
|
1827
|
-
const subMigrations = await discoverMigrations(baseDir, entryRelativePath);
|
|
1828
|
-
migrations.push(...subMigrations);
|
|
1829
|
-
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
1830
|
-
const cleanName = entry.name.replace(/\.md$/, "");
|
|
1831
|
-
migrations.push({
|
|
1832
|
-
path: relativePath ? path5__default.join(relativePath, cleanName) : cleanName,
|
|
1833
|
-
type: "file"
|
|
1834
|
-
});
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
} catch (error) {
|
|
1838
|
-
void logger.error("Failed to discover migrations", { path: fullPath, error });
|
|
1839
|
-
}
|
|
1840
|
-
return migrations;
|
|
1841
|
-
}
|
|
1842
|
-
async function listDirectoryContents(dirPath = "") {
|
|
1843
|
-
try {
|
|
1844
|
-
const fullPath = path5__default.join(migrationsBaseDir, dirPath);
|
|
1845
|
-
const resolvedPath = path5__default.resolve(fullPath);
|
|
1846
|
-
const resolvedBaseDir = path5__default.resolve(migrationsBaseDir);
|
|
1847
|
-
if (!resolvedPath.startsWith(resolvedBaseDir)) {
|
|
1848
|
-
return "Invalid path";
|
|
1849
|
-
}
|
|
1850
|
-
const entries = await fs3.readdir(fullPath, { withFileTypes: true });
|
|
1851
|
-
const directories = [];
|
|
1852
|
-
const files = [];
|
|
1853
|
-
for (const entry of entries) {
|
|
1854
|
-
if (entry.isDirectory()) {
|
|
1855
|
-
directories.push(entry.name);
|
|
1856
|
-
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
1857
|
-
files.push(entry.name.replace(/\.md$/, ""));
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
const output = [];
|
|
1861
|
-
const currentPath = dirPath || "migrations";
|
|
1862
|
-
output.push(`# ${currentPath}`);
|
|
1863
|
-
output.push("");
|
|
1864
|
-
if (directories.length > 0) {
|
|
1865
|
-
output.push("**Directories:**");
|
|
1866
|
-
directories.sort().forEach((dir) => {
|
|
1867
|
-
const nextPath = dirPath ? `${dirPath}/${dir}` : dir;
|
|
1868
|
-
output.push(`- **${dir}/** - Explore with \`{ path: "${nextPath}/" }\``);
|
|
1869
|
-
});
|
|
1870
|
-
output.push("");
|
|
1871
|
-
}
|
|
1872
|
-
if (files.length > 0) {
|
|
1873
|
-
output.push("**Migration Guides:**");
|
|
1874
|
-
files.sort().forEach((file) => {
|
|
1875
|
-
const filePath = dirPath ? `${dirPath}/${file}` : file;
|
|
1876
|
-
output.push(`- **${file}** - Get with \`{ path: "${filePath}" }\``);
|
|
1877
|
-
});
|
|
1878
|
-
output.push("");
|
|
1879
|
-
}
|
|
1880
|
-
if (directories.length === 0 && files.length === 0) {
|
|
1881
|
-
output.push("No migrations found in this directory.");
|
|
1882
|
-
}
|
|
1883
|
-
output.push("---");
|
|
1884
|
-
output.push("");
|
|
1885
|
-
output.push("**Actions:**");
|
|
1886
|
-
output.push("- Navigate to a directory by setting `path` to directory name with trailing `/`");
|
|
1887
|
-
output.push("- View a migration guide by setting `path` to the guide name");
|
|
1888
|
-
output.push("- List sections in a guide with `listSections: true`");
|
|
1889
|
-
output.push("- Search all guides with `queryKeywords`");
|
|
1890
|
-
return output.join("\n");
|
|
1891
|
-
} catch (error) {
|
|
1892
|
-
if (error.code === "ENOENT") {
|
|
1893
|
-
return `Directory "${dirPath}" not found. Use \`{}\` to see top-level migrations.`;
|
|
1894
|
-
}
|
|
1895
|
-
throw error;
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
async function readMigrationContent(migrationPath) {
|
|
1899
|
-
try {
|
|
1900
|
-
const cleanPath = migrationPath.replace(/\.(mdx|md)$/, "");
|
|
1901
|
-
const filePath = path5__default.join(migrationsBaseDir, cleanPath + ".md");
|
|
1902
|
-
const resolvedPath = path5__default.resolve(filePath);
|
|
1903
|
-
const resolvedBaseDir = path5__default.resolve(migrationsBaseDir);
|
|
1904
|
-
if (!resolvedPath.startsWith(resolvedBaseDir)) {
|
|
1905
|
-
void logger.error("Path traversal attempt detected");
|
|
1906
|
-
return null;
|
|
1907
|
-
}
|
|
1908
|
-
const content = await fs3.readFile(filePath, "utf-8");
|
|
1909
|
-
return content;
|
|
1910
|
-
} catch (error) {
|
|
1911
|
-
void logger.error("Failed to read migration", { path: migrationPath, error });
|
|
1912
|
-
return null;
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
async function getSectionHeaders(migrationPath) {
|
|
1916
|
-
const content = await readMigrationContent(migrationPath);
|
|
1917
|
-
if (!content) return [];
|
|
1918
|
-
const sections = parseSections(content);
|
|
1919
|
-
return sections.map((s) => ({ title: s.title, level: s.level }));
|
|
1920
|
-
}
|
|
1921
|
-
async function getSections(migrationPath, sectionTitles) {
|
|
1922
|
-
const content = await readMigrationContent(migrationPath);
|
|
1923
|
-
if (!content) {
|
|
1924
|
-
const availableMigrations = await discoverMigrations(migrationsBaseDir);
|
|
1925
|
-
const paths = availableMigrations.filter((m) => m.type === "file").map((m) => `- ${m.path}`);
|
|
1926
|
-
return `Migration "${migrationPath}" not found.
|
|
1927
|
-
|
|
1928
|
-
Available migrations:
|
|
1929
|
-
${paths.join("\n")}`;
|
|
1930
|
-
}
|
|
1931
|
-
if (!sectionTitles || sectionTitles.length === 0) {
|
|
1932
|
-
return content;
|
|
1933
|
-
}
|
|
1934
|
-
const sections = parseSections(content);
|
|
1935
|
-
const requestedSections = sections.filter(
|
|
1936
|
-
(s) => sectionTitles.some((title) => s.title.toLowerCase().includes(title.toLowerCase()))
|
|
1937
|
-
);
|
|
1938
|
-
if (requestedSections.length === 0) {
|
|
1939
|
-
const availableHeaders = sections.map((s) => `${"#".repeat(s.level)} ${s.title}`).join("\n");
|
|
1940
|
-
return `Requested sections not found in "${migrationPath}".
|
|
1941
|
-
|
|
1942
|
-
Available sections:
|
|
1943
|
-
${availableHeaders}`;
|
|
1944
|
-
}
|
|
1945
|
-
return requestedSections.map((s) => s.content).join("\n---\n\n");
|
|
1946
|
-
}
|
|
1947
|
-
var initialMigrations = await discoverMigrations(migrationsBaseDir);
|
|
1948
|
-
var migrationFiles = initialMigrations.filter((m) => m.type === "file");
|
|
1949
|
-
var migrationsListing = migrationFiles.length > 0 ? "\n\nExample migration paths:\n" + migrationFiles.slice(0, 5).map((m) => `- ${m.path}`).join("\n") + "\n..." : "\n\nNo migrations available. Run the documentation preparation script first.";
|
|
1950
|
-
var migrationInputSchema = z.object({
|
|
1951
|
-
path: z.string().optional().describe(
|
|
1952
|
-
'Path to the migration guide (e.g., "upgrade-to-v1/agent", "agentnetwork"). If not provided, lists all available migrations.' + migrationsListing
|
|
1953
|
-
),
|
|
1954
|
-
sections: z.array(z.string()).optional().describe(
|
|
1955
|
-
"Specific section titles to fetch from the migration guide. If not provided, returns the entire guide. Use this after exploring section headers."
|
|
1956
|
-
),
|
|
1957
|
-
listSections: z.boolean().optional().describe("Set to true to list all section headers in a migration guide without fetching full content."),
|
|
1958
|
-
queryKeywords: z.array(z.string()).optional().describe("Keywords to search across all migration guides. Use this to find guides related to specific topics.")
|
|
1959
|
-
});
|
|
1960
|
-
var migrationTool = {
|
|
1961
|
-
name: "mastraMigration",
|
|
1962
|
-
description: `[\u{1F310} REMOTE] Get migration guidance for Mastra version upgrades and breaking changes.
|
|
1963
|
-
|
|
1964
|
-
This tool works like a file browser - navigate through directories to find migration guides:
|
|
1965
|
-
|
|
1966
|
-
**Step 1: List top-level migrations**
|
|
1967
|
-
- Call with no parameters: \`{}\`
|
|
1968
|
-
- Shows all top-level migration guides and directories
|
|
1969
|
-
|
|
1970
|
-
**Step 2: Navigate into a directory**
|
|
1971
|
-
- Add trailing slash to explore: \`{ path: "upgrade-to-v1/" }\`
|
|
1972
|
-
- Lists all migration guides in that directory
|
|
1973
|
-
|
|
1974
|
-
**Step 3: View a migration guide**
|
|
1975
|
-
- Without trailing slash: \`{ path: "upgrade-to-v1/agent" }\`
|
|
1976
|
-
- Returns the full migration guide content
|
|
1977
|
-
|
|
1978
|
-
**Step 4: Explore guide sections (optional)**
|
|
1979
|
-
- List sections: \`{ path: "upgrade-to-v1/agent", listSections: true }\`
|
|
1980
|
-
- Get specific sections: \`{ path: "upgrade-to-v1/agent", sections: ["Voice methods"] }\`
|
|
1981
|
-
|
|
1982
|
-
**Alternative: Search by keywords**
|
|
1983
|
-
- \`{ queryKeywords: ["RuntimeContext", "pagination"] }\`
|
|
1984
|
-
|
|
1985
|
-
**Examples:**
|
|
1986
|
-
1. List top-level: \`{}\`
|
|
1987
|
-
2. Navigate to upgrade-to-v1: \`{ path: "upgrade-to-v1/" }\`
|
|
1988
|
-
3. Get agent guide: \`{ path: "upgrade-to-v1/agent" }\`
|
|
1989
|
-
4. List guide sections: \`{ path: "upgrade-to-v1/agent", listSections: true }\`
|
|
1990
|
-
5. Search: \`{ queryKeywords: ["RuntimeContext"] }\`
|
|
1991
|
-
|
|
1992
|
-
**Tip:** Paths ending with \`/\` list directory contents. Paths without \`/\` fetch the migration guide.`,
|
|
1993
|
-
parameters: migrationInputSchema,
|
|
1994
|
-
execute: async (args) => {
|
|
1995
|
-
void logger.debug("Executing mastraMigration tool", { args });
|
|
1996
|
-
try {
|
|
1997
|
-
if (args.queryKeywords && args.queryKeywords.length > 0) {
|
|
1998
|
-
const suggestions = await getMatchingPaths("", args.queryKeywords, migrationsBaseDir);
|
|
1999
|
-
return [
|
|
2000
|
-
"# Migration Guide Search Results",
|
|
2001
|
-
"",
|
|
2002
|
-
suggestions || "No migration guides found matching your keywords.",
|
|
2003
|
-
"",
|
|
2004
|
-
"---",
|
|
2005
|
-
"",
|
|
2006
|
-
"To see all available migrations, call with no parameters."
|
|
2007
|
-
].join("\n");
|
|
2008
|
-
}
|
|
2009
|
-
if (args.path) {
|
|
2010
|
-
if (args.path.endsWith("/")) {
|
|
2011
|
-
const dirPath = args.path.slice(0, -1);
|
|
2012
|
-
return await listDirectoryContents(dirPath);
|
|
2013
|
-
}
|
|
2014
|
-
if (args.listSections) {
|
|
2015
|
-
const headers = await getSectionHeaders(args.path);
|
|
2016
|
-
if (headers.length === 0) {
|
|
2017
|
-
return await listDirectoryContents();
|
|
2018
|
-
}
|
|
2019
|
-
return [
|
|
2020
|
-
`# ${args.path} - Section Headers`,
|
|
2021
|
-
"",
|
|
2022
|
-
"Available sections in this migration guide:",
|
|
2023
|
-
"",
|
|
2024
|
-
...headers.map((h) => `${"#".repeat(h.level)} ${h.title}`),
|
|
2025
|
-
"",
|
|
2026
|
-
"---",
|
|
2027
|
-
"",
|
|
2028
|
-
'To get specific sections, provide their titles in the "sections" parameter.'
|
|
2029
|
-
].join("\n");
|
|
2030
|
-
}
|
|
2031
|
-
const content = await getSections(args.path, args.sections);
|
|
2032
|
-
return `# ${args.path}
|
|
2033
|
-
|
|
2034
|
-
${content}`;
|
|
2035
|
-
}
|
|
2036
|
-
return await listDirectoryContents();
|
|
2037
|
-
} catch (error) {
|
|
2038
|
-
void logger.error("Failed to execute mastraMigration tool", error);
|
|
2039
|
-
throw error;
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
};
|
|
2043
|
-
|
|
2044
|
-
// src/index.ts
|
|
2045
|
-
var server;
|
|
2046
|
-
server = new MCPServer({
|
|
2047
|
-
name: "Mastra Documentation Server",
|
|
2048
|
-
version: JSON.parse(await fs3.readFile(fromPackageRoot(`package.json`), "utf8")).version,
|
|
2049
|
-
tools: {
|
|
2050
|
-
mastraDocs: docsTool,
|
|
2051
|
-
mastraMigration: migrationTool,
|
|
2052
|
-
startMastraCourse,
|
|
2053
|
-
getMastraCourseStatus,
|
|
2054
|
-
startMastraCourseLesson,
|
|
2055
|
-
nextMastraCourseStep,
|
|
2056
|
-
clearMastraCourseHistory,
|
|
2057
|
-
// Embedded docs tools for reading docs from installed packages
|
|
2058
|
-
...embeddedDocsTools
|
|
2059
|
-
},
|
|
2060
|
-
prompts: migrationPromptMessages
|
|
2061
|
-
});
|
|
2062
|
-
Object.assign(logger, createLogger(server));
|
|
2063
|
-
async function runServer() {
|
|
2064
|
-
try {
|
|
2065
|
-
await server.startStdio();
|
|
2066
|
-
void logger.info("Started Mastra Docs MCP Server");
|
|
2067
|
-
} catch (error) {
|
|
2068
|
-
void logger.error("Failed to start server", error);
|
|
2069
|
-
process.exit(1);
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2
|
+
import { setLogLevel, runServer, writeErrorLog } from './chunk-GLPCVXXO.js';
|
|
2072
3
|
|
|
2073
4
|
// src/stdio.ts
|
|
2074
5
|
function parseLogLevel() {
|