Skip to content

feat(cloud): launcher integration for the cloud-context MCP#52

Merged
sourcehawk merged 6 commits into
feature/cloud-context-mcpfrom
feature/cloud-context-mcp--launcher
May 30, 2026
Merged

feat(cloud): launcher integration for the cloud-context MCP#52
sourcehawk merged 6 commits into
feature/cloud-context-mcpfrom
feature/cloud-context-mcp--launcher

Conversation

@sourcehawk
Copy link
Copy Markdown
Owner

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

  • Shared provider factory pkg/mcp/cloud/providers.New(name) — the single construction site for a cloud.Provider, importing the concrete gcp/aws packages (which cloud itself cannot, without a cycle). serve.go's newCloudProvider now delegates to it; preflight + connections use it to obtain a provider to probe.
  • Profile cloud: blockProfile.Cloud []CloudSource{Alias, Provider, AssumedIdentity, Profile, Scope, CommandAllowlistPath}, inherited through base: merge like linked_repos.
  • Per-session triagent-cloud-<alias> servers — emitted from mcpconfig.go with args ["serve","--kind=cloud","--provider=<p>"] and the pinned-identity env (per-provider model below).
  • Preflight probe with visible degradeResult.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.
  • Read-only cloud status in GET /api/connections — a cloud array of {provider, assumed_identity, valid, hint} built from the profile's cloud sources probed at request time. No PUT/DELETE route for cloud.
  • Read-only cloud pills in the connections panel — assumed identity + a checkmark when valid, the reauth hint when not, no edit affordance.

Design notes

Per-provider identity env diverges by mechanism, so CloudSource can't be one symmetric field. GCP derives validity from CLOUDSDK_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.AssumedIdentity is the canonical displayed pinned identity (SA email for gcp, role ARN for aws — matches IdentityStatus.AssumedIdentity).
  • CloudSource.Profile is an aws-only AWS_PROFILE selector; gcp ignores it.
  • mcpconfig.go injects per provider: gcp → CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT = AssumedIdentity; aws → AWS_PROFILE = Profile plus TRIAGENT_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.Getenv in-process, so providers.ProbeSource pins 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 cloud array is profile-sourced, not wallet-sourced. The plan named internal/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 beside slack_channel_prefix instead — 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 new ConnectionsPanel cloud-pill spec).
  • make build — frontend bundle + both binaries rebuilt so the embedded bundle stays fresh.
  • Strict TDD per task (D0 factory → D5 pill), each test watched fail for the right reason before implementation.

🤖 Generated with Claude Code

sourcehawk and others added 6 commits May 30, 2026 05:50
…#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 sourcehawk merged commit 9f8c72f into feature/cloud-context-mcp May 30, 2026
4 checks passed
@sourcehawk sourcehawk deleted the feature/cloud-context-mcp--launcher branch May 30, 2026 04:15
sourcehawk added a commit that referenced this pull request May 30, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant