Repository navigation
🤖 feat: activate the CoderMCPServer controller - #381
Conversation
A deleted CoderMCPServer now deletes the config that it created and then releases its finalizer (plan 4.10): - With a config ID, one DELETE by ID. Coder's own 404 also releases. Any other answer keeps the finalizer and retries every 60 s. - With only a create marker, the slug read decides: only the nonce proves that the config is this object's. A missing config waits until a late create cannot commit any more (2 minutes), and needs a site owner. - A deleted control plane releases the finalizer without a Coder request. A control plane that is being deleted gets one DELETE attempt bounded at 10 s, then the finalizer goes. - The scope gate never blocks the delete. - The released object's credential hashes leave the reconciler's map. Refs #155 Change-Id: Iaa3d096523bb12408656377958c20563409982f3 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Refs #155 Change-Id: Ia6b1789a575744cc04e056fd4cfbd20e4ff6b057 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I44afe5ff6138f70fcb974f57384b23b445dc6f66 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ie5d1d6db06850b4571b0cfeb697b94e203e46cca Signed-off-by: Thomas Kosiewski <tk@coder.com>
Refs #155 Change-Id: I87e3d28b11cffb1a553df0ed73072ac3bf8b59a7 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Refs #155 Change-Id: If4f948b82f1d598d4b49b3f861379b6c87001937 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I9d69335cc1c02fe315c28a16a5516dd296fe2c78 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I84ac2f3c9478f30d0b95ca40e5f0d4f0f58788d4 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Refs #155 Change-Id: I4559e944c3ea6cf0e0d1506234ae964bd089cac8 Signed-off-by: Thomas Kosiewski <tk@coder.com>
…w-to ships Change-Id: Ieaabbcdd0d9d86fee7c40ae1c75a1d3be5c7c39c Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I683bf385bb5c085554d0142e79bdb010439542af Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ic5ecff3c40758ebbfa45e8ef225240737741e6f4 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I5b4346439f1f02c38cda613a32c45b37781acd5f Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ib12d8baad8b551aa8ce2561db582b6f794d29673 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I5f46e212918e0211f4d7b7ce91c60e4787f94485 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I0aa9d5a3fbd27961db36dfb3c74f2353df242ef0 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I687b13b09e120b2382289b186e58a21944b7f7ff Signed-off-by: Thomas Kosiewski <tk@coder.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
Security findingsAdvisory findings (1)ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
@codex security review |
Dogfood evidence (plan section 8)Images built from Dashboard: the managed server (step 5)Chat with the server selected (step 6)Kill switch: disabled in the dashboard (step 9)After the delete (step 13)Recording of the whole runRendered from the asciinema cast with idle gaps capped at 3 s (4 min 48 s, the run took about 14 min). run-small.mp4Step results
Step 12 and P8 (tool lists) are M11 only and were skipped. |
There was a problem hiding this comment.
🛡️ Codex Security Review
Here are some automated security review suggestions for this pull request.
Reviewed commit: 9930bc719b
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
|
@codex review |
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |








Summary
The
CoderMCPServercontroller now runs. ACoderMCPServercreates and manages one MCP server config in a Coder organization, which Coder chats offer to the model. Before this PR the kind was dormant: its controller was not registered and its CRD was not installed.This is plan step M9 of #155. Steps M1 to M8 landed in earlier PRs, the last ones as #368 (delete path), #371 (E2E stub), #376 (test S9) and #377 (the opt-in
/statusadmission policy).What changes
internal/app/controllerapp/controllerapp.goregistersCoderMCPServerReconciler.config/crd/dormant/toconfig/crd/bases/, intoconfig/default/kustomization.yamlanddist/install.yaml.hack/update-manifests.shdrops it fromDORMANT_CRDS, and the CI dormancy guard step for the kind goes.get,list,watch,patchoncodermcpservers,getandupdateoncodermcpservers/status,updateoncodermcpservers/finalizers. Thesecretsrule does not change (test S9, 🤖 test: pin the Secret RBAC rules and the standalone API server RBAC #376, passes).config/samples/coder_v1alpha1_codermcpserver.yaml.docs/how-to/manage-mcp-servers.md, plus the nav, the index, the architecture page, the controller deploy page and the getting-started cleanup.docs/reference/api/codermcpserver.md. The "no effect yet" sentences leave thespec.mcpServersfield docs ofCoderControlPlaneand theCoderMCPServertypes.internal/controller/codermcpserver*.What the how-to documents
spec.mcpServers.organizations,allowForceOn): organization names resolve to IDs, and a withdrawn opt-in disables each affected server with oneenabled: falseupdate and no credential read. The object then reportsDisabledExternallyuntil a spec edit.spec.accesslists groups only, so every sync removes user grants added in the dashboard. Withaccessomitted, Coder's default lets the organization's Everyone group read the server.enabled: falseif the generation disables the server, and add the groups once they exist.ResourceQuotanote.config/admission/: opt-in (the install bundle does not apply it), needsValidatingAdmissionPolicy(GA in Kubernetes 1.30), and hard-codes the ServiceAccountsystem:serviceaccount:coder-system:coder-k8s. Edit it when the operator runs as another ServiceAccount.Dogfood
A full run of plan section 8 on a private Kind cluster (kind v0.26.0, kindest/node v1.32.0, Coder v2.37.2, no license), with images built from
091c7fcf. That commit has the same controller code as this head. Later commits changed only docs, the CI path filter, and moved the GitOps health rules out. Every step passed:Ready233 ms after apply. Coder holds the slug, URL,enabled,has_api_key, and an ACL of Everyone read only. The key appears 0 times in the CR JSON and the operator logs.e2e-stub__alpha,e2e-stub__betaande2e-stub__Gamma.updated_atunchanged.CredentialNotOptedIn,CredentialRefused,CredentialDestinationNotAllowed,SlugConflict(the other config unchanged),MCPServersNotAllowed,ForceOnNotAllowed. No Coder config for a, b, c, e, f.DisabledExternally, and Coder stays disabled. AdisplayNameedit enables it again.groups: []: a member's chat gets 400.[Everyone]: 201.system:mastersuser and by the Kind admin are denied with the policy message. The controller still reconciles.ControlPlaneGone(7 s), and the namespace goes (17 s).For P4 (e), unlicensed Coder allows one organization, so the run listed
[no-such-org]instead. The dogfood found one how-to mismatch (the reason after a restored opt-in), which this PR fixes. The screenshots and the recording are in the evidence comment below.Validation
make verify-vendor,make build,make lint,GOFLAGS=-mod=vendor go tool govulncheck ./...,make test,make test-integration,go test -race ./internal/controller/, andmake docs-checkpass.make codegen,make manifests,make build-installerandmake docs-referenceleave no diff. actionlint passes.TestSetupControllersStartsEveryController(S18) now also needs aCoderMCPServerto get its finalizer andControlPlaneNotReady, which proves the registration. A mutation that removes the registration fails it.TestCoderMCPServerControlPlaneAndOptIn: a listed name that resolves to the object's organization ID is allowed (IDs are compared, not names). A mutation to a name compare fails it.bash ./hack/e2e-workspace-lifecycle_test.shtimes out locally under host load, also on unchangedmain. CI runs it.Size
451 hand-written changed lines (300 of them docs) and 740 generated (
dist/install.yaml, the CRDs,role.yaml, the kustomization, the reference pages).Follow-ups
CoderMCPServercome in the next PR, as 🤖 docs: add GitOps health rules for CoderTemplateTest #212 did forCoderTemplateTest.CoderControlPlanewhileCoderAIGatewayKeyandCoderAIProviderobjects still use it. It does not coverCoderMCPServer. A control plane deletion can therefore leave MCP server configs in Coder. The how-to says to deleteCoderMCPServerobjects first. Follow-up to Namespace deletion leaves AI Gateway keys and AI providers in Coder #353: addCoderMCPServertocontrolPlaneDependents.Refs #155
Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high• Cost:$220.71