astrid-runtime/astrid · error

system-resident capsule

Error message

system-resident capsule '{id}' cannot host principal-bearing stdio MCP servers

What it means

Capsules classified as system-resident (operator-approved, shared runtime) are forbidden from declaring stdio MCP servers, because those servers run with a principal's identity and would break the isolation of a shared system runtime. The kernel throws this during load when `classify_runtime_residency` returns system residency and `manifest.mcp_servers` is non-empty.

Solutions

  1. Remove the `mcp_servers` entries from the capsule's Capsule.toml.
  2. Remove the capsule id from the operator's `system_capsules` admission set so it runs as a principal-scoped runtime.
  3. Split the MCP servers into a separate principal-scoped capsule.

Example fix

# before (Capsule.toml)
[mcp_servers]
stdio = { command = "./mcp.sh" }
# after
# [mcp_servers] section removed for system-resident capsule
Defensive patterns

Strategy: validation

Validate before calling

let manifest = astrid_capsule::discovery::load_manifest(&dir.join("Capsule.toml"))?;
if is_system_capsule(&manifest.package.name) && !manifest.mcp_servers.is_empty() {
    return Err(anyhow!("system-resident capsule cannot declare mcp_servers"));
}

Try / catch

if let Err(e) = kernel.load_capsule(&dir, &principal).await {
    if e.to_string().contains("cannot host principal-bearing stdio MCP servers") {
        strip_mcp_servers_from_manifest(&dir)?;
        return kernel.load_capsule(&dir, &principal).await;
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Loading a capsule whose Capsule.toml declares `[mcp_servers]` entries while the capsule id is listed in `system_capsules` (or otherwise classified system-resident).

Common situations: An operator promotes a capsule with MCP servers to the system capsule list; a Capsule.toml is copied into a system-resident deployment without stripping MCP server entries; residency classification changed after the manifest was written.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/3d87206a77075876. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-kernel/src/lib.rs:1736

                principal,
                &manifest,
                &bound.snapshot,
            )?;
        }
        if *principal != PrincipalId::default()
            && self.capabilities.is_principal_retiring(principal).await
        {
            anyhow::bail!("cannot load capsule '{id}' for retiring principal '{principal}'");
        }
        let wasm_hash = capsule_instance_hash(&manifest, &runtime_dir);
        // `capabilities.uplink` alone remains a principal-scoped daemon/host
        // grant unless the operator explicitly promotes it. A manifest that
        // actually provides an uplink must be operator-approved.
        let system_allowed = self.system_capsules.read().await.contains(id.as_str());
        let system_runtime =
            classify_runtime_residency(&manifest, &id, system_allowed)?.is_system();
        if system_runtime && !manifest.mcp_servers.is_empty() {
            anyhow::bail!(
                "system-resident capsule '{id}' cannot host principal-bearing stdio MCP servers"
            );
        }
        self.verify_workspace_capsule_tree(&runtime_dir)?;

        // Mutable runtimes are authority-scoped. A principal always receives a
        // fresh runtime for its immutable UID; only an explicitly classified
        // SystemResident service may attach another view to one runtime.
        {
            let mut registry = self.capsules.write().await;
            if registry.get_for(principal, &id).is_some() {
                return Ok(());
            }
            if system_runtime && registry.contains_system_runtime(&id, &wasm_hash) {
                registry
                    .register_existing(&id, &wasm_hash, principal)
                    .map_err(|e| anyhow::anyhow!("Failed to add capsule view: {e}"))?;
                if let Some(capsule) = registry.get_for(principal, &id) {

View on GitHub (pinned to affd8760f4)