Skip to content

Expose observed MCP route outcomes in sandbox status #3205

Description

@shiju-nv

User Story

As an operator running an MCP client in an OpenShell sandbox, I want sandbox status to show the latest OpenShell-observed outcome for each identifiable MCP endpoint, so that I can determine whether a recent request stopped at local policy, credential resolution, TLS or transport setup, or reached the upstream server without correlating several log sources.

Problem Statement

OpenShell reports whether a sandbox runtime is usable through the gateway. It also emits specific errors and security events when sandbox egress is denied or fails inside the supervisor. MCP clients do not consistently preserve or present that information. A client may reduce a local policy rejection, credential-resolution failure, or upstream rejection to a generic connection or protocol error.

The platform therefore lacks a compact, out-of-band summary of the most recent MCP egress outcome for a sandbox. Operators must correlate sandbox lifecycle status, effective network policy, provider state, supervisor logs, and the client error to identify the failing boundary.

The known incident in OpenShell #2161 and NemoClaw #6379 proves this failure category: provider metadata appeared healthy while actual MCP traffic failed across different agent stacks.

Impact / Why This Matters

An operator troubleshooting a failed MCP request currently has to consult separate surfaces to determine whether the request matched an allowed MCP policy rule, whether an applicable OpenShell-managed credential was available and authorized, whether OpenShell completed its TLS and transport work, and whether an upstream HTTP server responded.

OpenShell already owns or observes these boundaries. A structured sandbox-status summary would make the latest outcome available to interactive users and automation without requiring every MCP client to reproduce OpenShell-specific diagnostics.

Proposed Design

Expose one McpRouteReady entry in Sandbox.status.conditions for each distinct canonical MCP route in the active effective policy. The condition is passive evidence from observed traffic. It does not change SandboxPhase::Ready, create a health lease, or prove that a later tool call will succeed.

Use SandboxCondition.type for the condition class and SandboxCondition.subject as its secondary route key. The subject is an opaque OpenShell-owned SHA-256 identifier derived only from the normalized configured host, canonical path, and sorted deduplicated effective port set. Equivalent declarations collapse to one subject. Different paths on the same authority remain distinct. One multi-port endpoint produces one condition for its complete effective port set. Consumers select conditions by type plus subject and must not parse the human-readable message as an identifier.

The route subject is deliberately not reversible to a structured policy endpoint. It does not include the requesting binary, a client session, credentials, payloads, headers, tool arguments, query values, or raw upstream errors. Runtime policy still authorizes the actual request, including any binary scope, before an outcome can be attributed to a configured route. The public condition describes sandbox-level evidence for that route and does not claim caller-specific readiness.

Each condition uses a stable status and reason pair:

  • Unknown/NoObservedExchange means no qualifying exchange has been observed for the current route state.
  • True/Reachable means OpenShell received a syntactically valid upstream HTTP status below 400.
  • False/PolicyDenied means OpenShell policy or SSRF controls rejected an attributable request.
  • False/CredentialUnavailable means an applicable OpenShell-managed credential could not be resolved or authorized.
  • False/TlsFailed means TLS establishment or verification failed at the boundary OpenShell owns or observes.
  • False/TransportFailed means name resolution or upstream transport failed before a valid HTTP response arrived.
  • False/UpstreamRejected means the upstream returned a syntactically valid HTTP status of 400 or greater.

Acceptance Criteria

Status shape and identity

  • Sandbox.status.conditions contains one McpRouteReady condition for each distinct canonical MCP route in the active policy.
  • Every route condition has an opaque deterministic subject derived only from normalized host, canonical path, and the complete effective port set.
  • Equivalent declarations deduplicate, different paths remain distinct, and a multi-port endpoint produces one subject for its sorted deduplicated port set.
  • Public route identity does not include binary identity or client-session state and is documented as sandbox-level prior evidence rather than caller-specific readiness.

Outcome semantics

  • A route with no qualifying traffic under the current policy, applicable provider environment, and supervisor state reports Unknown/NoObservedExchange.
  • OpenShell-attributable policy, credential, TLS, transport, and upstream-rejection outcomes produce distinct stable status and reason pairs.
  • A syntactically valid upstream HTTP status below 400 reports True/Reachable; a valid status of 400 or greater reports False/UpstreamRejected; malformed or incomplete HTTP reports False/TransportFailed.
  • The status does not claim MCP protocol success, session health, tool availability, or tool-call success without parsing and validating the corresponding MCP response.
  • Every observed condition includes an RFC 3339 observation time. New repeated evidence advances that time, an idempotent private retry does not, and transition time changes only when status or reason changes.

Freshness and attribution

  • A policy change invalidates all current observations. An applicable provider-environment change invalidates credential-dependent observations. Supervisor disconnect or replacement invalidates observations from the previous session.
  • Stale, reordered, conflicting, or replayed reports cannot restore an observation from an obsolete policy, provider state, supervisor session, or gateway process.
  • Public reads project route conditions to Unknown when the current gateway process has no initialized live observation authority.
  • Requests that cannot be attributed to a current configured endpoint remain structured denial events or logs and do not mutate an unrelated endpoint observation.

Safety and compatibility

  • Route conditions omit credential values, request and response bodies, tool arguments, arbitrary headers, query values, session identifiers, report cursors, and raw upstream error text.
  • SandboxPhase::Ready and its existing operation gates remain unchanged.
  • The CLI and supported SDKs expose the opaque subject, status, reason, transition time, and observation time without exposing an upstream status code.

Verification and publication

  • Focused tests cover canonical identity, duplicate routes, distinct paths, multi-port sets, credentialed and uncredentialed routes, supported credential bindings, policy denial, credential unavailability, TLS failure, resolution failure, transport failure, upstream success and rejection, policy/provider invalidation, authenticated session replacement, private report idempotency, public fail-closed projection, and gateway restart.
  • A deterministic integration test sends an authenticated report through the real gateway handler and store, reads the public sandbox condition, verifies an unchanged sibling route, exercises identical and conflicting retries, then proves disconnect and supervisor replacement reset authority.
  • The Linux-only sandbox sidecar caller compiles through the owning crate on a Linux target.
  • Before publication, the issue includes a current end-to-end transcript against the pinned OpenShell revision showing a supported MCP client that loses or obscures OpenShell's structured failure and explaining why current logs do not satisfy the workflow.

Alternatives Considered

Add a dedicated MCP observation collection

Rejected. SandboxCondition already carries status, reason, message, and transition time. Adding an opaque subject and observation time lets the existing collection represent route-scoped evidence without introducing a parallel status model. The McpRouteReady name does not change lifecycle readiness because SandboxPhase::Ready remains authoritative for sandbox usability.

Expose a reversible endpoint reference

Rejected. A public structured reference would couple clients to policy representation and could reveal more route configuration than diagnostics require. An opaque deterministic subject gives clients a stable secondary key while keeping route canonicalization and future policy representation under OpenShell ownership.

Publish one condition per effective port

Rejected. A configured endpoint authorizes its effective port set as one route scope. Hashing the complete sorted set preserves that scope, deduplicates equivalent declarations, and avoids presenting one policy endpoint as several independent health claims.

Include binary or client identity in the public subject

Rejected for the first version. Runtime authorization still evaluates the requesting binary and ancestry, but the public condition reports sandbox-level evidence for the configured route. Caller-specific subjects would add unbounded cardinality, expose process details, and still would not prove that another request will succeed.

Agent Investigation

No response

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions