Compare commits

...

6 Commits

Author SHA1 Message Date
Jakub Domeracki
3bf6a00876 chore: revert CLI binary publishing for releases.coder.com (#19236) 2025-08-07 11:06:14 -05:00
Jakub Domeracki
9eb5fc695e chore: fix CLI binary publishing for releases.coder.com (#19230) 2025-08-07 10:41:48 -05:00
Spike Curtis
079328d874 fix: upgrade to 1.24.6 to fix race in lib/pq queries (#19214) (#19218)
THIS IS A SECURITY FIX - cherry picked from #19214 

upgrade to go 1.24.6 to avoid https://github.com/golang/go/issues/74831
(CVE-2025-47907)

Also points to a new version of our lib/pq fork that worked around the
Go issue, which should restore better performance.
2025-08-07 15:18:55 +04:00
Cian Johnston
e68ffe85b7 ci: bump xcode version to 16.1.0 (#19125) (#19221)
(cherry picked from commit 0d7cc5c156)

required for CI to pass with new runner version
2025-08-07 11:40:40 +01:00
Stephen Kirby
e6ec95757a Cherry-pick for release 2.25 (#19169)
Co-authored-by: Sas Swart <sas.swart.cdk@gmail.com>
Co-authored-by: Danielle Maywood <danielle@themaywoods.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com>
Co-authored-by: Hugo Dutka <hugo@coder.com>
Co-authored-by: Thomas Kosiewski <tk@coder.com>
Co-authored-by: Cian Johnston <cian@coder.com>
2025-08-05 11:50:51 -05:00
gcp-cherry-pick-bot[bot]
f1cf81c10b chore: add openai icon (cherry-pick #19118) (#19176)
Co-authored-by: ケイラ <mckayla@hey.com>
Co-authored-by: 35C4n0r <70096901+35C4n0r@users.noreply.github.com>
2025-08-05 12:17:53 +05:00
21 changed files with 566 additions and 35 deletions

View File

@@ -4,7 +4,7 @@ description: |
inputs:
version:
description: "The Go version to use."
default: "1.24.4"
default: "1.24.6"
use-preinstalled-go:
description: "Whether to use preinstalled Go."
default: "false"

View File

@@ -340,6 +340,11 @@ jobs:
- name: Disable Spotlight Indexing
if: runner.os == 'macOS'
run: |
enabled=$(sudo mdutil -a -s | grep "Indexing enabled" | wc -l)
if [ $enabled -eq 0 ]; then
echo "Spotlight indexing is already disabled"
exit 0
fi
sudo mdutil -a -i off
sudo mdutil -X /
sudo launchctl bootout system /System/Library/LaunchDaemons/com.apple.metadata.mds.plist
@@ -959,7 +964,7 @@ jobs:
- name: Switch XCode Version
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: "16.0.0"
xcode-version: "16.1.0"
- name: Setup Go
uses: ./.github/actions/setup-go

View File

@@ -60,7 +60,7 @@ jobs:
- name: Switch XCode Version
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: "16.0.0"
xcode-version: "16.1.0"
- name: Setup Go
uses: ./.github/actions/setup-go
@@ -655,7 +655,7 @@ jobs:
detached_signature="${binary}.asc"
gcloud storage cp "./site/out/bin/${binary}" "gs://releases.coder.com/coder-cli/${version}/${binary}"
gcloud storage cp "./site/out/bin/${detached_signature}" "gs://releases.coder.com/coder-cli/${version}/${detached_signature}"
done
done
- name: Publish release
run: |

View File

@@ -77,7 +77,8 @@ type API struct {
subAgentURL string
subAgentEnv []string
projectDiscovery bool // If we should perform project discovery or not.
projectDiscovery bool // If we should perform project discovery or not.
discoveryAutostart bool // If we should autostart discovered projects.
ownerName string
workspaceName string
@@ -144,7 +145,8 @@ func WithCommandEnv(ce CommandEnv) Option {
strings.HasPrefix(s, "CODER_AGENT_TOKEN=") ||
strings.HasPrefix(s, "CODER_AGENT_AUTH=") ||
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_ENABLE=") ||
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_PROJECT_DISCOVERY_ENABLE=")
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_PROJECT_DISCOVERY_ENABLE=") ||
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_DISCOVERY_AUTOSTART_ENABLE=")
})
return shell, dir, env, nil
}
@@ -287,6 +289,14 @@ func WithProjectDiscovery(projectDiscovery bool) Option {
}
}
// WithDiscoveryAutostart sets if the API should attempt to autostart
// projects that have been discovered
func WithDiscoveryAutostart(discoveryAutostart bool) Option {
return func(api *API) {
api.discoveryAutostart = discoveryAutostart
}
}
// ScriptLogger is an interface for sending devcontainer logs to the
// controlplane.
type ScriptLogger interface {
@@ -542,11 +552,13 @@ func (api *API) discoverDevcontainersInProject(projectPath string) error {
Container: nil,
}
config, err := api.dccli.ReadConfig(api.ctx, workspaceFolder, path, []string{})
if err != nil {
logger.Error(api.ctx, "read project configuration", slog.Error(err))
} else if config.Configuration.Customizations.Coder.AutoStart {
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStarting
if api.discoveryAutostart {
config, err := api.dccli.ReadConfig(api.ctx, workspaceFolder, path, []string{})
if err != nil {
logger.Error(api.ctx, "read project configuration", slog.Error(err))
} else if config.Configuration.Customizations.Coder.AutoStart {
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStarting
}
}
api.knownDevcontainers[workspaceFolder] = dc

View File

@@ -3792,6 +3792,7 @@ func TestDevcontainerDiscovery(t *testing.T) {
agentcontainers.WithContainerCLI(&fakeContainerCLI{}),
agentcontainers.WithDevcontainerCLI(mDCCLI),
agentcontainers.WithProjectDiscovery(true),
agentcontainers.WithDiscoveryAutostart(true),
)
api.Start()
defer api.Close()
@@ -3813,5 +3814,74 @@ func TestDevcontainerDiscovery(t *testing.T) {
// Then: We expect the mock infra to not fail.
})
}
t.Run("Disabled", func(t *testing.T) {
t.Parallel()
var (
ctx = testutil.Context(t, testutil.WaitShort)
logger = testutil.Logger(t)
mClock = quartz.NewMock(t)
mDCCLI = acmock.NewMockDevcontainerCLI(gomock.NewController(t))
fs = map[string]string{
"/home/coder/.git/HEAD": "",
"/home/coder/.devcontainer/devcontainer.json": "",
}
r = chi.NewRouter()
)
// We expect that neither `ReadConfig`, nor `Up` are called as we
// have explicitly disabled the agentcontainers API from attempting
// to autostart devcontainers that it discovers.
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
"/home/coder",
"/home/coder/.devcontainer/devcontainer.json",
[]string{},
).Return(agentcontainers.DevcontainerConfig{
Configuration: agentcontainers.DevcontainerConfiguration{
Customizations: agentcontainers.DevcontainerCustomizations{
Coder: agentcontainers.CoderCustomization{
AutoStart: true,
},
},
},
}, nil).Times(0)
mDCCLI.EXPECT().Up(gomock.Any(),
"/home/coder",
"/home/coder/.devcontainer/devcontainer.json",
gomock.Any(),
).Return("", nil).Times(0)
api := agentcontainers.NewAPI(logger,
agentcontainers.WithClock(mClock),
agentcontainers.WithWatcher(watcher.NewNoop()),
agentcontainers.WithFileSystem(initFS(t, fs)),
agentcontainers.WithManifestInfo("owner", "workspace", "parent-agent", "/home/coder"),
agentcontainers.WithContainerCLI(&fakeContainerCLI{}),
agentcontainers.WithDevcontainerCLI(mDCCLI),
agentcontainers.WithProjectDiscovery(true),
agentcontainers.WithDiscoveryAutostart(false),
)
api.Start()
defer api.Close()
r.Mount("/", api.Routes())
// When: All expected dev containers have been found.
require.Eventuallyf(t, func() bool {
req := httptest.NewRequest(http.MethodGet, "/", nil).WithContext(ctx)
rec := httptest.NewRecorder()
r.ServeHTTP(rec, req)
got := codersdk.WorkspaceAgentListContainersResponse{}
err := json.NewDecoder(rec.Body).Decode(&got)
require.NoError(t, err)
return len(got.Devcontainers) >= 1
}, testutil.WaitShort, testutil.IntervalFast, "dev containers never found")
// Then: We expect the mock infra to not fail.
})
})
}

View File

@@ -40,23 +40,24 @@ import (
func (r *RootCmd) workspaceAgent() *serpent.Command {
var (
auth string
logDir string
scriptDataDir string
pprofAddress string
noReap bool
sshMaxTimeout time.Duration
tailnetListenPort int64
prometheusAddress string
debugAddress string
slogHumanPath string
slogJSONPath string
slogStackdriverPath string
blockFileTransfer bool
agentHeaderCommand string
agentHeader []string
devcontainers bool
devcontainerProjectDiscovery bool
auth string
logDir string
scriptDataDir string
pprofAddress string
noReap bool
sshMaxTimeout time.Duration
tailnetListenPort int64
prometheusAddress string
debugAddress string
slogHumanPath string
slogJSONPath string
slogStackdriverPath string
blockFileTransfer bool
agentHeaderCommand string
agentHeader []string
devcontainers bool
devcontainerProjectDiscovery bool
devcontainerDiscoveryAutostart bool
)
cmd := &serpent.Command{
Use: "agent",
@@ -366,6 +367,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
DevcontainerAPIOptions: []agentcontainers.Option{
agentcontainers.WithSubAgentURL(r.agentURL.String()),
agentcontainers.WithProjectDiscovery(devcontainerProjectDiscovery),
agentcontainers.WithDiscoveryAutostart(devcontainerDiscoveryAutostart),
},
})
@@ -519,6 +521,13 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
Description: "Allow the agent to search the filesystem for devcontainer projects.",
Value: serpent.BoolOf(&devcontainerProjectDiscovery),
},
{
Flag: "devcontainers-discovery-autostart-enable",
Default: "false",
Env: "CODER_AGENT_DEVCONTAINERS_DISCOVERY_AUTOSTART_ENABLE",
Description: "Allow the agent to autostart devcontainer projects it discovers based on their configuration.",
Value: serpent.BoolOf(&devcontainerDiscoveryAutostart),
},
}
return cmd

View File

@@ -33,6 +33,10 @@ OPTIONS:
--debug-address string, $CODER_AGENT_DEBUG_ADDRESS (default: 127.0.0.1:2113)
The bind address to serve a debug HTTP server.
--devcontainers-discovery-autostart-enable bool, $CODER_AGENT_DEVCONTAINERS_DISCOVERY_AUTOSTART_ENABLE (default: false)
Allow the agent to autostart devcontainer projects it discovers based
on their configuration.
--devcontainers-enable bool, $CODER_AGENT_DEVCONTAINERS_ENABLE (default: true)
Allow the agent to automatically detect running devcontainers.

View File

@@ -26,6 +26,14 @@ func tagValidationError(diags hcl.Diagnostics) *DiagnosticError {
}
}
func presetValidationError(diags hcl.Diagnostics) *DiagnosticError {
return &DiagnosticError{
Message: "Unable to validate presets",
Diagnostics: diags,
KeyedDiagnostics: make(map[string]hcl.Diagnostics),
}
}
type DiagnosticError struct {
// Message is the human-readable message that will be returned to the user.
Message string

View File

@@ -0,0 +1,28 @@
package dynamicparameters
import (
"github.com/hashicorp/hcl/v2"
"github.com/coder/preview"
)
// CheckPresets extracts the preset related diagnostics from a template version preset
func CheckPresets(output *preview.Output, diags hcl.Diagnostics) *DiagnosticError {
de := presetValidationError(diags)
if output == nil {
return de
}
presets := output.Presets
for _, preset := range presets {
if hcl.Diagnostics(preset.Diagnostics).HasErrors() {
de.Extend(preset.Name, hcl.Diagnostics(preset.Diagnostics))
}
}
if de.HasError() {
return de
}
return nil
}

View File

@@ -11,6 +11,10 @@ import (
func CheckTags(output *preview.Output, diags hcl.Diagnostics) *DiagnosticError {
de := tagValidationError(diags)
if output == nil {
return de
}
failedTags := output.WorkspaceTags.UnusableTags()
if len(failedTags) == 0 && !de.HasError() {
return nil // No errors, all is good!

View File

@@ -1822,6 +1822,14 @@ func (api *API) dynamicTemplateVersionTags(ctx context.Context, rw http.Response
return nil, false
}
// Fails early if presets are invalid to prevent downstream workspace creation errors
presetErr := dynamicparameters.CheckPresets(output, nil)
if presetErr != nil {
code, resp := presetErr.Response()
httpapi.Write(ctx, rw, code, resp)
return nil, false
}
return output.WorkspaceTags.Tags(), true
}

View File

@@ -620,6 +620,119 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) {
})
}
})
t.Run("Presets", func(t *testing.T) {
t.Parallel()
store, ps := dbtestutil.NewDB(t)
client := coderdtest.New(t, &coderdtest.Options{
Database: store,
Pubsub: ps,
})
owner := coderdtest.CreateFirstUser(t, client)
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin())
for _, tt := range []struct {
name string
files map[string]string
expectError string
}{
{
name: "valid preset",
files: map[string]string{
`main.tf`: `
terraform {
required_providers {
coder = {
source = "coder/coder"
version = "2.8.0"
}
}
}
data "coder_parameter" "valid_parameter" {
name = "valid_parameter_name"
default = "valid_option_value"
option {
name = "valid_option_name"
value = "valid_option_value"
}
}
data "coder_workspace_preset" "valid_preset" {
name = "valid_preset"
parameters = {
"valid_parameter_name" = "valid_option_value"
}
}
`,
},
},
{
name: "invalid preset",
files: map[string]string{
`main.tf`: `
terraform {
required_providers {
coder = {
source = "coder/coder"
version = "2.8.0"
}
}
}
data "coder_parameter" "valid_parameter" {
name = "valid_parameter_name"
default = "valid_option_value"
option {
name = "valid_option_name"
value = "valid_option_value"
}
}
data "coder_workspace_preset" "invalid_parameter_name" {
name = "invalid_parameter_name"
parameters = {
"invalid_parameter_name" = "irrelevant_value"
}
}
`,
},
expectError: "Undefined Parameter",
},
} {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
// Create an archive from the files provided in the test case.
tarFile := testutil.CreateTar(t, tt.files)
// Post the archive file
fi, err := templateAdmin.Upload(ctx, "application/x-tar", bytes.NewReader(tarFile))
require.NoError(t, err)
// Create a template version from the archive
tvName := testutil.GetRandomNameHyphenated(t)
tv, err := templateAdmin.CreateTemplateVersion(ctx, owner.OrganizationID, codersdk.CreateTemplateVersionRequest{
Name: tvName,
StorageMethod: codersdk.ProvisionerStorageMethodFile,
Provisioner: codersdk.ProvisionerTypeTerraform,
FileID: fi.ID,
})
if tt.expectError == "" {
require.NoError(t, err)
// Assert the expected provisioner job is created from the template version import
pj, err := store.GetProvisionerJobByID(ctx, tv.Job.ID)
require.NoError(t, err)
require.NotNil(t, pj)
// Also assert that we get the expected information back from the API endpoint
require.Zero(t, tv.MatchedProvisioners.Count)
require.Zero(t, tv.MatchedProvisioners.Available)
require.Zero(t, tv.MatchedProvisioners.MostRecentlySeen.Time)
} else {
require.ErrorContains(t, err, tt.expectError)
require.Equal(t, tv.Job.ID, uuid.Nil)
}
})
}
})
}
func TestPatchCancelTemplateVersion(t *testing.T) {

View File

@@ -0,0 +1,236 @@
# OAuth2 Provider (Experimental)
> [!WARNING]
> The OAuth2 provider functionality is currently **experimental and unstable**. This feature:
>
> - Is subject to breaking changes without notice
> - May have incomplete functionality
> - Is not recommended for production use
> - Requires the `oauth2` experiment flag to be enabled
>
> Use this feature for development and testing purposes only.
Coder can act as an OAuth2 authorization server, allowing third-party applications to authenticate users through Coder and access the Coder API on their behalf. This enables integrations where external applications can leverage Coder's authentication and user management.
## Requirements
- Admin privileges in Coder
- OAuth2 experiment flag enabled
- HTTPS recommended for production deployments
## Enable OAuth2 Provider
Add the `oauth2` experiment flag to your Coder server:
```bash
coder server --experiments oauth2
```
Or set the environment variable:
```env
CODER_EXPERIMENTS=oauth2
```
## Creating OAuth2 Applications
### Method 1: Web UI
1. Navigate to **Deployment Settings****OAuth2 Applications**
2. Click **Create Application**
3. Fill in the application details:
- **Name**: Your application name
- **Callback URL**: `https://yourapp.example.com/callback`
- **Icon**: Optional icon URL
### Method 2: Management API
Create an application using the Coder API:
```bash
curl -X POST \
-H "Authorization: Bearer $CODER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My Application",
"callback_url": "https://myapp.example.com/callback",
"icon": "https://myapp.example.com/icon.png"
}' \
"$CODER_URL/api/v2/oauth2-provider/apps"
```
Generate a client secret:
```bash
curl -X POST \
-H "Authorization: Bearer $CODER_SESSION_TOKEN" \
"$CODER_URL/api/v2/oauth2-provider/apps/$APP_ID/secrets"
```
## Integration Patterns
### Standard OAuth2 Flow
1. **Authorization Request**: Redirect users to Coder's authorization endpoint:
```url
https://coder.example.com/oauth2/authorize?
client_id=your-client-id&
response_type=code&
redirect_uri=https://yourapp.example.com/callback&
state=random-string
```
2. **Token Exchange**: Exchange the authorization code for an access token:
```bash
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=$AUTH_CODE" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "redirect_uri=https://yourapp.example.com/callback" \
"$CODER_URL/oauth2/tokens"
```
3. **API Access**: Use the access token to call Coder's API:
```bash
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
"$CODER_URL/api/v2/users/me"
```
### PKCE Flow (Public Clients)
For mobile apps and single-page applications, use PKCE for enhanced security:
1. Generate a code verifier and challenge:
```bash
CODE_VERIFIER=$(openssl rand -base64 96 | tr -d "=+/" | cut -c1-128)
CODE_CHALLENGE=$(echo -n $CODE_VERIFIER | openssl dgst -sha256 -binary | base64 | tr -d "=+/" | cut -c1-43)
```
2. Include PKCE parameters in the authorization request:
```url
https://coder.example.com/oauth2/authorize?
client_id=your-client-id&
response_type=code&
code_challenge=$CODE_CHALLENGE&
code_challenge_method=S256&
redirect_uri=https://yourapp.example.com/callback
```
3. Include the code verifier in the token exchange:
```bash
curl -X POST \
-d "grant_type=authorization_code" \
-d "code=$AUTH_CODE" \
-d "client_id=$CLIENT_ID" \
-d "code_verifier=$CODE_VERIFIER" \
"$CODER_URL/oauth2/tokens"
```
## Discovery Endpoints
Coder provides OAuth2 discovery endpoints for programmatic integration:
- **Authorization Server Metadata**: `GET /.well-known/oauth-authorization-server`
- **Protected Resource Metadata**: `GET /.well-known/oauth-protected-resource`
These endpoints return server capabilities and endpoint URLs according to [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414) and [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728).
## Token Management
### Refresh Tokens
Refresh an expired access token:
```bash
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=$REFRESH_TOKEN" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
"$CODER_URL/oauth2/tokens"
```
### Revoke Access
Revoke all tokens for an application:
```bash
curl -X DELETE \
-H "Authorization: Bearer $CODER_SESSION_TOKEN" \
"$CODER_URL/oauth2/tokens?client_id=$CLIENT_ID"
```
## Testing and Development
Coder provides comprehensive test scripts for OAuth2 development:
```bash
# Navigate to the OAuth2 test scripts
cd scripts/oauth2/
# Run the full automated test suite
./test-mcp-oauth2.sh
# Create a test application for manual testing
eval $(./setup-test-app.sh)
# Run an interactive browser-based test
./test-manual-flow.sh
# Clean up when done
./cleanup-test-app.sh
```
For more details on testing, see the [OAuth2 test scripts README](../../../scripts/oauth2/README.md).
## Common Issues
### "OAuth2 experiment not enabled"
Add `oauth2` to your experiment flags: `coder server --experiments oauth2`
### "Invalid redirect_uri"
Ensure the redirect URI in your request exactly matches the one registered for your application.
### "PKCE verification failed"
Verify that the `code_verifier` used in the token request matches the one used to generate the `code_challenge`.
## Security Considerations
- **Use HTTPS**: Always use HTTPS in production to protect tokens in transit
- **Implement PKCE**: Use PKCE for all public clients (mobile apps, SPAs)
- **Validate redirect URLs**: Only register trusted redirect URIs for your applications
- **Rotate secrets**: Periodically rotate client secrets using the management API
## Limitations
As an experimental feature, the current implementation has limitations:
- No scope system - all tokens have full API access
- No client credentials grant support
- Limited to opaque access tokens (no JWT support)
## Standards Compliance
This implementation follows established OAuth2 standards including [RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749) (OAuth2 core), [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636) (PKCE), and related specifications for discovery and client registration.
## Next Steps
- Review the [API Reference](../../reference/api/index.md) for complete endpoint documentation
- Check [External Authentication](../external-auth/index.md) for configuring Coder as an OAuth2 client
- See [Security Best Practices](../security/index.md) for deployment security guidance
## Feedback
This is an experimental feature under active development. Please report issues and feedback through [GitHub Issues](https://github.com/coder/coder/issues) with the `oauth2` label.

View File

@@ -1,6 +1,6 @@
# MCP Server
Power users can configure Claude Desktop, Cursor, or other external agents to interact with Coder in order to:
Power users can configure [claude.ai](https://claude.ai), Claude Desktop, Cursor, or other external agents to interact with Coder in order to:
- List workspaces
- Create/start/stop workspaces
@@ -12,6 +12,8 @@ Power users can configure Claude Desktop, Cursor, or other external agents to in
In this model, any custom agent could interact with a remote Coder workspace, or Coder can be used in a remote pipeline or a larger workflow.
## Local MCP server
The Coder CLI has options to automatically configure MCP servers for you. On your local machine, run the following command:
```sh
@@ -30,4 +32,27 @@ coder exp mcp server
```
> [!NOTE]
> The MCP server is authenticated with the same identity as your Coder CLI and can perform any action on the user's behalf. Fine-grained permissions and a remote MCP server are in development. [Contact us](https://coder.com/contact) if this use case is important to you.
> The MCP server is authenticated with the same identity as your Coder CLI and can perform any action on the user's behalf. Fine-grained permissions are in development. [Contact us](https://coder.com/contact) if this use case is important to you.
## Remote MCP server
Coder can expose an MCP server via HTTP. This is useful for connecting web-based agents, like https://claude.ai/, to Coder. This is an experimental feature and is subject to change.
To enable this feature, activate the `oauth2` and `mcp-server-http` experiments using an environment variable or a CLI flag:
```sh
CODER_EXPERIMENTS="oauth2,mcp-server-http" coder server
# or
coder server --experiments=oauth2,mcp-server-http
```
The Coder server will expose the MCP server at:
```txt
https://coder.example.com/api/experimental/mcp/http
```
> [!NOTE]
> At this time, the remote MCP server is not compatible with web-based ChatGPT.
Users can authenticate applications to use the remote MCP server with [OAuth2](../admin/integrations/oauth2-provider.md). An authenticated application can perform any action on the user's behalf. Fine-grained permissions are in development.

View File

@@ -718,6 +718,11 @@
"title": "Hashicorp Vault",
"description": "Integrate Coder with Hashicorp Vault",
"path": "./admin/integrations/vault.md"
},
{
"title": "OAuth2 Provider",
"description": "Use Coder as an OAuth2 provider",
"path": "./admin/integrations/oauth2-provider.md"
}
]
},

View File

@@ -11,7 +11,7 @@ RUN cargo install jj-cli typos-cli watchexec-cli
FROM ubuntu:jammy@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 AS go
# Install Go manually, so that we can control the version
ARG GO_VERSION=1.24.4
ARG GO_VERSION=1.24.6
# Boring Go is needed to build FIPS-compliant binaries.
RUN apt-get update && \

4
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/coder/coder/v2
go 1.24.4
go 1.24.6
// Required until a v3 of chroma is created to lazily initialize all XML files.
// None of our dependencies seem to use the registries anyways, so this
@@ -58,7 +58,7 @@ replace github.com/imulab/go-scim/pkg/v2 => github.com/coder/go-scim/pkg/v2 v2.0
// Adds support for a new Listener from a driver.Connector
// This lets us use rotating authentication tokens for passwords in connection strings
// which we use in the awsiamrds package.
replace github.com/lib/pq => github.com/coder/pq v1.10.5-0.20250630052411-a259f96b6102
replace github.com/lib/pq => github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151
// Removes an init() function that causes terminal sequences to be printed to the web terminal when
// used in conjunction with agent-exec. See https://github.com/coder/coder/pull/15817

4
go.sum
View File

@@ -912,8 +912,8 @@ github.com/coder/go-scim/pkg/v2 v2.0.0-20230221055123-1d63c1222136 h1:0RgB61LcNs
github.com/coder/go-scim/pkg/v2 v2.0.0-20230221055123-1d63c1222136/go.mod h1:VkD1P761nykiq75dz+4iFqIQIZka189tx1BQLOp0Skc=
github.com/coder/guts v1.5.0 h1:a94apf7xMf5jDdg1bIHzncbRiTn3+BvBZgrFSDbUnyI=
github.com/coder/guts v1.5.0/go.mod h1:0Sbv5Kp83u1Nl7MIQiV2zmacJ3o02I341bkWkjWXSUQ=
github.com/coder/pq v1.10.5-0.20250630052411-a259f96b6102 h1:ahTJlTRmTogsubgRVGOUj40dg62WvqPQkzTQP7pyepI=
github.com/coder/pq v1.10.5-0.20250630052411-a259f96b6102/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151 h1:YAxwg3lraGNRwoQ18H7R7n+wsCqNve7Brdvj0F1rDnU=
github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs=
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc=
github.com/coder/preview v1.0.3-0.20250714153828-a737d4750448 h1:S86sFp4Dr4dUn++fXOMOTu6ClnEZ/NrGCYv7bxZjYYc=

View File

@@ -156,6 +156,7 @@ export const defaultParametersForBuiltinIcons = new Map<string, string>([
["/icon/kasmvnc.svg", "whiteWithColor"],
["/icon/kiro.svg", "whiteWithColor"],
["/icon/memory.svg", "monochrome"],
["/icon/openai.svg", "monochrome"],
["/icon/rust.svg", "monochrome"],
["/icon/terminal.svg", "monochrome"],
["/icon/widgets.svg", "monochrome"],

View File

@@ -85,6 +85,7 @@
"nomad.svg",
"novnc.svg",
"okta.svg",
"openai.svg",
"personalize.svg",
"php.svg",
"phpstorm.svg",

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg"><title>OpenAI icon</title><path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB