@mastra/mcp-docs-server 1.2.13 → 1.2.14-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.
Files changed (65) hide show
  1. package/.docs/docs/agents/a2a.md +1 -1
  2. package/.docs/docs/agents/acp.md +1 -1
  3. package/.docs/docs/agents/agent-approval.md +1 -1
  4. package/.docs/docs/agents/networks.md +2 -2
  5. package/.docs/docs/agents/overview.md +2 -2
  6. package/.docs/docs/agents/using-tools.md +2 -2
  7. package/.docs/docs/capabilities/channels/overview.md +1 -1
  8. package/.docs/docs/{agents/supervisor-agents.md → capabilities/subagents.md} +58 -58
  9. package/.docs/docs/deployment/sandbox.md +95 -1
  10. package/.docs/docs/getting-started/develop.md +2 -0
  11. package/.docs/docs/harness/agent-controller.md +370 -0
  12. package/.docs/docs/long-running-agents/background-tasks.md +1 -1
  13. package/.docs/docs/long-running-agents/goals.md +3 -3
  14. package/.docs/docs/mcp/overview.md +222 -281
  15. package/.docs/docs/memory/overview.md +2 -2
  16. package/.docs/docs/observability/integrations/exporters/confident-ai.md +140 -0
  17. package/.docs/docs/observability/integrations/overview.md +1 -1
  18. package/.docs/docs/observability/overview.md +122 -8
  19. package/.docs/docs/observability/tracing/overview.md +2 -2
  20. package/.docs/docs/server/mastra-client.md +1 -1
  21. package/.docs/docs/server/server-adapters.md +2 -0
  22. package/.docs/docs/storage/overview.md +2 -2
  23. package/.docs/guides/build-your-ui/copilotkit/channels.md +49 -39
  24. package/.docs/guides/concepts/multi-agent-systems.md +7 -7
  25. package/.docs/guides/guide/coding-agent.md +2 -2
  26. package/.docs/guides/guide/research-coordinator.md +1 -1
  27. package/.docs/guides/migrations/network-to-supervisor.md +1 -1
  28. package/.docs/models/index.md +1 -1
  29. package/.docs/models/providers/cortecs.md +2 -1
  30. package/.docs/models/providers/digitalocean.md +9 -9
  31. package/.docs/models/providers/kilo.md +1 -1
  32. package/.docs/models/providers/llmgateway.md +1 -1
  33. package/.docs/models/providers/opencode.md +2 -1
  34. package/.docs/reference/acp/acp-agent.md +1 -1
  35. package/.docs/reference/agent-controller/agent-controller-class.md +195 -506
  36. package/.docs/reference/agent-controller/session.md +274 -111
  37. package/.docs/reference/agents/network.md +1 -1
  38. package/.docs/reference/ai-sdk/handle-network-stream.md +1 -1
  39. package/.docs/reference/ai-sdk/network-route.md +1 -1
  40. package/.docs/reference/cli/create-mastra.md +1 -1
  41. package/.docs/reference/editor/versioning.md +1 -1
  42. package/.docs/reference/evals/rubric.md +1 -1
  43. package/.docs/reference/file-based-agents/observability.md +2 -2
  44. package/.docs/reference/file-based-agents/storage.md +1 -1
  45. package/.docs/reference/file-based-agents/subagents.md +1 -1
  46. package/.docs/reference/file-based-agents/tools.md +1 -1
  47. package/.docs/reference/observability/tracing/configuration.md +1 -1
  48. package/.docs/reference/observability/tracing/exporters/confident-ai.md +138 -0
  49. package/.docs/reference/observability/tracing/interfaces.md +29 -0
  50. package/.docs/reference/signals/signal-provider.md +2 -0
  51. package/.docs/reference/tools/mcp-server.md +1 -1
  52. package/.docs/reference/tools/submit-plan-tool.md +1 -1
  53. package/.docs/reference/workspace/railway-sandbox.md +5 -5
  54. package/CHANGELOG.md +14 -0
  55. package/package.json +3 -3
  56. package/.docs/docs/agent-controller/channels.md +0 -111
  57. package/.docs/docs/agent-controller/modes.md +0 -147
  58. package/.docs/docs/agent-controller/overview.md +0 -136
  59. package/.docs/docs/agent-controller/session.md +0 -161
  60. package/.docs/docs/agent-controller/subagents.md +0 -110
  61. package/.docs/docs/agent-controller/threads-and-state.md +0 -148
  62. package/.docs/docs/agent-controller/tool-approvals.md +0 -147
  63. package/.docs/docs/mcp/mcp-apps.md +0 -306
  64. package/.docs/docs/observability/config.md +0 -140
  65. package/.docs/docs/observability/storage.md +0 -81
@@ -1,148 +0,0 @@
1
- > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
-
3
- # Threads and state
4
-
5
- Threads and state are how an AgentController conversation survives beyond a single exchange. A **thread** is the persistent record of a conversation, its messages and metadata, saved to storage so a user can close the app and resume the same conversation later, or switch between several conversations. **State** is structured data attached to the conversation, values like model preferences, feature flags, or progress, that agents and your UI read and write as the conversation runs.
6
-
7
- The thread and state work together: the thread is the message history, and state is the shared scratchpad alongside it.
8
-
9
- Both persist across mode switches, model changes, and restarts, so nothing is lost when a user switches from plan mode to build mode or reopens the app the next day.
10
-
11
- Thread _lifecycle_ transitions, create, switch, clone, delete, live on the AgentController because they coordinate the shared thread lock and emit events. The active thread binding and thread/message _reads_ live on the [`Session`](https://mastra.ai/docs/agent-controller/session) as `agentController.session.thread`.
12
-
13
- ## Threads
14
-
15
- A thread holds one conversation's full message history. The AgentController binds the Session to one active thread at a time. Messages you send and the agent's replies are appended to that thread and saved to storage. Threads let users resume a past conversation or keep several conversations side by side, or alternatively branch one into alternatives.
16
-
17
- ### Creating and selecting threads
18
-
19
- On startup, call `selectOrCreateThread()` to resume the most recent thread or create a new one:
20
-
21
- ```typescript
22
- await agentController.init()
23
- const thread = await agentController.selectOrCreateThread()
24
- ```
25
-
26
- Create a thread explicitly with a title:
27
-
28
- ```typescript
29
- const thread = await agentController.createThread({ title: 'New conversation' })
30
- ```
31
-
32
- ### Switching threads
33
-
34
- Switch to an existing thread.
35
-
36
- The agentController aborts any in-progress generation and acquires a lock on the new thread, plus emits a `thread_changed` event:
37
-
38
- ```typescript
39
- await agentController.switchThread({ threadId: 'thread-abc123' })
40
- ```
41
-
42
- ### Listing threads
43
-
44
- List threads for the current resource. Forked subagent threads are hidden by default:
45
-
46
- ```typescript
47
- const threads = await agentController.session.thread.list()
48
-
49
- // Include all resources
50
- const allThreads = await agentController.session.thread.list({ allResources: true })
51
- ```
52
-
53
- ### Cloning threads
54
-
55
- Clone a thread to create a branch of the conversation. The agentController copies all messages and switches to the clone:
56
-
57
- ```typescript
58
- const cloned = await agentController.cloneThread({ title: 'Alternative approach' })
59
- ```
60
-
61
- ### Thread locking
62
-
63
- Pass a `threadLock` to the AgentController constructor to prevent concurrent access from multiple processes. The lock is acquired before any thread operation and released on switch or delete:
64
-
65
- ```typescript
66
- const agentController = new AgentController({
67
- id: 'my-agent',
68
- threadLock: {
69
- acquire: async threadId => {
70
- /* acquire lock or throw */
71
- },
72
- release: async threadId => {
73
- /* release lock */
74
- },
75
- },
76
- })
77
- ```
78
-
79
- ## State
80
-
81
- Where a thread stores the conversation's messages, state stores structured values that describe the conversation but aren't messages, model preferences, feature flags, UI settings, or progress markers. Agents can read and update state during a run, and your UI can react to changes, so state is how the agent and the interface stay in sync on shared facts. You define its shape with a schema, and every update is validated against that schema before it's applied.
82
-
83
- ### Defining a state schema
84
-
85
- Pass a `stateSchema` (Standard JSON Schema) to validate state and extract defaults:
86
-
87
- ```typescript
88
- import { Agent } from '@mastra/core/agent'
89
- import { AgentController } from '@mastra/core/agent-controller'
90
- import { z } from 'zod'
91
-
92
- const agent = new Agent({
93
- id: 'assistant',
94
- name: 'assistant',
95
- instructions: 'Help the user manage a stateful session.',
96
- model: 'openai/gpt-5.6-sol',
97
- })
98
-
99
- const agentController = new AgentController({
100
- id: 'stateful-agent',
101
- agent,
102
- modes: [{ id: 'default', name: 'Default', metadata: { default: true } }],
103
- stateSchema: z.object({
104
- currentModelId: z.string().optional(),
105
- theme: z.enum(['light', 'dark']).default('dark'),
106
- }),
107
- })
108
- ```
109
-
110
- ### Reading and writing state
111
-
112
- State is owned by the [`Session`](https://mastra.ai/docs/agent-controller/session) as `agentController.session.state`:
113
-
114
- ```typescript
115
- // Read the current state snapshot
116
- const state = agentController.session.state.get()
117
-
118
- // Update state — validates against schema and emits state_changed
119
- await agentController.session.state.set({ theme: 'light' })
120
- ```
121
-
122
- State changes emit a `state_changed` event with the new state and the set of changed keys.
123
-
124
- ## Resource IDs
125
-
126
- Threads are scoped to a resource ID, which groups a conversation's threads by project, user, or workspace. Set it on the AgentController constructor; it defaults to the agentController `id` when omitted:
127
-
128
- ```typescript
129
- const agentController = new AgentController({
130
- id: 'my-agent',
131
- resourceId: 'project-xyz',
132
- })
133
- ```
134
-
135
- The resource ID is part of a conversation's identity, so you read it from the Session:
136
-
137
- ```typescript
138
- const resourceId = agentController.session.identity.getResourceId()
139
- ```
140
-
141
- The session also has a stable `id` and `ownerId` (read with `session.identity.getId()` and `session.identity.getOwnerId()`). Unlike the resource ID, these don't change when you switch resources, see [Session identity](https://mastra.ai/docs/agent-controller/session) for details.
142
-
143
- ## Related
144
-
145
- - [AgentController overview](https://mastra.ai/docs/agent-controller/overview)
146
- - [Session](https://mastra.ai/docs/agent-controller/session)
147
- - [Modes](https://mastra.ai/docs/agent-controller/modes)
148
- - [API reference](https://mastra.ai/reference/agent-controller/agent-controller-class)
@@ -1,147 +0,0 @@
1
- > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
-
3
- # Tool approvals and permissions
4
-
5
- The AgentController provides a permission system that controls which tools require user approval before execution.
6
-
7
- You can configure policies at the category level or per-tool, and grant session-wide exceptions for trusted tools.
8
-
9
- This gives agents with access to destructive or sensitive tools, file writes, command execution, API calls, a human-in-the-loop checkpoint before those tools run.
10
-
11
- ## Permission policies
12
-
13
- Policies control tool behavior:
14
-
15
- - `allow`: The tool runs without prompting
16
- - `ask`: The tool pauses and emits a `tool_approval_required` event; the user must approve or decline
17
- - `deny`: The tool is blocked from execution
18
-
19
- ### Setting policies
20
-
21
- Set policies per-category or per-tool. Per-tool policies take precedence over category policies:
22
-
23
- ```typescript
24
- // Category-level: all execute tools require approval
25
- await agentController.session.permissions.setForCategory({ category: 'execute', policy: 'ask' })
26
-
27
- // Tool-level: this specific tool is always blocked
28
- await agentController.session.permissions.setForTool({ toolName: 'dangerous_tool', policy: 'deny' })
29
- ```
30
-
31
- ### Tool categories
32
-
33
- The `toolCategoryResolver` maps tool names to categories. Pass it to the AgentController constructor:
34
-
35
- ```typescript
36
- const agentController = new AgentController({
37
- id: 'my-agent',
38
- toolCategoryResolver: toolName => {
39
- if (toolName.includes('write') || toolName.includes('delete')) return 'edit'
40
- if (toolName.includes('execute')) return 'execute'
41
- return 'read'
42
- },
43
- })
44
- ```
45
-
46
- Built-in categories are `read`, `edit`, `execute`, `mcp`, and `other`.
47
-
48
- ## Responding to approval requests
49
-
50
- When a tool's policy is `ask`, the AgentController emits a `tool_approval_required` event. Your UI should display a prompt and call `session.respondToToolApproval()`:
51
-
52
- ```typescript
53
- agentController.subscribe(event => {
54
- if (event.type === 'tool_approval_required') {
55
- // Show approval UI...
56
- agentController.session.respondToToolApproval({ decision: 'approve' })
57
- }
58
- })
59
- ```
60
-
61
- The `decision` field accepts `'approve'`, `'decline'`, or `'always_allow_category'`. When `always_allow_category` is used, the tool's category is granted for the rest of the session. Future tools in the same category are auto-approved.
62
-
63
- ```typescript
64
- agentController.session.respondToToolApproval({ decision: 'always_allow_category' })
65
- ```
66
-
67
- ## Session grants
68
-
69
- The AgentController owns permission _policy_ (which categories require approval). The [`Session`](https://mastra.ai/docs/agent-controller/session) owns the _grants_ a user makes during a conversation. Grant a category or tool for the rest of the session so it runs without further prompting:
70
-
71
- ```typescript
72
- // Grant all edit tools for this session
73
- agentController.session.grantCategory('edit')
74
-
75
- // Grant a specific tool
76
- agentController.session.grantTool('mastra_workspace_execute_command')
77
-
78
- // Check current grants
79
- const grants = agentController.session.getGrants()
80
- // { categories: ['edit'], tools: ['mastra_workspace_execute_command'] }
81
- ```
82
-
83
- ## Tool suspensions
84
-
85
- Interactive built-in tools (`ask_user`, `submit_plan`) use the native tool-suspension primitive instead of the approval flow. They emit a `tool_suspended` event with `toolCallId`, `toolName`, and `suspendPayload`. Resume with `respondToToolSuspension()`:
86
-
87
- ```typescript
88
- agentController.subscribe(event => {
89
- if (event.type === 'tool_suspended' && event.toolName === 'ask_user') {
90
- const { question } = event.suspendPayload as { question: string }
91
- // Show question to user, then resume:
92
- agentController.respondToToolSuspension({
93
- toolCallId: event.toolCallId,
94
- resumeData: 'User response here',
95
- })
96
- }
97
- })
98
- ```
99
-
100
- ### Plan approval
101
-
102
- The `submit_plan` tool suspends via the same mechanism. Resume with an `action` field:
103
-
104
- ```typescript
105
- // Approve the plan
106
- agentController.respondToToolSuspension({
107
- toolCallId: event.toolCallId,
108
- resumeData: { action: 'approved' },
109
- })
110
-
111
- // Reject with feedback
112
- agentController.respondToToolSuspension({
113
- toolCallId: event.toolCallId,
114
- resumeData: { action: 'rejected', feedback: 'Needs more detail' },
115
- })
116
- ```
117
-
118
- ## Built-in tools
119
-
120
- The AgentController provides these built-in tools to agents in every mode:
121
-
122
- | Tool | Description |
123
- | --------------- | ------------------------------------------------------------------- |
124
- | `ask_user` | Ask the user a question (free text, single-select, or multi-select) |
125
- | `submit_plan` | Submit a plan for user review and approval |
126
- | `task_write` | Create or replace a structured task list |
127
- | `task_update` | Update one tracked task by ID |
128
- | `task_complete` | Mark one tracked task completed |
129
- | `task_check` | Check task list completion status |
130
- | `subagent` | Spawn a focused subagent (requires `subagents` config) |
131
-
132
- Disable specific built-in tools with `disableBuiltinTools`:
133
-
134
- ```typescript
135
- const agentController = new AgentController({
136
- id: 'no-plans',
137
- disableBuiltinTools: ['submit_plan'],
138
- })
139
- ```
140
-
141
- ## Related
142
-
143
- - [AgentController overview](https://mastra.ai/docs/agent-controller/overview)
144
- - [Session](https://mastra.ai/docs/agent-controller/session)
145
- - [Subagents](https://mastra.ai/docs/agent-controller/subagents)
146
- - [Agent approval](https://mastra.ai/docs/agents/agent-approval)
147
- - [API reference](https://mastra.ai/reference/agent-controller/agent-controller-class)
@@ -1,306 +0,0 @@
1
- > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
-
3
- # MCP Apps
4
-
5
- The [MCP Apps extension](https://github.com/modelcontextprotocol/ext-apps) allows MCP tools to serve interactive HTML UIs via `ui://` resources. When a tool has an associated app resource, Mastra Studio renders it in a sandboxed iframe alongside the tool form or inline in agent chat.
6
-
7
- ## When to use MCP Apps
8
-
9
- Use MCP Apps when a tool result is better presented as an interactive UI rather than plain text. For example:
10
-
11
- - A calculator that renders input fields and buttons for computation
12
- - A color picker that displays swatches and hex values
13
- - A form builder that captures structured user input
14
- - A data visualizer that renders charts
15
-
16
- ## Quickstart
17
-
18
- Define app resources on your `MCPServer` by providing a `ui://` URI mapped to inline HTML or an HTML file path.
19
-
20
- ```typescript
21
- import { MCPServer } from '@mastra/mcp'
22
- import { createTool } from '@mastra/core/tools'
23
- import { z } from 'zod'
24
-
25
- const calculatorTool = createTool({
26
- id: 'calculatorWithUI',
27
- description: 'An interactive calculator',
28
- inputSchema: z.object({
29
- num1: z.number(),
30
- num2: z.number(),
31
- operation: z.enum(['add', 'subtract']),
32
- }),
33
- execute: async ({ num1, num2, operation }) => {
34
- const result = operation === 'add' ? num1 + num2 : num1 - num2
35
- return {
36
- content: [{ type: 'text', text: 'An interactive calculator is displayed.' }],
37
- structuredContent: { result },
38
- }
39
- },
40
- })
41
-
42
- const server = new MCPServer({
43
- id: 'my-app-server',
44
- name: 'My App Server',
45
- version: '1.0.0',
46
- tools: { calculatorTool },
47
- appResources: {
48
- 'ui://calculator/main': {
49
- name: 'Interactive Calculator',
50
- html: `<html>
51
- <body>
52
- <h2>Calculator</h2>
53
- <button id="btn">Compute</button>
54
- <script type="module">
55
- import { App } from 'https://cdn.jsdelivr.net/npm/@modelcontextprotocol/ext-apps/+esm';
56
- const app = new App({ name: 'Calculator', version: '1.0.0' });
57
- app.ontoolinput = (params) => {
58
- console.log('Tool input:', params.arguments);
59
- };
60
- document.getElementById('btn').addEventListener('click', async () => {
61
- const result = await app.callServerTool({
62
- name: 'calculatorWithUI',
63
- arguments: { num1: 10, num2: 5, operation: 'add' }
64
- });
65
- document.body.innerHTML += '<p>Result: ' + JSON.stringify(result) + '</p>';
66
- });
67
- await app.connect();
68
- </script>
69
- </body>
70
- </html>`,
71
- },
72
- },
73
- })
74
- ```
75
-
76
- Link the tool to its app resource by adding `_meta.ui.resourceUri` to the tool definition:
77
-
78
- ```typescript
79
- calculatorTool._meta = {
80
- ui: { resourceUri: 'ui://calculator/main' },
81
- }
82
- ```
83
-
84
- Visit [MCPServer reference](https://mastra.ai/reference/tools/mcp-server) for the full `appResources` configuration.
85
-
86
- ## Connecting MCP Apps to agents
87
-
88
- Agents consume tools, they don't need to know about MCP servers. Pass tools to the agent's `tools` config, and register the MCP server at the Mastra level so Studio can resolve app resources.
89
-
90
- ```typescript
91
- import { Agent } from '@mastra/core/agent'
92
- import { calculatorTool } from '../mcp/tools'
93
-
94
- export const myAgent = new Agent({
95
- id: 'my-agent',
96
- name: 'My Agent',
97
- instructions: 'You have access to interactive UI tools.',
98
- model: 'openai/gpt-5-mini',
99
- tools: { calculatorTool },
100
- })
101
- ```
102
-
103
- Register the MCP server at the Mastra level. Studio scans registered MCP servers to map tools to their app resources.
104
-
105
- ```typescript
106
- import { Mastra } from '@mastra/core/mastra'
107
- import { myAgent } from './agents'
108
- import { myAppServer } from './mcp/server'
109
-
110
- export const mastra = new Mastra({
111
- agents: { myAgent },
112
- mcpServers: { myAppServer },
113
- })
114
- ```
115
-
116
- For remote MCP servers, use `MCPClient.listTools()` to get tools and `toMCPServerProxies()` to register the server:
117
-
118
- ```typescript
119
- import { MCPClient } from '@mastra/mcp'
120
-
121
- const mcpClient = new MCPClient({
122
- servers: {
123
- remoteApp: { url: new URL('https://remote-mcp-server.example.com/mcp') },
124
- },
125
- })
126
-
127
- const myAgent = new Agent({
128
- id: 'my-agent',
129
- name: 'My Agent',
130
- model: 'openai/gpt-5-mini',
131
- tools: await mcpClient.listTools(),
132
- })
133
-
134
- export const mastra = new Mastra({
135
- agents: { myAgent },
136
- mcpServers: { ...mcpClient.toMCPServerProxies() },
137
- })
138
- ```
139
-
140
- When tools come from `MCPClient.listTools()`, each tool's `_meta.ui` is automatically stamped with a `serverId` so Studio can resolve its app resources without scanning all servers.
141
-
142
- ## How MCP Apps work
143
-
144
- MCP Apps follow a specific communication pattern between the host (Mastra Studio) and the iframe:
145
-
146
- 1. The tool executes and returns a brief summary in `content` (visible to the model) and detailed data in `structuredContent` (visible to the UI only).
147
- 2. The host renders the app HTML in a sandboxed iframe.
148
- 3. The iframe communicates with the host via a JSON-RPC postMessage protocol.
149
- 4. The app can call server tools using `callServerTool()` and inject messages into the chat using `sendMessage()`.
150
-
151
- ```text
152
- Agent calls tool → Tool returns brief content + structuredContent
153
- → Host renders iframe with app HTML
154
- → User interacts with UI
155
- → UI calls callServerTool() for computation
156
- → UI calls sendMessage() to inject result into chat
157
- ```
158
-
159
- ## Tool result format
160
-
161
- Tools with app resources should return two fields:
162
-
163
- - `content`: A brief text summary for the model. Keep this short so the agent doesn't parrot the full result.
164
- - `structuredContent`: The data payload that hydrates the UI. The model doesn't see this field.
165
-
166
- ```typescript
167
- execute: async ({ num1, num2, operation }) => {
168
- const result = operation === 'add' ? num1 + num2 : num1 - num2
169
- return {
170
- content: [{ type: 'text', text: 'An interactive calculator is displayed.' }],
171
- structuredContent: { result },
172
- }
173
- }
174
- ```
175
-
176
- ## App API (guest-side)
177
-
178
- MCP App HTML uses the standard [`App` class from `@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) to communicate with the host. Import it via ESM CDN or bundle it.
179
-
180
- ```javascript
181
- import { App } from 'https://cdn.jsdelivr.net/npm/@modelcontextprotocol/ext-apps/+esm'
182
- const app = new App({ name: 'MyApp', version: '1.0.0' })
183
- ```
184
-
185
- ### `app.callServerTool(params)`
186
-
187
- Calls an MCP server tool from within the iframe. This is useful for interactive computation without leaving the UI.
188
-
189
- ```javascript
190
- const result = await app.callServerTool({
191
- name: 'calculatorWithUI',
192
- arguments: { num1: 42, num2: 8, operation: 'add' },
193
- })
194
- ```
195
-
196
- ### `app.sendMessage(params)`
197
-
198
- Injects a user message into the agent chat, triggering a new model turn. Use this for sharing results or requesting follow-up actions.
199
-
200
- ```javascript
201
- await app.sendMessage({
202
- role: 'user',
203
- content: [{ type: 'text', text: 'The result of 42 + 8 is 50' }],
204
- })
205
- ```
206
-
207
- ### `app.ontoolinput`
208
-
209
- A callback that fires when the host delivers tool input data to the iframe, allowing pre-population of form fields. The `params.arguments` object contains the tool call arguments.
210
-
211
- ```javascript
212
- app.ontoolinput = params => {
213
- document.getElementById('num1').value = params.arguments.num1
214
- }
215
- ```
216
-
217
- > **Preventing UI flicker:** If your app has default form values, the user may briefly see them before `ontoolinput` hydrates the correct values. To prevent this, start the body hidden and reveal it after hydration:
218
- >
219
- > ```html
220
- > <style>
221
- > body {
222
- > opacity: 0;
223
- > transition: opacity 0.15s;
224
- > }
225
- > body.ready {
226
- > opacity: 1;
227
- > }
228
- > </style>
229
- > <script type="module">
230
- > import { App } from 'https://cdn.jsdelivr.net/npm/@modelcontextprotocol/ext-apps/+esm'
231
- > const app = new App({ name: 'MyApp', version: '1.0.0' })
232
- >
233
- > app.ontoolinput = params => {
234
- > // Hydrate form fields from params.arguments
235
- > document.body.classList.add('ready')
236
- > }
237
- >
238
- > await app.connect()
239
- > // Fallback: reveal after connection if no tool input arrives
240
- > setTimeout(() => document.body.classList.add('ready'), 150)
241
- > </script>
242
- > ```
243
-
244
- ### `app.connect()`
245
-
246
- Establishes the connection to the host. Call this after registering all event handlers.
247
-
248
- ```javascript
249
- await app.connect()
250
- ```
251
-
252
- See the [`App` class API reference](https://apps.extensions.modelcontextprotocol.io/api/classes/app.App.html) for the full list of methods, callbacks, and lifecycle hooks.
253
-
254
- ## Using external MCP servers with apps
255
-
256
- External (non-Mastra) MCP servers that implement the MCP Apps extension work with Mastra via `MCPClient`. Use `listTools()` for agent tools and `toMCPServerProxies()` to register them in Studio.
257
-
258
- ```typescript
259
- import { Mastra } from '@mastra/core/mastra'
260
- import { MCPClient } from '@mastra/mcp'
261
- import { Agent } from '@mastra/core/agent'
262
-
263
- const mcpClient = new MCPClient({
264
- servers: {
265
- 'external-server': {
266
- command: 'node',
267
- args: ['path/to/external-server.js'],
268
- },
269
- },
270
- })
271
-
272
- const myAgent = new Agent({
273
- id: 'my-agent',
274
- name: 'My Agent',
275
- model: 'openai/gpt-5-mini',
276
- tools: await mcpClient.listTools(),
277
- })
278
-
279
- export const mastra = new Mastra({
280
- agents: { myAgent },
281
- mcpServers: {
282
- ...mcpClient.toMCPServerProxies(),
283
- },
284
- })
285
- ```
286
-
287
- Visit [MCPClient reference](https://mastra.ai/reference/tools/mcp-client) for more details on proxying external servers.
288
-
289
- ## Sandbox security
290
-
291
- Mastra Studio uses [`@mcp-ui/client`](https://www.npmjs.com/package/@mcp-ui/client) to render MCP App iframes through a sandbox proxy. The proxy loads app HTML via `postMessage` rather than `srcDoc`, providing additional isolation.
292
-
293
- App iframes are sandboxed with the following permissions:
294
-
295
- - `allow-scripts`: Enables JavaScript execution
296
- - `allow-forms`: Allows form submission
297
- - `allow-popups`: Permits `window.open()` and link targets
298
-
299
- The iframe doesn't have access to the parent page's DOM, cookies, or storage. All communication happens through the JSON-RPC postMessage protocol managed by `@mcp-ui/client`'s `AppRenderer` on the host side and `@modelcontextprotocol/ext-apps`'s `App` class on the guest side.
300
-
301
- ## Related
302
-
303
- - [MCP overview](https://mastra.ai/docs/mcp/overview)
304
- - [MCPServer reference](https://mastra.ai/reference/tools/mcp-server)
305
- - [MCPClient reference](https://mastra.ai/reference/tools/mcp-client)
306
- - [MCP Apps extension spec](https://github.com/modelcontextprotocol/ext-apps)