From e5f82b0da854a9d0773ac1aaa32fb3045516e89a Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Thu, 27 Aug 2026 11:36:09 +0000 Subject: [PATCH] feat(coderd_agents_mcp_server)!: import by organization name and slug --- docs/resources/agents_mcp_server.md | 10 +++---- .../coderd_agents_mcp_server/import.sh | 6 ++--- .../provider/agents_mcp_server_resource.go | 27 ++++++++++++++----- .../agents_mcp_server_resource_test.go | 2 +- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/docs/resources/agents_mcp_server.md b/docs/resources/agents_mcp_server.md index e2ec4e8..3ee6167 100644 --- a/docs/resources/agents_mcp_server.md +++ b/docs/resources/agents_mcp_server.md @@ -7,7 +7,7 @@ description: |- ~> Warning This resource is only compatible with Coder version 2.37.0 https://github.com/coder/coder/releases/tag/v2.37.0 and later. -> _wo attributes are write-only https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments: their values are sent to Coder but never stored in Terraform state. This resource therefore requires Terraform 1.11 or later. - Configures an organization-scoped MCP server for Coder Agents. Import IDs use /. Changing url, auth_type, oauth2_token_url, oauth2_revocation_url, or oauth2_client_id invalidates users' stored OAuth tokens. + Configures an organization-scoped MCP server for Coder Agents. Import IDs use /. Changing url, auth_type, oauth2_token_url, oauth2_revocation_url, or oauth2_client_id invalidates users' stored OAuth tokens. Coder runs OAuth2 discovery and dynamic client registration only when a server is created with auth_type = "oauth2" and no manual endpoints; updates never re-run discovery. To switch an existing server from manual OAuth2 configuration back to discovery, replace the resource (for example with terraform apply -replace). Removing the manual OAuth2 attributes from configuration leaves the stored values unmanaged rather than clearing them. --- @@ -20,7 +20,7 @@ This resource is only compatible with Coder version [2.37.0](https://github.com/ -> `_wo` attributes are [write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments): their values are sent to Coder but never stored in Terraform state. This resource therefore requires Terraform 1.11 or later. -Configures an organization-scoped MCP server for Coder Agents. Import IDs use `/`. Changing `url`, `auth_type`, `oauth2_token_url`, `oauth2_revocation_url`, or `oauth2_client_id` invalidates users' stored OAuth tokens. +Configures an organization-scoped MCP server for Coder Agents. Import IDs use `/`. Changing `url`, `auth_type`, `oauth2_token_url`, `oauth2_revocation_url`, or `oauth2_client_id` invalidates users' stored OAuth tokens. Coder runs OAuth2 discovery and dynamic client registration only when a server is created with `auth_type = "oauth2"` and no manual endpoints; updates never re-run discovery. To switch an existing server from manual OAuth2 configuration back to discovery, replace the resource (for example with `terraform apply -replace`). Removing the manual OAuth2 attributes from configuration leaves the stored values unmanaged rather than clearing them. @@ -100,14 +100,14 @@ Import is supported using the following syntax: The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example: ```shell -# The ID must contain the organization UUID and MCP server configuration UUID. -$ terraform import coderd_agents_mcp_server.example / +# The ID must contain the organization name and the MCP server slug. +$ terraform import coderd_agents_mcp_server.example / ``` Alternatively, in Terraform v1.5.0 and later, an [`import` block](https://developer.hashicorp.com/terraform/language/import) can be used: ```terraform import { to = coderd_agents_mcp_server.example - id = "/" + id = "/" } ``` diff --git a/examples/resources/coderd_agents_mcp_server/import.sh b/examples/resources/coderd_agents_mcp_server/import.sh index f276ab9..f8db1e6 100644 --- a/examples/resources/coderd_agents_mcp_server/import.sh +++ b/examples/resources/coderd_agents_mcp_server/import.sh @@ -1,10 +1,10 @@ -# The ID must contain the organization UUID and MCP server configuration UUID. -$ terraform import coderd_agents_mcp_server.example / +# The ID must contain the organization name and the MCP server slug. +$ terraform import coderd_agents_mcp_server.example / ``` Alternatively, in Terraform v1.5.0 and later, an [`import` block](https://developer.hashicorp.com/terraform/language/import) can be used: ```terraform import { to = coderd_agents_mcp_server.example - id = "/" + id = "/" } diff --git a/internal/provider/agents_mcp_server_resource.go b/internal/provider/agents_mcp_server_resource.go index 40025cb..0bce152 100644 --- a/internal/provider/agents_mcp_server_resource.go +++ b/internal/provider/agents_mcp_server_resource.go @@ -183,7 +183,7 @@ func (r *AgentsMCPServerResource) Schema(ctx context.Context, req resource.Schem MarkdownDescription: "~> This resource is experimental. Changes are expected, and it is not recommended for production use.\n\n" + "~> **Warning**\nThis resource is only compatible with Coder version [" + agentsMCPServerMinVersion + "](https://github.com/coder/coder/releases/tag/v" + agentsMCPServerMinVersion + ") and later.\n\n" + "-> `_wo` attributes are [write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments): their values are sent to Coder but never stored in Terraform state. This resource therefore requires Terraform 1.11 or later.\n\n" + - "Configures an organization-scoped MCP server for Coder Agents. Import IDs use `/`. Changing `url`, `auth_type`, `oauth2_token_url`, `oauth2_revocation_url`, or `oauth2_client_id` invalidates users' stored OAuth tokens.\n\n" + + "Configures an organization-scoped MCP server for Coder Agents. Import IDs use `/`. Changing `url`, `auth_type`, `oauth2_token_url`, `oauth2_revocation_url`, or `oauth2_client_id` invalidates users' stored OAuth tokens.\n\n" + "Coder runs OAuth2 discovery and dynamic client registration only when a server is created with `auth_type = \"oauth2\"` and no manual endpoints; updates never re-run discovery. To switch an existing server from manual OAuth2 configuration back to discovery, replace the resource (for example with `terraform apply -replace`). Removing the manual OAuth2 attributes from configuration leaves the stored values unmanaged rather than clearing them.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -602,20 +602,33 @@ func (r *AgentsMCPServerResource) Delete(ctx context.Context, req resource.Delet func (r *AgentsMCPServerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { parts := strings.Split(req.ID, "/") if len(parts) != 2 { - resp.Diagnostics.AddError("Invalid Import ID", "Expected `/`.") + resp.Diagnostics.AddError("Invalid Import ID", "Expected `/`.") return } - organizationID, err := uuid.Parse(parts[0]) + org, err := r.data.Client.OrganizationByName(ctx, parts[0]) if err != nil { - resp.Diagnostics.AddError("Invalid Import ID", fmt.Sprintf("Unable to parse organization ID as UUID: %s", err)) + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to get organization %q: %s", parts[0], err)) return } - id, err := uuid.Parse(parts[1]) + // Slugs are unique per organization, but the get-by-ID endpoint only + // accepts UUIDs, so resolve the slug from the organization's list. + configs, err := r.data.Client.MCPServerConfigs(ctx, org.ID) if err != nil { - resp.Diagnostics.AddError("Invalid Import ID", fmt.Sprintf("Unable to parse MCP server ID as UUID: %s", err)) + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to list MCP servers for organization %q: %s", parts[0], err)) return } - resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("organization_id"), organizationID.String())...) + var id uuid.UUID + for _, config := range configs { + if config.Slug == parts[1] { + id = config.ID + break + } + } + if id == uuid.Nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("No MCP server with slug %q exists in organization %q.", parts[1], parts[0])) + return + } + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("organization_id"), org.ID.String())...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), id.String())...) } diff --git a/internal/provider/agents_mcp_server_resource_test.go b/internal/provider/agents_mcp_server_resource_test.go index dcb3b3b..01df0e2 100644 --- a/internal/provider/agents_mcp_server_resource_test.go +++ b/internal/provider/agents_mcp_server_resource_test.go @@ -700,7 +700,7 @@ resource "terraform_data" "nullendpoint" { if !ok { return "", fmt.Errorf("coderd_agents_mcp_server.test not found in state") } - return rs.Primary.Attributes["organization_id"] + "/" + rs.Primary.ID, nil + return organizations[0].Name + "/" + rs.Primary.Attributes["slug"], nil }, ImportStateVerifyIgnore: []string{ "oauth2_client_secret_wo",