Skip to content

fix: register one OAuth MCP server for each AWS account - #983

Merged
cchu-ld merged 7 commits into
mainfrom
cchu/mcp-oauth
Oct 28, 2026
Merged

cchu-ld merged 7 commits into
mainfrom
cchu/mcp-oauth

Conversation

@cchu-ld

@cchu-ld cchu-ld commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

For Humans

Updates ldcli aws-devops-agent setup script to more closely align with what's in the manual setup docs: https://github.com/launchdarkly/ld-docs-private/pull/8632

Mainly:
Old: One MCP server registration <=> one agent space. via access token
New: MCP server registration <=> one account (multiple agent space) via Oauth (browser).

This removes the numerous confusing MCP registrations that could show up for users:
Screenshot 2026-10-28 at 11 36 23 AM

Instead of asking for access token, we direct users to LD Oauth page and poll results to continue with the rest of setup.

Previously concerns were raised about re-using the access token - the issues are still present, but there's nothing to enforce it on AWS side either (you can use other teammates' registered MCP server through the UI). Leaving the issue standing, and recommending that users use a service account if appropriate.

Summary

ldcli aws-devops-agent setup registered one LaunchDarkly MCP server for each agent space, with a LaunchDarkly access token. AWS keeps the credentials on the registration (RegisterService). The association (AssociateService) holds only the tool allowlist. So a different token for each space needed a different server.

With this change, setup registers one MCP server for the AWS account, with OAuth:

  1. Setup calls RegisterService with AuthorizationDiscovery. The return URL is the DevOps Agent console URL of the region.
  2. AWS sends an approval URL and no service ID. Setup opens the URL in a browser and prints it.
  3. The user approves access in LaunchDarkly. LaunchDarkly sends the user to the AWS console. If the user has no console session, AWS asks the user to sign in first.
  4. Setup polls ListServices every 5 seconds, for 10 minutes at most. It looks for a server at the LaunchDarkly endpoint with the oauth-3lo method.
  5. Setup associates the server with the agent space and the tool allowlist.

If another agent space runs setup, setup finds the server of the account and associates it. That space needs no browser step.

The new server has the name LaunchDarkly-OAuth. Setup never reuses a server that uses a token, even one with the name LaunchDarkly from the AWS console guide. If an agent space is already connected to a LaunchDarkly server, setup keeps that server. --mcp-service-id still works.

Removed

  • The token prompt and the token check against the LaunchDarkly API
  • --replace-mcp-token and the token replacement logic: disconnect, deregister, and the handling of shared servers
  • The server name for each space (MCPServerNameFor)
  • DeregisterService and DisassociateService from the AgentAPI interface

Tradeoffs

  • Setup now needs a browser, so it cannot run in CI.
  • Every agent space that uses the server acts as the LaunchDarkly member who approved access. That member's role controls the access, and the LaunchDarkly audit log shows that member for every change.

Testing

  • go test ./... and golangci-lint pass.
  • Manual run in a development AWS account: setup created a new agent space, opened the approval URL, found LaunchDarkly-OAuth after the approval, and associated it.

🤖 Generated with Claude Code


Note

Overview
ldcli aws-devops-agent setup no longer registers a separate LaunchDarkly MCP server per agent space using --access-token. It uses one account-level MCP registration shared by all agent spaces, authenticated with OAuth in the browser instead of pasted service tokens.

When no OAuth server exists at the configured endpoint, setup calls RegisterService with authorization discovery, opens the approval URL (via OpenURL / browser.OpenURL), and polls ListServices until AWS exposes an oauth-3lo server named LaunchDarkly-OAuth, then associates it with the agent space. Later runs reuse that account server or an MCP server already linked to the space; --mcp-service-id still selects among multiple OAuth servers.

Removed: interactive token prompts, LaunchDarkly token validation (token.go), --replace-mcp-token, per-space server naming, and MCP deregister/disassociate paths used for token replacement. Help text and tests are updated to match; OAuth behavior is covered by new polling/approval tests.

Reviewed by Cursor Bugbot for commit 56eca35. Bugbot is set up for automated code reviews on this repo. Configure here.

aws-devops-agent setup registered one LaunchDarkly MCP server for each
agent space, with a LaunchDarkly access token. AWS keeps the credentials
on the registration, so a different token needed a different server.

Now setup registers one server for the AWS account, with OAuth. Setup
opens the approval URL that AWS sends, waits until AWS registers the
server, and associates the server with the agent space. Another agent
space reuses the server and needs no browser step.

This change removes the token path: the token prompt, the token check,
--replace-mcp-token, and the server name for each space.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cchu-ld
cchu-ld marked this pull request as ready for review October 28, 2026 18:59
@cchu-ld
cchu-ld requested a review from a team as a code owner October 28, 2026 18:59
@cchu-ld
cchu-ld requested review from ld-ahartmann and removed request for a team October 28, 2026 18:59
# Conflicts:
#	cmd/aws_devops_agent/confirm_test.go
#	cmd/aws_devops_agent/plan_print_test.go
#	cmd/aws_devops_agent/setup.go
#	internal/awsdevops/awsdevops.go
#	internal/awsdevops/awsdevops_test.go
#	internal/awsdevops/setup.go

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0d27684. Configure here.

Comment thread internal/awsdevops/setup.go Resolved
@cchu-ld
cchu-ld requested a review from a team October 28, 2026 19:50

@Andrewjeska Andrewjeska left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent review incoming

Comment on lines +49 to +54
func SetOAuthPollInterval(d time.Duration) func() {
old := oauthPollInterval
oauthPollInterval = d

return func() { oauthPollInterval = old }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont see this called anywhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It was a leftover test helper I think. Updated test cases to actually use it.

@Andrewjeska

Copy link
Copy Markdown
Contributor

Note: This is an automated agent review (not a formal human review). Treat as a second opinion; the author/human reviewers decide what matters.

Scope: 20 files, +311/−1697 — removes the per-agent-space, token-based MCP registration path in favor of one OAuth-based LaunchDarkly-OAuth server per AWS account. Dropped --replace-mcp-token, the token prompt / CheckToken, MCPServerNameFor, DeregisterService/DisassociateService from the agent API, MCPReplace/MCPAsk/MCPSkip actions, and ~500 lines of shared-server lifecycle code. Direction and magnitude look right — this collapses a complicated branch of state that caused the "numerous confusing MCP registrations" the PR screenshot shows.

Nits worth addressing

  1. No duplicate-detection at the account level. accountMCPServer (internal/awsdevops/lookup.go:203-221) returns the first OAuth server whose endpoint matches, with no error for "more than one." Contrast with linkedMCPServer, which explicitly errors when a space has multiple LD servers. Low likelihood, but if a race, a console action, or an earlier bug leaves two OAuth registrations at the same endpoint, different agent spaces could silently anchor to different approving members. Cheap fix: mirror the "connected to more than one server" error.

  2. Lost actionable error for already exists on register. The old registerMCPServer wrapped strings.Contains(err.Error(), "already exists") with a specific "rename it in the console, then run setup again" message. In the OAuth path, that's gone — the user gets "unable to register the LaunchDarkly MCP server: %w". The checkMCP pre-flight catches most races, but the window between pre-flight and RegisterService is still open, and a raw AWS error gives no path forward. Worth preserving a tailored message for the known case.

  3. Plan line omits the AWS console sign-in step. describe() for MCPRegister says "You approve access to LaunchDarkly in a browser," but the Long help text correctly notes "AWS can ask you to sign in to the AWS console first." Users confirming the plan without having read --help won't know that. One extra clause fixes it.

  4. No surfacing of who approved on MCPReuse. The PR body flags that every space acts as the approving LD member, which is a non-obvious shared-account hazard. The CLI output for MCPReuse prints Reused MCP server LaunchDarkly-OAuth (<id>) with no indication of which member's identity the second engineer is inheriting. If AWS exposes the approving principal, printing it here would prevent a quiet audit-log surprise. If not, a one-liner reminder would still help.

  5. Smoke-test elevated actions on an OAuth associate. associateMCPServer signature changed to pass opts, step for extendAllowlist. Given PR feat: turn on elevated actions in aws-devops-agent setup #980 just landed, worth confirming end-to-end that elevated actions still turn on after an OAuth associate (the dev-account test covered association but not necessarily elevated-actions).

Already-raised (Cursor Bugbot)

The reply at #discussion_r4405123593 looks correct — AWS doesn't persist a pending registration when approval is cancelled, so a retry gets a fresh approval URL and does not hit a name conflict. Safe to resolve.

No concerns

  • token.go deletion and DeregisterService/DisassociateService removal from AgentAPI: no callers remain.
  • --mcp-service-id escape hatch preserves scripted/CI use after one browser bootstrap.
  • Test deltas (−1700 LOC) mirror removed scenarios (shared servers, token replace, name conflicts) rather than coverage loss. oauthPollInterval is overridable via SetOAuthPollInterval for tests.

Overall

Ship it after addressing #1 and #2; #3–#5 are nice-to-haves.

via LD Research 🤖

cchu-ld and others added 2 commits October 28, 2026 12:59
The new test adds the server on the third poll, so setup must wait and
poll again. The approval test no longer sets the poll interval, because
that test finds the server on the first poll.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Setup took the first OAuth server that ListServices sent. Each server
acts as the member who approved it, so two agent spaces could act as
different members. Now the plan stops, names each server, and asks for
--mcp-service-id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cchu-ld cchu-ld changed the title feat: register one OAuth MCP server for each AWS account fix: register one OAuth MCP server for each AWS account Oct 28, 2026
cchu-ld and others added 3 commits October 28, 2026 13:08
If RegisterService reports that LaunchDarkly-OAuth already exists, setup
now says to rename or delete that server in the AWS console, or to pass
--mcp-service-id. Before, setup showed only the error from AWS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The help said that AWS can ask for a console sign-in before the approval.
AWS registers the server before it opens the console, so the sign-in
comes after the approval, and setup does not wait for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cchu-ld

cchu-ld commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review!

  1. Duplicate detection: fixed in c6cecb8. If the account has more than one OAuth server at the LaunchDarkly endpoint, the plan stops before it changes AWS. The error names each server and asks for --mcp-service-id to pick one.

  2. "already exists" on register: fixed in 71384b9. The error now says to rename or delete the LaunchDarkly-OAuth server in the AWS console, or to pass --mcp-service-id.

  3. Console sign-in: no change to the plan line. The console sign-in is not required for the registration.

  4. Who approved: no change. AWS does not report the approving member. The help text already say that every space acts as the member who approved.

  5. Elevated actions: elevated actions are a preference of the agent space, unrelated to auth pattern. All mutative actions require human approvals.

@cchu-ld
cchu-ld merged commit 5308e01 into main Oct 28, 2026
11 checks passed
@cchu-ld
cchu-ld deleted the cchu/mcp-oauth branch October 28, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants