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

feat: add task MCP tools#19901

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
code-asher merged 4 commits intomainfromasher/mcp-tasks
Oct 1, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletionscoderd/database/dbfake/dbfake.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ import (
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/telemetry"
"github.com/coder/coder/v2/coderd/wspubsub"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisionersdk"
sdkproto"github.com/coder/coder/v2/provisionersdk/proto"
)
Expand DownExpand Up@@ -55,6 +56,7 @@ type WorkspaceBuildBuilder struct {
params []database.WorkspaceBuildParameter
agentTokenstring
dispoworkspaceBuildDisposition
taskAppID uuid.UUID
}

typeworkspaceBuildDispositionstruct {
Expand DownExpand Up@@ -117,6 +119,23 @@ func (b WorkspaceBuildBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []
returnb
}

func (bWorkspaceBuildBuilder)WithTask()WorkspaceBuildBuilder {
//nolint: revive // returns modified struct
b.taskAppID=uuid.New()
returnb.Params(database.WorkspaceBuildParameter{
Name:codersdk.AITaskPromptParameterName,
Value:"list me",
}).WithAgent(func(a []*sdkproto.Agent) []*sdkproto.Agent {
a[0].Apps= []*sdkproto.App{
{
Id:b.taskAppID.String(),
Slug:"vcode",
},
}
returna
})
}

func (bWorkspaceBuildBuilder)Starting()WorkspaceBuildBuilder {
//nolint: revive // returns modified struct
b.dispo.starting=true
Expand All@@ -134,6 +153,14 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse {
b.seed.ID=uuid.New()
b.seed.JobID=jobID

ifb.taskAppID!=uuid.Nil {
b.seed.HasAITask= sql.NullBool{
Bool:true,
Valid:true,
}
b.seed.AITaskSidebarAppID= uuid.NullUUID{UUID:b.taskAppID,Valid:true}
}

resp:=WorkspaceResponse{
AgentToken:b.agentToken,
}
Expand Down
253 changes: 250 additions & 3 deletionscodersdk/toolsdk/toolsdk.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,10 @@ const (
ToolNameWorkspaceEditFile = "coder_workspace_edit_file"
ToolNameWorkspaceEditFiles = "coder_workspace_edit_files"
ToolNameWorkspacePortForward = "coder_workspace_port_forward"
ToolNameCreateTask = "coder_create_task"
ToolNameDeleteTask = "coder_delete_task"
ToolNameListTasks = "coder_list_tasks"
ToolNameGetTaskStatus = "coder_get_task_status"
)

func NewDeps(client *codersdk.Client, opts ...func(*Deps)) (Deps, error) {
Expand DownExpand Up@@ -223,6 +227,10 @@ var All = []GenericTool{
WorkspaceEditFile.Generic(),
WorkspaceEditFiles.Generic(),
WorkspacePortForward.Generic(),
CreateTask.Generic(),
DeleteTask.Generic(),
ListTasks.Generic(),
GetTaskStatus.Generic(),
}

type ReportTaskArgs struct {
Expand DownExpand Up@@ -344,7 +352,7 @@ is provisioned correctly and the agent can connect to the control plane.
Properties: map[string]any{
"user": map[string]any{
"type": "string",
"description":"Username or ID of the user tocreatethe workspace for. Use the `me` keyword to createa workspace for the authenticated user.",
"description":userDescription("create a workspace"),
},
"template_version_id": map[string]any{
"type": "string",
Expand DownExpand Up@@ -1393,8 +1401,6 @@ type WorkspaceLSResponse struct {
Contents []WorkspaceLSFile `json:"contents"`
}

const workspaceDescription = "The workspace name in the format [owner/]workspace[.agent]. If an owner is not specified, the authenticated user is used."

var WorkspaceLS = Tool[WorkspaceLSArgs, WorkspaceLSResponse]{
Tool: aisdk.Tool{
Name: ToolNameWorkspaceLS,
Expand DownExpand Up@@ -1750,6 +1756,237 @@ var WorkspacePortForward = Tool[WorkspacePortForwardArgs, WorkspacePortForwardRe
},
}

type CreateTaskArgs struct {
Input string `json:"input"`
TemplateVersionID string `json:"template_version_id"`
TemplateVersionPresetID string `json:"template_version_preset_id"`
User string `json:"user"`
}

var CreateTask = Tool[CreateTaskArgs, codersdk.Task]{
Tool: aisdk.Tool{
Name: ToolNameCreateTask,
Description: `Create a task.`,
Schema: aisdk.Schema{
Properties: map[string]any{
"input": map[string]any{
"type": "string",
"description": "Input/prompt for the task.",
},
"template_version_id": map[string]any{
"type": "string",
"description": "ID of the template version to create the task from.",
},
"template_version_preset_id": map[string]any{
"type": "string",
"description": "Optional ID of the template version preset to create the task from.",
},
"user": map[string]any{
"type": "string",
"description": userDescription("create a task"),
},
},
Required: []string{"input", "template_version_id"},
},
},
UserClientOptional: true,
Handler: func(ctx context.Context, deps Deps, args CreateTaskArgs) (codersdk.Task, error) {
if args.Input == "" {
return codersdk.Task{}, xerrors.New("input is required")
}

tvID, err := uuid.Parse(args.TemplateVersionID)
if err != nil {
return codersdk.Task{}, xerrors.New("template_version_id must be a valid UUID")
}

var tvPresetID uuid.UUID
if args.TemplateVersionPresetID != "" {
tvPresetID, err = uuid.Parse(args.TemplateVersionPresetID)
if err != nil {
return codersdk.Task{}, xerrors.New("template_version_preset_id must be a valid UUID")
}
}

if args.User == "" {
args.User = codersdk.Me
}

expClient := codersdk.NewExperimentalClient(deps.coderClient)
task, err := expClient.CreateTask(ctx, args.User, codersdk.CreateTaskRequest{
Input: args.Input,
TemplateVersionID: tvID,
TemplateVersionPresetID: tvPresetID,
})
if err != nil {
return codersdk.Task{}, xerrors.Errorf("create task: %w", err)
}

return task, nil
},
}

type DeleteTaskArgs struct {
TaskID string `json:"task_id"`
}

var DeleteTask = Tool[DeleteTaskArgs, codersdk.Response]{
Tool: aisdk.Tool{
Name: ToolNameDeleteTask,
Description: `Delete a task.`,
Schema: aisdk.Schema{
Properties: map[string]any{
"task_id": map[string]any{
"type": "string",
"description": taskIDDescription("delete"),
},
},
Required: []string{"task_id"},
},
},
UserClientOptional: true,
Handler: func(ctx context.Context, deps Deps, args DeleteTaskArgs) (codersdk.Response, error) {
if args.TaskID == "" {
return codersdk.Response{}, xerrors.New("task_id is required")
}

expClient := codersdk.NewExperimentalClient(deps.coderClient)

var owner string
id, err := uuid.Parse(args.TaskID)
if err == nil {
task, err := expClient.TaskByID(ctx, id)
if err != nil {
return codersdk.Response{}, xerrors.Errorf("get task %q: %w", args.TaskID, err)
}
owner = task.OwnerName
} else {
ws, err := normalizedNamedWorkspace(ctx, deps.coderClient, args.TaskID)
if err != nil {
return codersdk.Response{}, xerrors.Errorf("get task workspace %q: %w", args.TaskID, err)
}
owner = ws.OwnerName
id = ws.ID
}

err = expClient.DeleteTask(ctx, owner, id)
if err != nil {
return codersdk.Response{}, xerrors.Errorf("delete task: %w", err)
}

return codersdk.Response{
Message: "Task deleted successfully",
}, nil
},
}

type ListTasksArgs struct {
Status string `json:"status"`
User string `json:"user"`
}

type ListTasksResponse struct {
Tasks []codersdk.Task `json:"tasks"`
}

var ListTasks = Tool[ListTasksArgs, ListTasksResponse]{
Tool: aisdk.Tool{
Name: ToolNameListTasks,
Description: `List tasks.`,
Schema: aisdk.Schema{
Properties: map[string]any{
"status": map[string]any{
"type": "string",
"description": "Optional filter by task status.",
},
"user": map[string]any{
"type": "string",
"description": userDescription("list tasks"),
},
},
Required: []string{},
},
},
UserClientOptional: true,
Handler: func(ctx context.Context, deps Deps, args ListTasksArgs) (ListTasksResponse, error) {
if args.User == "" {
args.User = codersdk.Me
}

expClient := codersdk.NewExperimentalClient(deps.coderClient)
tasks, err := expClient.Tasks(ctx, &codersdk.TasksFilter{
Owner: args.User,
Status: args.Status,
})
if err != nil {
return ListTasksResponse{}, xerrors.Errorf("list tasks: %w", err)
}

return ListTasksResponse{
Tasks: tasks,
}, nil
},
}

type GetTaskStatusArgs struct {
TaskID string `json:"task_id"`
}

type GetTaskStatusResponse struct {
Status codersdk.WorkspaceStatus `json:"status"`
State *codersdk.TaskStateEntry `json:"state"`
}

var GetTaskStatus = Tool[GetTaskStatusArgs, GetTaskStatusResponse]{
Tool: aisdk.Tool{
Name: ToolNameGetTaskStatus,
Description: `Get the status of a task.`,
Schema: aisdk.Schema{
Properties: map[string]any{
"task_id": map[string]any{
"type": "string",
"description": taskIDDescription("get"),
},
},
Required: []string{"task_id"},
},
},
UserClientOptional: true,
Handler: func(ctx context.Context, deps Deps, args GetTaskStatusArgs) (GetTaskStatusResponse, error) {
if args.TaskID == "" {
return GetTaskStatusResponse{}, xerrors.New("task_id is required")
}

expClient := codersdk.NewExperimentalClient(deps.coderClient)

id, err := uuid.Parse(args.TaskID)
if err != nil {
ws, err := normalizedNamedWorkspace(ctx, deps.coderClient, args.TaskID)
if err != nil {
return GetTaskStatusResponse{}, xerrors.Errorf("get task workspace %q: %w", args.TaskID, err)
}
id = ws.ID
}

task, err := expClient.TaskByID(ctx, id)
if err != nil {
return GetTaskStatusResponse{}, xerrors.Errorf("get task %q: %w", args.TaskID, err)
}

return GetTaskStatusResponse{
Status: task.Status,
State: task.CurrentState,
}, nil
},
}

// normalizedNamedWorkspace normalizes the workspace name before getting the
// workspace by name.
func normalizedNamedWorkspace(ctx context.Context, client *codersdk.Client, name string) (codersdk.Workspace, error) {
// Maybe namedWorkspace should itself call NormalizeWorkspaceInput?
return namedWorkspace(ctx, client, NormalizeWorkspaceInput(name))
}

// NormalizeWorkspaceInput converts workspace name input to standard format.
// Handles the following input formats:
// - workspace → workspace
Expand DownExpand Up@@ -1810,3 +2047,13 @@ func newAgentConn(ctx context.Context, client *codersdk.Client, workspace string
}
return conn, nil
}

const workspaceDescription = "The workspace name in the format [owner/]workspace[.agent]. If an owner is not specified, the authenticated user is used."

func taskIDDescription(action string) string {
return fmt.Sprintf("ID or workspace identifier in the format [owner/]workspace[.agent] for the task to %s. If an owner is not specified, the authenticated user is used.", action)
}

func userDescription(action string) string {
return fmt.Sprintf("Username or ID of the user for which to %s. Omit or use the `me` keyword to %s for the authenticated user.", action, action)
}
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp