@mastra/mcp-docs-server 1.1.37-alpha.1 → 1.1.37-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.
@@ -0,0 +1,267 @@
1
+ # xAI Realtime voice
2
+
3
+ The `XAIRealtimeVoice` class provides realtime voice interaction capabilities using the xAI Grok Voice Agent API. It implements Mastra's `MastraVoice` realtime contract and supports bidirectional audio streaming, text turns, server VAD, xAI voices, function tools, and xAI server-side tools.
4
+
5
+ ## Usage example
6
+
7
+ ```typescript
8
+ import { Agent } from '@mastra/core/agent'
9
+ import { getMicrophoneStream, playAudio } from '@mastra/node-audio'
10
+ import { XAIRealtimeVoice } from '@mastra/voice-xai-realtime'
11
+
12
+ const voice = new XAIRealtimeVoice({
13
+ apiKey: process.env.XAI_API_KEY,
14
+ model: 'grok-voice-think-fast-1.0',
15
+ speaker: 'eve',
16
+ instructions: 'You are a concise voice assistant.',
17
+ turnDetection: { type: 'server_vad' },
18
+ })
19
+
20
+ const agent = new Agent({
21
+ id: 'voice-agent',
22
+ name: 'Voice Agent',
23
+ instructions: 'You are a helpful voice assistant.',
24
+ model: 'xai/grok-4.3',
25
+ voice,
26
+ })
27
+
28
+ await agent.voice.connect()
29
+
30
+ agent.voice.on('speaker', audioStream => {
31
+ playAudio(audioStream)
32
+ })
33
+
34
+ agent.voice.on('writing', ({ text, role }) => {
35
+ console.log(`${role}: ${text}`)
36
+ })
37
+
38
+ await agent.voice.speak('How can I help you today?')
39
+
40
+ const microphoneStream = getMicrophoneStream()
41
+ await agent.voice.send(microphoneStream)
42
+
43
+ agent.voice.close()
44
+ ```
45
+
46
+ ## Configuration
47
+
48
+ ### Constructor options
49
+
50
+ **apiKey** (`string`): xAI API key. Falls back to the XAI\_API\_KEY environment variable.
51
+
52
+ **ephemeralToken** (`string`): Short-lived xAI token sent with the WebSocket protocol instead of an authorization header.
53
+
54
+ **model** (`XAIRealtimeModel`): The Grok voice model to use. (Default: `'grok-voice-think-fast-1.0'`)
55
+
56
+ **speaker** (`XAIVoice`): Voice ID to use for speech output. Built-in values are eve, ara, rex, sal, and leo. Custom xAI voice IDs are also supported. (Default: `'eve'`)
57
+
58
+ **instructions** (`string`): System instructions sent in session.update.
59
+
60
+ **turnDetection** (`XAITurnDetection`): Voice activity detection configuration. (Default: `{ type: 'server_vad' }`)
61
+
62
+ **audio** (`XAIAudioConfig`): Input and output audio format configuration. (Default: `24 kHz audio/pcm input and output`)
63
+
64
+ **serverTools** (`XAIServerTool[]`): xAI server-side tools to send in session.update. Supports file\_search, web\_search, x\_search, and mcp. These are merged with session.tools.
65
+
66
+ **session** (`Partial<XAISessionConfig>`): Additional xAI session fields to merge into the initial session.update event.
67
+
68
+ **url** (`string`): Override the xAI realtime WebSocket URL. (Default: `'wss://api.x.ai/v1/realtime'`)
69
+
70
+ **debug** (`boolean`): Enable debug logging for received xAI events. Debug logs can include transcripts and tool-call arguments. (Default: `false`)
71
+
72
+ ### VoiceConfig pattern
73
+
74
+ You can also use Mastra's shared voice configuration shape:
75
+
76
+ ```typescript
77
+ const voice = new XAIRealtimeVoice({
78
+ speaker: 'ara',
79
+ realtimeConfig: {
80
+ model: 'grok-voice-think-fast-1.0',
81
+ apiKey: process.env.XAI_API_KEY,
82
+ options: {
83
+ instructions: 'Answer briefly.',
84
+ turnDetection: { type: 'server_vad', threshold: 0.85 },
85
+ },
86
+ },
87
+ })
88
+ ```
89
+
90
+ ## Authentication
91
+
92
+ Use `apiKey` or `XAI_API_KEY` for server-side applications. This provider is built for Node.js server-side runtimes. If you already mint xAI ephemeral tokens on your server, you can pass one as `ephemeralToken`; the provider uses the `xai-client-secret.<token>` WebSocket protocol instead of an authorization header. If both `apiKey` and `ephemeralToken` are configured, the provider uses the ephemeral token.
93
+
94
+ ## Methods
95
+
96
+ ### `connect()`
97
+
98
+ Establishes the WebSocket connection and sends the initial `session.update`.
99
+
100
+ **requestContext** (`RequestContext`): Optional Mastra request context passed to function tool executions.
101
+
102
+ Returns: `Promise<void>`
103
+
104
+ ### `close()`
105
+
106
+ Closes the WebSocket connection, ends active speaker streams, and clears queued events, pending function-call state, and request context. `disconnect()` is an alias for `close()`.
107
+
108
+ Returns: `void`
109
+
110
+ ### `addInstructions()`
111
+
112
+ Sets session instructions. If the WebSocket is open, the provider sends a `session.update`; passing `undefined` stores an empty string and clears the active instructions on the current session or the next connection.
113
+
114
+ **instructions** (`string`): System instructions to send to xAI.
115
+
116
+ Returns: `void`
117
+
118
+ ### `addTools()`
119
+
120
+ Registers Mastra function tools and, when connected, refreshes the session tools with `session.update`.
121
+
122
+ **tools** (`ToolsInput`): Mastra tools to expose as xAI function tools.
123
+
124
+ Returns: `void`
125
+
126
+ ### `updateConfig()`
127
+
128
+ Sends a `session.update` event with additional xAI session fields.
129
+
130
+ **sessionConfig** (`Partial<XAISessionConfig>`): Session fields to update.
131
+
132
+ Returns: `void`
133
+
134
+ ### `speak()`
135
+
136
+ Sends a text turn using `conversation.item.create` and then requests a response.
137
+
138
+ **input** (`string | NodeJS.ReadableStream`): Text or a readable text stream to send as user input.
139
+
140
+ **options.speaker** (`XAIVoice`): Voice override. This updates the active xAI session voice and is used for subsequent turns.
141
+
142
+ **options.response** (`Record<string, unknown>`): Additional xAI response.create fields.
143
+
144
+ Returns: `Promise<void>`
145
+
146
+ ### `send()`
147
+
148
+ Streams realtime audio chunks with `input_audio_buffer.append`.
149
+
150
+ `send()` requires an open connection. Use it for live microphone audio after `connect()` resolves. Readable stream chunks must be binary audio chunks (`Buffer`, `ArrayBuffer`, or a typed array).
151
+
152
+ **audioData** (`NodeJS.ReadableStream | Int16Array`): PCM audio stream or Int16Array audio data.
153
+
154
+ **eventId** (`string`): Optional xAI event ID.
155
+
156
+ Returns: `Promise<void>`
157
+
158
+ ### `listen()`
159
+
160
+ Sends a finite audio stream with `input_audio_buffer.append`. By default it commits the input buffer and requests a response.
161
+
162
+ **audioData** (`NodeJS.ReadableStream`): Audio stream to send.
163
+
164
+ **options.commit** (`boolean`): Whether to send input\_audio\_buffer.commit after the audio item. (Default: `true`)
165
+
166
+ **options.createResponse** (`boolean`): Whether to send response.create after the audio item. (Default: `true`)
167
+
168
+ Returns: `Promise<void>`
169
+
170
+ ### `answer()`
171
+
172
+ Sends `response.create` to ask xAI to continue the conversation.
173
+
174
+ Returns: `Promise<void>`
175
+
176
+ ### `commitAudioBuffer()` and `clearAudioBuffer()`
177
+
178
+ Send the matching xAI realtime client events for manual turn control.
179
+
180
+ Returns: `Promise<void>`
181
+
182
+ ### `cancelResponse()`
183
+
184
+ Sends `response.cancel` to interrupt an in-flight response.
185
+
186
+ **responseId** (`string`): Optional xAI response ID to cancel.
187
+
188
+ **eventId** (`string`): Optional xAI event ID.
189
+
190
+ Returns: `Promise<void>`
191
+
192
+ ## Events
193
+
194
+ `XAIRealtimeVoice` maps xAI realtime server events onto Mastra voice events:
195
+
196
+ - `speaker`: emits a readable stream for assistant audio.
197
+ - `speaking`: emits assistant audio deltas.
198
+ - `speaking.done`: emits when an assistant audio response completes.
199
+ - `writing`: emits assistant text deltas and user input transcriptions.
200
+ - `error`: emits xAI errors, provider execution errors, tool execution errors, and malformed function-call arguments. Tool errors include `details.call_id` and `details.name`.
201
+ - `close`: emits when the WebSocket closes.
202
+ - `tool-call-start`: emits before a Mastra function tool is executed.
203
+ - `tool-call-result`: emits after a Mastra function tool returns.
204
+
205
+ Raw xAI event names are also emitted, so you can subscribe to events such as `response.output_audio.delta`, `response.text.delta`, `response.function_call_arguments.done`, and `response.done`.
206
+
207
+ ## Tools
208
+
209
+ ### Mastra function tools
210
+
211
+ Tools added with `addTools()` are converted into xAI function tools and included in `session.update`.
212
+
213
+ ```typescript
214
+ import { createTool } from '@mastra/core/tools'
215
+ import { z } from 'zod'
216
+
217
+ const weatherTool = createTool({
218
+ id: 'getWeather',
219
+ description: 'Get current weather for a location.',
220
+ inputSchema: z.object({
221
+ location: z.string(),
222
+ }),
223
+ execute: async ({ location }) => {
224
+ return { location, temperature: 22 }
225
+ },
226
+ })
227
+
228
+ voice.addTools({ getWeather: weatherTool })
229
+ ```
230
+
231
+ When xAI emits `response.function_call_arguments.done`, the provider executes the matching Mastra tool and sends a `function_call_output` item. If xAI emits multiple function calls for one response, the provider waits for every tool result and the response's `response.done` event before sending one continuation `response.create`.
232
+
233
+ ### xAI server-side tools
234
+
235
+ xAI server-side tools are passed through in the session configuration and executed by xAI. Tools passed in `session.tools` and `serverTools` are merged:
236
+
237
+ ```typescript
238
+ const voice = new XAIRealtimeVoice({
239
+ apiKey: process.env.XAI_API_KEY,
240
+ serverTools: [
241
+ { type: 'web_search' },
242
+ { type: 'x_search', allowed_x_handles: ['xai'] },
243
+ { type: 'file_search', vector_store_ids: ['collection_123'], max_num_results: 10 },
244
+ {
245
+ type: 'mcp',
246
+ server_url: 'https://mcp.example.com/mcp',
247
+ server_label: 'business-tools',
248
+ allowed_tools: ['lookup_order'],
249
+ },
250
+ ],
251
+ })
252
+ ```
253
+
254
+ ## Audio formats
255
+
256
+ The default input and output format is 24 kHz PCM16. You can also configure supported PCM sample rates or telephony codecs:
257
+
258
+ ```typescript
259
+ const voice = new XAIRealtimeVoice({
260
+ audio: {
261
+ input: { format: { type: 'audio/pcm', rate: 16000 } },
262
+ output: { format: { type: 'audio/pcm', rate: 16000 } },
263
+ },
264
+ })
265
+ ```
266
+
267
+ Supported format types are `audio/pcm`, `audio/pcmu`, and `audio/pcma`. PCM supports the documented sample rates from 8 kHz through 48 kHz. `audio/pcmu` and `audio/pcma` are G.711 telephony codecs and use 8 kHz.
@@ -70,6 +70,30 @@ const agent = new Agent({
70
70
 
71
71
  **privileged** (`boolean`): Run in privileged mode. (Default: `false`)
72
72
 
73
+ **memory** (`number`): Memory limit in bytes. Docker treats 0 as unlimited. Maps to Docker HostConfig.Memory.
74
+
75
+ **memorySwap** (`number`): Total memory plus swap in bytes. Maps to Docker HostConfig.MemorySwap.
76
+
77
+ **cpuShares** (`number`): CPU shares relative weight. Maps to Docker HostConfig.CpuShares.
78
+
79
+ **cpuQuota** (`number`): CPU quota in microseconds per period. Maps to Docker HostConfig.CpuQuota.
80
+
81
+ **cpuPeriod** (`number`): CPU period in microseconds. Maps to Docker HostConfig.CpuPeriod.
82
+
83
+ **pidsLimit** (`number`): Maximum number of process IDs in the container. Maps to Docker HostConfig.PidsLimit.
84
+
85
+ **readonlyRootfs** (`boolean`): Mount the container root filesystem as read-only. Maps to Docker HostConfig.ReadonlyRootfs.
86
+
87
+ **capDrop** (`string[]`): Linux capabilities to drop. Use \['ALL'] to drop all capabilities before adding specific ones. Maps to Docker HostConfig.CapDrop.
88
+
89
+ **capAdd** (`string[]`): Linux capabilities to add back after drops, such as NET\_BIND\_SERVICE. Maps to Docker HostConfig.CapAdd.
90
+
91
+ **securityOpt** (`string[]`): Docker security options, such as \['no-new-privileges:true']. Maps to Docker HostConfig.SecurityOpt.
92
+
93
+ **ulimits** (`Array<{ name: string; soft: number; hard: number }>`): Ulimit entries for the container. Maps to Docker HostConfig.Ulimits.
94
+
95
+ **tmpfs** (`Record<string, string>`): tmpfs mount paths and options. Maps to Docker HostConfig.Tmpfs.
96
+
73
97
  **workingDir** (`string`): Working directory inside the container. (Default: `'/workspace'`)
74
98
 
75
99
  **labels** (`Record<string, string>`): Additional container labels. Mastra labels (mastra.sandbox, mastra.sandbox.id) are always included.
@@ -146,6 +170,42 @@ const sandbox = new DockerSandbox({
146
170
 
147
171
  Bind mounts are applied at container creation time. The host paths must exist before the sandbox starts.
148
172
 
173
+ ## Hardening
174
+
175
+ Use Docker-specific resource and hardening options to limit a sandbox container. The following example caps memory and process count, limits CPU to a single core with matching `cpuPeriod` and `cpuQuota` values, drops Linux capabilities, makes the root filesystem read-only, and mounts `/tmp` as writable scratch space:
176
+
177
+ ```typescript
178
+ const sandbox = new DockerSandbox({
179
+ image: 'node:22-slim',
180
+ memory: 512 * 1024 * 1024,
181
+ memorySwap: 512 * 1024 * 1024,
182
+ cpuPeriod: 100_000,
183
+ cpuQuota: 100_000,
184
+ pidsLimit: 256,
185
+ readonlyRootfs: true,
186
+ capDrop: ['ALL'],
187
+ capAdd: ['NET_BIND_SERVICE'],
188
+ securityOpt: ['no-new-privileges:true'],
189
+ ulimits: [{ name: 'nofile', soft: 1024, hard: 2048 }],
190
+ tmpfs: {
191
+ '/tmp': 'rw,noexec,nosuid,size=64m',
192
+ },
193
+ })
194
+ ```
195
+
196
+ These options map directly to Docker `HostConfig` fields and aren't set unless you pass them.
197
+
198
+ Review these trade-offs before enabling hardening:
199
+
200
+ - `readonlyRootfs`: In-container package installs and tools that write outside mounted paths can fail. Add `tmpfs` entries for writable scratch paths such as `/tmp`, and mount tmpfs or volumes for package-manager caches such as `~/.npm` when needed.
201
+ - `capDrop`: Dropping all capabilities disables commands that need Linux capabilities, including `ping`, mount operations, and FUSE-backed tools. Add back only the capabilities your workload needs.
202
+ - `memory`: Docker treats `0` as unlimited. Omit `memory` or pass `0` only when you don't want a memory cap.
203
+ - `memorySwap`: Docker memory and swap behavior depends on the host and Docker daemon configuration. When you set `memory` without `memorySwap`, Docker allows swap up to twice the memory limit by default. Set `memorySwap` equal to `memory` when you want to disable swap for the container; Docker also accepts `-1` for unlimited swap.
204
+ - `pidsLimit`: Very low values can break `docker exec` workloads because each command starts additional processes inside the long-lived container.
205
+ - `privileged`: Privileged containers bypass capability and security-option controls. Don't combine `privileged: true` with capability or security options unless the workload requires it.
206
+ - Reconnection: `DockerSandbox` reuses an existing container when the sandbox ID matches and warns if inspected `HostConfig` hardening values differ. Destroy and recreate the sandbox to apply changed hardening options. Docker can normalize inspected values, and changing `memorySwap` on reconnect can trigger a warning if the original container used Docker's default swap behavior.
207
+ - Docker Desktop: Resource limits apply inside the Docker Desktop virtual machine on macOS and Windows, so the VM's allocated resources can cap what containers receive.
208
+
149
209
  ## Reconnection
150
210
 
151
211
  `DockerSandbox` can reconnect to existing containers by matching labels. When `start()` is called, it checks for a container with the `mastra.sandbox.id` label matching the sandbox ID. If found:
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @mastra/mcp-docs-server
2
2
 
3
+ ## 1.1.37-alpha.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`bdb4cbf`](https://github.com/mastra-ai/mastra/commit/bdb4cbf8ba4b685d7481f28bb9dc3de6c79c9ed2)]:
8
+ - @mastra/core@1.34.0-alpha.2
9
+
10
+ ## 1.1.37-alpha.2
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [[`fceae1f`](https://github.com/mastra-ai/mastra/commit/fceae1f5f5db4722cb078a663c6eb4bd22944123), [`bf02acb`](https://github.com/mastra-ai/mastra/commit/bf02acbb8a6110f638ac844e89f1ebf04cb7fe74), [`0fd3fbe`](https://github.com/mastra-ai/mastra/commit/0fd3fbe40fb63657aedd72f6e7b38c8e8ee6940d), [`fed0475`](https://github.com/mastra-ai/mastra/commit/fed0475ccfea31e4fc251469ac05640d0742c1f0), [`522f44d`](https://github.com/mastra-ai/mastra/commit/522f44d947214bfc06cff50599bae1ef3494880d)]:
15
+ - @mastra/core@1.34.0-alpha.1
16
+
3
17
  ## 1.1.37-alpha.0
4
18
 
5
19
  ### Patch Changes
package/dist/stdio.js CHANGED
@@ -1444,12 +1444,12 @@ var readSourceMapTool = {
1444
1444
  void logger.debug("Executing readMastraSourceMap tool", { args });
1445
1445
  const sourceMap = await readSourceMap(args.package, args.projectPath);
1446
1446
  if (!sourceMap) return `No SOURCE_MAP.json found for ${args.package}.`;
1447
- let exports$1 = Object.entries(sourceMap.exports);
1447
+ let exports = Object.entries(sourceMap.exports);
1448
1448
  if (args.filter) {
1449
1449
  const filterLower = args.filter.toLowerCase();
1450
- exports$1 = exports$1.filter(([name]) => name.toLowerCase().includes(filterLower));
1450
+ exports = exports.filter(([name]) => name.toLowerCase().includes(filterLower));
1451
1451
  }
1452
- if (exports$1.length === 0) {
1452
+ if (exports.length === 0) {
1453
1453
  return args.filter ? `No exports matching "${args.filter}" in ${args.package}.
1454
1454
 
1455
1455
  Try running without a filter to see all available exports.` : `No exports found in ${args.package}.`;
@@ -1457,9 +1457,9 @@ Try running without a filter to see all available exports.` : `No exports found
1457
1457
  return [
1458
1458
  `# ${sourceMap.package} v${sourceMap.version} - API Exports`,
1459
1459
  "",
1460
- `Found ${exports$1.length} export(s)${args.filter ? ` matching "${args.filter}"` : ""}:`,
1460
+ `Found ${exports.length} export(s)${args.filter ? ` matching "${args.filter}"` : ""}:`,
1461
1461
  "",
1462
- ...exports$1.map(([name, info]) => {
1462
+ ...exports.map(([name, info]) => {
1463
1463
  const line = info.line ? `:${info.line}` : "";
1464
1464
  return `- **${name}**: \`${info.implementation}${line}\``;
1465
1465
  }),