@mastra/mcp-docs-server 1.1.40-alpha.12 → 1.1.40-alpha.16
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/channels.md +1 -1
- package/.docs/docs/observability/metrics/overview.md +2 -0
- package/.docs/docs/server/custom-api-routes.md +15 -0
- package/.docs/reference/agents/channels.md +73 -4
- package/.docs/reference/index.md +1 -0
- package/.docs/reference/storage/spanner.md +213 -0
- package/CHANGELOG.md +14 -0
- package/package.json +4 -4
|
@@ -112,7 +112,7 @@ const deleteFile = createTool({
|
|
|
112
112
|
|
|
113
113
|
When the agent calls this tool, users see a card with the tool name, arguments, and Approve/Deny buttons. The tool only executes after approval.
|
|
114
114
|
|
|
115
|
-
Set `
|
|
115
|
+
Set `toolDisplay: 'text'` on an adapter to render tool calls as plain text instead of interactive cards. In `'hidden'` mode the agent uses `autoResumeSuspendedTools` to let the LLM decide based on the conversation context, since hidden mode does not post approval buttons.
|
|
116
116
|
|
|
117
117
|
## Multi-user awareness
|
|
118
118
|
|
|
@@ -11,6 +11,8 @@ Three categories of metrics are emitted automatically:
|
|
|
11
11
|
> **Note:** Metrics require an OLAP-capable store for observability. Relational databases like PostgreSQL, LibSQL, etc. are not supported for metrics. In-memory storage resets on restart.
|
|
12
12
|
>
|
|
13
13
|
> For local development, use [DuckDB](https://duckdb.org/) through `@mastra/duckdb`. For production, use [ClickHouse](https://clickhouse.com/) through `@mastra/clickhouse`.
|
|
14
|
+
>
|
|
15
|
+
> Google Cloud Spanner supports metrics, but it is not recommended for heavy metrics workloads. The Spanner adapter disables metrics by default because metrics are write-heavy and scan-heavy. Set `disableMetrics: false` only for light workloads, or route metrics to an OLAP store.
|
|
14
16
|
|
|
15
17
|
## When to use metrics
|
|
16
18
|
|
|
@@ -264,6 +264,21 @@ For more information about authentication providers, see the [Auth documentation
|
|
|
264
264
|
|
|
265
265
|
Built-in streaming helpers such as [`chatRoute()`](https://mastra.ai/reference/ai-sdk/chat-route) forward the incoming request's `AbortSignal` to `agent.stream()`. That's the right default when a browser disconnect should cancel the model call.
|
|
266
266
|
|
|
267
|
+
For custom streaming routes that should stop when the client disconnects, pass `c.req.raw.signal` to long-running work such as `agent.stream()`. Mastra's Node-based adapters also stop reading streamed `Response` bodies from custom routes when the client connection closes. Streamed response body errors that are not caused by client disconnects still propagate through the adapter's normal error handling. In Hono, disconnect behavior depends on the host runtime forwarding connection closes to `request.signal`.
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
registerApiRoute('/stream', {
|
|
271
|
+
method: 'GET',
|
|
272
|
+
handler: async c => {
|
|
273
|
+
const stream = await agent.stream(prompt, {
|
|
274
|
+
abortSignal: c.req.raw.signal,
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
return stream.toTextStreamResponse()
|
|
278
|
+
},
|
|
279
|
+
})
|
|
280
|
+
```
|
|
281
|
+
|
|
267
282
|
If you want the server to keep generating and persist the final response even after the client disconnects, build a custom route around the underlying `MastraModelOutput`. Start the agent stream without forwarding `c.req.raw.signal`, then call `consumeStream()` in the background so generation continues server-side.
|
|
268
283
|
|
|
269
284
|
```typescript
|
|
@@ -62,7 +62,7 @@ const agent = new Agent({
|
|
|
62
62
|
adapters: {
|
|
63
63
|
discord: {
|
|
64
64
|
adapter: createDiscordAdapter(),
|
|
65
|
-
|
|
65
|
+
toolDisplay: 'text',
|
|
66
66
|
cors: {
|
|
67
67
|
origin: ['https://customer-saas.example'],
|
|
68
68
|
credentials: true,
|
|
@@ -79,14 +79,83 @@ const agent = new Agent({
|
|
|
79
79
|
|
|
80
80
|
**gateway** (`boolean`): Start a persistent Gateway WebSocket listener for receiving DMs, @mentions, and reactions. Set to \`false\` for serverless deployments that only need webhook-based interactions. (Default: `true`)
|
|
81
81
|
|
|
82
|
-
**cards** (`boolean`):
|
|
82
|
+
**cards** (`boolean`): \*\*Deprecated\*\* — use \`toolDisplay\` instead. When \`toolDisplay\` is not set, \`cards: true\` maps to \`toolDisplay: "cards"\` and \`cards: false\` maps to \`toolDisplay: "text"\`. IDEs flag the field with a strikethrough; runtime behavior is preserved.
|
|
83
83
|
|
|
84
84
|
**cors** (`CorsOptions`): CORS configuration for this adapter webhook route. Use this for browser-based channel adapters that need cross-origin credentials.
|
|
85
85
|
|
|
86
|
-
**formatToolCall** (`(info: { toolName, args, result, isError? }) => PostableMessage | null`): Override how tool calls are rendered in the chat. Called once per tool invocation after the result is available. Return \`null\` to suppress the message entirely.
|
|
87
|
-
|
|
88
86
|
**formatError** (`(error: Error) => PostableMessage`): Override how errors are rendered in the chat. Return a user-friendly message instead of exposing the raw error. (Default: `"❌ Error: <error.message>"`)
|
|
89
87
|
|
|
88
|
+
**formatToolCall** (`(args: { toolName: string; args: unknown; result: unknown; isError: boolean }) => PostableMessage | null`): \*\*Deprecated\*\* — use \`toolDisplay\` (function form) instead. When set, runs as a \`ToolDisplayFn\` that only fires on \`result\`/\`error\` events; \`running\` and \`approval\` events fall through to no render. Mutually exclusive with \`toolDisplay\` at the type level.
|
|
89
|
+
|
|
90
|
+
**streaming** (`boolean | { updateIntervalMs?: number }`): Stream agent text deltas to the channel as the agent generates them instead of buffering and posting once per step. Requires the underlying adapter to support post-and-edit streaming. Slack defaults to \`true\`; other adapters default to \`false\`. (Default: `false (true for Slack)`)
|
|
91
|
+
|
|
92
|
+
**toolDisplay** (`'cards' | 'text' | 'timeline' | 'grouped' | 'hidden' | ToolDisplayFn`): How tool calls are rendered in the channel. \`"cards"\` posts per-tool running/result cards as rich Block Kit. \`"text"\` posts the same lifecycle as plain text (no Block Kit). \`"timeline"\` and \`"grouped"\` stream tool state as inline \`task\_update\` chunks (requires \`streaming: true\`; Slack only today — other adapters may render a placeholder). \`"hidden"\` executes tools silently. Pass a function to render tool events yourself; return \`{ kind: "post", message }\` for a discrete post/edit, \`{ kind: "stream", chunk }\` to push into the active streaming widget, or \`undefined\` to skip rendering that event. Approve/deny prompts always render as a separate card regardless of mode. (Default: `'cards' ('grouped' for Slack)`)
|
|
93
|
+
|
|
94
|
+
**typingStatus** (`boolean | ((chunk: AgentChunkType, ctx: TypingStatusContext) => string | false | null | undefined | void)`): Control the platform typing indicator. \`true\` uses built-in defaults (\`is typing…\` on text, \`is calling {tool}…\` on tool-call, \`is waiting for approval…\` on tool-call-approval). \`false\` suppresses typing entirely — useful when a live streaming widget (e.g. \`toolDisplay: "grouped"\` in Slack) already conveys progress. Pass a function to set custom status copy per chunk; return a string to set the status, or \`false\`/\`null\`/\`undefined\` to leave it unchanged. Compose with \`defaultTypingStatus\` (exported from \`@mastra/core/channels\`) to fall back to defaults for chunks you don't handle. (Default: `true`)
|
|
95
|
+
|
|
96
|
+
## Tool display modes
|
|
97
|
+
|
|
98
|
+
`toolDisplay` controls how tool calls render in chat. The default `'cards'` posts a "Running…" card per tool and edits it with the result, matching the behavior in earlier versions. `'text'` is the same lifecycle but without rich Block Kit, useful for platforms that don't render cards well.
|
|
99
|
+
|
|
100
|
+
`'timeline'` and `'grouped'` stream tool state as inline `task_update` chunks alongside the agent's text. These modes require `streaming: true` and rely on the chat adapter to render the chunks. Slack supports both natively; other adapters may render a placeholder until they ship support. If `streaming` is disabled, the channel logs a one-time warning and falls back to `'cards'`.
|
|
101
|
+
|
|
102
|
+
`'hidden'` executes tools silently. Only the typing status indicates work in progress.
|
|
103
|
+
|
|
104
|
+
Pass a function to `toolDisplay` for fully custom rendering. The function receives a `ToolDisplayEvent` (`running` / `result` / `error` / `approval`) and a `ToolDisplayContext` (`{ mode, platform }`); return `{ kind: 'post', message }` for a discrete post/edit, `{ kind: 'stream', chunk }` to push into the active streaming widget, or `undefined` to skip rendering that event.
|
|
105
|
+
|
|
106
|
+
Approve/deny prompts (`requireApproval`) always render as a separate card regardless of mode, because inline task entries can't carry interactive buttons.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { Agent } from '@mastra/core/agent'
|
|
110
|
+
import { createSlackAdapter } from '@chat-adapter/slack'
|
|
111
|
+
|
|
112
|
+
const agent = new Agent({
|
|
113
|
+
name: 'Streaming Agent',
|
|
114
|
+
instructions: '...',
|
|
115
|
+
model: 'openai/gpt-5.4',
|
|
116
|
+
channels: {
|
|
117
|
+
adapters: {
|
|
118
|
+
slack: {
|
|
119
|
+
adapter: createSlackAdapter(),
|
|
120
|
+
streaming: true, // already the Slack default
|
|
121
|
+
toolDisplay: 'timeline',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Custom typing status
|
|
129
|
+
|
|
130
|
+
Pass a function to `typingStatus` to customize the status copy. The function is called once per stream chunk; return a string to set the status, or `false` / `null` / `undefined` to leave the current status unchanged. Return values are de-duplicated so the platform only sees a call when the status changes.
|
|
131
|
+
|
|
132
|
+
`defaultTypingStatus` is exported from `@mastra/core/channels` so you can fall back to the built-in defaults for chunks you don't handle.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { Agent } from '@mastra/core/agent'
|
|
136
|
+
import { defaultTypingStatus } from '@mastra/core/channels'
|
|
137
|
+
import { createDiscordAdapter } from '@chat-adapter/discord'
|
|
138
|
+
|
|
139
|
+
const agent = new Agent({
|
|
140
|
+
name: 'Custom Typing Agent',
|
|
141
|
+
instructions: '...',
|
|
142
|
+
model: 'openai/gpt-5.4',
|
|
143
|
+
channels: {
|
|
144
|
+
adapters: {
|
|
145
|
+
discord: {
|
|
146
|
+
adapter: createDiscordAdapter(),
|
|
147
|
+
typingStatus: (chunk, ctx) => {
|
|
148
|
+
if (chunk.type === 'tool-call' && chunk.payload.toolName === 'searchDocs') {
|
|
149
|
+
return 'is searching docs…'
|
|
150
|
+
}
|
|
151
|
+
return defaultTypingStatus(chunk, ctx)
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
90
159
|
## Handlers
|
|
91
160
|
|
|
92
161
|
Override built-in event handlers. Each handler can be:
|
package/.docs/reference/index.md
CHANGED
|
@@ -208,6 +208,7 @@ The Reference section provides documentation of Mastra's API, including paramete
|
|
|
208
208
|
- [Convex Storage](https://mastra.ai/reference/storage/convex)
|
|
209
209
|
- [DuckDB Storage](https://mastra.ai/reference/storage/duckdb)
|
|
210
210
|
- [DynamoDB Storage](https://mastra.ai/reference/storage/dynamodb)
|
|
211
|
+
- [Google Cloud Spanner Storage](https://mastra.ai/reference/storage/spanner)
|
|
211
212
|
- [LanceDB Storage](https://mastra.ai/reference/storage/lance)
|
|
212
213
|
- [libSQL Storage](https://mastra.ai/reference/storage/libsql)
|
|
213
214
|
- [MongoDB Storage](https://mastra.ai/reference/storage/mongodb)
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Google Cloud Spanner storage
|
|
2
|
+
|
|
3
|
+
The Google Cloud Spanner storage implementation provides a horizontally scalable, strongly consistent storage backend for Mastra. It targets the GoogleSQL dialect of Cloud Spanner.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
**npm**:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @mastra/spanner@latest
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**pnpm**:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @mastra/spanner@latest
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Yarn**:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
yarn add @mastra/spanner@latest
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Bun**:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bun add @mastra/spanner@latest
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { SpannerStore } from '@mastra/spanner'
|
|
35
|
+
|
|
36
|
+
const storage = new SpannerStore({
|
|
37
|
+
id: 'spanner-storage',
|
|
38
|
+
projectId: process.env.SPANNER_PROJECT_ID!,
|
|
39
|
+
instanceId: process.env.SPANNER_INSTANCE_ID!,
|
|
40
|
+
databaseId: process.env.SPANNER_DATABASE_ID!,
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The instance and database must already exist. The adapter creates the required tables on first use, so the credentials provided to the Spanner client need permission to run schema changes (or run `storage.init()` once during a deploy step with elevated credentials).
|
|
45
|
+
|
|
46
|
+
## Parameters
|
|
47
|
+
|
|
48
|
+
**id** (`string`): Unique identifier for this storage instance.
|
|
49
|
+
|
|
50
|
+
**projectId** (`string`): Google Cloud project ID. Required unless \`database\` is provided.
|
|
51
|
+
|
|
52
|
+
**instanceId** (`string`): Cloud Spanner instance ID. Required unless \`database\` is provided.
|
|
53
|
+
|
|
54
|
+
**databaseId** (`string`): Cloud Spanner database ID. Required unless \`database\` is provided.
|
|
55
|
+
|
|
56
|
+
**database** (`@google-cloud/spanner Database`): Pre-configured Spanner Database handle. Use this when you manage the Spanner client elsewhere (for example, to share auth or connection options across services).
|
|
57
|
+
|
|
58
|
+
**spannerOptions** (`object`): Options forwarded to the \`@google-cloud/spanner\` client constructor. Use this to set credentials, custom endpoints, or to point at the local emulator.
|
|
59
|
+
|
|
60
|
+
**disableInit** (`boolean`): When true, skip automatic table creation on first use. You must call \`storage.init()\` explicitly during a separate deploy step. (Default: `false`)
|
|
61
|
+
|
|
62
|
+
**skipDefaultIndexes** (`boolean`): When true, skip creation of default indexes during initialization. (Default: `false`)
|
|
63
|
+
|
|
64
|
+
**indexes** (`CreateIndexOptions[]`): Custom secondary indexes to create. Each index must specify the table it belongs to. Indexes are routed to the appropriate domain based on the table name.
|
|
65
|
+
|
|
66
|
+
**initMode** (`'sync' | 'validate'`): Controls schema-initialization behavior. \`'sync'\` creates missing tables, columns, and indexes during \`init()\` (the historical behavior). \`'validate'\` issues no DDL and instead verifies that every expected table, column, and default/custom index already exists, throwing a typed user error if anything is missing — useful when an external process (Terraform, Liquibase, a release pipeline, etc.) owns the schema and Mastra should only verify it. (Default: `'sync'`)
|
|
67
|
+
|
|
68
|
+
## Constructor examples
|
|
69
|
+
|
|
70
|
+
You can instantiate `SpannerStore` in several ways:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { Spanner } from '@google-cloud/spanner'
|
|
74
|
+
import { SpannerStore } from '@mastra/spanner'
|
|
75
|
+
|
|
76
|
+
// Using projectId / instanceId / databaseId
|
|
77
|
+
const store1 = new SpannerStore({
|
|
78
|
+
id: 'spanner-storage-1',
|
|
79
|
+
projectId: 'my-gcp-project',
|
|
80
|
+
instanceId: 'my-instance',
|
|
81
|
+
databaseId: 'mastra',
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Reusing an existing Spanner Database handle
|
|
85
|
+
const spanner = new Spanner({ projectId: 'my-gcp-project' })
|
|
86
|
+
const database = spanner.instance('my-instance').database('mastra')
|
|
87
|
+
|
|
88
|
+
const store2 = new SpannerStore({
|
|
89
|
+
id: 'spanner-storage-2',
|
|
90
|
+
database,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Using the local Spanner emulator (set the SPANNER_EMULATOR_HOST env var)
|
|
94
|
+
process.env.SPANNER_EMULATOR_HOST = 'localhost:9010'
|
|
95
|
+
const store3 = new SpannerStore({
|
|
96
|
+
id: 'spanner-storage-emulator',
|
|
97
|
+
projectId: 'test-project',
|
|
98
|
+
instanceId: 'test-instance',
|
|
99
|
+
databaseId: 'test-db',
|
|
100
|
+
spannerOptions: { servicePath: 'localhost', port: 9010, sslCreds: undefined },
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Additional notes
|
|
105
|
+
|
|
106
|
+
### Schema management
|
|
107
|
+
|
|
108
|
+
The storage adapter creates the following tables, all using the GoogleSQL dialect:
|
|
109
|
+
|
|
110
|
+
- `mastra_workflow_snapshot`: workflow state and execution data
|
|
111
|
+
- `mastra_threads`: conversation threads
|
|
112
|
+
- `mastra_messages`: individual messages
|
|
113
|
+
- `mastra_resources`: resource working memory
|
|
114
|
+
- `mastra_scorers`: evaluation scores
|
|
115
|
+
- `mastra_background_tasks`: background tool execution state
|
|
116
|
+
- `mastra_agents`: thin agent records (id, status, active version)
|
|
117
|
+
- `mastra_agent_versions`: versioned agent configuration snapshots
|
|
118
|
+
- `mastra_mcp_clients` / `mastra_mcp_client_versions`: MCP client configurations and their version history
|
|
119
|
+
- `mastra_mcp_servers` / `mastra_mcp_server_versions`: MCP server configurations and their version history
|
|
120
|
+
- `mastra_skills` / `mastra_skill_versions`: skill records and versioned skill snapshots (instructions, references, scripts, assets, content tree)
|
|
121
|
+
- `mastra_skill_blobs`: content-addressable blob store keyed by SHA-256 hash, used for skill version contents
|
|
122
|
+
- `mastra_prompt_blocks` / `mastra_prompt_block_versions`: prompt block records and versioned content snapshots (template content, rules, request-context schema)
|
|
123
|
+
- `mastra_scorer_definitions` / `mastra_scorer_definition_versions`: scorer definition records and versioned config snapshots (judge instructions, model, score range, preset config, default sampling)
|
|
124
|
+
- `mastra_schedules` / `mastra_schedule_triggers`: cron-driven workflow schedules and trigger history, consumed by Mastra's built-in `WorkflowScheduler`
|
|
125
|
+
- `mastra_ai_spans`: AI tracing spans for observability (per-trace and per-span records, used to power the Studio traces UI)
|
|
126
|
+
|
|
127
|
+
Tables are created with `STRING(MAX)` for text and JSON payloads, `INT64`, `FLOAT64`, `BOOL`, and `TIMESTAMP`.
|
|
128
|
+
|
|
129
|
+
Two tables also carry Spanner-specific `STORED` generated columns that the adapter populates from JSON payloads so common filters can use a regular secondary index instead of a `JSON_VALUE` scan:
|
|
130
|
+
|
|
131
|
+
- `mastra_workflow_snapshot.snapshotStatus` — extracts `$.status` from `snapshot`; backs `listWorkflowRuns({ status })`.
|
|
132
|
+
- `mastra_schedules.target_workflow_id` — extracts `$.workflowId` from `target`; backs `listSchedules({ workflowId })`.
|
|
133
|
+
|
|
134
|
+
Both are added via `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` during `init()` and skipped under `initMode: 'validate'` (where the schema is owned externally). When the column is absent, the adapter falls back to a `JSON_VALUE` filter at runtime.
|
|
135
|
+
|
|
136
|
+
The adapter does not create or use named schemas; use a dedicated database for isolation.
|
|
137
|
+
|
|
138
|
+
### Initialization
|
|
139
|
+
|
|
140
|
+
When you pass storage to the `Mastra` class, `init()` is called automatically before any storage operation:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { Mastra } from '@mastra/core'
|
|
144
|
+
import { SpannerStore } from '@mastra/spanner'
|
|
145
|
+
|
|
146
|
+
const storage = new SpannerStore({
|
|
147
|
+
id: 'spanner-storage',
|
|
148
|
+
projectId: process.env.SPANNER_PROJECT_ID!,
|
|
149
|
+
instanceId: process.env.SPANNER_INSTANCE_ID!,
|
|
150
|
+
databaseId: process.env.SPANNER_DATABASE_ID!,
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const mastra = new Mastra({
|
|
154
|
+
storage, // init() is called automatically
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
If you use storage directly, call `init()` once before the first operation. Spanner does not allow concurrent schema changes, so `SpannerStore.init()` runs each domain's setup sequentially.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const storage = new SpannerStore({
|
|
162
|
+
id: 'spanner-storage',
|
|
163
|
+
projectId: process.env.SPANNER_PROJECT_ID!,
|
|
164
|
+
instanceId: process.env.SPANNER_INSTANCE_ID!,
|
|
165
|
+
databaseId: process.env.SPANNER_DATABASE_ID!,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
await storage.init()
|
|
169
|
+
const memory = await storage.getStore('memory')
|
|
170
|
+
const thread = await memory?.getThreadById({ threadId: '...' })
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
> **Warning:** If `init()` is not called and `disableInit` is true, the required tables will not exist and storage operations will fail.
|
|
174
|
+
|
|
175
|
+
### GoogleSQL specifics
|
|
176
|
+
|
|
177
|
+
A few behaviors differ from other relational adapters:
|
|
178
|
+
|
|
179
|
+
- Upserts use `INSERT OR UPDATE`. Spanner does not provide a `RETURNING` clause for upserts, so callers needing the post-write state must read it back.
|
|
180
|
+
- There is no `TRUNCATE`; `dangerouslyClearAll()` issues `DELETE WHERE TRUE`.
|
|
181
|
+
- Identifiers are quoted with backticks.
|
|
182
|
+
- DDL is applied through `database.updateSchema(...)`, which is asynchronous (long-running operation).
|
|
183
|
+
- `NULLS FIRST/LAST` is not supported. Ordering with NULL handling is emulated through an `IS NULL` ordering key.
|
|
184
|
+
- JSON containment is not supported natively. `listTraces` `metadata` and `scope` filters compile to per-key `JSON_VALUE(...) = @v` equality checks, and `tags` filters compile to `EXISTS` over `JSON_QUERY_ARRAY(...)`. This differs from Postgres' `@>` containment operator (which can match nested structure in a single index scan) — most one-shot lookups still work but deeply nested structural matches are not expressible.
|
|
185
|
+
|
|
186
|
+
### Direct database access
|
|
187
|
+
|
|
188
|
+
`SpannerStore` exposes the underlying Spanner client objects:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
store.database // @google-cloud/spanner Database
|
|
192
|
+
store.instance // @google-cloud/spanner Instance (when created internally)
|
|
193
|
+
store.spanner // @google-cloud/spanner Spanner client (when created internally)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
These are intended for advanced scenarios such as bespoke transactions or schema introspection. When you reuse the database directly, you bypass the adapter's validation and JSON conversion logic.
|
|
197
|
+
|
|
198
|
+
### Local development with the emulator
|
|
199
|
+
|
|
200
|
+
Run the Cloud Spanner emulator locally with Docker:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
docker run -p 9010:9010 -p 9020:9020 gcr.io/cloud-spanner-emulator/emulator
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Set `SPANNER_EMULATOR_HOST=localhost:9010` and create the instance and database before running your app:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
gcloud spanner instances create test-instance --config=emulator-config --nodes=1
|
|
210
|
+
gcloud spanner databases create test-db --instance=test-instance
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Then connect with the same env var set in your Node.js process; the `@google-cloud/spanner` client detects the emulator automatically.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @mastra/mcp-docs-server
|
|
2
2
|
|
|
3
|
+
## 1.1.40-alpha.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`168fa09`](https://github.com/mastra-ai/mastra/commit/168fa09d6b39114cb8c13bd06f1dccb9bc81c6cd)]:
|
|
8
|
+
- @mastra/core@1.37.0-alpha.7
|
|
9
|
+
|
|
10
|
+
## 1.1.40-alpha.13
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [[`0cbece9`](https://github.com/mastra-ai/mastra/commit/0cbece9d832cb134a74cdbf3682d390a058215a4), [`7dfe1bc`](https://github.com/mastra-ai/mastra/commit/7dfe1bcfe71d261a6fd6bbf29b1dec49d78fb98f), [`70cb714`](https://github.com/mastra-ai/mastra/commit/70cb7149c8f16f478e15b58498254a53181750a4), [`7f9da22`](https://github.com/mastra-ai/mastra/commit/7f9da22efd5aa595e138a31de55a5f0f2f28b33d)]:
|
|
15
|
+
- @mastra/core@1.37.0-alpha.6
|
|
16
|
+
|
|
3
17
|
## 1.1.40-alpha.11
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/mcp-docs-server",
|
|
3
|
-
"version": "1.1.40-alpha.
|
|
3
|
+
"version": "1.1.40-alpha.16",
|
|
4
4
|
"description": "MCP server for accessing Mastra.ai documentation, changelogs, and news.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"jsdom": "^26.1.0",
|
|
30
30
|
"local-pkg": "^1.1.2",
|
|
31
31
|
"zod": "^4.3.6",
|
|
32
|
-
"@mastra/
|
|
33
|
-
"@mastra/
|
|
32
|
+
"@mastra/mcp": "^1.8.0",
|
|
33
|
+
"@mastra/core": "1.37.0-alpha.7"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@hono/node-server": "^1.19.11",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"vitest": "4.1.5",
|
|
49
49
|
"@internal/lint": "0.0.97",
|
|
50
50
|
"@internal/types-builder": "0.0.72",
|
|
51
|
-
"@mastra/core": "1.37.0-alpha.
|
|
51
|
+
"@mastra/core": "1.37.0-alpha.7"
|
|
52
52
|
},
|
|
53
53
|
"homepage": "https://mastra.ai",
|
|
54
54
|
"repository": {
|