feat: add status watcher to MCP server (#18320)

This is meant to complement the existing task reporter since the LLM
does not call it reliably.

It also includes refactoring to use the common agent flags/env vars.
This commit is contained in:
Asher
2025-06-13 12:53:43 -08:00
committed by GitHub
parent 5bcde58bdc
commit 4bd5609e13
12 changed files with 931 additions and 186 deletions

View File

@ -12,7 +12,6 @@ import (
"golang.org/x/xerrors"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
)
func NewDeps(client *codersdk.Client, opts ...func(*Deps)) (Deps, error) {
@ -27,23 +26,16 @@ func NewDeps(client *codersdk.Client, opts ...func(*Deps)) (Deps, error) {
return d, nil
}
func WithAgentClient(client *agentsdk.Client) func(*Deps) {
return func(d *Deps) {
d.agentClient = client
}
}
func WithAppStatusSlug(slug string) func(*Deps) {
return func(d *Deps) {
d.appStatusSlug = slug
}
}
// Deps provides access to tool dependencies.
type Deps struct {
coderClient *codersdk.Client
agentClient *agentsdk.Client
appStatusSlug string
coderClient *codersdk.Client
report func(ReportTaskArgs) error
}
func WithTaskReporter(fn func(ReportTaskArgs) error) func(*Deps) {
return func(d *Deps) {
d.report = fn
}
}
// HandlerFunc is a typed function that handles a tool call.
@ -225,22 +217,12 @@ ONLY report a "complete" or "failure" state if you have FULLY completed the task
},
},
UserClientOptional: true,
Handler: func(ctx context.Context, deps Deps, args ReportTaskArgs) (codersdk.Response, error) {
if deps.agentClient == nil {
return codersdk.Response{}, xerrors.New("tool unavailable as CODER_AGENT_TOKEN or CODER_AGENT_TOKEN_FILE not set")
}
if deps.appStatusSlug == "" {
return codersdk.Response{}, xerrors.New("tool unavailable as CODER_MCP_APP_STATUS_SLUG is not set")
}
Handler: func(_ context.Context, deps Deps, args ReportTaskArgs) (codersdk.Response, error) {
if len(args.Summary) > 160 {
return codersdk.Response{}, xerrors.New("summary must be less than 160 characters")
}
if err := deps.agentClient.PatchAppStatus(ctx, agentsdk.PatchAppStatus{
AppSlug: deps.appStatusSlug,
Message: args.Summary,
URI: args.Link,
State: codersdk.WorkspaceAppStatusState(args.State),
}); err != nil {
err := deps.report(args)
if err != nil {
return codersdk.Response{}, err
}
return codersdk.Response{

View File

@ -72,7 +72,14 @@ func TestTools(t *testing.T) {
})
t.Run("ReportTask", func(t *testing.T) {
tb, err := toolsdk.NewDeps(memberClient, toolsdk.WithAgentClient(agentClient), toolsdk.WithAppStatusSlug("some-agent-app"))
tb, err := toolsdk.NewDeps(memberClient, toolsdk.WithTaskReporter(func(args toolsdk.ReportTaskArgs) error {
return agentClient.PatchAppStatus(setupCtx, agentsdk.PatchAppStatus{
AppSlug: "some-agent-app",
Message: args.Summary,
URI: args.Link,
State: codersdk.WorkspaceAppStatusState(args.State),
})
}))
require.NoError(t, err)
_, err = testTool(t, toolsdk.ReportTask, tb, toolsdk.ReportTaskArgs{
Summary: "test summary",