Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1e11e82

Browse files
authored
fix(mcp): report task status correctly (#17187)
1 parent3a243c1 commit1e11e82

File tree

3 files changed

+144
-113
lines changed

3 files changed

+144
-113
lines changed

‎cli/exp_mcp.go

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@ import (
44
"context"
55
"encoding/json"
66
"errors"
7-
"log"
87
"os"
98
"path/filepath"
109

10+
"github.com/mark3labs/mcp-go/server"
11+
"golang.org/x/xerrors"
12+
1113
"cdr.dev/slog"
1214
"cdr.dev/slog/sloggers/sloghuman"
15+
"github.com/coder/coder/v2/buildinfo"
1316
"github.com/coder/coder/v2/cli/cliui"
1417
"github.com/coder/coder/v2/codersdk"
18+
"github.com/coder/coder/v2/codersdk/agentsdk"
1519
codermcp"github.com/coder/coder/v2/mcp"
1620
"github.com/coder/serpent"
1721
)
@@ -191,14 +195,16 @@ func (*RootCmd) mcpConfigureCursor() *serpent.Command {
191195

192196
func (r*RootCmd)mcpServer()*serpent.Command {
193197
var (
194-
client=new(codersdk.Client)
195-
instructionsstring
196-
allowedTools []string
198+
client=new(codersdk.Client)
199+
instructionsstring
200+
allowedTools []string
201+
appStatusSlugstring
202+
mcpServerAgentbool
197203
)
198204
return&serpent.Command{
199205
Use:"server",
200206
Handler:func(inv*serpent.Invocation)error {
201-
returnmcpServerHandler(inv,client,instructions,allowedTools)
207+
returnmcpServerHandler(inv,client,instructions,allowedTools,appStatusSlug,mcpServerAgent)
202208
},
203209
Short:"Start the Coder MCP server.",
204210
Middleware:serpent.Chain(
@@ -209,24 +215,39 @@ func (r *RootCmd) mcpServer() *serpent.Command {
209215
Name:"instructions",
210216
Description:"The instructions to pass to the MCP server.",
211217
Flag:"instructions",
218+
Env:"CODER_MCP_INSTRUCTIONS",
212219
Value:serpent.StringOf(&instructions),
213220
},
214221
{
215222
Name:"allowed-tools",
216223
Description:"Comma-separated list of allowed tools. If not specified, all tools are allowed.",
217224
Flag:"allowed-tools",
225+
Env:"CODER_MCP_ALLOWED_TOOLS",
218226
Value:serpent.StringArrayOf(&allowedTools),
219227
},
228+
{
229+
Name:"app-status-slug",
230+
Description:"When reporting a task, the coder_app slug under which to report the task.",
231+
Flag:"app-status-slug",
232+
Env:"CODER_MCP_APP_STATUS_SLUG",
233+
Value:serpent.StringOf(&appStatusSlug),
234+
Default:"",
235+
},
236+
{
237+
Flag:"agent",
238+
Env:"CODER_MCP_SERVER_AGENT",
239+
Description:"Start the MCP server in agent mode, with a different set of tools.",
240+
Value:serpent.BoolOf(&mcpServerAgent),
241+
},
220242
},
221243
}
222244
}
223245

224-
funcmcpServerHandler(inv*serpent.Invocation,client*codersdk.Client,instructionsstring,allowedTools []string)error {
246+
//nolint:revive // control coupling
247+
funcmcpServerHandler(inv*serpent.Invocation,client*codersdk.Client,instructionsstring,allowedTools []string,appStatusSlugstring,mcpServerAgentbool)error {
225248
ctx,cancel:=context.WithCancel(inv.Context())
226249
defercancel()
227250

228-
logger:=slog.Make(sloghuman.Sink(inv.Stdout))
229-
230251
me,err:=client.User(ctx,codersdk.Me)
231252
iferr!=nil {
232253
cliui.Errorf(inv.Stderr,"Failed to log in to the Coder deployment.")
@@ -253,19 +274,40 @@ func mcpServerHandler(inv *serpent.Invocation, client *codersdk.Client, instruct
253274
inv.Stderr=invStderr
254275
}()
255276

256-
options:= []codermcp.Option{
257-
codermcp.WithInstructions(instructions),
258-
codermcp.WithLogger(&logger),
277+
mcpSrv:=server.NewMCPServer(
278+
"Coder Agent",
279+
buildinfo.Version(),
280+
server.WithInstructions(instructions),
281+
)
282+
283+
// Create a separate logger for the tools.
284+
toolLogger:=slog.Make(sloghuman.Sink(invStderr))
285+
286+
toolDeps:= codermcp.ToolDeps{
287+
Client:client,
288+
Logger:&toolLogger,
289+
AppStatusSlug:appStatusSlug,
290+
AgentClient:agentsdk.New(client.URL),
291+
}
292+
293+
ifmcpServerAgent {
294+
// Get the workspace agent token from the environment.
295+
agentToken,ok:=os.LookupEnv("CODER_AGENT_TOKEN")
296+
if!ok||agentToken=="" {
297+
returnxerrors.New("CODER_AGENT_TOKEN is not set")
298+
}
299+
toolDeps.AgentClient.SetSessionToken(agentToken)
259300
}
260301

261-
// Add allowed tools option if specified
302+
// Register tools based on the allowlist (if specified)
303+
reg:=codermcp.AllTools()
262304
iflen(allowedTools)>0 {
263-
options=append(options,codermcp.WithAllowedTools(allowedTools))
305+
reg=reg.WithOnlyAllowed(allowedTools...)
264306
}
265307

266-
srv:=codermcp.NewStdio(client,options...)
267-
srv.SetErrorLogger(log.New(invStderr,"",log.LstdFlags))
308+
reg.Register(mcpSrv,toolDeps)
268309

310+
srv:=server.NewStdioServer(mcpSrv)
269311
done:=make(chanerror)
270312
gofunc() {
271313
deferclose(done)

‎mcp/mcp.go

Lines changed: 46 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"encoding/json"
77
"errors"
88
"io"
9-
"os"
109
"slices"
1110
"strings"
1211
"time"
@@ -17,76 +16,12 @@ import (
1716
"golang.org/x/xerrors"
1817

1918
"cdr.dev/slog"
20-
"cdr.dev/slog/sloggers/sloghuman"
21-
"github.com/coder/coder/v2/buildinfo"
2219
"github.com/coder/coder/v2/coderd/util/ptr"
2320
"github.com/coder/coder/v2/codersdk"
21+
"github.com/coder/coder/v2/codersdk/agentsdk"
2422
"github.com/coder/coder/v2/codersdk/workspacesdk"
2523
)
2624

27-
typemcpOptionsstruct {
28-
instructionsstring
29-
logger*slog.Logger
30-
allowedTools []string
31-
}
32-
33-
// Option is a function that configures the MCP server.
34-
typeOptionfunc(*mcpOptions)
35-
36-
// WithInstructions sets the instructions for the MCP server.
37-
funcWithInstructions(instructionsstring)Option {
38-
returnfunc(o*mcpOptions) {
39-
o.instructions=instructions
40-
}
41-
}
42-
43-
// WithLogger sets the logger for the MCP server.
44-
funcWithLogger(logger*slog.Logger)Option {
45-
returnfunc(o*mcpOptions) {
46-
o.logger=logger
47-
}
48-
}
49-
50-
// WithAllowedTools sets the allowed tools for the MCP server.
51-
funcWithAllowedTools(tools []string)Option {
52-
returnfunc(o*mcpOptions) {
53-
o.allowedTools=tools
54-
}
55-
}
56-
57-
// NewStdio creates a new MCP stdio server with the given client and options.
58-
// It is the responsibility of the caller to start and stop the server.
59-
funcNewStdio(client*codersdk.Client,opts...Option)*server.StdioServer {
60-
options:=&mcpOptions{
61-
instructions:``,
62-
logger:ptr.Ref(slog.Make(sloghuman.Sink(os.Stdout))),
63-
}
64-
for_,opt:=rangeopts {
65-
opt(options)
66-
}
67-
68-
mcpSrv:=server.NewMCPServer(
69-
"Coder Agent",
70-
buildinfo.Version(),
71-
server.WithInstructions(options.instructions),
72-
)
73-
74-
logger:=slog.Make(sloghuman.Sink(os.Stdout))
75-
76-
// Register tools based on the allowed list (if specified)
77-
reg:=AllTools()
78-
iflen(options.allowedTools)>0 {
79-
reg=reg.WithOnlyAllowed(options.allowedTools...)
80-
}
81-
reg.Register(mcpSrv,ToolDeps{
82-
Client:client,
83-
Logger:&logger,
84-
})
85-
86-
srv:=server.NewStdioServer(mcpSrv)
87-
returnsrv
88-
}
89-
9025
// allTools is the list of all available tools. When adding a new tool,
9126
// make sure to update this list.
9227
varallTools=ToolRegistry{
@@ -120,6 +55,8 @@ Choose an emoji that helps the user understand the current phase at a glance.`),
12055
mcp.WithBoolean("done",mcp.Description(`Whether the overall task the user requested is complete.
12156
Set to true only when the entire requested operation is finished successfully.
12257
For multi-step processes, use false until all steps are complete.`),mcp.Required()),
58+
mcp.WithBoolean("need_user_attention",mcp.Description(`Whether the user needs to take action on the task.
59+
Set to true if the task is in a failed state or if the user needs to take action to continue.`),mcp.Required()),
12360
),
12461
MakeHandler:handleCoderReportTask,
12562
},
@@ -265,8 +202,10 @@ Can be either "start" or "stop".`)),
265202

266203
// ToolDeps contains all dependencies needed by tool handlers
267204
typeToolDepsstruct {
268-
Client*codersdk.Client
269-
Logger*slog.Logger
205+
Client*codersdk.Client
206+
AgentClient*agentsdk.Client
207+
Logger*slog.Logger
208+
AppStatusSlugstring
270209
}
271210

272211
// ToolHandler associates a tool with its handler creation function
@@ -313,18 +252,23 @@ func AllTools() ToolRegistry {
313252
}
314253

315254
typehandleCoderReportTaskArgsstruct {
316-
Summarystring`json:"summary"`
317-
Linkstring`json:"link"`
318-
Emojistring`json:"emoji"`
319-
Donebool`json:"done"`
255+
Summarystring`json:"summary"`
256+
Linkstring`json:"link"`
257+
Emojistring`json:"emoji"`
258+
Donebool`json:"done"`
259+
NeedUserAttentionbool`json:"need_user_attention"`
320260
}
321261

322262
// Example payload:
323-
// {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_report_task", "arguments": {"summary": "I'm working onthe login page.", "link": "https://github.com/coder/coder/pull/1234", "emoji": "🔍", "done": false}}}
263+
// {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_report_task", "arguments": {"summary": "I need help withthe login page.", "link": "https://github.com/coder/coder/pull/1234", "emoji": "🔍", "done": false, "need_user_attention": true}}}
324264
funchandleCoderReportTask(depsToolDeps) server.ToolHandlerFunc {
325265
returnfunc(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
326-
ifdeps.Client==nil {
327-
returnnil,xerrors.New("developer error: client is required")
266+
ifdeps.AgentClient==nil {
267+
returnnil,xerrors.New("developer error: agent client is required")
268+
}
269+
270+
ifdeps.AppStatusSlug=="" {
271+
returnnil,xerrors.New("No app status slug provided, set CODER_MCP_APP_STATUS_SLUG when running the MCP server to report tasks.")
328272
}
329273

330274
// Convert the request parameters to a json.RawMessage so we can unmarshal
@@ -334,20 +278,33 @@ func handleCoderReportTask(deps ToolDeps) server.ToolHandlerFunc {
334278
returnnil,xerrors.Errorf("failed to unmarshal arguments: %w",err)
335279
}
336280

337-
// TODO: Waiting on support for tasks.
338-
deps.Logger.Info(ctx,"report task tool called",slog.F("summary",args.Summary),slog.F("link",args.Link),slog.F("done",args.Done),slog.F("emoji",args.Emoji))
339-
/*
340-
err := sdk.PostTask(ctx, agentsdk.PostTaskRequest{
341-
Reporter: "claude",
342-
Summary: summary,
343-
URL: link,
344-
Completion: done,
345-
Icon: emoji,
346-
})
347-
if err != nil {
348-
return nil, err
349-
}
350-
*/
281+
deps.Logger.Info(ctx,"report task tool called",
282+
slog.F("summary",args.Summary),
283+
slog.F("link",args.Link),
284+
slog.F("emoji",args.Emoji),
285+
slog.F("done",args.Done),
286+
slog.F("need_user_attention",args.NeedUserAttention),
287+
)
288+
289+
newStatus:= agentsdk.PatchAppStatus{
290+
AppSlug:deps.AppStatusSlug,
291+
Message:args.Summary,
292+
URI:args.Link,
293+
Icon:args.Emoji,
294+
NeedsUserAttention:args.NeedUserAttention,
295+
State:codersdk.WorkspaceAppStatusStateWorking,
296+
}
297+
298+
ifargs.Done {
299+
newStatus.State=codersdk.WorkspaceAppStatusStateComplete
300+
}
301+
ifargs.NeedUserAttention {
302+
newStatus.State=codersdk.WorkspaceAppStatusStateFailure
303+
}
304+
305+
iferr:=deps.AgentClient.PatchAppStatus(ctx,newStatus);err!=nil {
306+
returnnil,xerrors.Errorf("failed to patch app status: %w",err)
307+
}
351308

352309
return&mcp.CallToolResult{
353310
Content: []mcp.Content{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp