Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bed23c4
feat(mcp): checkpoint qualified managed service integration
pallakatos Sep 9, 2026
21a6399
Merge qualified governed-service privacy into managed MCP
pallakatos Sep 9, 2026
f876b40
build(mcp): prepare explicit patched URI dependency lock
pallakatos Sep 9, 2026
fdcbc43
build(mcp): apply reviewed patched URI lockfile
pallakatos Sep 9, 2026
7b11446
fix(mcp): close composed source gates and prepare remaining patched d…
pallakatos Sep 9, 2026
394ca12
build(mcp): apply fully audited IP parser lock repair
pallakatos Sep 9, 2026
cb3a24f
Merge current SRE migration and patched runtime dependencies into MCP
pallakatos Sep 9, 2026
13a2681
fix(mcp): align managed schema with the generated Kubernetes contract
pallakatos Sep 9, 2026
a1bc9eb
Merge proven SRE controller handoff into managed MCP
pallakatos Sep 9, 2026
84617e7
fix(mcp): keep bounded probes buildable with controller-only dependen…
pallakatos Sep 9, 2026
04bd43f
fix(mcp): preserve typed tool result content when forwarding
pallakatos Sep 10, 2026
fbd0159
merge: qualify MCP content compatibility on the current services stack
pallakatos Sep 10, 2026
211d0d4
docs(security): record delegated MCP compatibility audit
pallakatos Sep 10, 2026
c56b90d
fix(e2e): negotiate both MCP response media types
pallakatos Sep 10, 2026
81ee4fe
Merge current services integration base into managed MCP
pallakatos Sep 10, 2026
0f2dd75
docs(mcp): record native client repair and real-router qualification
pallakatos Sep 10, 2026
6f513c4
Merge landed governed services into managed MCP
pallakatos Sep 10, 2026
3b479c9
chore(mcp): prepare reviewed hosted dependency lock repair
pallakatos Sep 10, 2026
30d89dc
test(mcp): retain fatal lock-generation diagnostics and bounded cleanup
pallakatos Sep 10, 2026
0dd1e8c
fix(mcp): close reviewed Everything dependency advisories
pallakatos Sep 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cli/src/commands/push-apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import {
import { coreImageValues, imageValueArgs, PUSH_COMPONENTS, resolvePushedArtifacts, type PushedImage } from "../lib/image-targets.js";
import { inspectCoreInstallation, recheckCoreOwnership, requireHealthyDeployment, updateLegacyCore, verifyCoreConfiguration } from "../lib/core-image-apply.js";
import { inspectSandboxPlans, refreshSandboxImages } from "../lib/sandbox-image-apply.js";
import { inspectManagedMcpPlans, refreshManagedMcpImages } from "../lib/managed-mcp-image-apply.js";
import { assertSafeMutation } from "../lib/sre-authority.js";

export interface PushApplyResult {
applied: string[];
buildOnly: string[];
updatedSandboxes: number;
preservedOverrides: number;
updatedManagedMcp?: number;
}

/** Update owning defaults, prove their selected artifact references, then ask
Expand Down Expand Up @@ -48,6 +50,7 @@ export async function applyPushedImages(
const meshImages: MeshImages = {};
for (const item of artifacts) if (item.name === "relay" || item.name === "registry") meshImages[item.name] = item.image;
const plans = await inspectSandboxPlans(execute, coreImages);
const managedPlans = await inspectManagedMcpPlans(execute, coreImages);
if (core) await recheckCoreOwnership(execute, core);
if (mesh && (selectedMesh || core?.kind === "helm")) await recheckMeshOwnership(execute, mesh);

Expand Down Expand Up @@ -82,6 +85,8 @@ export async function applyPushedImages(
requireHealthyDeployment(await verifyCoreConfiguration(execute, core, coreImages));
}
const updatedSandboxes = await refreshSandboxImages(execute, plans);
const updatedManagedMcp = await refreshManagedMcpImages(execute, managedPlans, core?.deployment.metadata.namespace ?? "kars-system");
return { applied: artifacts.map(item => item.name), buildOnly, updatedSandboxes,
...(coreImages.some(image => image.name === "mcp-everything") ? { updatedManagedMcp } : {}),
preservedOverrides: plans.filter(plan => plan.pinned).length };
}
7 changes: 5 additions & 2 deletions cli/src/commands/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { loadContext } from "../config.js";
import { preparePushTarget } from "../lib/deployment-target.js";
import { inspectMeshInstallation } from "../lib/mesh-release.js";
import { applyPushedImages } from "./push-apply.js";
import { dockerPushDigest, PUSH_COMPONENTS, RUNTIME_IMAGE_TARGETS } from "../lib/image-targets.js";
import { dockerPushDigest, MANAGED_MCP_IMAGE_TARGET, PUSH_COMPONENTS, RUNTIME_IMAGE_TARGETS } from "../lib/image-targets.js";
import { stageRustBinaries } from "../lib/stage-rust-bin.js";
import { stageMeshPlugin } from "../lib/stage-mesh-plugin.js";
import { ensureAgtRepo, ensureAgtWheels } from "../lib/agt-bootstrap.js";
Expand All @@ -25,7 +25,7 @@ export function pushCommand(): Command {
.description("Build and push images to ACR (uses cached context from last deploy)")
.option("--acr <name>", "ACR name (default: from last deploy)")
.option("--subscription <id>", "Azure subscription (must match saved deployment when present)")
.option("--only <image>", "Build one image: controller, router, sandbox, sandbox-base, relay, registry, or runtime-*")
.option("--only <image>", "Build one image: controller, router, sandbox, sandbox-base, relay, registry, mcp-everything, or runtime-*")
.option("--include-base", "Include sandbox-base in a full push (skipped by default — rebuild only when upgrading OpenClaw/Python/Go)")
.option("--apply", "Apply selected image configuration and verify the resulting rollouts")
.option(
Expand Down Expand Up @@ -256,6 +256,8 @@ export function pushCommand(): Command {
{ name: "sandbox", tag: "openclaw-sandbox:latest", dockerfile: "sandbox-images/openclaw/Dockerfile",
buildArgs: sandboxBuildArgs },
...meshImages,
{ name: MANAGED_MCP_IMAGE_TARGET.name, tag: `${MANAGED_MCP_IMAGE_TARGET.repo}:latest`,
dockerfile: "sandbox-images/mcp-everything/Dockerfile", context: "sandbox-images/mcp-everything" },
// Shared with release imports, upgrade values, and push application.
...RUNTIME_IMAGE_TARGETS.map(runtime => ({
name: runtime.name, tag: `${runtime.repo}:latest`,
Expand Down Expand Up @@ -396,6 +398,7 @@ export function pushCommand(): Command {
name: image.name, image: pushedArtifacts.get(image.name) ?? `${acrLoginServer}/${image.tag}`,
})), path.join(repoRoot, "deploy/helm/kars"), mesh);
spin.succeed(`Selected image configuration applied; ${applied.updatedSandboxes} eligible sandbox deployment(s) verified`);
if (applied.updatedManagedMcp !== undefined) console.log(chalk.dim(` ${applied.updatedManagedMcp} managed MCP workload(s) verified; unreferenced defaults updated only.`));
if (applied.preservedOverrides) console.log(chalk.dim(` Preserved ${applied.preservedOverrides} explicit sandbox/overlay image override(s).`));
if (applied.buildOnly.length) console.log(chalk.dim(` Build-only (not deployed): ${applied.buildOnly.join(", ")}.`));
} catch (e: any) {
Expand Down
13 changes: 10 additions & 3 deletions cli/src/lib/image-targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ export const RUNTIME_IMAGE_TARGETS: ReadonlyArray<{
];

export interface PushedImage { name: string; image: string }
export const PUSH_COMPONENTS = ["controller", "router", "sandbox", "sandbox-base", "relay", "registry", ...RUNTIME_IMAGE_TARGETS.map(item => item.name)];
export const MANAGED_MCP_IMAGE_TARGET = {
name: "mcp-everything", repo: "mcp-everything",
valueKey: "managedMcp.everythingImage", env: "MCP_EVERYTHING_IMAGE",
} as const;
export const PUSH_COMPONENTS = ["controller", "router", "sandbox", "sandbox-base", "relay", "registry",
MANAGED_MCP_IMAGE_TARGET.name, ...RUNTIME_IMAGE_TARGETS.map(item => item.name)];

export function splitImage(image: string): { repository: string; tag: string } {
const withoutDigest = image.split("@")[0];
Expand All @@ -43,14 +48,16 @@ export function dockerPushDigest(output: string): string | undefined {

export function controllerEnv(name: string): string | undefined {
return name === "router" ? "INFERENCE_ROUTER_IMAGE"
: name === "sandbox" ? "SANDBOX_IMAGE" : runtimeTarget(name)?.env;
: name === "sandbox" ? "SANDBOX_IMAGE"
: name === MANAGED_MCP_IMAGE_TARGET.name ? MANAGED_MCP_IMAGE_TARGET.env : runtimeTarget(name)?.env;
}

export function coreImageValues(images: PushedImage[]): Record<string, string> {
const values: Record<string, string> = {};
for (const { name, image } of images) {
const runtime = runtimeTarget(name);
if (runtime) values[runtime.valueKey] = image;
if (name === MANAGED_MCP_IMAGE_TARGET.name) values[MANAGED_MCP_IMAGE_TARGET.valueKey] = image;
else if (runtime) values[runtime.valueKey] = image;
else if (["controller", "router", "sandbox"].includes(name)) {
const prefix = name === "router" ? "inferenceRouter" : name;
const { repository, tag } = splitImage(image);
Expand Down
71 changes: 71 additions & 0 deletions cli/src/lib/managed-mcp-image-apply.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { describe, expect, it, vi } from "vitest";
import { controllerEnv, coreImageValues } from "./image-targets.js";
import { inspectManagedMcpPlans, refreshManagedMcpImages } from "./managed-mcp-image-apply.js";
import type { Execute } from "./deployment-target.js";

const image = `registry.azurecr.io/mcp-everything:latest@sha256:${"a".repeat(64)}`;
const source = {
metadata: { name: "tools", namespace: "workspace", uid: "source", resourceVersion: "1", generation: 2 },
spec: { managed: { preset: "everything" }, allowedTools: ["echo"] },
status: { phase: "Ready", observedGeneration: 2, workloadImage: image, workloadGeneration: 3,
workloadRef: "kars-mcp/mcp-tools", managedNamespaceUid: "managed-ns" },
};
function fixture() {
const annotations = { "kars.azure.com/mcp-source-namespace": "workspace", "kars.azure.com/mcp-source-name": "tools",
"kars.azure.com/mcp-source-uid": "source", "kars.azure.com/mcp-namespace-uid": "managed-ns" };
const metadata = { name: "mcp-tools", namespace: "kars-mcp", uid: "resource", resourceVersion: "1", generation: 3,
annotations, labels: { "app.kubernetes.io/managed-by": "kars-controller" } };
const selector = { "kars.azure.com/mcp-source-uid": "source" };
const resources: Record<string, any> = {
mcpserver: structuredClone(source),
deployment: { metadata: structuredClone(metadata), spec: { selector: { matchLabels: selector },
template: { spec: { containers: [{ name: "mcp", image }] } } },
status: { observedGeneration: 3, availableReplicas: 1, updatedReplicas: 1 } },
service: { metadata: structuredClone(metadata), spec: { selector: structuredClone(selector) } },
};
const execute = vi.fn(async (_file: string, args: readonly string[]) => {
if (args[0] === "get" && args[1] === "mcpservers") return { stdout: JSON.stringify({ items: [source] }) };
if (args[0] === "get" && args[1] === "namespace") return { stdout: JSON.stringify(args[2] === "kars-system"
? { metadata: { name: "kars-system", uid: "system", resourceVersion: "1" } }
: { metadata: { name: "kars-mcp", uid: "managed-ns", resourceVersion: "1", annotations: {
"kars.azure.com/mcp-namespace-claim": "v1", "kars.azure.com/mcp-controller-namespace": "kars-system",
"kars.azure.com/mcp-controller-namespace-uid": "system" } } }) };
if (args[0] === "get" && resources[args[1]]) return { stdout: JSON.stringify(resources[args[1]]) };
if (["patch", "wait"].includes(args[0])) return { stdout: "" };
throw new Error("Unexpected command");
}) as unknown as Execute & ReturnType<typeof vi.fn>;
return { execute, resources };
}

describe("managed MCP image application", () => {
it("maps the actual artifact to owning Helm values and controller environment", () => {
expect(controllerEnv("mcp-everything")).toBe("MCP_EVERYTHING_IMAGE");
expect(coreImageValues([{ name: "mcp-everything", image }])).toEqual({ "managedMcp.everythingImage": image });
});
it("leaves unrelated component inventories untouched", async () => {
const { execute } = fixture();
expect(await inspectManagedMcpPlans(execute, [{ name: "controller", image }])).toEqual([]);
expect(execute).not.toHaveBeenCalled();
});
it("CAS-triggers only the source and verifies actual artifact, selector and probed generation", async () => {
const { execute } = fixture();
const plans = await inspectManagedMcpPlans(execute, [{ name: "mcp-everything", image }]);
expect(await refreshManagedMcpImages(execute, plans, "kars-system")).toBe(1);
const calls = execute.mock.calls.map(call => call[1] as string[]);
const patch = calls.find(args => args[0] === "patch")!;
expect(JSON.parse(patch.at(-1)!).metadata).toMatchObject({ uid: "source", resourceVersion: "1" });
expect(calls.some(args => args[0] === "patch" && args[1] === "deployment")).toBe(false);
});
it.each(["selector", "image", "probe", "owner"])("rejects %s mismatches instead of reporting applied", async field => {
const { execute, resources } = fixture();
const plans = await inspectManagedMcpPlans(execute, [{ name: "mcp-everything", image }]);
if (field === "selector") resources.service.spec.selector = { app: "foreign" };
if (field === "image") resources.deployment.spec.template.spec.containers[0].image = "old:latest";
if (field === "probe") resources.mcpserver.status.workloadGeneration = 1;
if (field === "owner") resources.deployment.metadata.annotations["kars.azure.com/mcp-source-uid"] = "foreign";
await expect(refreshManagedMcpImages(execute, plans, "kars-system")).rejects.toThrow("Managed MCP");
});
});
111 changes: 111 additions & 0 deletions cli/src/lib/managed-mcp-image-apply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import type { Execute } from "./deployment-target.js";
import { MANAGED_MCP_IMAGE_TARGET, type PushedImage } from "./image-targets.js";

interface ObjectRecord {
metadata: { name: string; namespace?: string; uid: string; resourceVersion: string; generation?: number;
deletionTimestamp?: string; annotations?: Record<string, string>; labels?: Record<string, string>; ownerReferences?: unknown[] };
spec?: Record<string, any>;
status?: Record<string, any>;
}
export interface ManagedMcpImagePlan { source: ObjectRecord; image: string }

function checked(value: unknown): ObjectRecord {
const object = value as ObjectRecord;
if (!object?.metadata?.name || !object.metadata.uid || !object.metadata.resourceVersion
|| object.metadata.deletionTimestamp) throw new Error("Managed MCP object lacks a live exact API identity");
return object;
}

async function read(execute: Execute, kind: string, name: string, namespace?: string): Promise<ObjectRecord> {
const result = await execute("kubectl", ["get", kind, name, ...(namespace ? ["-n", namespace] : []), "-o", "json"], { stdio: "pipe" });
const object = checked(JSON.parse(String(result.stdout)));
if (object.metadata.name !== name || (namespace && object.metadata.namespace !== namespace)) {
throw new Error("Managed MCP API returned another object");
}
return object;
}

export async function inspectManagedMcpPlans(execute: Execute, images: PushedImage[]): Promise<ManagedMcpImagePlan[]> {
const image = images.find(image => image.name === MANAGED_MCP_IMAGE_TARGET.name)?.image;
if (!image) return [];
const result = await execute("kubectl", ["get", "mcpservers", "-A", "-o", "json"], { stdio: "pipe" });
const list = JSON.parse(String(result.stdout));
if (!Array.isArray(list.items)) throw new Error("Managed MCP inventory is invalid");
return list.items.filter((item: ObjectRecord) => item.spec?.managed?.preset === "everything").map((item: unknown) => {
const source = checked(item);
if (!source.metadata.namespace || !source.metadata.generation) throw new Error("Managed MCP source lacks workspace/generation");
return { source, image };
});
}

function sourceUnchanged(current: ObjectRecord, original: ObjectRecord): void {
if (current.metadata.uid !== original.metadata.uid || current.metadata.generation !== original.metadata.generation
|| JSON.stringify(current.spec) !== JSON.stringify(original.spec)) {
throw new Error("Managed MCP source changed during image application; no CR spec was overwritten");
}
}

function owned(resource: ObjectRecord, source: ObjectRecord, namespaceUid: string): void {
const annotations = resource.metadata.annotations ?? {};
if (resource.metadata.ownerReferences?.length
|| resource.metadata.labels?.["app.kubernetes.io/managed-by"] !== "kars-controller"
|| annotations["kars.azure.com/mcp-source-namespace"] !== source.metadata.namespace
|| annotations["kars.azure.com/mcp-source-name"] !== source.metadata.name
|| annotations["kars.azure.com/mcp-source-uid"] !== source.metadata.uid
|| annotations["kars.azure.com/mcp-namespace-uid"] !== namespaceUid) {
throw new Error("Managed MCP workload ownership does not match its source/namespace UIDs");
}
}

export async function refreshManagedMcpImages(
execute: Execute, plans: ManagedMcpImagePlan[], controllerNamespace: string,
): Promise<number> {
if (!plans.length) return 0;
const controller = await read(execute, "namespace", controllerNamespace);
for (const { source, image } of plans) {
const current = await read(execute, "mcpserver", source.metadata.name, source.metadata.namespace);
sourceUnchanged(current, source);
await execute("kubectl", ["patch", "mcpserver", source.metadata.name, "-n", source.metadata.namespace!,
"--type=merge", "-p", JSON.stringify({ metadata: { uid: current.metadata.uid,
resourceVersion: current.metadata.resourceVersion, annotations: { "kars.azure.com/image-refresh": String(Date.now()) } } })],
{ stdio: "pipe" });
await execute("kubectl", ["wait", `mcpserver/${source.metadata.name}`, "-n", source.metadata.namespace!,
`--for=jsonpath={.status.workloadImage}=${image}`, "--timeout=300s"], { stdio: "pipe" });
const verified = await read(execute, "mcpserver", source.metadata.name, source.metadata.namespace);
sourceUnchanged(verified, source);
const status = verified.status;
if (status?.phase !== "Ready" || status.observedGeneration !== verified.metadata.generation
|| status.workloadImage !== image || !status.workloadRef || !status.managedNamespaceUid) {
throw new Error("Managed MCP source did not qualify the selected image");
}
const parts = String(status.workloadRef).split("/");
if (parts.length !== 2 || parts.some(part => !/^[a-z0-9](?:[-a-z0-9]*[a-z0-9])?$/.test(part))) {
throw new Error("Managed MCP workload reference is invalid");
}
const [namespace, name] = parts;
const ns = await read(execute, "namespace", namespace);
if (ns.metadata.uid !== status.managedNamespaceUid
|| ns.metadata.annotations?.["kars.azure.com/mcp-namespace-claim"] !== "v1"
|| ns.metadata.annotations?.["kars.azure.com/mcp-controller-namespace"] !== controllerNamespace
|| ns.metadata.annotations?.["kars.azure.com/mcp-controller-namespace-uid"] !== controller.metadata.uid) {
throw new Error("Managed MCP namespace ownership changed");
}
const deployment = await read(execute, "deployment", name, namespace);
const service = await read(execute, "service", name, namespace);
owned(deployment, source, ns.metadata.uid);
owned(service, source, ns.metadata.uid);
const selector = { "kars.azure.com/mcp-source-uid": source.metadata.uid };
if (JSON.stringify(deployment.spec?.selector?.matchLabels) !== JSON.stringify(selector)
|| JSON.stringify(service.spec?.selector) !== JSON.stringify(selector)
|| deployment.spec?.template?.spec?.containers?.find((container: { name: string }) => container.name === "mcp")?.image !== image
|| deployment.status?.observedGeneration !== deployment.metadata.generation
|| deployment.status?.availableReplicas !== 1 || deployment.status?.updatedReplicas !== 1
|| status.workloadGeneration !== deployment.metadata.generation) {
throw new Error("Managed MCP workload/Service did not converge to the qualified artifact and selector");
}
}
return plans.length;
}
17 changes: 15 additions & 2 deletions controller/src/crd_validations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,25 @@ pub fn mcp_server_validations() -> Vec<ValidationRule> {
ValidationRule {
rule: "!has(self.bundleRef) || (!has(self.url) && !has(self.oauth) && \
!has(self.productionMode) && !has(self.scopes) && \
!has(self.allowedTools) && !has(self.displayName))"
!has(self.allowedTools) && !has(self.displayName) && !has(self.managed))"
.into(),
message: Some(
"spec.bundleRef is mutually exclusive with spec.url, spec.oauth, \
spec.productionMode, spec.scopes, spec.allowedTools, and \
spec.displayName"
spec.displayName, and spec.managed"
.into(),
),
reason: Some("FieldValueInvalid".into()),
..ValidationRule::default()
},
ValidationRule {
rule: "!has(self.managed) || (!has(self.url) && !has(self.oauth) && \
!has(self.productionMode) && !has(self.scopes) && \
!has(self.bearerFromEnv) && !has(self.bundleRef))"
.into(),
message: Some(
"spec.managed is mutually exclusive with spec.url, spec.oauth, \
spec.productionMode, spec.scopes, spec.bearerFromEnv, and spec.bundleRef"
.into(),
),
reason: Some("FieldValueInvalid".into()),
Expand Down
Loading
Loading