Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
chore: comment cleanup
  • Loading branch information
ibetitsmike committed Aug 13, 2026
commit aa9e33e08ec9de23e45be6e5a1e7ff925d337169
2 changes: 0 additions & 2 deletions cli/exp_mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -755,8 +755,6 @@ func (s *mcpServer) startServer(ctx context.Context, inv *serpent.Invocation, in
coderdmcp.RegisterSDKTool(mcpSrv, tool, toolDeps)
}

// The prompts guide the use of the chat tools, so register them only
// when those tools are available.
if s.client != nil && (len(allowedTools) == 0 || slices.Contains(allowedTools, toolsdk.ToolNameCreateChat)) {
for _, prompt := range toolsdk.AllPrompts {
coderdmcp.RegisterSDKPrompt(mcpSrv, prompt)
Expand Down
1 change: 0 additions & 1 deletion coderd/mcp/mcp_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ func TestMCPHTTP_E2E_ClientIntegration(t *testing.T) {
// Check for some basic tools that should be available
assert.Contains(t, foundTools, toolsdk.ToolNameGetAuthenticatedUser, "Should have authenticated user tool")

// The standard toolset should also expose the prompt templates.
prompts, err := mcpClient.ListPrompts(ctx, nil)
require.NoError(t, err)
var foundPrompts []string
Expand Down
27 changes: 9 additions & 18 deletions codersdk/toolsdk/chats.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ The chat runs asynchronously. Poll coder_get_chat for status and read the transc
if err != nil {
return ChatToolStatus{}, err
}
// A user's only membership can be removed by an admin, so an
// empty slice is reachable and indexing it would panic.
// Admins can remove a user's only organization membership.
if len(me.OrganizationIDs) == 0 {
return ChatToolStatus{}, xerrors.New("authenticated user belongs to no organization; pass organization_id explicitly")
}
Expand Down Expand Up @@ -212,14 +211,11 @@ type GetChatMessagesResponse struct {
// true. It is derived from the unfiltered API page, so it stays valid
// even when every message in this page was filtered out as non-text.
NextBeforeID int64 `json:"next_before_id,omitempty"`
// QueuedMessages holds prompts waiting for the current run to finish.
// The API returns them only on the initial page (no cursor).
// QueuedMessages is populated only on the initial page.
QueuedMessages []string `json:"queued_messages,omitempty"`
}

// userFacingText concatenates the user-facing text parts of a message.
// Hook notices are user-facing per the SDK part contract, unlike hook
// context, which is model-only.
// Hook notices are user-facing per the SDK contract; hook context is model-only.
func userFacingText(parts []codersdk.ChatMessagePart) string {
var texts []string
for _, part := range parts {
Expand Down Expand Up @@ -487,9 +483,8 @@ Per-user provider credentials are validated when creating a chat, so coder_creat
if err != nil {
return ListChatModelConfigsResponse{}, xerrors.Errorf("list chat model configs: %w", err)
}
// Admin model lists include configs backed by disabled providers.
// Filter them with provider state; non-admin lists are already
// filtered server-side.
// Admin model lists include disabled providers; non-admin lists are
// already filtered server-side.
var providerEnabled map[uuid.UUID]bool
providers, err := deps.coderClient.AIProviders(ctx)
switch {
Expand All @@ -499,11 +494,8 @@ Per-user provider credentials are validated when creating a chat, so coder_creat
providerEnabled[provider.ID] = provider.Enabled
}
case isForbiddenError(err):
// Deployment-config readers without AI provider read access
// (such as auditors) receive the unverifiable admin list, so
// fail closed instead of leaking provider-disabled configs.
// Only a confirmed 403 selects the member path, whose list
// the server already filtered.
// Deployment-config readers can receive the unfiltered admin list
// without provider access, so fail closed unless both requests return 403.
_, dcErr := deps.coderClient.DeploymentConfig(ctx)
switch {
case dcErr == nil:
Expand All @@ -519,9 +511,8 @@ Per-user provider credentials are validated when creating a chat, so coder_creat
if !config.Enabled {
continue
}
Comment thread
ibetitsmike marked this conversation as resolved.
// A non-nil map is the authoritative provider set: absent IDs
// are soft-deleted providers whose configs stay listed by the
// admin endpoint, so exclude them along with disabled ones.
// A non-nil map is authoritative because soft-deleted providers are
// absent while their configs remain in the admin response.
if providerEnabled != nil && !providerEnabled[config.AIProviderID] {
continue
}
Expand Down
8 changes: 0 additions & 8 deletions codersdk/toolsdk/chats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"github.com/coder/coder/v2/testutil"
)

// failPathTransport fails requests to one path and passes the rest through.
type failPathTransport struct {
path string
}
Expand Down Expand Up @@ -226,8 +225,6 @@ func TestChatTools(t *testing.T) {
require.NoError(t, err)
require.Equal(t, codersdk.ChatStatusRunning, created.Status)

// While the run is blocked, a queued send must surface in the
// transcript's queued_messages rather than disappearing.
sent, err := testTool(t, toolsdk.SendChatMessage, tb, toolsdk.SendChatMessageArgs{
ChatID: created.ID,
Text: "Queued while busy.",
Expand All @@ -247,9 +244,6 @@ func TestChatTools(t *testing.T) {
})

t.Run("ListChatModelConfigsMemberAndAuditor", func(t *testing.T) {
// A member cannot read providers but gets the server-filtered
// list; an auditor gets the unverifiable admin list and must
// fail closed rather than leak provider-disabled configs.
memberClient, _ := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID)
memberDeps, err := toolsdk.NewDeps(memberClient)
require.NoError(t, err)
Expand All @@ -267,8 +261,6 @@ func TestChatTools(t *testing.T) {
_, err = testTool(t, toolsdk.ListChatModelConfigs, auditorDeps, toolsdk.NoArgs{})
require.ErrorContains(t, err, "missing AI provider read permission")

// A failing deployment-config probe must propagate, not fall
// through to the fail-open member path.
brokenProbeClient := codersdk.New(auditorClient.URL)
brokenProbeClient.SetSessionToken(auditorClient.SessionToken())
brokenProbeClient.HTTPClient = &http.Client{
Expand Down
5 changes: 1 addition & 4 deletions codersdk/toolsdk/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,13 @@ type PromptArgument struct {
Required bool
}

// Prompt is a user-selectable MCP prompt template, defined here so the
// coderd-hosted and CLI MCP servers can adapt one shared definition into
// their protocol types, mirroring how tools flow through All.
// 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

// Render returns the user-message text for a prompts/get request.
Render func(args map[string]string) (string, error)
}

Expand Down