@mastra/mcp-docs-server 1.2.26 → 1.2.27-alpha.11
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/agents/structured-output.md +17 -0
- package/.docs/docs/connections/a2a.md +4 -3
- package/.docs/docs/deployment/monorepo.md +2 -2
- package/.docs/docs/evals/datasets.md +53 -0
- package/.docs/docs/guides/build-an-eval-loop.md +395 -0
- package/.docs/docs/harness/durable-agents.md +27 -2
- package/.docs/docs/mastra-platform/alerts.md +83 -0
- package/.docs/docs/mastra-platform/observability.md +184 -0
- package/.docs/docs/mastra-platform/overview.md +2 -0
- package/.docs/docs/memory/message-history.md +21 -0
- package/.docs/docs/memory/observational-memory.md +33 -0
- package/.docs/docs/observability/feedback.md +1 -1
- package/.docs/docs/observability/tracing/overview.md +2 -0
- package/.docs/docs/server/custom-adapters.md +43 -0
- package/.docs/docs/subagents.md +38 -7
- package/.docs/integrations/channels/github.md +6 -2
- package/.docs/integrations/databases/clickhouse.md +1 -1
- package/.docs/integrations/observability/confident-ai.md +67 -43
- package/.docs/integrations/observability/langfuse.md +4 -0
- package/.docs/integrations/observability/opentelemetry.md +14 -6
- package/.docs/integrations/sandboxes/cloudflare-sandbox.md +36 -4
- package/.docs/models/environment-variables.md +5 -1
- package/.docs/models/gateways/netlify.md +8 -4
- package/.docs/models/gateways/openrouter.md +5 -2
- package/.docs/models/gateways/vercel.md +378 -379
- package/.docs/models/index.md +22 -1
- package/.docs/models/providers/ai21.md +78 -0
- package/.docs/models/providers/ainetcafe.md +77 -0
- package/.docs/models/providers/alibaba-cn.md +8 -6
- package/.docs/models/providers/alibaba-token-plan-cn.md +2 -1
- package/.docs/models/providers/alibaba-token-plan.md +3 -1
- package/.docs/models/providers/alibaba.md +2 -1
- package/.docs/models/providers/chutes.md +2 -2
- package/.docs/models/providers/cortecs.md +6 -7
- package/.docs/models/providers/digitalocean.md +1 -1
- package/.docs/models/providers/edenai.md +4 -7
- package/.docs/models/providers/empiriolabs.md +2 -1
- package/.docs/models/providers/fireworks-ai.md +11 -10
- package/.docs/models/providers/hyper.md +26 -37
- package/.docs/models/providers/inception.md +3 -3
- package/.docs/models/providers/inco.md +83 -0
- package/.docs/models/providers/iteracompute.md +14 -7
- package/.docs/models/providers/kilo.md +12 -9
- package/.docs/models/providers/llmgateway-providers.md +4 -2
- package/.docs/models/providers/llmgateway.md +1 -1
- package/.docs/models/providers/mistral.md +3 -2
- package/.docs/models/providers/nano-gpt.md +10 -19
- package/.docs/models/providers/nvidia.md +2 -1
- package/.docs/models/providers/oci.md +85 -0
- package/.docs/models/providers/ofox.md +24 -23
- package/.docs/models/providers/opencode.md +2 -1
- package/.docs/models/providers/ovhcloud.md +1 -1
- package/.docs/models/providers/privatemode-ai.md +3 -3
- package/.docs/models/providers/scnet-token-plan.md +2 -1
- package/.docs/models/providers/synthetic.md +2 -1
- package/.docs/models/providers/tensorx.md +2 -1
- package/.docs/models/providers/tinfoil.md +1 -1
- package/.docs/models/providers/umans-ai-coding-plan.md +3 -4
- package/.docs/models/providers/umans-ai.md +3 -4
- package/.docs/models/providers/vancine.md +10 -10
- package/.docs/models/providers/volcengine.md +3 -2
- package/.docs/models/providers/wandb.md +4 -4
- package/.docs/models/providers/xai.md +1 -3
- package/.docs/models/providers/zhipuai-coding-plan.md +2 -8
- package/.docs/models/providers.md +5 -1
- package/.docs/reference/agents/durable-agent.md +9 -1
- package/.docs/reference/agents/generate.md +1 -1
- package/.docs/reference/agents/inngest-agent.md +2 -0
- package/.docs/reference/auth/clerk.md +25 -1
- package/.docs/reference/cli/mastra.md +84 -0
- package/.docs/reference/client-js/agents.md +25 -0
- package/.docs/reference/client-js/mastra-client.md +1 -1
- package/.docs/reference/client-js/observability.md +101 -4
- package/.docs/reference/code-sdk/mount-agent-controller.md +23 -0
- package/.docs/reference/core/getMCPServer.md +47 -0
- package/.docs/reference/core/mastra-class.md +1 -1
- package/.docs/reference/index.md +2 -0
- package/.docs/reference/memory/memory-class.md +2 -0
- package/.docs/reference/memory/observational-memory.md +34 -4
- package/.docs/reference/observability/tracing/interfaces.md +3 -1
- package/.docs/reference/observability/tracing/trace-query.md +219 -46
- package/.docs/reference/pubsub/redis-streams.md +34 -0
- package/.docs/reference/rag/vector-databases.md +73 -0
- package/.docs/reference/storage/retention.md +56 -4
- package/.docs/reference/streaming/agents/stream.md +1 -1
- package/.docs/reference/tools/mcp-server.md +0 -28
- package/.docs/reference/vectors/azure-ai-search.md +150 -0
- package/.docs/reference/vectors/weaviate.md +128 -0
- package/.docs/reference/workspace/workspace-class.md +10 -2
- package/package.json +10 -12
- package/.docs/docs/connections/connect-mcp-client.md +0 -211
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
# Advanced trace queries
|
|
6
6
|
|
|
7
|
-
Use `POST /api/observability/traces/query` to find completed logical traces that match trace fields and related span or
|
|
7
|
+
Use `queryTraces()` or `POST /api/observability/traces/query` to find completed logical traces that match trace fields and related span, score, or feedback records. Use `queryTraceThreads()` or `POST /api/observability/threads/query` to find thread identities that qualify across multiple eligible traces.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Both endpoints use the same authentication as other observability routes and require the `observability:read` permission. The configured observability store must support the requested trace-query or thread-query operation.
|
|
10
10
|
|
|
11
11
|
## Query traces with the client SDK
|
|
12
12
|
|
|
@@ -37,7 +37,74 @@ const result = await mastraClient.queryTraces({
|
|
|
37
37
|
|
|
38
38
|
A `some` clause matches when one related record satisfies its complete nested predicate. In this example, `scorerId`, `scorerVersion`, and `score` must match on the same score record. A `none` clause uses anti-existence semantics: it matches when no related record satisfies its complete nested predicate. A trace with no related scores therefore matches `scores.none`.
|
|
39
39
|
|
|
40
|
-
Span clauses examine the current root span and current child spans. The root `timeRange` applies only to the selected current root's `startedAt`. Related spans and
|
|
40
|
+
Span clauses examine the current root span and current child spans. The root `timeRange` applies only to the selected current root's `startedAt`. Related spans, scores, and feedback can participate even when their own timestamps are outside that range. Related records correlate only through a matching non-null `traceId`.
|
|
41
|
+
|
|
42
|
+
## Query threads across traces
|
|
43
|
+
|
|
44
|
+
Pass an eligible trace selection and an optional thread predicate to `queryTraceThreads()`:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const result = await mastraClient.queryTraceThreads({
|
|
48
|
+
traces: {
|
|
49
|
+
timeRange: {
|
|
50
|
+
from: '2026-08-01T00:00:00.000Z',
|
|
51
|
+
to: '2026-08-08T00:00:00.000Z',
|
|
52
|
+
},
|
|
53
|
+
where: {
|
|
54
|
+
op: 'eq',
|
|
55
|
+
left: { path: 'environment' },
|
|
56
|
+
right: { literal: 'production' },
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
where: {
|
|
60
|
+
op: 'and',
|
|
61
|
+
args: [
|
|
62
|
+
{
|
|
63
|
+
traces: {
|
|
64
|
+
some: {
|
|
65
|
+
scores: {
|
|
66
|
+
some: {
|
|
67
|
+
op: 'lt',
|
|
68
|
+
left: { path: 'score' },
|
|
69
|
+
right: { literal: 0.6 },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
traces: {
|
|
77
|
+
some: {
|
|
78
|
+
feedback: {
|
|
79
|
+
some: {
|
|
80
|
+
op: 'eq',
|
|
81
|
+
left: { path: 'feedbackType' },
|
|
82
|
+
right: { literal: 'clinician-correction' },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
page: { limit: 25 },
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The server applies `traces.timeRange` and `traces.where` first. This produces the complete eligible trace population. It then derives distinct non-null `threadId` values and evaluates the top-level `where` predicate against eligible traces in each thread.
|
|
95
|
+
|
|
96
|
+
One `traces.some` clause requires one eligible trace to satisfy its complete nested trace predicate. Separate `traces.some` clauses are independent, so different traces in the same thread can satisfy them. Nested `spans.some`, `scores.some`, and `feedback.some` clauses still require one related record to satisfy every condition inside that clause.
|
|
97
|
+
|
|
98
|
+
`traces.none: P` uses anti-existence semantics: it matches only when no eligible trace in the thread satisfies `P`. This differs from `traces.some: { feedback: { none: P } }`, which requires at least one eligible trace with no matching feedback record.
|
|
99
|
+
|
|
100
|
+
The response contains identities only:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"threads": [{ "threadId": "thread-123" }],
|
|
105
|
+
"page": { "next": null }
|
|
106
|
+
}
|
|
107
|
+
```
|
|
41
108
|
|
|
42
109
|
## Send an HTTP request
|
|
43
110
|
|
|
@@ -65,34 +132,143 @@ curl --request POST \
|
|
|
65
132
|
}'
|
|
66
133
|
```
|
|
67
134
|
|
|
135
|
+
## Discover fields and values
|
|
136
|
+
|
|
137
|
+
Use trace-query discovery to build field and value autocomplete without scanning trace payloads or copying the query grammar into your client. The configured observability store must support `trace-query-discovery`.
|
|
138
|
+
|
|
139
|
+
Call `getTraceQueryFields()` for one predicate scope:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
const timeRange = {
|
|
143
|
+
from: '2026-08-01T00:00:00.000Z',
|
|
144
|
+
to: '2026-08-08T00:00:00.000Z',
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const fields = await mastraClient.getTraceQueryFields({
|
|
148
|
+
timeRange,
|
|
149
|
+
predicateScope: 'trace',
|
|
150
|
+
search: 'region',
|
|
151
|
+
limit: 25,
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The response separates canonical fields from observed metadata fields:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"canonicalFields": [],
|
|
160
|
+
"observedFields": [
|
|
161
|
+
{
|
|
162
|
+
"path": "metadata.region",
|
|
163
|
+
"valueKind": "string",
|
|
164
|
+
"operators": ["eq", "ne", "in", "notIn", "exists", "notExists"],
|
|
165
|
+
"valueSuggestions": true,
|
|
166
|
+
"occurrences": 125
|
|
167
|
+
}
|
|
168
|
+
],
|
|
169
|
+
"observedFieldsTruncated": false
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Canonical fields come from the same registry used to validate trace queries. They don't consume `limit`. The canonical order follows that registry. `observedFields` contains top-level string `metadata.<key>` paths found on qualifying current root spans. These fields are ordered by `occurrences` descending, then by path. Nested metadata, keys containing `.`, empty keys, non-string values, and oversized paths are omitted.
|
|
174
|
+
|
|
175
|
+
`predicateScope` selects the local predicate grammar. It isn't an authorization scope:
|
|
176
|
+
|
|
177
|
+
| `predicateScope` | Example returned path | Query usage |
|
|
178
|
+
| ---------------- | ---------------------------------- | ----------------------------------------- |
|
|
179
|
+
| `trace` | `environment` or `metadata.region` | Root `where` predicate |
|
|
180
|
+
| `spans` | `model` | Inside `spans.some` or `spans.none` |
|
|
181
|
+
| `scores` | `scorerId` | Inside `scores.some` or `scores.none` |
|
|
182
|
+
| `feedback` | `feedbackType` | Inside `feedback.some` or `feedback.none` |
|
|
183
|
+
|
|
184
|
+
A global field picker can request all four scopes in parallel and qualify display labels locally. Every returned path is directly accepted by `queryTraces()` in its matching scope. `metadata` by itself isn't a field. A user interface can offer manual metadata-key entry separately.
|
|
185
|
+
|
|
186
|
+
After a user selects one field with `valueSuggestions: true`, call `getTraceQueryValues()`:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const abortController = new AbortController()
|
|
190
|
+
const suggestions = await mastraClient.getTraceQueryValues(
|
|
191
|
+
{
|
|
192
|
+
timeRange,
|
|
193
|
+
predicateScope: 'trace',
|
|
194
|
+
path: 'metadata.region',
|
|
195
|
+
search: 'west',
|
|
196
|
+
limit: 25,
|
|
197
|
+
},
|
|
198
|
+
{ signal: abortController.signal },
|
|
199
|
+
)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"values": [
|
|
205
|
+
{ "value": "eu-west-1", "count": 82 },
|
|
206
|
+
{ "value": "us-west-2", "count": 43 }
|
|
207
|
+
],
|
|
208
|
+
"valuesTruncated": false
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Discovery returns string values only. Missing values, empty strings, and strings larger than 4,096 UTF-8 bytes are omitted. Values are ordered by `count` descending, then by value. Suggestions are advisory: `queryTraces()` continues to accept valid manual literals that discovery doesn't return.
|
|
213
|
+
|
|
214
|
+
Value suggestions are available for these canonical fields:
|
|
215
|
+
|
|
216
|
+
| Scope | Fields |
|
|
217
|
+
| -------- | ----------------------------------------------------------------------------- |
|
|
218
|
+
| Trace | `entityName`, `entityType`, `environment`, `status` |
|
|
219
|
+
| Spans | `name`, `spanType`, `model`, `provider`, `status`, `entityType`, `entityName` |
|
|
220
|
+
| Scores | `scorerId`, `scorerVersion`, `scoreSource` |
|
|
221
|
+
| Feedback | `feedbackType`, `feedbackSource` |
|
|
222
|
+
|
|
223
|
+
Each executable top-level string `metadata.<key>` field also supports value suggestions in the `trace` scope. Identifiers, timestamps, durations, numeric score or feedback values, comments, errors, and version IDs don't. The fields response still includes canonical fields with `valueSuggestions: false`. The values endpoint rejects those paths with `422 TRACE_QUERY_INVALID`.
|
|
224
|
+
|
|
225
|
+
Both discovery requests require a half-open time range of at most 31 days. They use the same current-record semantics as `queryTraces()`: qualifying completed roots satisfy `from <= startedAt < to`, and related values come only from current records joined to those roots by `traceId`. Discovery doesn't accept or apply a draft `where` predicate.
|
|
226
|
+
|
|
227
|
+
`search` defaults to the empty string, is trimmed, and matches case-insensitive literal substrings. Characters such as `%` and `_` have no wildcard meaning. An empty search returns the most frequent results. `limit` defaults to 25 and has a maximum of 100. Both endpoints fetch one extra result to set their truncation flag, but don't provide a cursor. When a flag is `true`, refine `search`. It doesn't imply that a next page is available. No matches return an empty array and `false` truncation.
|
|
228
|
+
|
|
229
|
+
If discovery exceeds its execution timeout, the route returns `504 TRACE_QUERY_EXECUTION_TIMEOUT`. If the backend exceeds a memory or resource budget, it returns `503 TRACE_QUERY_RESOURCE_LIMIT`. Neither failure returns partial suggestions or sets a truncation flag. Truncation only means a complete ranking was cut to the requested response limit.
|
|
230
|
+
|
|
231
|
+
ClickHouse discovery requests default to a 5-second timeout and a 256 MiB per-query memory limit. Configure these with `observability.traceQuery.discovery.timeoutMs` and `observability.traceQuery.discovery.memoryLimitBytes` on `ClickhouseStoreVNext`. Discovery falls back to `observability.traceQuery.timeoutMs` when its timeout isn't configured, without changing ordinary `queryTraces()` execution limits.
|
|
232
|
+
|
|
233
|
+
Both routes require `observability:read` and use the configured observability storage boundary. Don't send organization or project authorization fields in the request. Treat discovered paths, values, and counts as observability data.
|
|
234
|
+
|
|
68
235
|
## Request fields
|
|
69
236
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
237
|
+
### Trace queries
|
|
238
|
+
|
|
239
|
+
| Field | Required | Description |
|
|
240
|
+
| ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
241
|
+
| `timeRange` | Yes | Trace start-time boundary. `from` is inclusive and `to` is exclusive. Both values must be ISO timestamps, `from` must be earlier than `to`, and the range can't exceed 31 days. |
|
|
242
|
+
| `where` | No | Recursive trace predicate. Supports scalar conditions and `spans`, `scores`, and `feedback` `some` or `none` clauses. |
|
|
243
|
+
| `orderBy` | No | One item ordering results by `startedAt` or `endedAt`, in `asc` or `desc` order. Defaults to `startedAt desc`. |
|
|
244
|
+
| `page` | No | `{ limit, after }`. `limit` defaults to 100 and has a maximum of 1000. Pass the opaque `page.next` value as `after`. |
|
|
245
|
+
|
|
246
|
+
### Thread queries
|
|
77
247
|
|
|
78
|
-
|
|
248
|
+
| Field | Required | Description |
|
|
249
|
+
| -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
250
|
+
| `traces` | Yes | Eligible trace selection containing `timeRange` and an optional trace `where` predicate. |
|
|
251
|
+
| `where` | No | Recursive thread predicate composed with boolean operators and `traces.some` or `traces.none`. Each quantifier contains a complete trace predicate. |
|
|
252
|
+
| `page` | No | `{ limit, after }`. Thread identities always use fixed ordinal `threadId` ascending order. Pass the opaque `page.next` value as `after`. |
|
|
253
|
+
|
|
254
|
+
Unknown fields are rejected. Requests can't select a projection, declare joins, request counts or measures, or control authorization. Request bodies are limited to 256 KiB.
|
|
79
255
|
|
|
80
256
|
## Query limits
|
|
81
257
|
|
|
82
258
|
The planner rejects a query before storage execution when it exceeds any of these limits:
|
|
83
259
|
|
|
84
|
-
| Input
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
| Predicate nesting depth
|
|
88
|
-
| Predicate nodes
|
|
89
|
-
| Related `spans` and `
|
|
90
|
-
| Values in one `in` or `notIn` set
|
|
91
|
-
| Comparison literals and membership values
|
|
92
|
-
| String literal
|
|
93
|
-
| Raw predicate path
|
|
94
|
-
| `page.limit`
|
|
95
|
-
| HTTP request body
|
|
260
|
+
| Input | Maximum |
|
|
261
|
+
| ----------------------------------------------------------- | ----------------- |
|
|
262
|
+
| Trace selection `timeRange` | 31 days |
|
|
263
|
+
| Predicate nesting depth | 12 levels |
|
|
264
|
+
| Predicate nodes | 100 |
|
|
265
|
+
| Related `traces`, `spans`, `scores`, and `feedback` clauses | 8 total |
|
|
266
|
+
| Values in one `in` or `notIn` set | 100 |
|
|
267
|
+
| Comparison literals and membership values | 1,000 total |
|
|
268
|
+
| String literal | 4,096 UTF-8 bytes |
|
|
269
|
+
| Raw predicate path | 128 UTF-8 bytes |
|
|
270
|
+
| `page.limit` | 1,000 |
|
|
271
|
+
| HTTP request body | 256 KiB |
|
|
96
272
|
|
|
97
273
|
Each comparison literal counts as one literal. Each member of an `in` or `notIn` set also counts as one literal, even when the set is within its per-set limit.
|
|
98
274
|
|
|
@@ -271,7 +447,7 @@ const messageTrace = {
|
|
|
271
447
|
|
|
272
448
|
The key must name one top-level property. Empty keys and nested paths are rejected. Metadata keys aren't trimmed, so leading and trailing whitespace remains part of the exact key identity. When metadata duplicates a canonical trace field, such as `resourceId`, `threadId`, or `environment`, prefer the canonical field because it uses the dedicated storage column. The `metadata.<key>` form remains available when you specifically need the value from the metadata object. Metadata and canonical values may differ.
|
|
273
449
|
|
|
274
|
-
Metadata fields aren't available for grouping
|
|
450
|
+
Metadata fields aren't available for grouping. Trace-query discovery returns executable top-level string metadata fields observed in the selected time range.
|
|
275
451
|
|
|
276
452
|
### Filter by feedback
|
|
277
453
|
|
|
@@ -336,43 +512,40 @@ An ungrouped query returns only lightweight completed traces:
|
|
|
336
512
|
}
|
|
337
513
|
```
|
|
338
514
|
|
|
339
|
-
A
|
|
515
|
+
A thread query returns distinct non-null thread IDs in ordinal ascending order:
|
|
340
516
|
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
},
|
|
347
|
-
group: { by: ['threadId'] },
|
|
348
|
-
})
|
|
349
|
-
// { groups: [{ threadId: 'thread-123' }], page: { next: null } }
|
|
517
|
+
```json
|
|
518
|
+
{
|
|
519
|
+
"threads": [{ "threadId": "thread-123" }],
|
|
520
|
+
"page": { "next": null }
|
|
521
|
+
}
|
|
350
522
|
```
|
|
351
523
|
|
|
352
524
|
Related evidence isn't embedded in either response. Use the trace-detail and branch APIs to load spans after selecting a result.
|
|
353
525
|
|
|
354
526
|
## Pagination and errors
|
|
355
527
|
|
|
356
|
-
Ordering is deterministic.
|
|
528
|
+
Ordering is deterministic. Trace ordering appends `traceId` ascending as a tie-breaker. Thread queries always use raw ordinal `threadId` ascending order. Callers can't override it.
|
|
357
529
|
|
|
358
|
-
Cursors are bound to the accepted normalized query
|
|
530
|
+
Cursors are bound to the operation, accepted normalized query, authorization state, and ordering. Reusing a cursor after changing the trace selection, predicates, or ordering returns `409`. Trace and thread cursors aren't interchangeable. A malformed cursor returns `400`.
|
|
359
531
|
|
|
360
532
|
Cursor pagination is deterministic, but it isn't a database snapshot. Traces or replacement signals written between page requests can change later pages.
|
|
361
533
|
|
|
362
|
-
PostgreSQL and ClickHouse stop
|
|
534
|
+
PostgreSQL and ClickHouse stop advanced trace and thread queries after 15 seconds by default and return `504` when the database timeout is exceeded. Set `traceQueryTimeoutMs` in the store's vNext observability configuration to an integer from 1 through 300,000 milliseconds to change the timeout. DuckDB doesn't currently provide query-scoped timeout or cancellation through its driver wrapper, so this `504` guarantee doesn't apply to DuckDB.
|
|
363
535
|
|
|
364
|
-
| Status | Meaning
|
|
365
|
-
| ------ |
|
|
366
|
-
| `400` | Malformed JSON or malformed cursor.
|
|
367
|
-
| `409` | The cursor doesn't match the query.
|
|
368
|
-
| `413` | The request body exceeds 256 KiB.
|
|
369
|
-
| `422` | The JSON is well formed, but the request is structurally or semantically invalid. The response includes stable issue codes and paths. |
|
|
370
|
-
| `501` | The configured observability store doesn't support
|
|
371
|
-
| `
|
|
536
|
+
| Status | Meaning |
|
|
537
|
+
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
538
|
+
| `400` | Malformed JSON or malformed cursor. |
|
|
539
|
+
| `409` | The cursor doesn't match the query. |
|
|
540
|
+
| `413` | The request body exceeds 256 KiB. |
|
|
541
|
+
| `422` | The JSON is well formed, but the request is structurally or semantically invalid. The response includes stable issue codes and paths. Discovery validation uses `TRACE_QUERY_INVALID`. |
|
|
542
|
+
| `501` | The installed Core or configured observability store doesn't support the requested operation. Discovery returns `TRACE_QUERY_DISCOVERY_UNSUPPORTED` unless Core provides the discovery contract and the store implements both field and value discovery. |
|
|
543
|
+
| `503` | Discovery exceeded a backend memory or resource budget and returned `TRACE_QUERY_RESOURCE_LIMIT`. |
|
|
544
|
+
| `504` | A PostgreSQL or ClickHouse query exceeded its configured database execution timeout. Discovery returns `TRACE_QUERY_EXECUTION_TIMEOUT`. |
|
|
372
545
|
|
|
373
546
|
## Limitations
|
|
374
547
|
|
|
375
|
-
|
|
548
|
+
Both operations consider completed traces only. They don't support running traces, custom projections, embedded evidence, summaries, aggregations, counts, measures, or custom grouping.
|
|
376
549
|
|
|
377
550
|
## Related
|
|
378
551
|
|
|
@@ -55,6 +55,36 @@ export const mastra = new Mastra({
|
|
|
55
55
|
})
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
### Redis Cluster
|
|
59
|
+
|
|
60
|
+
Pass `cluster` instead of `url`/`redisOptions` to connect to a Redis Cluster (for example, AWS ElastiCache with cluster mode enabled). The options are forwarded to `createCluster()` from `redis`.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { Mastra } from '@mastra/core'
|
|
64
|
+
import { RedisStreamsPubSub } from '@mastra/redis-streams'
|
|
65
|
+
|
|
66
|
+
export const mastra = new Mastra({
|
|
67
|
+
pubsub: new RedisStreamsPubSub({
|
|
68
|
+
cluster: {
|
|
69
|
+
rootNodes: [{ url: 'redis://node-1:6379' }, { url: 'redis://node-2:6379' }],
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Bring your own client
|
|
76
|
+
|
|
77
|
+
To control client construction yourself (TLS, credential providers, and so on), pass an unconnected `redis` client as `client`. The pubsub uses it as its writer and calls `client.duplicate()` for each subscription's blocking reader, so every connection stays distinct. The pubsub owns the client's lifecycle (it connects on first use and quits it on `close()`), so don't share it with the rest of your app.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { RedisStreamsPubSub } from '@mastra/redis-streams'
|
|
81
|
+
import { createClient } from 'redis'
|
|
82
|
+
|
|
83
|
+
const pubsub = new RedisStreamsPubSub({
|
|
84
|
+
client: createClient({ url: process.env.REDIS_URL, socket: { tls: true } }),
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
58
88
|
## Constructor parameters
|
|
59
89
|
|
|
60
90
|
**url** (`string`): Redis connection URL. Falls back to redisOptions.url. (Default: `redis://localhost:6379`)
|
|
@@ -65,6 +95,10 @@ export const mastra = new Mastra({
|
|
|
65
95
|
|
|
66
96
|
**redisOptions** (`RedisClientOptions`): Options passed to the underlying redis client for advanced configuration.
|
|
67
97
|
|
|
98
|
+
**cluster** (`RedisClusterOptions`): Connect to a Redis Cluster. Options are passed to createCluster() from redis. Mutually exclusive with url, redisOptions, and client.
|
|
99
|
+
|
|
100
|
+
**client** (`RedisClientType | RedisClusterType`): A pre-configured, unconnected redis client (standalone or cluster) used as the writer; readers are created with client.duplicate(). The pubsub owns its lifecycle (connects lazily, quits on close()). Mutually exclusive with url, redisOptions, and cluster.
|
|
101
|
+
|
|
68
102
|
**maxStreamLength** (`number`): Approximate maximum number of entries kept per stream. Set to 0 to disable trimming. (Default: `10000`)
|
|
69
103
|
|
|
70
104
|
**streamIdleTtlMs** (`number`): Idle expiry in milliseconds: a sliding TTL refreshed on every write (publish, nack retry, group re-creation). Each write resets it, so an actively-written stream never expires mid-flight; a stream left idle for the full duration is deleted by Redis automatically. Note that only writes refresh the TTL — a consumer slowly draining a backlog does not — so set it well above the longest expected gap between writes on a live topic. This is a backstop, not the primary cleanup — clearTopic handles normal end-of-lifecycle deletion; this only bounds memory for streams that never reach a clearTopic call (e.g. a crashed run). Must be a non-negative integer. Defaults to 0 (disabled). (Default: `0`)
|
|
@@ -248,6 +248,34 @@ await store.upsert({
|
|
|
248
248
|
})
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
+
**Weaviate**:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
import { WeaviateVector } from '@mastra/weaviate'
|
|
255
|
+
|
|
256
|
+
const store = new WeaviateVector({
|
|
257
|
+
id: 'weaviate-vector',
|
|
258
|
+
httpHost: process.env.WEAVIATE_HOST,
|
|
259
|
+
httpPort: 443,
|
|
260
|
+
httpSecure: true,
|
|
261
|
+
grpcHost: process.env.WEAVIATE_GRPC_HOST,
|
|
262
|
+
grpcPort: 443,
|
|
263
|
+
grpcSecure: true,
|
|
264
|
+
apiKey: process.env.WEAVIATE_API_KEY,
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
await store.createIndex({
|
|
268
|
+
indexName: 'myCollection',
|
|
269
|
+
dimension: 1536,
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
await store.upsert({
|
|
273
|
+
indexName: 'myCollection',
|
|
274
|
+
vectors: embeddings,
|
|
275
|
+
metadata: chunks.map(chunk => ({ text: chunk.text })),
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
251
279
|
**Cloudflare**:
|
|
252
280
|
|
|
253
281
|
```ts
|
|
@@ -317,6 +345,29 @@ await store.upsert({
|
|
|
317
345
|
|
|
318
346
|
For detailed setup instructions and best practices, see the [official Elasticsearch documentation](https://www.elastic.co/docs/solutions/search/get-started).
|
|
319
347
|
|
|
348
|
+
**Azure AI Search**:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
import { AzureAISearchVector } from '@mastra/azure-ai-search'
|
|
352
|
+
|
|
353
|
+
const store = new AzureAISearchVector({
|
|
354
|
+
id: 'azure-search-vectors',
|
|
355
|
+
endpoint: process.env.AZURE_AI_SEARCH_ENDPOINT!,
|
|
356
|
+
credential: process.env.AZURE_AI_SEARCH_CREDENTIAL!,
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
await store.createIndex({
|
|
360
|
+
indexName: 'my-collection',
|
|
361
|
+
dimension: 1536,
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
await store.upsert({
|
|
365
|
+
indexName: 'my-collection',
|
|
366
|
+
vectors: embeddings,
|
|
367
|
+
metadata: chunks.map(chunk => ({ text: chunk.text })),
|
|
368
|
+
})
|
|
369
|
+
```
|
|
370
|
+
|
|
320
371
|
**Couchbase**:
|
|
321
372
|
|
|
322
373
|
```ts
|
|
@@ -534,6 +585,17 @@ Namespace names must:
|
|
|
534
585
|
|
|
535
586
|
- Example: `_namespace` isn't valid (starts with underscore)
|
|
536
587
|
|
|
588
|
+
**Weaviate**:
|
|
589
|
+
|
|
590
|
+
Index names map to Weaviate collections, which:
|
|
591
|
+
|
|
592
|
+
- Are capitalized by Weaviate (the first letter is upper-cased)
|
|
593
|
+
- Should contain only letters, numbers, and the `_` character
|
|
594
|
+
- Must start with a letter, since only the first character is upper-cased (a leading digit or symbol stays invalid)
|
|
595
|
+
- Preserve the original Mastra index name in the collection description, so `listIndexes()` and `describeIndex()` return the name you supplied
|
|
596
|
+
- Example: `my_collection` is stored as `My_collection` and returned as `my_collection`
|
|
597
|
+
- Example: `123_collection` isn't valid (starts with a digit)
|
|
598
|
+
|
|
537
599
|
**Cloudflare**:
|
|
538
600
|
|
|
539
601
|
Index names must:
|
|
@@ -587,6 +649,17 @@ Index names must:
|
|
|
587
649
|
- Example: `myindex-` isn't valid (ends with hyphen)
|
|
588
650
|
- Example: `MyIndex` isn't valid (contains uppercase letters)
|
|
589
651
|
|
|
652
|
+
**Azure AI Search**:
|
|
653
|
+
|
|
654
|
+
Index names must:
|
|
655
|
+
|
|
656
|
+
- Use only lowercase letters, numbers, dashes (`-`), and underscore (`_`) characters
|
|
657
|
+
- Not start or end with a dash
|
|
658
|
+
- Be between 2 and 128 characters long
|
|
659
|
+
- Example: `my-index-123` and `my_index` are valid
|
|
660
|
+
- Example: `MyIndex` isn't valid (contains uppercase letters)
|
|
661
|
+
- Example: `my-index-` isn't valid (ends with a dash)
|
|
662
|
+
|
|
590
663
|
### Upserting Embeddings
|
|
591
664
|
|
|
592
665
|
After creating an index, you can store embeddings along with their basic metadata:
|
|
@@ -6,11 +6,37 @@
|
|
|
6
6
|
|
|
7
7
|
Because storage grows without bound by default, Mastra provides an opt-in, age-based retention system. Declare per-table `maxAge` policies in the `retention` config, then call `storage.prune()` to delete rows older than their configured age. Unconfigured data is kept forever, so behavior doesn't change until you opt in.
|
|
8
8
|
|
|
9
|
-
`prune()` deletes rows.
|
|
9
|
+
`prune()` deletes rows in bounded batches. Runs are resumable and cancellable, so you can limit how much work each maintenance window performs. Pruning doesn't reclaim disk space by itself. Use the database-specific maintenance guidance below when you need to return freed space to the operating system.
|
|
10
10
|
|
|
11
11
|
Retention covers **growth tables** only: tables that accumulate rows unbounded as a side effect of normal operation (conversation history, telemetry, job and run records, schedule fire history, event feeds). User-authored artifacts and config (agents, skills, workspaces, prompt blocks, datasets, schedule definitions, channel installations, and so on) grow with user intent and are edited or deleted explicitly, so they're not valid retention keys.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Storage adapters use the shared core retention contract for `prune()`, or a database-native mechanism when that better matches the backend.
|
|
14
|
+
|
|
15
|
+
| Adapter | Mechanism | Retention support |
|
|
16
|
+
| -------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| libSQL | `prune()` | All supported growth domains |
|
|
18
|
+
| PostgreSQL | `prune()` | All supported growth domains. V-next observability drops expired partitions or chunks |
|
|
19
|
+
| MongoDB | `prune()` or native TTL | All supported growth domains. Native TTL indexes are also available |
|
|
20
|
+
| DuckDB | `prune()` | Observability spans, metrics, logs, scores, and feedback |
|
|
21
|
+
| MySQL | `prune()` | Observability spans |
|
|
22
|
+
| Microsoft SQL Server | `prune()` | Observability spans |
|
|
23
|
+
| Oracle Database | `prune()` | Observability spans and logs |
|
|
24
|
+
| Amazon Aurora DSQL | `prune()` | Observability spans |
|
|
25
|
+
| Google Cloud Spanner | `prune()` | Observability spans, plus metrics when metrics storage is enabled |
|
|
26
|
+
| ClickHouse | Native TTL | Observability spans, metrics, logs, scores, and feedback. When all five signals have finite retention, deletion-request records expire after the longest signal retention plus 30 days |
|
|
27
|
+
|
|
28
|
+
## Storage-specific maintenance
|
|
29
|
+
|
|
30
|
+
| Adapter | Maintenance guidance |
|
|
31
|
+
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| SQLite and libSQL | Freed pages are reused by future writes, which stops the database file from growing. Reclaiming disk space requires database-level maintenance. |
|
|
33
|
+
| DuckDB | For file-backed stores, run `CHECKPOINT` after pruning to reclaim deleted rows in storage. DuckDB's `VACUUM` doesn't reclaim deleted rows. |
|
|
34
|
+
|
|
35
|
+
## Schedule pruning
|
|
36
|
+
|
|
37
|
+
Run `prune()` from a scheduler or maintenance worker, not from application startup or shutdown hooks. For deployments that share a database, prefer a single active scheduler or worker for pruning.
|
|
38
|
+
|
|
39
|
+
Prefer lower-traffic periods when pruning large tables. Use `maxBatches`, `maxRows`, and `pauseMs` to bound each run, and pass an `AbortSignal` when the maintenance process needs to stop promptly. These recommendations apply to adapters that expose `prune()`. ClickHouse applies its native time to live (TTL) policy within the database.
|
|
14
40
|
|
|
15
41
|
## Usage example
|
|
16
42
|
|
|
@@ -94,7 +120,8 @@ Each domain specifies its age-prunable tables and the timestamp column that anch
|
|
|
94
120
|
> - Experiments prune as whole units: an aged experiment's result rows are deleted together with it (results cascade with their parent), so a run is never left partially deleted. Retention doesn't have a separate `results` key.
|
|
95
121
|
> - For `schedules`, the growth table is the fire history (`schedule_triggers`, one row per fire): schedule definitions are config and aren't pruned.
|
|
96
122
|
> - On PostgreSQL, timestamp anchors use the timezone-aware mirror columns (for example `createdAtZ`, `completedAtZ`).
|
|
97
|
-
> -
|
|
123
|
+
> - DuckDB observability stores append-only events for all five signals. Its `spans` policy uses the event `timestamp` column rather than `startedAt`.
|
|
124
|
+
> - LibSQL and PostgreSQL support all domains above except `harness`, which PostgreSQL doesn't implement. MongoDB supports all except `threadState` and `harness`. DuckDB, MySQL, Microsoft SQL Server, Oracle Database, Amazon Aurora DSQL, and Google Cloud Spanner currently support retention only in their `observability` domains, with the signal coverage shown in the support matrix.
|
|
98
125
|
> - The v-next PostgreSQL observability domain stores signal events in day-partitioned tables (`spans`, `metrics`, `logs`, `scores`, `feedback`). For it, `prune()` drops whole day partitions (or TimescaleDB chunks) that are entirely older than the cutoff instead of deleting rows: effective level of detail is one day, and a partition is only dropped once its entire day is past `maxAge`. `PruneResult.deleted` reports the number of rows in the dropped partitions.
|
|
99
126
|
|
|
100
127
|
## Methods
|
|
@@ -109,7 +136,7 @@ Deletes rows older than their configured `maxAge` across every domain that has a
|
|
|
109
136
|
|
|
110
137
|
Pass `options.retention` to replace the configured policies for that call only: for example to skip a domain (keep chat history) or prune more aggressively than the standing config. The store's configured `retention` is unchanged.
|
|
111
138
|
|
|
112
|
-
|
|
139
|
+
Adapters that use anchor-column indexes create them lazily on the first `prune()` call for each table with a policy (never at `init()`) so deployments that don't configure retention pay no extra index write or disk overhead. The first prune of an existing large table pays a one-time index build. Subsequent prunes reuse the index. DuckDB uses its built-in zone maps instead of creating retention indexes.
|
|
113
140
|
|
|
114
141
|
```typescript
|
|
115
142
|
const results = await storage.prune({
|
|
@@ -177,6 +204,31 @@ async function retentionTick() {
|
|
|
177
204
|
|
|
178
205
|
You can also cancel a long-running prune with an `AbortSignal`: the loop stops between batches and returns partial results with `done: false`, so the next run resumes cleanly.
|
|
179
206
|
|
|
207
|
+
## ClickHouse native TTL
|
|
208
|
+
|
|
209
|
+
ClickHouse observability storage uses native table TTLs instead of `prune()`. Configure retention as days per signal. `init()` applies the TTLs to new and existing tables and skips `ALTER TABLE` statements when the configured TTL is already present.
|
|
210
|
+
|
|
211
|
+
For deployments that need to update TTL configuration without running the full initialization path, call `applyRetention()` on the v-next observability store:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { ObservabilityStorageClickhouseVNext } from '@mastra/clickhouse'
|
|
215
|
+
|
|
216
|
+
const observability = new ObservabilityStorageClickhouseVNext({
|
|
217
|
+
client,
|
|
218
|
+
retention: {
|
|
219
|
+
tracing: 30,
|
|
220
|
+
logs: 7,
|
|
221
|
+
metrics: 14,
|
|
222
|
+
scores: 90,
|
|
223
|
+
feedback: 60,
|
|
224
|
+
},
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
await observability.applyRetention()
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Deletion requests are retained long enough to keep enforcing erasure after signal rows expire. Mastra applies a TTL to `mastra_deletion_requests` only when tracing, logs, metrics, scores, and feedback all have finite retention. The deletion-request TTL is the longest of those periods plus 30 days. For example, if score retention is the longest period at 90 days, deletion requests expire after 120 days. When any signal is unbounded, deletion requests remain unbounded because trace deletion requests cover rows across all five signals.
|
|
231
|
+
|
|
180
232
|
## MongoDB TTL indexes (alternative to prune)
|
|
181
233
|
|
|
182
234
|
MongoDB offers native [TTL (Time-To-Live) indexes](https://www.mongodb.com/docs/manual/core/index-ttl/) that automatically delete expired documents without requiring manual `prune()` calls. This is a database-level feature that runs as a background thread.
|
|
@@ -104,7 +104,7 @@ const stream = await agent.stream('message for agent')
|
|
|
104
104
|
|
|
105
105
|
**options.structuredOutput.fallbackValue** (`<S extends ZodTypeAny>`): Fallback value to use when schema validation fails and errorStrategy is 'fallback'.
|
|
106
106
|
|
|
107
|
-
**options.structuredOutput.instructions** (`string`): Additional instructions for the structured output model.
|
|
107
|
+
**options.structuredOutput.instructions** (`string`): Additional instructions for the structured output model. With jsonPromptInjection and no structuring model, this text is injected into the prompt in place of the serialized schema.
|
|
108
108
|
|
|
109
109
|
**options.structuredOutput.jsonPromptInjection** (`boolean | 'system' | 'inline' | 'auto'`): Controls how the JSON schema reaches the model. Set to 'auto' to use native structured output when supported and inline prompt injection otherwise.
|
|
110
110
|
|
|
@@ -10,34 +10,6 @@ Note that if you only need to use your tools or agents directly within your Mast
|
|
|
10
10
|
|
|
11
11
|
It supports both [stdio (subprocess) and SSE (HTTP) MCP transports](https://modelcontextprotocol.io/docs/concepts/transports).
|
|
12
12
|
|
|
13
|
-
## Operate a remote Mastra server
|
|
14
|
-
|
|
15
|
-
Use `MastraApiMCPServer` to give MCP clients the Mastra server operations from the `mastra api` CLI. It reads the target server's API schema when it starts and only registers operations supported by that server. Factory commands and routes outside this catalog aren't exposed.
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
import { Mastra } from '@mastra/core/mastra'
|
|
19
|
-
import { MastraApiMCPServer } from '@mastra/mcp'
|
|
20
|
-
|
|
21
|
-
const operations = await MastraApiMCPServer.create({
|
|
22
|
-
url: 'https://my-mastra-server.example.com',
|
|
23
|
-
headers: {
|
|
24
|
-
Authorization: `Bearer ${process.env.MASTRA_API_TOKEN}`,
|
|
25
|
-
},
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
export const mastra = new Mastra({
|
|
29
|
-
mcpServers: { operations },
|
|
30
|
-
})
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Tool names follow the CLI command hierarchy. For example, `mastra api workflow run start` becomes `workflow_run_start`.
|
|
34
|
-
|
|
35
|
-
The server can expose 60 tools across agents, workflows, tools, MCP servers, memory threads, working memory, traces, logs, metrics, scores, datasets, experiments, and Trace Intelligence. Each tool uses the input schema returned by the target server. Trace list and get tools also support the CLI's `verbose` option.
|
|
36
|
-
|
|
37
|
-
The server uses stateless MCP transport. Read operations, mutations, and destructive operations have separate MCP tool annotations. Agent, workflow, experiment, and tool execution are marked as potentially destructive because they can invoke operations that delete or overwrite data. These annotations are hints for MCP clients, not authorization checks. Each tool call sends one request to the target API and doesn't retry mutations.
|
|
38
|
-
|
|
39
|
-
Use `headers` to authenticate the API schema request. For tool calls, the MCP caller's bearer token replaces the configured `Authorization` header. You can also set `apiPrefix`, `timeoutMs`, `id`, `name`, and `version`.
|
|
40
|
-
|
|
41
13
|
## Constructor
|
|
42
14
|
|
|
43
15
|
To create a new `MCPServer`, you need to provide some basic information about your server, the tools it will offer, and optionally, any agents you want to expose as tools.
|