Skip to content

Commit a9637f6

Browse files
committed
feat: add MCP file support for ACP server configuration
1 parent d42b9ac commit a9637f6

File tree

3 files changed

+55
-3
lines changed

3 files changed

+55
-3
lines changed

cmd/server/server.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ func runServer(ctx context.Context, logger *slog.Logger, argsToPass []string) er
136136
}
137137

138138
experimentalACP := viper.GetBool(FlagExperimentalACP)
139+
acpMCPFile := viper.GetString(FlagMCPFile)
140+
141+
if acpMCPFile != "" && !experimentalACP {
142+
return xerrors.Errorf("--mcp-file requires --experimental-acp requires to be set")
143+
}
139144

140145
if experimentalACP && (saveState || loadState) {
141146
return xerrors.Errorf("ACP mode doesn't support state persistence")
@@ -169,6 +174,7 @@ func runServer(ctx context.Context, logger *slog.Logger, argsToPass []string) er
169174
acpResult, err = httpapi.SetupACP(ctx, httpapi.SetupACPConfig{
170175
Program: agent,
171176
ProgramArgs: argsToPass[1:],
177+
MCPFilePath: acpMCPFile,
172178
})
173179
if err != nil {
174180
return xerrors.Errorf("failed to setup ACP: %w", err)
@@ -382,6 +388,7 @@ const (
382388
FlagSaveState = "save-state"
383389
FlagPidFile = "pid-file"
384390
FlagExperimentalACP = "experimental-acp"
391+
FlagMCPFile = "mcp-file"
385392
)
386393

387394
func CreateServerCmd() *cobra.Command {
@@ -425,6 +432,7 @@ func CreateServerCmd() *cobra.Command {
425432
{FlagSaveState, "", false, "Save state to state-file on shutdown (defaults to true when state-file is set)", "bool"},
426433
{FlagPidFile, "", "", "Path to file where the server process ID will be written for shutdown scripts", "string"},
427434
{FlagExperimentalACP, "", false, "Use experimental ACP transport instead of PTY", "bool"},
435+
{FlagMCPFile, "", "", "MCP file for the ACP server", "string"},
428436
}
429437

430438
for _, spec := range flagSpecs {

lib/httpapi/setup.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func SetupProcess(ctx context.Context, config SetupProcessConfig) (*termexec.Pro
5353
type SetupACPConfig struct {
5454
Program string
5555
ProgramArgs []string
56+
MCPFilePath string
5657
Clock quartz.Clock
5758
}
5859

@@ -88,7 +89,7 @@ func SetupACP(ctx context.Context, config SetupACPConfig) (*SetupACPResult, erro
8889
return nil, fmt.Errorf("failed to start process: %w", err)
8990
}
9091

91-
agentIO, err := acpio.NewWithPipes(ctx, stdin, stdout, logger, os.Getwd)
92+
agentIO, err := acpio.NewWithPipes(ctx, stdin, stdout, logger, os.Getwd, config.MCPFilePath)
9293
if err != nil {
9394
_ = cmd.Process.Kill()
9495
return nil, fmt.Errorf("failed to initialize ACP connection: %w", err)

x/acpio/acpio.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ package acpio
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"io"
78
"log/slog"
9+
"os"
810
"strings"
911
"sync"
1012

1113
acp "github.com/coder/acp-go-sdk"
1214
st "github.com/coder/agentapi/lib/screentracker"
15+
"golang.org/x/xerrors"
1316
)
1417

1518
// Compile-time assertion that ACPAgentIO implements st.AgentIO
@@ -31,6 +34,10 @@ type acpClient struct {
3134
agentIO *ACPAgentIO
3235
}
3336

37+
type McpConfig struct {
38+
McpServers []acp.McpServer `json:"mcpServers"`
39+
}
40+
3441
var _ acp.Client = (*acpClient)(nil)
3542

3643
func (c *acpClient) SessionUpdate(ctx context.Context, params acp.SessionNotification) error {
@@ -131,7 +138,7 @@ func (a *ACPAgentIO) SetOnChunk(fn func(chunk string)) {
131138
}
132139

133140
// NewWithPipes creates an ACPAgentIO connected via the provided pipes
134-
func NewWithPipes(ctx context.Context, toAgent io.Writer, fromAgent io.Reader, logger *slog.Logger, getwd func() (string, error)) (*ACPAgentIO, error) {
141+
func NewWithPipes(ctx context.Context, toAgent io.Writer, fromAgent io.Reader, logger *slog.Logger, getwd func() (string, error), mcpFilePath string) (*ACPAgentIO, error) {
135142
if logger == nil {
136143
logger = slog.Default()
137144
}
@@ -154,6 +161,12 @@ func NewWithPipes(ctx context.Context, toAgent io.Writer, fromAgent io.Reader, l
154161
}
155162
logger.Debug("ACP initialized", "protocolVersion", initResp.ProtocolVersion)
156163

164+
// Prepare the MCPs for the session
165+
supportedMCPList, err := getSupportedMCPConfig(mcpFilePath, logger, &initResp)
166+
if err != nil {
167+
return nil, err
168+
}
169+
157170
// Create a session
158171
cwd, err := getwd()
159172
if err != nil {
@@ -162,7 +175,7 @@ func NewWithPipes(ctx context.Context, toAgent io.Writer, fromAgent io.Reader, l
162175
}
163176
sessResp, err := conn.NewSession(ctx, acp.NewSessionRequest{
164177
Cwd: cwd,
165-
McpServers: []acp.McpServer{},
178+
McpServers: supportedMCPList,
166179
})
167180
if err != nil {
168181
logger.Error("Failed to create ACP session", "error", err)
@@ -174,6 +187,36 @@ func NewWithPipes(ctx context.Context, toAgent io.Writer, fromAgent io.Reader, l
174187
return agentIO, nil
175188
}
176189

190+
func getSupportedMCPConfig(mcpFilePath string, logger *slog.Logger, initResp *acp.InitializeResponse) ([]acp.McpServer, error) {
191+
mcpFile, err := os.Open(mcpFilePath)
192+
if err != nil {
193+
return nil, xerrors.Errorf("Failed to open mcp file: %v", err)
194+
}
195+
196+
defer func() {
197+
if closeErr := mcpFile.Close(); closeErr != nil {
198+
logger.Error("Failed to close mcp file", "error", err)
199+
}
200+
}()
201+
202+
var allMcpList McpConfig
203+
decoder := json.NewDecoder(mcpFile)
204+
205+
if err = decoder.Decode(&allMcpList); err != nil {
206+
return nil, xerrors.Errorf("Failed to decode mcp file: %v", err)
207+
}
208+
209+
// Only send the MCPs that are supported by the agents
210+
var supportedMCPList []acp.McpServer
211+
for _, mcp := range allMcpList.McpServers {
212+
if (mcp.Http != nil && !initResp.AgentCapabilities.McpCapabilities.Http) || (mcp.Sse != nil && !initResp.AgentCapabilities.McpCapabilities.Sse) {
213+
continue
214+
}
215+
supportedMCPList = append(supportedMCPList, mcp)
216+
}
217+
return supportedMCPList, err
218+
}
219+
177220
// Write sends a message to the agent via ACP prompt
178221
func (a *ACPAgentIO) Write(data []byte) (int, error) {
179222
text := string(data)

0 commit comments

Comments
 (0)