@mastra/acp 0.1.0 → 0.2.0-alpha.1
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/CHANGELOG.md +29 -0
- package/README.md +53 -0
- package/dist/agent.d.ts +3 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/connection.d.ts +3 -1
- package/dist/connection.d.ts.map +1 -1
- package/dist/docs/SKILL.md +22 -0
- package/dist/docs/assets/SOURCE_MAP.json +6 -0
- package/dist/docs/references/docs-agents-acp.md +291 -0
- package/dist/index.cjs +130 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +130 -27
- package/dist/index.js.map +1 -1
- package/dist/tool.d.ts.map +1 -1
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @mastra/acp
|
|
2
2
|
|
|
3
|
+
## 0.2.0-alpha.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. ([#16726](https://github.com/mastra-ai/mastra/pull/16726))
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`c35b962`](https://github.com/mastra-ai/mastra/commit/c35b9625c7e854fcfdeee226a3338a750d0ff211), [`4084113`](https://github.com/mastra-ai/mastra/commit/408411370fc48a822e8b616b3b63f9409774e0e9)]:
|
|
10
|
+
- @mastra/core@1.37.0-alpha.8
|
|
11
|
+
|
|
12
|
+
## 0.2.0-alpha.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- Added programmatic model selection for ACP agents using the `model` option. ([#17010](https://github.com/mastra-ai/mastra/pull/17010))
|
|
17
|
+
|
|
18
|
+
You can now set the model directly when creating `AcpAgent` or `createACPTool`, instead of relying on environment variables.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
const codeAgent = new AcpAgent({
|
|
22
|
+
id: 'code-agent',
|
|
23
|
+
description: 'ACP-compatible coding agent',
|
|
24
|
+
command: 'claude',
|
|
25
|
+
args: ['--acp'],
|
|
26
|
+
model: 'claude-sonnet-4-20250514',
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Discover available models with `getAvailableModels()` and change the model at runtime with `setModel()`. Invalid model IDs throw a descriptive error listing valid options.
|
|
31
|
+
|
|
3
32
|
## 0.1.0
|
|
4
33
|
|
|
5
34
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -148,5 +148,58 @@ By default, the ACP workspace uses `cwd` as its filesystem root. Pass a Mastra `
|
|
|
148
148
|
| `persistSession` | `boolean` | Keep the ACP process alive after execution. Defaults to `true`. |
|
|
149
149
|
| `onPermissionRequest` | `(request) => Promise<Response>` | Callback for ACP permission requests. |
|
|
150
150
|
| `workspace` | `Workspace` | Workspace used for ACP file reads and writes. |
|
|
151
|
+
| `model` | `string` | Model ID to select after session creation via the ACP `session/set_model` method. |
|
|
151
152
|
|
|
152
153
|
`AcpAgent` also accepts `name` to set the display name used by Mastra agent delegation.
|
|
154
|
+
|
|
155
|
+
## Configure the model
|
|
156
|
+
|
|
157
|
+
ACP agents may expose selectable models. Instead of setting an environment variable like `ANTHROPIC_MODEL`, you can pass a `model` ID directly in the configuration.
|
|
158
|
+
|
|
159
|
+
### Discover available models
|
|
160
|
+
|
|
161
|
+
Call `getAvailableModels()` to see which models the ACP agent supports. This starts the agent process and returns the model list from the session:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { AcpAgent } from '@mastra/acp';
|
|
165
|
+
|
|
166
|
+
const codeAgent = new AcpAgent({
|
|
167
|
+
id: 'code-agent',
|
|
168
|
+
description: 'An ACP-compatible coding agent',
|
|
169
|
+
command: 'claude',
|
|
170
|
+
args: ['--acp'],
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const models = await codeAgent.getAvailableModels();
|
|
174
|
+
// [{ modelId: 'claude-sonnet-4-20250514', name: 'Claude Sonnet' }, ...]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Set the model
|
|
178
|
+
|
|
179
|
+
Pass the `model` option to select a model at connection time:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { AcpAgent } from '@mastra/acp';
|
|
183
|
+
|
|
184
|
+
const codeAgent = new AcpAgent({
|
|
185
|
+
id: 'code-agent',
|
|
186
|
+
description: 'An ACP-compatible coding agent',
|
|
187
|
+
command: 'claude',
|
|
188
|
+
args: ['--acp'],
|
|
189
|
+
model: 'claude-sonnet-4-20250514',
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
You can also change the model at runtime with `setModel()`:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
await codeAgent.setModel('claude-sonnet-4-20250514');
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
If the ACP agent advertises available models and your model ID doesn't match any of them, Mastra throws an error listing the valid options:
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
Model "bad-model-id" is not available. Available models: claude-sonnet-4-20250514, claude-haiku-4-20250514
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
If the agent doesn't advertise a model list, the value is passed through without validation.
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ModelInfo } from '@agentclientprotocol/sdk';
|
|
1
2
|
import type { AgentGenerateOptions, AgentStreamOptions, SubAgent, SubAgentGenerateResult, SubAgentStreamResult } from '@mastra/core/agent';
|
|
2
3
|
import type { MessageListInput } from '@mastra/core/agent/message-list';
|
|
3
4
|
import type { Mastra } from '@mastra/core/mastra';
|
|
@@ -20,6 +21,8 @@ export declare class AcpAgent<TId extends string = string, TRequestContext exten
|
|
|
20
21
|
__setMemory(_memory: DynamicArgument<any, any>): void;
|
|
21
22
|
getMemory(): undefined;
|
|
22
23
|
getInstructions(): string;
|
|
24
|
+
getAvailableModels(): Promise<ModelInfo[]>;
|
|
25
|
+
setModel(modelId: string): Promise<void>;
|
|
23
26
|
generate(messages: MessageListInput, options?: AgentGenerateOptions): Promise<SubAgentGenerateResult>;
|
|
24
27
|
resumeGenerate(): Promise<SubAgentGenerateResult>;
|
|
25
28
|
resumeStream(): Promise<SubAgentStreamResult>;
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EACV,oBAAoB,EAEpB,kBAAkB,EAElB,QAAQ,EACR,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AA0BpD,MAAM,MAAM,eAAe,GAAG,oBAAoB,GAAG;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qBAAa,QAAQ,CACnB,GAAG,SAAS,MAAM,GAAG,MAAM,EAC3B,eAAe,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,GAAG,OAAO,CAC/D,YAAW,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IACzC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,eAAe;IAOpC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAEvC,cAAc,IAAI,MAAM;IAIxB,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,UAAU,CAAC,CAAC;IAIlE,YAAY,IAAI,OAAO;IAIvB,WAAW,CAAC,OAAO,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI;IAErD,SAAS,IAAI,SAAS;IAItB,eAAe,IAAI,MAAM;IAInB,kBAAkB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAI1C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAmBrG,cAAc,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAIjD,YAAY,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAI7C,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAsErG,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,iBAAiB;CAM1B"}
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SessionUpdate } from '@agentclientprotocol/sdk';
|
|
1
|
+
import type { ModelInfo, SessionUpdate } from '@agentclientprotocol/sdk';
|
|
2
2
|
import type { CreateACPToolOptions } from './types.js';
|
|
3
3
|
export type ACPStreamEvent = {
|
|
4
4
|
type: 'text';
|
|
@@ -17,6 +17,8 @@ export declare class ACPConnection {
|
|
|
17
17
|
private stderr;
|
|
18
18
|
constructor(options: CreateACPToolOptions);
|
|
19
19
|
get sessionId(): string | undefined;
|
|
20
|
+
getAvailableModels(): Promise<ModelInfo[]>;
|
|
21
|
+
setModel(modelId: string): Promise<void>;
|
|
20
22
|
prompt(task: string, signal?: AbortSignal): Promise<string>;
|
|
21
23
|
promptStream(task: string, signal?: AbortSignal): AsyncGenerator<ACPStreamEvent>;
|
|
22
24
|
cancel(): Promise<void>;
|
package/dist/connection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,SAAS,EAUT,aAAa,EAGd,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC;AAuEhH,qBAAa,aAAa;IACxB,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IAEvC,OAAO,CAAC,YAAY,CAAC,CAAiC;IACtD,OAAO,CAAC,UAAU,CAAC,CAAuB;IAC1C,OAAO,CAAC,OAAO,CAAC,CAAqB;IACrC,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAC1C,OAAO,CAAC,aAAa,CAAC,CAAc;IACpC,OAAO,CAAC,MAAM,CAAM;gBAER,OAAO,EAAE,oBAAoB;IAIzC,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAEK,kBAAkB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAK1C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBxC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAY1D,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,cAAc,CAAC;IAgEjF,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAU7B,UAAU,IAAI,IAAI;YAaJ,eAAe;YASf,UAAU;IAwDxB,OAAO,CAAC,oBAAoB;IAc5B,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,2BAA2B;IAQnC,OAAO,CAAC,UAAU;CAanB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mastra-acp
|
|
3
|
+
description: Documentation for @mastra/acp. Use when working with @mastra/acp APIs, configuration, or implementation.
|
|
4
|
+
metadata:
|
|
5
|
+
package: "@mastra/acp"
|
|
6
|
+
version: "0.2.0-alpha.1"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## When to use
|
|
10
|
+
|
|
11
|
+
Use this skill whenever you are working with @mastra/acp to obtain the domain-specific knowledge.
|
|
12
|
+
|
|
13
|
+
## How to use
|
|
14
|
+
|
|
15
|
+
Read the individual reference documents for detailed explanations and code examples.
|
|
16
|
+
|
|
17
|
+
### Docs
|
|
18
|
+
|
|
19
|
+
- [ACP](references/docs-agents-acp.md) - Wrap ACP-compatible coding agents as Mastra tools or sub-agents.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Read [assets/SOURCE_MAP.json](assets/SOURCE_MAP.json) for source code references.
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# ACP (Agent Client Protocol)
|
|
2
|
+
|
|
3
|
+
Mastra supports the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/overview/introduction) for running ACP-compatible coding agents from a Mastra agent. Use `@mastra/acp` to wrap a coding agent process as a Mastra tool or as a subagent.
|
|
4
|
+
|
|
5
|
+
ACP is useful for coding agents such as Claude Code, Amp, Codex, or any other executable that implements the Agent Client Protocol over standard input and output.
|
|
6
|
+
|
|
7
|
+
## When to use ACP
|
|
8
|
+
|
|
9
|
+
- A Mastra agent should delegate code inspection, editing, or repository tasks to an external coding agent.
|
|
10
|
+
- An ACP-compatible agent process should stay alive across calls so it can keep session context.
|
|
11
|
+
- A parent agent needs real-time output from a coding agent while the task runs.
|
|
12
|
+
- An ACP-compatible agent needs permission prompts before it reads files, writes files, or runs actions.
|
|
13
|
+
- File access should go through Mastra's workspace abstraction instead of direct process-only file access.
|
|
14
|
+
|
|
15
|
+
## How ACP works
|
|
16
|
+
|
|
17
|
+
`@mastra/acp` starts the configured ACP agent command as a child process and communicates with it using newline-delimited JSON over standard input and output.
|
|
18
|
+
|
|
19
|
+
The flow is:
|
|
20
|
+
|
|
21
|
+
1. Configure `command`, `args`, and optional connection settings.
|
|
22
|
+
2. `@mastra/acp` spawns the ACP agent process on first use.
|
|
23
|
+
3. The client sends ACP `initialize` and `session/new` requests.
|
|
24
|
+
4. Mastra sends the user task to the ACP agent with `session/prompt`.
|
|
25
|
+
5. The ACP agent streams session updates and message chunks back to Mastra.
|
|
26
|
+
6. Mastra returns the buffered output, emits streaming chunks, or suspends for permission input.
|
|
27
|
+
7. The ACP process stays alive by default, or stops after the prompt when `persistSession` is `false`.
|
|
28
|
+
|
|
29
|
+
During execution, the ACP client also handles permission requests and file operations. File reads and writes go through Mastra's `Workspace`, so the ACP agent operates inside the workspace you provide.
|
|
30
|
+
|
|
31
|
+
## Getting started
|
|
32
|
+
|
|
33
|
+
Install `@mastra/acp` in a project that already uses `@mastra/core`:
|
|
34
|
+
|
|
35
|
+
**npm**:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @mastra/acp
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**pnpm**:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pnpm add @mastra/acp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Yarn**:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
yarn add @mastra/acp
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Bun**:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
bun add @mastra/acp
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`@mastra/acp` exports two APIs:
|
|
60
|
+
|
|
61
|
+
- `createACPTool`: Create a Mastra tool that sends a `task` string to an ACP agent and returns an `output` string.
|
|
62
|
+
- `AcpAgent`: Wrap an ACP agent as a Mastra subagent with `generate()` and `stream()` support.
|
|
63
|
+
|
|
64
|
+
The package requires `@mastra/core` version `1.34.0` or later.
|
|
65
|
+
|
|
66
|
+
## Use ACP as a subagent
|
|
67
|
+
|
|
68
|
+
Use `AcpAgent` when a parent Mastra agent should delegate directly to an ACP-compatible coding agent as a subagent. Create the ACP agent, then register it in the parent agent's `agents` map.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { AcpAgent } from '@mastra/acp'
|
|
72
|
+
import { Agent } from '@mastra/core/agent'
|
|
73
|
+
|
|
74
|
+
const codeAgent = new AcpAgent({
|
|
75
|
+
id: 'code-agent',
|
|
76
|
+
name: 'Code Agent',
|
|
77
|
+
description: 'An ACP-compatible coding agent that can inspect and edit files',
|
|
78
|
+
command: 'acp-agent',
|
|
79
|
+
args: ['--stdio'],
|
|
80
|
+
cwd: process.cwd(),
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export const codeSupervisor = new Agent({
|
|
84
|
+
id: 'code-supervisor',
|
|
85
|
+
name: 'Code Supervisor',
|
|
86
|
+
instructions: 'Delegate code editing tasks to the code-agent subagent.',
|
|
87
|
+
model: 'openai/gpt-5.4',
|
|
88
|
+
agents: {
|
|
89
|
+
codeAgent,
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`AcpAgent.generate()` buffers the ACP response and returns it as text. `AcpAgent.stream()` emits Mastra `text-delta` chunks as ACP `agent_message_chunk` updates arrive.
|
|
95
|
+
|
|
96
|
+
## Use ACP as a tool
|
|
97
|
+
|
|
98
|
+
Use `createACPTool` when the parent Mastra agent should decide when to call the ACP agent as a tool. The following example creates a code editing tool and registers it on a parent agent:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { createACPTool } from '@mastra/acp'
|
|
102
|
+
import { Agent } from '@mastra/core/agent'
|
|
103
|
+
|
|
104
|
+
const codeAgentTool = createACPTool({
|
|
105
|
+
id: 'code-agent',
|
|
106
|
+
description: 'Use an ACP-compatible coding agent to inspect and edit code',
|
|
107
|
+
command: 'acp-agent',
|
|
108
|
+
args: ['--stdio'],
|
|
109
|
+
cwd: process.cwd(),
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
export const codeSupervisor = new Agent({
|
|
113
|
+
id: 'code-supervisor',
|
|
114
|
+
name: 'Code Supervisor',
|
|
115
|
+
instructions: 'Use the code-agent tool when a task requires repository inspection or code edits.',
|
|
116
|
+
model: 'openai/gpt-5.4',
|
|
117
|
+
tools: {
|
|
118
|
+
codeAgentTool,
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Use the `command` and `args` required by the ACP-compatible agent you run. The tool input schema has a single `task` string, and the output schema returns the final ACP response as `output`.
|
|
124
|
+
|
|
125
|
+
If the ACP agent requests permission, the tool can suspend and resume through Mastra's tool suspension flow. Use `onPermissionRequest` when you need custom permission behavior.
|
|
126
|
+
|
|
127
|
+
## Options reference
|
|
128
|
+
|
|
129
|
+
`createACPTool` and `AcpAgent` accept the same ACP connection options. `AcpAgent` also accepts `name` to set the display name used during agent delegation.
|
|
130
|
+
|
|
131
|
+
| Option | Type | Description |
|
|
132
|
+
| --------------------- | -------------------------------- | --------------------------------------------------------------------------------------- |
|
|
133
|
+
| `id` | `string` | Unique tool or subagent identifier. |
|
|
134
|
+
| `description` | `string` | Description shown to the model when it can call the tool or delegate to the subagent. |
|
|
135
|
+
| `command` | `string` | ACP agent executable to spawn. |
|
|
136
|
+
| `args` | `string[]` | Arguments passed to the ACP agent executable. |
|
|
137
|
+
| `env` | `Record<string, string>` | Environment variables to merge with the current process environment. |
|
|
138
|
+
| `cwd` | `string` | Working directory for the ACP process, ACP session, and default workspace. |
|
|
139
|
+
| `session` | `Partial<NewSessionRequest>` | ACP session creation options. Defaults to `cwd` or `process.cwd()` and no MCP servers. |
|
|
140
|
+
| `initialize` | `Partial<InitializeRequest>` | ACP initialization options. Defaults to Mastra client information and protocol version. |
|
|
141
|
+
| `authMethodId` | `string` | ACP authentication method ID to invoke after initialization. |
|
|
142
|
+
| `persistSession` | `boolean` | Keep the ACP process alive after execution. Defaults to `true`. |
|
|
143
|
+
| `onPermissionRequest` | `(request) => Promise<Response>` | Callback for ACP permission requests. Defaults to selecting the first option. |
|
|
144
|
+
| `workspace` | `Workspace` | Workspace used for ACP file reads and writes. |
|
|
145
|
+
| `model` | `string` | Model ID to select after session creation via the ACP `session/set_model` method. |
|
|
146
|
+
|
|
147
|
+
## Model selection
|
|
148
|
+
|
|
149
|
+
ACP agents may expose selectable models. Instead of setting an environment variable like `ANTHROPIC_MODEL`, you can pass a `model` ID directly in the configuration.
|
|
150
|
+
|
|
151
|
+
### Discover available models
|
|
152
|
+
|
|
153
|
+
Call `getAvailableModels()` to see which models the ACP agent supports. This starts the agent process and returns the model list from the session:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { AcpAgent } from '@mastra/acp'
|
|
157
|
+
|
|
158
|
+
const codeAgent = new AcpAgent({
|
|
159
|
+
id: 'code-agent',
|
|
160
|
+
description: 'An ACP-compatible coding agent',
|
|
161
|
+
command: 'claude',
|
|
162
|
+
args: ['--acp'],
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const models = await codeAgent.getAvailableModels()
|
|
166
|
+
// [{ modelId: 'claude-sonnet-4-20250514', name: 'Claude Sonnet' }, ...]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Set the model
|
|
170
|
+
|
|
171
|
+
Pass the `model` option to select a model at connection time:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { AcpAgent } from '@mastra/acp'
|
|
175
|
+
|
|
176
|
+
export const codeAgent = new AcpAgent({
|
|
177
|
+
id: 'code-agent',
|
|
178
|
+
description: 'An ACP-compatible coding agent',
|
|
179
|
+
command: 'claude',
|
|
180
|
+
args: ['--acp'],
|
|
181
|
+
model: '__AI_SDK_ANTHROPIC_MODEL_SONNET__',
|
|
182
|
+
})
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
You can also change the model at runtime with `setModel()`:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
await codeAgent.setModel('__AI_SDK_ANTHROPIC_MODEL_SONNET__')
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
If the ACP agent advertises available models and your model ID doesn't match any of them, Mastra throws an error listing the valid options:
|
|
192
|
+
|
|
193
|
+
```text
|
|
194
|
+
Model "bad-model-id" is not available. Available models: claude-sonnet-4-20250514, claude-haiku-4-20250514
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
If the agent doesn't advertise a model list, the value is passed through without validation.
|
|
198
|
+
|
|
199
|
+
## Session lifecycle
|
|
200
|
+
|
|
201
|
+
`createACPTool` and `AcpAgent` start the configured command on first use and create an ACP session. By default, `persistSession` is `true`, so the child process stays alive across calls.
|
|
202
|
+
|
|
203
|
+
Use the default persistent session when:
|
|
204
|
+
|
|
205
|
+
- The ACP agent benefits from keeping conversation or repository context.
|
|
206
|
+
- Startup is expensive and repeated calls should reuse the same process.
|
|
207
|
+
- A parent agent may delegate several related tasks to the same coding agent.
|
|
208
|
+
|
|
209
|
+
Set `persistSession: false` when each prompt should run in an isolated process:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import { AcpAgent } from '@mastra/acp'
|
|
213
|
+
|
|
214
|
+
export const codeAgent = new AcpAgent({
|
|
215
|
+
id: 'code-agent',
|
|
216
|
+
description: 'Run one isolated ACP coding task',
|
|
217
|
+
command: 'acp-agent',
|
|
218
|
+
args: ['--stdio'],
|
|
219
|
+
cwd: process.cwd(),
|
|
220
|
+
persistSession: false,
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
With `persistSession: false`, `@mastra/acp` stops the ACP process after each prompt completes.
|
|
225
|
+
|
|
226
|
+
## Permission handling
|
|
227
|
+
|
|
228
|
+
ACP agents may ask the client to choose a permission option before they continue. By default, `@mastra/acp` selects the first option returned by the ACP agent.
|
|
229
|
+
|
|
230
|
+
Pass `onPermissionRequest` to inspect the request and return the selected option yourself:
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { createACPTool } from '@mastra/acp'
|
|
234
|
+
|
|
235
|
+
export const codeAgentTool = createACPTool({
|
|
236
|
+
id: 'code-agent',
|
|
237
|
+
description: 'Use an ACP-compatible coding agent',
|
|
238
|
+
command: 'acp-agent',
|
|
239
|
+
args: ['--stdio'],
|
|
240
|
+
async onPermissionRequest(request) {
|
|
241
|
+
const allowOption = request.options.find(option => option.name === 'Allow')
|
|
242
|
+
|
|
243
|
+
if (!allowOption) {
|
|
244
|
+
return { outcome: { outcome: 'cancelled' } }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
outcome: {
|
|
249
|
+
outcome: 'selected',
|
|
250
|
+
optionId: allowOption.optionId,
|
|
251
|
+
},
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
})
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Use this callback to enforce local policy, inspect the permission title, or route the decision to your own approval flow.
|
|
258
|
+
|
|
259
|
+
## Workspace integration
|
|
260
|
+
|
|
261
|
+
ACP file operations go through Mastra's workspace abstraction. If you don't pass `workspace`, `@mastra/acp` creates a `Workspace` backed by `LocalFilesystem` and uses `cwd` as the filesystem root.
|
|
262
|
+
|
|
263
|
+
Pass a custom `Workspace` when the ACP agent should read and write through a specific filesystem implementation:
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import { AcpAgent } from '@mastra/acp'
|
|
267
|
+
import { LocalFilesystem, Workspace } from '@mastra/core/workspace'
|
|
268
|
+
|
|
269
|
+
const workspace = new Workspace({
|
|
270
|
+
filesystem: new LocalFilesystem({
|
|
271
|
+
root: process.cwd(),
|
|
272
|
+
}),
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
export const codeAgent = new AcpAgent({
|
|
276
|
+
id: 'code-agent',
|
|
277
|
+
description: 'Run coding tasks in a controlled workspace',
|
|
278
|
+
command: 'acp-agent',
|
|
279
|
+
args: ['--stdio'],
|
|
280
|
+
workspace,
|
|
281
|
+
})
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Use `cwd` and `workspace` together when the ACP process should start in one directory but file operations should use an explicitly configured workspace root.
|
|
285
|
+
|
|
286
|
+
## Related
|
|
287
|
+
|
|
288
|
+
- [Agent reference](https://mastra.ai/reference/agents/agent)
|
|
289
|
+
- [Subagents](https://mastra.ai/docs/agents/supervisor-agents)
|
|
290
|
+
- [Agent Client Protocol introduction](https://agentclientprotocol.com/overview/introduction)
|
|
291
|
+
- [Agent Client Protocol schema](https://agentclientprotocol.com/protocol/schema)
|
package/dist/index.cjs
CHANGED
|
@@ -9,7 +9,6 @@ var stream = require('stream');
|
|
|
9
9
|
var sdk = require('@agentclientprotocol/sdk');
|
|
10
10
|
var workspace = require('@mastra/core/workspace');
|
|
11
11
|
var tools = require('@mastra/core/tools');
|
|
12
|
-
var zod = require('zod');
|
|
13
12
|
|
|
14
13
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
14
|
|
|
@@ -82,6 +81,22 @@ var ACPConnection = class {
|
|
|
82
81
|
get sessionId() {
|
|
83
82
|
return this.session?.sessionId;
|
|
84
83
|
}
|
|
84
|
+
async getAvailableModels() {
|
|
85
|
+
await this.ensureConnected();
|
|
86
|
+
return this.session?.models?.availableModels ?? [];
|
|
87
|
+
}
|
|
88
|
+
async setModel(modelId) {
|
|
89
|
+
await this.ensureConnected();
|
|
90
|
+
const available = this.session?.models?.availableModels;
|
|
91
|
+
if (available && !available.some((m) => m.modelId === modelId)) {
|
|
92
|
+
const ids = available.map((m) => m.modelId).join(", ") || "(none)";
|
|
93
|
+
throw new Error(`Model "${modelId}" is not available. Available models: ${ids}`);
|
|
94
|
+
}
|
|
95
|
+
await this.connection.unstable_setSessionModel({
|
|
96
|
+
sessionId: this.session.sessionId,
|
|
97
|
+
modelId
|
|
98
|
+
});
|
|
99
|
+
}
|
|
85
100
|
async prompt(task, signal) {
|
|
86
101
|
const parts = [];
|
|
87
102
|
for await (const event of this.promptStream(task, signal)) {
|
|
@@ -193,6 +208,17 @@ var ACPConnection = class {
|
|
|
193
208
|
await this.connection.authenticate({ methodId: this.options.authMethodId });
|
|
194
209
|
}
|
|
195
210
|
this.session = await this.connection.newSession(this.getNewSessionRequest());
|
|
211
|
+
if (this.options.model) {
|
|
212
|
+
const available = this.session.models?.availableModels;
|
|
213
|
+
if (available && !available.some((m) => m.modelId === this.options.model)) {
|
|
214
|
+
const ids = available.map((m) => m.modelId).join(", ") || "(none)";
|
|
215
|
+
throw new Error(`Model "${this.options.model}" is not available. Available models: ${ids}`);
|
|
216
|
+
}
|
|
217
|
+
await this.connection.unstable_setSessionModel({
|
|
218
|
+
sessionId: this.session.sessionId,
|
|
219
|
+
modelId: this.options.model
|
|
220
|
+
});
|
|
221
|
+
}
|
|
196
222
|
} catch (error) {
|
|
197
223
|
this.disconnect();
|
|
198
224
|
throw this.withStderr(error);
|
|
@@ -342,6 +368,12 @@ var AcpAgent = class {
|
|
|
342
368
|
getInstructions() {
|
|
343
369
|
return "";
|
|
344
370
|
}
|
|
371
|
+
async getAvailableModels() {
|
|
372
|
+
return this.connection.getAvailableModels();
|
|
373
|
+
}
|
|
374
|
+
async setModel(modelId) {
|
|
375
|
+
return this.connection.setModel(modelId);
|
|
376
|
+
}
|
|
345
377
|
async generate(messages, options) {
|
|
346
378
|
const prompt = this.getPrompt(messages, options?.instructions);
|
|
347
379
|
const text = await this.connection.prompt(
|
|
@@ -576,32 +608,103 @@ function createACPTool(options) {
|
|
|
576
608
|
return tools.createTool({
|
|
577
609
|
id: options.id,
|
|
578
610
|
description: options.description,
|
|
579
|
-
inputSchema:
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
611
|
+
inputSchema: {
|
|
612
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
613
|
+
"type": "object",
|
|
614
|
+
"properties": {
|
|
615
|
+
"task": {
|
|
616
|
+
"type": "string",
|
|
617
|
+
"description": "The task to send to the ACP agent"
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
"required": [
|
|
621
|
+
"task"
|
|
622
|
+
]
|
|
623
|
+
},
|
|
624
|
+
outputSchema: {
|
|
625
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
626
|
+
"type": "object",
|
|
627
|
+
"properties": {
|
|
628
|
+
"output": {
|
|
629
|
+
"type": "string",
|
|
630
|
+
"description": "The output of the ACP agent"
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
"required": [
|
|
634
|
+
"output"
|
|
635
|
+
]
|
|
636
|
+
},
|
|
637
|
+
suspendSchema: {
|
|
638
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
639
|
+
"type": "object",
|
|
640
|
+
"properties": {
|
|
641
|
+
"permissionRequest": {
|
|
642
|
+
"type": "object",
|
|
643
|
+
"properties": {
|
|
644
|
+
"title": {
|
|
645
|
+
"type": "string",
|
|
646
|
+
"description": "The title of the permission request"
|
|
647
|
+
},
|
|
648
|
+
"options": {
|
|
649
|
+
"type": "array",
|
|
650
|
+
"items": {
|
|
651
|
+
"type": "object",
|
|
652
|
+
"properties": {
|
|
653
|
+
"optionId": {
|
|
654
|
+
"type": "string",
|
|
655
|
+
"description": "The option id to select"
|
|
656
|
+
},
|
|
657
|
+
"name": {
|
|
658
|
+
"type": "string",
|
|
659
|
+
"description": "The title of the permission request"
|
|
660
|
+
}
|
|
661
|
+
},
|
|
662
|
+
"required": [
|
|
663
|
+
"optionId",
|
|
664
|
+
"name"
|
|
665
|
+
]
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
"required": [
|
|
670
|
+
"title",
|
|
671
|
+
"options"
|
|
672
|
+
]
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
"required": [
|
|
676
|
+
"permissionRequest"
|
|
677
|
+
]
|
|
678
|
+
},
|
|
679
|
+
resumeSchema: {
|
|
680
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
681
|
+
"anyOf": [
|
|
682
|
+
{
|
|
683
|
+
"type": "object",
|
|
684
|
+
"properties": {
|
|
685
|
+
"optionId": {
|
|
686
|
+
"description": "The option id to select",
|
|
687
|
+
"type": "string"
|
|
688
|
+
},
|
|
689
|
+
"outcome": {
|
|
690
|
+
"description": "The outcome of the permission request",
|
|
691
|
+
"type": "string",
|
|
692
|
+
"const": "selected"
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
"type": "object",
|
|
698
|
+
"properties": {
|
|
699
|
+
"outcome": {
|
|
700
|
+
"description": "The outcome of the permission request",
|
|
701
|
+
"type": "string",
|
|
702
|
+
"const": "cancelled"
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
]
|
|
707
|
+
},
|
|
605
708
|
execute: async ({ task }, context) => {
|
|
606
709
|
const workspace = await context?.mastra?.getWorkspace();
|
|
607
710
|
const connection = new ACPConnection({
|