mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
Exposes the experimental Coder Agents chats API through the MCP tool registry, so MCP clients (the hosted `/api/experimental/mcp/http` server and `coder exp mcp server`) can start and drive server-side coding agents. New tools in `codersdk/toolsdk`, all thin wrappers over existing `codersdk.ExperimentalClient` methods: | Tool | Wraps | |---|---| | `coder_create_chat` | `CreateChat` (prompt, optional org, model config, labels) | | `coder_get_chat` | `GetChat` (status, last error, last turn summary, workspace, files) | | `coder_get_chat_messages` | `GetChatMessages` (user-facing parts, chronological, cursor pagination, queued prompts) | | `coder_send_chat_message` | `CreateChatMessage` (queue or interrupt busy behavior) | | `coder_interrupt_chat` | `InterruptChat` | | `coder_archive_chat` | `UpdateChat` with `archived: true` | | `coder_list_chat_model_configs` | `ListChatModelConfigs` (enabled configs with default flag) | Both MCP servers register tools from `toolsdk.All`, so no additional wiring is needed. Responses are trimmed to what an MCP caller needs (IDs as strings, user-facing transcripts) rather than full SDK payloads. No new endpoints and no database changes. Also adds MCP [prompts](https://modelcontextprotocol.io/specification/2026-07-28/server/prompts) for the chat workflows, defined once in `codersdk/toolsdk` and registered by both servers: | Prompt | Purpose | |---|---| | `coder_agents_delegate` | delegate a task to a Coder Agents chat and monitor it to completion | | `coder_agents_check` | check the status and recent activity of an existing chat | Each prompt declares the tools its workflow needs; the stdio server skips prompts whose tools are excluded by `--allowed-tools`. Tests run the tools against a chat-enabled coderdtest instance (fake OpenAI-compatible provider plus in-process AI bridge), covering the full lifecycle, an interrupt against a blocked turn, pagination cursors, permission-dependent model config filtering, and argument validation. Prompt coverage spans SDK rendering, the hosted `prompts/list`/`prompts/get` round trip, and the stdio server including allowlist gating. > Mux created this PR on Mike's behalf.
126 lines
4.1 KiB
Go
126 lines
4.1 KiB
Go
package toolsdk
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
const (
|
|
PromptNameAgentsDelegate = "coder_agents_delegate"
|
|
PromptNameAgentsCheck = "coder_agents_check"
|
|
)
|
|
|
|
// PromptArgument describes one argument accepted by a Prompt.
|
|
type PromptArgument struct {
|
|
Name string
|
|
Description string
|
|
Required bool
|
|
}
|
|
|
|
// Prompt defines an MCP prompt shared by the HTTP and CLI servers.
|
|
// See https://modelcontextprotocol.io/specification/2026-07-28/server/prompts.
|
|
type Prompt struct {
|
|
Name string
|
|
Description string
|
|
Arguments []PromptArgument
|
|
|
|
// RequiredTools lists the tools the rendered workflow cannot run
|
|
// without; optional suggestions are excluded. Servers with a
|
|
// restricted tool set should skip prompts whose required tools are
|
|
// unavailable.
|
|
RequiredTools []string
|
|
|
|
Render func(args map[string]string) (string, error)
|
|
}
|
|
|
|
// AllPrompts is the canonical list of MCP prompts exposed by Coder MCP
|
|
// servers.
|
|
var AllPrompts = []Prompt{AgentsDelegate, AgentsCheck}
|
|
|
|
var AgentsDelegate = Prompt{
|
|
Name: PromptNameAgentsDelegate,
|
|
Description: "Delegate a coding task to a Coder Agents chat and monitor it to completion.",
|
|
RequiredTools: []string{
|
|
ToolNameCreateChat,
|
|
ToolNameGetChat,
|
|
ToolNameGetChatMessages,
|
|
ToolNameSendChatMessage,
|
|
},
|
|
Arguments: []PromptArgument{
|
|
{
|
|
Name: "task",
|
|
Description: "The task the Coder Agent should perform, including all context it needs.",
|
|
Required: true,
|
|
},
|
|
{
|
|
Name: "model_config_id",
|
|
Description: "Optional model config UUID for the chat. When omitted, a model is picked from " + ToolNameListChatModelConfigs + ".",
|
|
},
|
|
},
|
|
Render: func(args map[string]string) (string, error) {
|
|
task, err := requiredPromptArg(args, "task")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
var createStep string
|
|
if modelConfigID := strings.TrimSpace(args["model_config_id"]); modelConfigID != "" {
|
|
createStep = fmt.Sprintf("1. Call %s with the task above as the prompt and model_config_id %q.", ToolNameCreateChat, modelConfigID)
|
|
} else {
|
|
createStep = fmt.Sprintf("1. Call %s with the task above as the prompt. To pick a specific model, call %s first and pass its ID as model_config_id.", ToolNameCreateChat, ToolNameListChatModelConfigs)
|
|
}
|
|
return fmt.Sprintf(`Delegate the following task to a Coder Agent and see it through to completion.
|
|
|
|
<task>
|
|
%s
|
|
</task>
|
|
|
|
Follow these steps:
|
|
%s
|
|
2. Share the returned chat URL with the user right away so they can follow along.
|
|
3. Poll %s until the chat stops running, waiting between polls.
|
|
4. Read the transcript with %s; page older history with before_id while has_more is true.
|
|
5. If the agent needs input or the result needs iteration, reply with %s and keep monitoring.
|
|
6. Report the outcome to the user, including the chat URL and a summary of what the agent did.
|
|
`, task, createStep, ToolNameGetChat, ToolNameGetChatMessages, ToolNameSendChatMessage), nil
|
|
},
|
|
}
|
|
|
|
var AgentsCheck = Prompt{
|
|
Name: PromptNameAgentsCheck,
|
|
Description: "Check the status and recent activity of an existing Coder Agents chat.",
|
|
RequiredTools: []string{
|
|
ToolNameGetChat,
|
|
ToolNameGetChatMessages,
|
|
},
|
|
Arguments: []PromptArgument{
|
|
{
|
|
Name: "chat_id",
|
|
Description: "UUID of the Coder Agents chat to check.",
|
|
Required: true,
|
|
},
|
|
},
|
|
Render: func(args map[string]string) (string, error) {
|
|
chatID, err := requiredPromptArg(args, "chat_id")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return fmt.Sprintf(`Check on the Coder Agents chat %q and report back.
|
|
|
|
Follow these steps:
|
|
1. Call %s with the chat_id to get its status, last turn summary, and any last error.
|
|
2. Call %s with the chat_id for recent transcript context, including queued_messages.
|
|
3. Summarize for the user: what the agent is doing or has done, whether it is blocked or waiting for input, and any errors. Include the chat URL.
|
|
`, chatID, ToolNameGetChat, ToolNameGetChatMessages), nil
|
|
},
|
|
}
|
|
|
|
func requiredPromptArg(args map[string]string, name string) (string, error) {
|
|
value := strings.TrimSpace(args[name])
|
|
if value == "" {
|
|
return "", xerrors.Errorf("missing required prompt argument: %s", name)
|
|
}
|
|
return value, nil
|
|
}
|