feat(cloud): launcher integration for the cloud-context MCP#52
Merged
sourcehawk merged 6 commits intoMay 30, 2026
Merged
Conversation
…#47) Introduce pkg/mcp/cloud/providers.New(name), the single construction site for a cloud.Provider. It imports the concrete gcp and aws packages (which the cloud package itself cannot, without a cycle) and mirrors how the launcher builds an auth.Provider from pkg/auth/teleport and pkg/auth/kubeconfig. serve.go's newCloudProvider is removed in favour of delegating to the factory, so preflight and connections can obtain a provider the same way the serve arm does. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add Profile.Cloud []CloudSource so a deployment can declare read-only cloud connections in the profile. Each source carries the alias, provider, pinned AssumedIdentity, optional aws Profile selector, scope allowlist, and an optional command-allowlist override path. applyBase inherits cloud sources with the same replace-on-presence rule as linked_repos. AssumedIdentity is the canonical pinned identity (SA email for gcp, role ARN for aws); Profile is the aws-only AWS_PROFILE selector, ignored by gcp. Scope reuses cloud.ScopeAllowlist so the launcher can JSON-encode it into the cloud MCP subprocess env unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tity env (#47) Emit a triagent-cloud-<alias> MCP server per profile cloud source, with args ["serve","--kind=cloud","--provider=<p>"] and env carrying the provider selector, the optional allowlist-override path, the JSON-encoded scope, and the per-provider pinned-identity env. The cloud loop mirrors the per-repo git loop. Per-provider identity env, by mechanism: gcp impersonates the assumed identity directly via CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT (one env is both the impersonation target and the expected identity); aws selects an assume-role profile via AWS_PROFILE and checks the role ARN via TRIAGENT_CLOUD_AWS_EXPECTED_ROLE_ARN. mcpconfig references the env-name constants from the provider packages, never raw literals — so the gcp impersonation const and the aws profile/expected-role consts are exported. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Run the read-only identity probe for each profile cloud source after the kubeconfig freeze, recording the outcome in Result.CloudSources. The probe degrades, never blocks: a failed probe — or a provider construction error, e.g. a missing CLI binary — marks that source unavailable with a hint, and the session still starts. The existing k8s block-on-failure behaviour is unchanged. The shared providers.ProbeSource constructs the source's provider via the factory and pins the per-provider expected-identity env around the whoami (serialized, then restored) so each probe validates against its own pinned identity. Preflight exposes a CloudProbe seam for tests; nil uses the real prober. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#47) GET /api/connections grows a cloud array of {provider, assumed_identity, valid, hint}, built from the profile's cloud sources probed at request time. The fields mirror cloud.IdentityStatus so the panel renders directly from the probe. Read-only: no PUT/DELETE route for cloud, since a cloud connection is configured in the profile, not entered in the panel. The cloud array is profile-sourced (the connections wallet holds only stored tokens, which cloud has none of), so it lives on the response builder beside slack_channel_prefix rather than in the connections package. cloudProbe is an injectable seam; nil uses the real providers.ProbeSource. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render the profile-configured cloud connections as read-only pills in the
manage-connections modal: the assumed identity with a checkmark when the
request-time probe is valid, the reauth hint when not. Cloud is configured
in the deployment profile, never entered in the panel, so the pills carry
no edit affordance. The section is omitted when no cloud sources exist.
ConnectionStatus grows an optional cloud[] of {provider, assumed_identity,
valid, hint}, mirroring the /api/connections response.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sourcehawk
added a commit
that referenced
this pull request
May 30, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Towards #47
Wire the merged GCP (#43) and AWS (#46) cloud providers and the scaffold (#45) into the launcher: deployments declare read-only cloud connections in the profile, the launcher spawns a per-session
triagent-cloud-<alias>MCP pinned to that source's identity, and cloud auth readiness is visible before a session starts. This is the final consumer PR of the cloud-context MCP feature. Kubernetes triage is never blocked by a stale cloud credential — a failed cloud probe degrades that source visibly instead of failing the session.Changes
pkg/mcp/cloud/providers.New(name)— the single construction site for acloud.Provider, importing the concrete gcp/aws packages (whichclouditself cannot, without a cycle).serve.go'snewCloudProvidernow delegates to it; preflight + connections use it to obtain a provider to probe.cloud:block —Profile.Cloud []CloudSource{Alias, Provider, AssumedIdentity, Profile, Scope, CommandAllowlistPath}, inherited throughbase:merge likelinked_repos.triagent-cloud-<alias>servers — emitted frommcpconfig.gowith args["serve","--kind=cloud","--provider=<p>"]and the pinned-identity env (per-provider model below).Result.CloudSources []CloudSourceStatus; each source is probed after the kubeconfig freeze; a failed probe (or a provider construction error) marks the source unavailable with a hint, never erroring the session.GET /api/connections— acloudarray of{provider, assumed_identity, valid, hint}built from the profile's cloud sources probed at request time. NoPUT/DELETEroute for cloud.Design notes
Per-provider identity env diverges by mechanism, so
CloudSourcecan't be one symmetric field. GCP derives validity fromCLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT(the impersonation target doubles as the expected identity). AWS needs both an assume-role profile selector (AWS_PROFILE) for credentials and the expected role ARN (TRIAGENT_CLOUD_AWS_EXPECTED_ROLE_ARN) for strict validity. The model that landed:CloudSource.AssumedIdentityis the canonical displayed pinned identity (SA email for gcp, role ARN for aws — matchesIdentityStatus.AssumedIdentity).CloudSource.Profileis an aws-onlyAWS_PROFILEselector; gcp ignores it.mcpconfig.goinjects per provider: gcp →CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT = AssumedIdentity; aws →AWS_PROFILE = ProfileplusTRIAGENT_CLOUD_AWS_EXPECTED_ROLE_ARN = AssumedIdentity. Env-name constants come from the provider packages (gcp.EnvImpersonate,aws.EnvProfile,aws.EnvExpectedRoleARN), never raw literals.Probing a source's pinned identity from the launcher process. Providers read their expected-identity env via
os.Getenvin-process, soproviders.ProbeSourcepins the per-provider env around the whoami (mutex-serialized, then restored) so concurrent probes for different sources can't read each other's pin. A provider construction error (missing CLI binary) degrades to an invalid status, never a fatal error. (Known v1 limitation: this mutates process-global env for the probe window; a future cleanup would thread the expected identity explicitly rather than via env.)The connections
cloudarray is profile-sourced, not wallet-sourced. The plan namedinternal/connections/connections.go, but that package is the stored-token wallet and cloud holds no token. The read-only cloud array is built on the server response builder besideslack_channel_prefixinstead — a coherent deviation.Related
Testing
make test-go— race-clean, green across all touched packages.make lint— 0 issues.cd frontend && npm run typecheck— clean;npm test -- --run— all tests passing (includes the newConnectionsPanelcloud-pill spec).make build— frontend bundle + both binaries rebuilt so the embedded bundle stays fresh.🤖 Generated with Claude Code