@mastra/mcp-docs-server 1.2.2 → 1.2.3-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,226 @@
1
+ # SlackProvider
2
+
3
+ `SlackProvider` is the managed path for connecting agents to Slack. Register it on `Mastra.channels` and it provisions Slack apps via the Manifest API, runs the OAuth install flow, rotates configuration tokens, and routes Slack events to your agents. Use it when you want Mastra to own app creation and installation. For the lower-level path where you create the Slack app and configure scopes and webhooks yourself, use [`createSlackAdapter`](https://mastra.ai/docs/agents/channels) on the agent's `channels.adapters` instead.
4
+
5
+ ## Usage example
6
+
7
+ Register the provider on the `Mastra` constructor. The refresh token is single-use and rotates on startup; the resulting access tokens are persisted to `Mastra.storage`.
8
+
9
+ ```typescript
10
+ import { Mastra } from '@mastra/core/mastra'
11
+ import { SlackProvider } from '@mastra/slack'
12
+
13
+ export const mastra = new Mastra({
14
+ storage,
15
+ channels: {
16
+ slack: new SlackProvider({
17
+ refreshToken: process.env.SLACK_APP_CONFIG_REFRESH_TOKEN,
18
+ baseUrl: process.env.MASTRA_BASE_URL,
19
+ }),
20
+ },
21
+ })
22
+ ```
23
+
24
+ When credentials aren't available at construction time (for example, entered through the Editor UI or loaded from a vault), construct the provider without them and call [`configure()`](#configurecredentials) later:
25
+
26
+ ```typescript
27
+ const slack = new SlackProvider()
28
+
29
+ await slack.configure({
30
+ refreshToken: process.env.SLACK_APP_CONFIG_REFRESH_TOKEN,
31
+ })
32
+ ```
33
+
34
+ ## Constructor parameters
35
+
36
+ `SlackProviderConfig` combines Slack-specific fields, Slack-adapter overrides (`toolDisplay`, `streaming`, `typingStatus`), and a curated subset of [`ChannelConfig`](https://mastra.ai/reference/agents/channels) options (such as `handlers`, `inlineMedia`, and `state`) forwarded to every connected agent. All fields are optional.
37
+
38
+ **refreshToken** (`string`): Slack App Configuration refresh token, used for automatic token rotation. Single-use; each rotation returns a new pair. Can also be provided later via \`configure()\`. If omitted, the provider starts unconfigured and cannot create apps until \`configure()\` is called or tokens are loaded from storage. Generate it under "Your App Configuration Tokens" at api.slack.com/apps.
39
+
40
+ **token** (`string`): Slack App Configuration access token for programmatic app creation. Optional, because the provider rotates to a fresh token on startup using \`refreshToken\`.
41
+
42
+ **baseUrl** (`string`): Public base URL for webhook and OAuth callbacks. Required when calling \`connect()\` to create apps. Can also be set via \`setBaseUrl()\` or auto-detected from the Mastra server config. For local development, use a tunnel such as \`cloudflared\`.
43
+
44
+ **encryptionKey** (`string`): Encryption key for sensitive stored data (client secret, signing secret, bot token). Use a 32+ character random string. Can be set via the \`MASTRA\_ENCRYPTION\_KEY\` env var. If omitted, secrets are stored in plaintext (not recommended for production).
45
+
46
+ **storage** (`ChannelsStorage`): Custom storage for installations. Defaults to Mastra's \`ChannelsStorage\` from the global storage. Throws if no persistent storage is available.
47
+
48
+ **redirectPath** (`string`): Path to redirect to after OAuth completion. (Default: `"/"`)
49
+
50
+ **onInstall** (`(installation: SlackInstallation) => Promise<void>`): Called when a workspace successfully installs the app.
51
+
52
+ **streaming** (`StreamingConfig | false`): Stream agent text deltas to Slack as they're generated. Pass \`{ updateIntervalMs }\` to customize the post-and-edit interval, or \`false\` to buffer text until step-finish. Disabling streaming restricts \`toolDisplay\` to static modes. (Default: `true`)
53
+
54
+ **toolDisplay** (`ToolDisplay`): How tool calls are rendered in Slack: \`'cards'\`, \`'text'\`, \`'timeline'\`, \`'grouped'\`, \`'hidden'\`, or a function. \`'hidden'\` suppresses tool call/result rendering entirely. \`'timeline'\` and \`'grouped'\` require streaming. With \`streaming: false\`, only static modes are available and the default is \`'cards'\`. (Default: `'grouped'`)
55
+
56
+ **typingStatus** (`boolean | TypingStatusFn`): Show a typing indicator while the agent works. Set \`false\` to disable, or pass a function to return custom status text per stream chunk (return \`undefined\` to fall back to the default for that chunk). (Default: `true`)
57
+
58
+ **waitUntil** (`WaitUntilFn`): Returns a \`waitUntil\` for the current Slack webhook request. Required on serverless runtimes where Hono can't bridge the platform's \`ExecutionContext\` (Vercel, AWS Lambda). Without it, the invocation freezes after the 200 ack and kills the run mid-flight. Pass the bare \`waitUntil(promise)\` from your platform SDK (for example, \`@vercel/functions\`). Cloudflare Workers and Netlify users typically don't need this.
59
+
60
+ **resolveWaitUntil** (`WaitUntilResolver`): Resolve \`waitUntil\` from the request's Hono \`Context\` when the runtime exposes it through the request and core's default doesn't cover it. Resolution order: \`waitUntil\` → \`resolveWaitUntil\` → core default.
61
+
62
+ **handlers** (`ChannelHandlers`): Override built-in event handlers (\`onDirectMessage\`, \`onMention\`). Forwarded to \`AgentChannels\` for every agent connected via this provider.
63
+
64
+ **inlineMedia** (`ChannelConfig['inlineMedia']`): Which media types to send inline to the model.
65
+
66
+ **inlineLinks** (`ChannelConfig['inlineLinks']`): Promote URLs in message text to file parts.
67
+
68
+ **threadContext** (`ChannelConfig['threadContext']`): Fetch recent thread messages from Slack when the agent joins mid-conversation.
69
+
70
+ **tools** (`ChannelConfig['tools']`): Whether to include channel tools (\`add\_reaction\`, \`remove\_reaction\`).
71
+
72
+ **state** (`ChannelConfig['state']`): State adapter for message deduplication, locking, and subscriptions. Defaults to the \`MastraStateAdapter\` backed by the Mastra instance's configured storage, so subscriptions persist across restarts.
73
+
74
+ **chatOptions** (`ChannelConfig['chatOptions']`): Additional options passed directly to the Chat SDK.
75
+
76
+ **logger** (`SlackAdapterConfig['logger']`): Logger forwarded to the underlying \`SlackAdapter\`. Defaults to the adapter's \`ConsoleLogger\`.
77
+
78
+ ## Methods
79
+
80
+ ### Agent connections
81
+
82
+ #### `connect(agentId, options?)`
83
+
84
+ Creates a new Slack app for the agent via the Manifest API and returns an OAuth result with the authorization URL to redirect the user to. Requires `baseUrl` to be set. If a pending installation already exists for the agent, returns its existing authorization URL instead of creating a duplicate app.
85
+
86
+ ```typescript
87
+ const result = await slack.connect('support-agent', {
88
+ name: 'Support Bot',
89
+ })
90
+
91
+ // Redirect the user to result.authorizationUrl to install the app
92
+ ```
93
+
94
+ Returns: `Promise<ChannelConnectResult>`
95
+
96
+ ```typescript
97
+ interface ChannelConnectResult {
98
+ type: 'oauth'
99
+ installationId: string
100
+ authorizationUrl: string
101
+ }
102
+ ```
103
+
104
+ `SlackConnectOptions` is serializable and can be stored for stored agents:
105
+
106
+ **name** (`string`): Display name for the Slack bot. Defaults to the agent name, then the agent ID.
107
+
108
+ **description** (`string`): Bot description shown in Slack. Defaults to "{name} - Powered by Mastra".
109
+
110
+ **iconUrl** (`string`): URL to a square image (min 512x512) for the app icon. Downloaded and uploaded to Slack automatically.
111
+
112
+ **manifest** (`(defaults: SlackAppManifest) => SlackAppManifest`): Customize the Slack app manifest before it is sent to the Manifest API. Receives the default manifest and returns the final one. Use it for custom scopes, additional events, or interactivity settings.
113
+
114
+ **redirectUrl** (`string`): URL to redirect to after successful OAuth completion. Defaults to the provider's \`redirectPath\` or \`/\`.
115
+
116
+ #### `disconnect(agentId)`
117
+
118
+ Disconnects an agent from Slack by deleting its app and removing the installation from storage.
119
+
120
+ ```typescript
121
+ await slack.disconnect('support-agent')
122
+ ```
123
+
124
+ Returns: `Promise<void>`
125
+
126
+ #### `getInstallation(agentId)`
127
+
128
+ Returns the Slack installation for an agent, or `null` if none exists.
129
+
130
+ ```typescript
131
+ const installation = await slack.getInstallation('support-agent')
132
+ ```
133
+
134
+ Returns: `Promise<SlackInstallation | null>`
135
+
136
+ #### `listInstallations()`
137
+
138
+ Lists all Slack installations (public info only), including active and pending.
139
+
140
+ ```typescript
141
+ const installations = await slack.listInstallations()
142
+ ```
143
+
144
+ Returns: `Promise<ChannelInstallationInfo[]>`
145
+
146
+ ### Configuration
147
+
148
+ #### `configure(credentials)`
149
+
150
+ Provides or clears Slack App Configuration credentials at runtime. Use this when credentials aren't available at construction time. Pass `null` to clear credentials and delete stored tokens.
151
+
152
+ ```typescript
153
+ // Provide credentials (persists to storage immediately)
154
+ await slack.configure({ refreshToken: 'xoxe-1-...' })
155
+
156
+ // Clear credentials and stored tokens
157
+ await slack.configure(null)
158
+ ```
159
+
160
+ Returns: `Promise<void>`
161
+
162
+ #### `setBaseUrl(baseUrl)`
163
+
164
+ Sets the public base URL used for webhook and OAuth callbacks. Use this when the URL isn't known at construction time and can't be auto-detected from the server config.
165
+
166
+ ```typescript
167
+ slack.setBaseUrl('https://abc123.trycloudflare.com')
168
+ ```
169
+
170
+ #### `initialize()`
171
+
172
+ Recreates a `SlackAdapter` for each active installation in storage and injects `AgentChannels` into the corresponding agent so it receives Slack events on startup. Does not auto-provision new apps; use `connect()` to create one. Mastra calls this automatically, so you rarely call it directly.
173
+
174
+ ```typescript
175
+ await slack.initialize()
176
+ ```
177
+
178
+ Returns: `Promise<void>`
179
+
180
+ ## Default manifest
181
+
182
+ When `connect()` builds a Slack app, the generated manifest requests a default set of bot scopes and event subscriptions. Override them with the `manifest` option on `connect()`.
183
+
184
+ | Default bot scopes | Default bot events |
185
+ | ------------------- | ------------------ |
186
+ | `chat:write` | `app_mention` |
187
+ | `chat:write.public` | `message.channels` |
188
+ | `im:write` | `message.groups` |
189
+ | `channels:history` | `message.im` |
190
+ | `channels:read` | `message.mpim` |
191
+ | `groups:history` | |
192
+ | `groups:read` | |
193
+ | `im:history` | |
194
+ | `im:read` | |
195
+ | `mpim:history` | |
196
+ | `mpim:read` | |
197
+ | `app_mentions:read` | |
198
+ | `users:read` | |
199
+ | `reactions:write` | |
200
+ | `files:read` | |
201
+ | `assistant:write` | |
202
+
203
+ ## Accessing the provider
204
+
205
+ Access the registered provider through the typed `channels` getter, keyed by the id you registered it under:
206
+
207
+ ```typescript
208
+ const result = await mastra.channels.slack.connect('support-agent')
209
+ ```
210
+
211
+ When the key is only known at runtime, look it up by string id and pass the concrete type:
212
+
213
+ ```typescript
214
+ const slack = mastra.getChannelProvider<SlackProvider>('slack')
215
+ const result = await slack.connect('support-agent')
216
+ ```
217
+
218
+ ## Storage requirement
219
+
220
+ `SlackProvider` requires persistent storage on `Mastra` to encrypt and persist installations and rotating configuration tokens. The constructor throws if no persistent storage is available and no custom `storage` is passed.
221
+
222
+ ## Related
223
+
224
+ - [ChannelProvider](https://mastra.ai/reference/channels/channel-provider): the interface `SlackProvider` implements
225
+ - [Channels](https://mastra.ai/docs/agents/channels): concepts, platform setup, and the `createSlackAdapter` path
226
+ - [Channels reference](https://mastra.ai/reference/agents/channels): the `channels` config on the `Agent` constructor
@@ -4,6 +4,8 @@ The Reference section provides documentation of Mastra's API, including paramete
4
4
 
5
5
  - [AcpAgent](https://mastra.ai/reference/acp/acp-agent)
6
6
  - [createACPTool()](https://mastra.ai/reference/acp/create-acp-tool)
7
+ - [AgentController Class](https://mastra.ai/reference/agent-controller/agent-controller-class)
8
+ - [Session Class](https://mastra.ai/reference/agent-controller/session)
7
9
  - [Agent Class](https://mastra.ai/reference/agents/agent)
8
10
  - [Channels](https://mastra.ai/reference/agents/channels)
9
11
  - [createInngestAgent()](https://mastra.ai/reference/agents/inngest-agent)
@@ -54,6 +56,8 @@ The Reference section provides documentation of Mastra's API, including paramete
54
56
  - [BrowserViewer](https://mastra.ai/reference/browser/browser-viewer)
55
57
  - [MastraBrowser Class](https://mastra.ai/reference/browser/mastra-browser)
56
58
  - [StagehandBrowser](https://mastra.ai/reference/browser/stagehand-browser)
59
+ - [ChannelProvider](https://mastra.ai/reference/channels/channel-provider)
60
+ - [SlackProvider](https://mastra.ai/reference/channels/slack-provider)
57
61
  - [create-mastra](https://mastra.ai/reference/cli/create-mastra)
58
62
  - [mastra](https://mastra.ai/reference/cli/mastra)
59
63
  - [Agent Builder API](https://mastra.ai/reference/client-js/agent-builder)
@@ -165,8 +169,6 @@ The Reference section provides documentation of Mastra's API, including paramete
165
169
  - [.startExperimentAsync()](https://mastra.ai/reference/datasets/startExperimentAsync)
166
170
  - [.update()](https://mastra.ai/reference/datasets/update)
167
171
  - [.updateItem()](https://mastra.ai/reference/datasets/updateItem)
168
- - [Harness Class](https://mastra.ai/reference/harness/harness-class)
169
- - [Session Class](https://mastra.ai/reference/harness/session)
170
172
  - [API Reference](https://mastra.ai/reference/mastra-platform/api)
171
173
  - [Cloned Thread Utilities](https://mastra.ai/reference/memory/clone-utilities)
172
174
  - [Memory Class](https://mastra.ai/reference/memory/memory-class)
@@ -209,6 +211,7 @@ The Reference section provides documentation of Mastra's API, including paramete
209
211
  - [CachingPubSub](https://mastra.ai/reference/pubsub/caching-pubsub)
210
212
  - [EventEmitterPubSub](https://mastra.ai/reference/pubsub/event-emitter)
211
213
  - [GoogleCloudPubSub](https://mastra.ai/reference/pubsub/google-cloud-pubsub)
214
+ - [LeaseProvider](https://mastra.ai/reference/pubsub/lease-provider)
212
215
  - [PubSub](https://mastra.ai/reference/pubsub/base)
213
216
  - [RedisStreamsPubSub](https://mastra.ai/reference/pubsub/redis-streams)
214
217
  - [UnixSocketPubSub](https://mastra.ai/reference/pubsub/unix-socket-pubsub)
@@ -0,0 +1,131 @@
1
+ # LeaseProvider
2
+
3
+ `LeaseProvider` is the distributed leasing contract, separate from event delivery ([`PubSub`](https://mastra.ai/reference/pubsub/base)). Mastra's [signals layer](https://mastra.ai/docs/agents/signals) uses it to elect a single owner across multiple processes (for example, serverless invocations) for a given resource, most commonly a thread key. The owner is the process that wakes and runs the agent stream, so other processes route follow-up work to it instead of starting a competing run.
4
+
5
+ Leasing is a distinct concern from pub/sub. A backend implements `LeaseProvider` only when it can genuinely coordinate a lock, such as Redis via atomic `SET`/Lua, or an in-memory map for single-process. Backends that cannot lease omit it; the signals runtime feature-detects the capability and falls back to a no-op provider, preserving single-process behavior.
6
+
7
+ The built-in [`RedisStreamsPubSub`](https://mastra.ai/reference/pubsub/redis-streams) implements `LeaseProvider`, which is what enables signals to coordinate across instances in distributed and serverless deployments.
8
+
9
+ ## Usage example
10
+
11
+ You don't construct a `LeaseProvider` directly. Configure a pub/sub backend that implements it (such as `RedisStreamsPubSub`) on the `Mastra` constructor, and the signals runtime uses it automatically for cross-process coordination.
12
+
13
+ ```typescript
14
+ import { Mastra } from '@mastra/core'
15
+ import { RedisStreamsPubSub } from '@mastra/redis-streams'
16
+
17
+ export const mastra = new Mastra({
18
+ // RedisStreamsPubSub implements both PubSub and LeaseProvider
19
+ pubsub: new RedisStreamsPubSub({
20
+ url: process.env.REDIS_URL,
21
+ }),
22
+ })
23
+ ```
24
+
25
+ To implement leasing in a custom backend, implement the methods below. The signals runtime detects the capability structurally (so it works across package boundaries) and only uses it when all methods are present.
26
+
27
+ ```typescript
28
+ import { PubSub } from '@mastra/core/events'
29
+ import type { LeaseProvider } from '@mastra/core/events'
30
+
31
+ export class CustomPubSub extends PubSub implements LeaseProvider {
32
+ async acquireLease(key: string, owner: string, ttlMs: number) {
33
+ // Atomically claim the lease, or report the current holder.
34
+ return { acquired: true, owner }
35
+ }
36
+
37
+ // ...getLeaseOwner, releaseLease, renewLease, transferLease
38
+ }
39
+ ```
40
+
41
+ ## Methods
42
+
43
+ ### Leasing
44
+
45
+ #### `acquireLease(key, owner, ttlMs)`
46
+
47
+ Atomically tries to acquire a lease on a key. Returns `{ acquired: true, owner }` if the caller claimed the lease, or `{ acquired: false, owner }` where `owner` is the current holder, so the caller can route follow-up work to them. The same owner can call `acquireLease` idempotently to renew or re-claim.
48
+
49
+ ```typescript
50
+ const result = await pubsub.acquireLease('thread:abc', runId, 15000)
51
+
52
+ if (result.acquired) {
53
+ // This process owns the thread, so wake and run the agent.
54
+ } else {
55
+ // result.owner holds the lease, so route the signal to them.
56
+ }
57
+ ```
58
+
59
+ Returns: `Promise<{ acquired: boolean; owner?: string }>`
60
+
61
+ **key** (`string`): The lease key, such as a thread key.
62
+
63
+ **owner** (`string`): Identifier for the owner, such as a \`runId\`. The same owner can call \`acquireLease\` idempotently to renew or release.
64
+
65
+ **ttlMs** (`number`): Time-to-live in milliseconds for the lease.
66
+
67
+ #### `getLeaseOwner(key)`
68
+
69
+ Reads the current owner of a lease, or `undefined` if no lease is held.
70
+
71
+ ```typescript
72
+ const owner = await pubsub.getLeaseOwner('thread:abc')
73
+ ```
74
+
75
+ Returns: `Promise<string | undefined>`
76
+
77
+ #### `releaseLease(key, owner)`
78
+
79
+ Releases a lease. This is a no-op if the caller is not the current owner: implementations check ownership atomically before releasing, so a concurrent renewal by another owner is never clobbered.
80
+
81
+ ```typescript
82
+ await pubsub.releaseLease('thread:abc', runId)
83
+ ```
84
+
85
+ Returns: `Promise<void>`
86
+
87
+ #### `renewLease(key, owner, ttlMs)`
88
+
89
+ Renews an existing lease owned by `owner`, extending its TTL. Returns `true` if the renewal succeeded and the caller still owns the lease, or `false` if the lease was lost (TTL expired or another owner took it).
90
+
91
+ ```typescript
92
+ const stillOwned = await pubsub.renewLease('thread:abc', runId, 15000)
93
+
94
+ if (!stillOwned) {
95
+ // Lost the lease, so stop renewing and let the new owner take over.
96
+ }
97
+ ```
98
+
99
+ Returns: `Promise<boolean>`
100
+
101
+ #### `transferLease(key, fromOwner, toOwner, ttlMs)`
102
+
103
+ Atomically hands a held lease from `fromOwner` to `toOwner`, refreshing its TTL, without releasing the key in between. This is the gap-free primitive used when one owner finishes but a follow-up owner must take over the same key immediately, for example when a thread run completes and a queued follow-up run drains on the same thread. A naive release-then-acquire would briefly leave the key empty, letting a racing process win the freed lease and start a competing run.
104
+
105
+ Returns `true` if `fromOwner` still held the lease and ownership moved to `toOwner`, or `false` if the lease was already lost, in which case the caller should fall back to a fresh `acquireLease`.
106
+
107
+ ```typescript
108
+ const transferred = await pubsub.transferLease('thread:abc', currentRunId, nextRunId, 15000)
109
+
110
+ if (!transferred) {
111
+ // Lease was lost, so acquire fresh instead.
112
+ await pubsub.acquireLease('thread:abc', nextRunId, 15000)
113
+ }
114
+ ```
115
+
116
+ Returns: `Promise<boolean>`
117
+
118
+ > **Warning:** Backends that cannot perform the transfer atomically must still implement it as a best-effort `releaseLease(fromOwner)` followed by `acquireLease(toOwner)`, and document that the swap is non-atomic, since a racing process can win the key in the gap. Keeping the method required means callers have a single code path and atomicity is an explicit per-backend decision.
119
+
120
+ ## Capability detection
121
+
122
+ The signals runtime detects `LeaseProvider` structurally rather than with `instanceof`, so detection works even when a separately published backend resolves a different copy of `@mastra/core`. A value is treated as a `LeaseProvider` when it exposes all five methods (`acquireLease`, `getLeaseOwner`, `releaseLease`, `renewLease`, `transferLease`).
123
+
124
+ When the configured pub/sub backend does not implement `LeaseProvider`, the runtime falls back to an always-win no-op provider. Every caller wins its own lease race, and release, renew, and transfer are inert, which preserves the expected single-process behavior.
125
+
126
+ ## Related
127
+
128
+ - [PubSub](https://mastra.ai/reference/pubsub/base): The event delivery contract, separate from leasing
129
+ - [RedisStreamsPubSub](https://mastra.ai/reference/pubsub/redis-streams): The built-in backend that implements `LeaseProvider`
130
+ - [Signals](https://mastra.ai/docs/agents/signals): The runtime that uses leasing to coordinate thread runs across processes
131
+ - [Channels](https://mastra.ai/docs/agents/channels): Uses leasing to coordinate agent runs in serverless and multi-instance deployments
@@ -1,6 +1,6 @@
1
1
  # RedisStreamsPubSub
2
2
 
3
- `RedisStreamsPubSub` is a [`PubSub`](https://mastra.ai/reference/pubsub/base) implementation backed by [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams/). It delivers events across processes and hosts, with persistence, consumer groups, and redelivery on failure.
3
+ `RedisStreamsPubSub` is a [`PubSub`](https://mastra.ai/reference/pubsub/base) implementation backed by [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams/). It delivers events across processes and hosts, with persistence, consumer groups, and redelivery on failure. It also implements [`LeaseProvider`](https://mastra.ai/reference/pubsub/lease-provider), so the signals layer can elect a single owner per resource across instances, which is what lets signals coordinate runs in distributed and serverless deployments.
4
4
 
5
5
  Use it for distributed deployments where several services share an event stream. For single-process delivery, use [`EventEmitterPubSub`](https://mastra.ai/reference/pubsub/event-emitter). For Google Cloud, use [`GoogleCloudPubSub`](https://mastra.ai/reference/pubsub/google-cloud-pubsub).
6
6
 
@@ -105,4 +105,12 @@ await pubsub.close()
105
105
 
106
106
  ## Redelivery and reclaim
107
107
 
108
- When a subscriber calls `nack`, the event is republished with an incremented `deliveryAttempt` and the original is acknowledged. Once an event reaches `maxDeliveryAttempts`, it's dropped instead of redelivered. Separately, each subscription periodically reclaims events that an earlier consumer in the group read but never acknowledged, controlled by `reclaimIntervalMs` and `reclaimIdleMs`.
108
+ When a subscriber calls `nack`, the event is republished with an incremented `deliveryAttempt` and the original is acknowledged. Once an event reaches `maxDeliveryAttempts`, it's dropped instead of redelivered. Separately, each subscription periodically reclaims events that an earlier consumer in the group read but never acknowledged, controlled by `reclaimIntervalMs` and `reclaimIdleMs`.
109
+
110
+ ## Distributed leasing
111
+
112
+ `RedisStreamsPubSub` implements the [`LeaseProvider`](https://mastra.ai/reference/pubsub/lease-provider) contract on top of the same Redis connection. The [signals runtime](https://mastra.ai/docs/agents/signals) uses it to elect a single owner (usually per thread key) so that across instances only one process wakes and runs the agent, and others route follow-up work to the holder. This is what makes signals work on serverless and multi-instance deployments; without a shared lease, each instance would start its own competing run.
113
+
114
+ Lease keys are namespaced under the same `keyPrefix` as topics, as `<keyPrefix>:lease:<key>`. All operations are atomic: `acquireLease` uses `SET NX PX` and refreshes its own TTL idempotently, while `releaseLease`, `renewLease`, and `transferLease` use Lua scripts that check ownership before mutating, so a concurrent renewal from another owner is never clobbered.
115
+
116
+ You don't call these methods directly. Configuring `RedisStreamsPubSub` as the `pubsub` backend is enough for the runtime to detect and use the capability. See [`LeaseProvider`](https://mastra.ai/reference/pubsub/lease-provider) for the full method contract.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @mastra/mcp-docs-server
2
2
 
3
+ ## 1.2.3-alpha.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`95857bc`](https://github.com/mastra-ai/mastra/commit/95857bcd6669da7193f503e803f0d72a2bd66be6), [`8e9c0fb`](https://github.com/mastra-ai/mastra/commit/8e9c0fb48fd58da2efcdff2cf1202ee41092c315)]:
8
+ - @mastra/core@1.48.0-alpha.1
9
+
10
+ ## 1.2.3-alpha.0
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [[`b9a2961`](https://github.com/mastra-ai/mastra/commit/b9a2961c1be81e3639c0879e58588c26dd0ae866), [`1274eb3`](https://github.com/mastra-ai/mastra/commit/1274eb3a9508f579ceb3187fbce34408222d4b71), [`1274eb3`](https://github.com/mastra-ai/mastra/commit/1274eb3a9508f579ceb3187fbce34408222d4b71), [`9566d27`](https://github.com/mastra-ai/mastra/commit/9566d27ead3d95bdbe5a69e5a082a68222829cf2)]:
15
+ - @mastra/core@1.48.0-alpha.0
16
+
3
17
  ## 1.2.2
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.2.2",
3
+ "version": "1.2.3-alpha.3",
4
4
  "description": "MCP server for accessing Mastra.ai documentation, changelogs, and news.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,7 +29,7 @@
29
29
  "local-pkg": "^1.1.2",
30
30
  "zod": "^4.4.3",
31
31
  "@mastra/mcp": "^1.12.0",
32
- "@mastra/core": "1.47.0"
32
+ "@mastra/core": "1.48.0-alpha.1"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@hono/node-server": "^1.19.11",
@@ -45,9 +45,9 @@
45
45
  "tsx": "^4.22.4",
46
46
  "typescript": "^6.0.3",
47
47
  "vitest": "4.1.8",
48
- "@internal/lint": "0.0.109",
49
48
  "@internal/types-builder": "0.0.84",
50
- "@mastra/core": "1.47.0"
49
+ "@internal/lint": "0.0.109",
50
+ "@mastra/core": "1.48.0-alpha.1"
51
51
  },
52
52
  "homepage": "https://mastra.ai",
53
53
  "repository": {
@@ -1,128 +0,0 @@
1
- # Harness overview
2
-
3
- > **Beta:** The `Harness` feature is in beta stage and subject to breaking changes in minor versions until it graduates from its beta status.
4
-
5
- The Harness is a session controller for building interactive agent applications. It handles the runtime concerns that sit between your UI and the agent loop: managing conversation threads, switching between agent modes, persisting state, gating tool execution with approvals, and coordinating subagents. You can focus on what your agent does rather than how to wire it together.
6
-
7
- A Harness exposes a [`Session`](https://mastra.ai/docs/harness/session) — the per-conversation runtime state that tracks the active mode, model, thread binding, permission grants, follow-up queue, and token usage. The Harness is the shared host; the Session is the conversation running inside it. In a multi-user host, the same Harness can back many Sessions at once.
8
-
9
- [Mastra Code](https://code.mastra.ai/) is the flagship Harness implementation: A terminal-based coding agent with multi-model support, persistent conversations, and plan-then-execute workflows.
10
-
11
- ## What you can build
12
-
13
- The Harness gives you the runtime pieces to ship interactive agent applications. Each outcome below maps to a capability you can use today:
14
-
15
- - **Resume a conversation exactly where the user left off.** Persistent [threads and state](https://mastra.ai/docs/harness/threads-and-state) reload the active mode, model, and progress across restarts, so a coding agent or assistant picks up mid-task instead of starting over.
16
- - **Gate destructive actions behind human approval.** [Tool approvals and permission policies](https://mastra.ai/docs/harness/tool-approvals) let you require confirmation for risky operations like file writes or deployments, while trusted tools run automatically.
17
- - **Move a task through distinct phases without losing context.** [Modes](https://mastra.ai/docs/harness/modes) switch the agent's instructions, tools, and model on the same thread, so you can build a plan-then-execute coding agent or a research-then-draft assistant.
18
- - **Let users pick the right model for each step.** Per-mode [model management](https://mastra.ai/reference/harness/harness-class) switches models at runtime and tracks usage, which powers copilot UIs where users trade speed for capability.
19
- - **Delegate focused work to child agents.** [Subagents](https://mastra.ai/docs/harness/subagents) run subtasks with constrained tools and can fork the parent conversation, so a research mode can spin off web search or code review without polluting the main thread.
20
- - **Drive a live UI from agent activity.** The [event system](https://mastra.ai/docs/harness/session) emits typed events and coalesced display snapshots, so your TUI or web app reflects message updates, mode changes, and pending approvals in real time.
21
- - **Run long-lived autonomous agents.** Structured task lists, heartbeat handlers, and observational memory keep background task runners on track and let them learn across threads.
22
-
23
- ## When to use the Harness
24
-
25
- Use the Harness when your application needs:
26
-
27
- - Multiple agent modes that share one conversation thread (e.g., plan → build → review)
28
- - A control layer between your UI and the agent loop (model switching, state persistence, thread management)
29
- - Tool approval flows and permission policies for human-in-the-loop gating
30
- - Subagent orchestration to delegate focused subtasks with constrained tools
31
- - Session continuity with persistent threads, state, and observational memory across restarts
32
-
33
- You could assemble all of this yourself on top of the [Agent class](https://mastra.ai/docs/agents/overview), which exposes the full agent loop, tools, and memory. The Harness is an opinionated set of defaults that wires those pieces into one application style: an ongoing session where the agent acts as a collaborator rather than a one-shot endpoint. Reach for the Agent class directly when you want full control or a simple request-response call; reach for the Harness when you want the collaborative-session model without building the runtime around it.
34
-
35
- ## Key capabilities
36
-
37
- - **Session**: Per-conversation state — active thread, mode, model, grants, follow-ups, token usage, and the display snapshot — accessed through `harness.session`. See [Session](https://mastra.ai/docs/harness/session).
38
- - **Modes**: Define distinct agent personalities (instructions, tools, model) and switch between them without losing conversation context. See [Modes](https://mastra.ai/docs/harness/modes).
39
- - **Threads and state**: Persist conversations and structured state across sessions, users, and mode switches. See [Threads and state](https://mastra.ai/docs/harness/threads-and-state).
40
- - **Subagents**: Spawn focused child agents with constrained tools for subtasks, optionally forking the parent conversation. See [Subagents](https://mastra.ai/docs/harness/subagents).
41
- - **Tool approvals and permissions**: Configure which tools require user confirmation, grant session-wide exceptions, and handle interactive tool suspension. See [Tool approvals](https://mastra.ai/docs/harness/tool-approvals).
42
- - **Model management**: Switch models per-mode at runtime, track usage, and resolve gateway-backed models through Mastra's [model router](https://mastra.ai/models).
43
- - **Follow-ups and steering**: Queue messages while the agent is running, or inject mid-stream instructions to redirect the agent. Built on [signals](https://mastra.ai/docs/agents/signals).
44
- - **Event system**: Subscribe to typed events (message updates, mode changes, tool approvals) or coalesced `HarnessDisplayState` snapshots to drive your UI. See [Events](https://mastra.ai/reference/harness/harness-class).
45
- - **Observational memory**: Automatic summarization and reflection across threads for long-running agent sessions. See [Observational memory](https://mastra.ai/docs/memory/observational-memory).
46
-
47
- ## Quickstart
48
-
49
- Import the `Harness` class and create a new instance with an agent, storage backend, and modes:
50
-
51
- ```typescript
52
- import { Agent } from '@mastra/core/agent'
53
- import { Harness } from '@mastra/core/harness'
54
- import { LibSQLStore } from '@mastra/libsql'
55
-
56
- const agent = new Agent({
57
- name: 'assistant',
58
- instructions: 'Help the user plan and complete tasks.',
59
- model: 'openai/gpt-5.5',
60
- })
61
-
62
- const harness = new Harness({
63
- id: 'my-agent',
64
- agent,
65
- storage: new LibSQLStore({ url: 'file:./data.db' }),
66
- modes: [
67
- {
68
- id: 'plan',
69
- name: 'Plan',
70
- metadata: { default: true },
71
- instructions: 'Reason about changes before making them.',
72
- },
73
- { id: 'build', name: 'Build', instructions: 'Implement the approved plan.' },
74
- ],
75
- })
76
-
77
- harness.subscribe(event => {
78
- if (event.type === 'message_update') {
79
- console.log(event.message)
80
- }
81
- })
82
-
83
- await harness.init()
84
- await harness.selectOrCreateThread()
85
- await harness.sendMessage({ content: 'Hello!' })
86
- ```
87
-
88
- > **Note:** Visit the [Harness reference](https://mastra.ai/reference/harness/harness-class) for the full constructor parameters and method signatures.
89
-
90
- ## Architecture
91
-
92
- The Harness sits between your application layer and the underlying agent loop:
93
-
94
- ```text
95
- ┌───────────────────────────────────────┐
96
- │ Your App (TUI/Web/API) │
97
- └───────────────────────────────────────┘
98
- │ commands ▲ events
99
- ▼ │
100
- ┌───────────────────────────────────────┐
101
- │ Harness │
102
- │ Config · storage · threads │
103
- │ permissions · subagents · events │
104
- │ │
105
- │ ┌─────────────────────────────────┐ │
106
- │ │ harness.session │ │
107
- │ │ identity · thread · mode │ │
108
- │ │ model · run · grants │ │
109
- │ │ display state │ │
110
- │ └─────────────────────────────────┘ │
111
- └───────────────────────────────────────┘
112
-
113
-
114
- ┌───────────────────────────────────────┐
115
- │ Agent + Memory + Tools │
116
- └───────────────────────────────────────┘
117
- ```
118
-
119
- Your app sends commands — send a message, switch mode, approve a tool call — and receives typed events such as `message_update` and `tool_approval_required`. The Harness manages the lifecycle internally: persisting threads, routing to the correct mode agent, enforcing permissions, and emitting events as state changes.
120
-
121
- ## Next steps
122
-
123
- - [Session](https://mastra.ai/docs/harness/session): The per-conversation state on a Harness
124
- - [Modes](https://mastra.ai/docs/harness/modes): Define and switch between agent personalities
125
- - [Threads and state](https://mastra.ai/docs/harness/threads-and-state): Manage persistent conversations
126
- - [Subagents](https://mastra.ai/docs/harness/subagents): Delegate focused subtasks
127
- - [Tool approvals and permissions](https://mastra.ai/docs/harness/tool-approvals): Human-in-the-loop gating
128
- - [API reference](https://mastra.ai/reference/harness/harness-class): Full constructor and method docs