User Story
As an OpenShell operator who allows MCP traffic, I want each endpoint policy to name the exact MCP revisions it permits and return that same set after every policy operation, so that runtime enforcement can apply the right wire rules without guessing or changing my policy.
Problem Statement
OpenShell must know which MCP revisions an endpoint permits before it can enforce revision-specific wire behavior. Stable MCP revisions are not interchangeable. For example, 2025-03-26 permits JSON-RPC batches, while 2025-06-18 and 2025-11-25 prohibit top-level arrays. An endpoint policy without an exact revision set cannot determine whether the same message is valid.
Adding a versions field to one API is not enough. OpenShell must keep the same values when it accepts policy YAML or protobuf, saves a revision, combines policies, calculates a policy hash, returns sandbox configuration, or converts SDK objects. If any path drops or changes a value, the active policy may permit a revision the operator did not approve. Different ordering of the same values must not create a false policy change.
Impact / Why This Matters
Without an exact allowlist, later enforcement has two unsafe choices. It can reject traffic that is valid for an older permitted revision, or accept traffic that a newer revision forbids. Either choice substitutes an OpenShell assumption for the operator's policy.
The allowlist passes through several policy inputs and outputs. If those paths handle omission, ordering, or values differently, an operator cannot tell what OpenShell will enforce. Omission is especially risky if it means every known revision, because adding a revision in a later OpenShell release would silently widen an existing policy.
Proposed Design
MCP endpoint policy exposes mcp.versions as an exact, nonempty allowlist selected from a closed stable-revision registry. The initial supported revisions are 2025-03-26, 2025-06-18, and 2025-11-25.
When mcp.versions is omitted, OpenShell materializes exactly ["2025-11-25"]. The compatibility default is pinned to that one revision. It does not resolve a moving latest identifier or expand to all registered revisions, so adding a future revision cannot widen an existing policy.
OpenShell rejects empty allowlists, duplicate entries, padded strings, unknown revisions, draft identifiers, and moving identifiers such as latest. It stores and returns valid sets in fixed registry order. Two policies that name the same revisions therefore produce the same saved form and deterministic hash, regardless of authoring order.
Every supported policy input and output preserves all allowlist values. This includes policy YAML, protobuf APIs, provider profiles, stored revisions, combined policies, sandbox configuration, and Go SDK conversion. The deterministic hash uses that same fixed-order form. MCP options are valid only on MCP endpoints.
OpenShell refuses to activate a policy whose MCP revision state is invalid. A history list still returns the affected revision with its own load error instead of failing the whole page. A valid global policy may supply the effective policy while invalid local history remains inactive, so an operator can inspect and replace the bad revision without serving it.
Acceptance Criteria
Policy authoring and validation:
Save, merge, load, and identity:
Alternatives Considered
Treating every MCP endpoint as supporting every known revision would silently widen existing policy whenever the registry grows. It also cannot express deployments that intentionally support only one revision's wire rules.
Using a moving value such as latest would change the meaning of the same stored policy across OpenShell releases. Policy review and rollback would become unreliable, and operators could not use a stable hash during incident analysis.
Adding the field only where OpenShell enforces traffic would leave save, merge, and output paths free to disagree or discard the operator's values.
Agent Investigation
No response
Checklist
User Story
As an OpenShell operator who allows MCP traffic, I want each endpoint policy to name the exact MCP revisions it permits and return that same set after every policy operation, so that runtime enforcement can apply the right wire rules without guessing or changing my policy.
Problem Statement
OpenShell must know which MCP revisions an endpoint permits before it can enforce revision-specific wire behavior. Stable MCP revisions are not interchangeable. For example,
2025-03-26permits JSON-RPC batches, while2025-06-18and2025-11-25prohibit top-level arrays. An endpoint policy without an exact revision set cannot determine whether the same message is valid.Adding a versions field to one API is not enough. OpenShell must keep the same values when it accepts policy YAML or protobuf, saves a revision, combines policies, calculates a policy hash, returns sandbox configuration, or converts SDK objects. If any path drops or changes a value, the active policy may permit a revision the operator did not approve. Different ordering of the same values must not create a false policy change.
Impact / Why This Matters
Without an exact allowlist, later enforcement has two unsafe choices. It can reject traffic that is valid for an older permitted revision, or accept traffic that a newer revision forbids. Either choice substitutes an OpenShell assumption for the operator's policy.
The allowlist passes through several policy inputs and outputs. If those paths handle omission, ordering, or values differently, an operator cannot tell what OpenShell will enforce. Omission is especially risky if it means every known revision, because adding a revision in a later OpenShell release would silently widen an existing policy.
Proposed Design
MCP endpoint policy exposes
mcp.versionsas an exact, nonempty allowlist selected from a closed stable-revision registry. The initial supported revisions are2025-03-26,2025-06-18, and2025-11-25.When
mcp.versionsis omitted, OpenShell materializes exactly["2025-11-25"]. The compatibility default is pinned to that one revision. It does not resolve a movinglatestidentifier or expand to all registered revisions, so adding a future revision cannot widen an existing policy.OpenShell rejects empty allowlists, duplicate entries, padded strings, unknown revisions, draft identifiers, and moving identifiers such as
latest. It stores and returns valid sets in fixed registry order. Two policies that name the same revisions therefore produce the same saved form and deterministic hash, regardless of authoring order.Every supported policy input and output preserves all allowlist values. This includes policy YAML, protobuf APIs, provider profiles, stored revisions, combined policies, sandbox configuration, and Go SDK conversion. The deterministic hash uses that same fixed-order form. MCP options are valid only on MCP endpoints.
OpenShell refuses to activate a policy whose MCP revision state is invalid. A history list still returns the affected revision with its own load error instead of failing the whole page. A valid global policy may supply the effective policy while invalid local history remains inactive, so an operator can inspect and replace the bad revision without serving it.
Acceptance Criteria
Policy authoring and validation:
2025-03-26,2025-06-18, and2025-11-25, and OpenShell exposes the same set in deterministic registry order.mcp.versionsproduces exactly["2025-11-25"], and registering a later revision does not change the meaning or identity of that existing policy.latestrevision values are rejected with an error that identifies the invalid MCP revision constraint.Save, merge, load, and identity:
Alternatives Considered
Treating every MCP endpoint as supporting every known revision would silently widen existing policy whenever the registry grows. It also cannot express deployments that intentionally support only one revision's wire rules.
Using a moving value such as
latestwould change the meaning of the same stored policy across OpenShell releases. Policy review and rollback would become unreliable, and operators could not use a stable hash during incident analysis.Adding the field only where OpenShell enforces traffic would leave save, merge, and output paths free to disagree or discard the operator's values.
Agent Investigation
No response
Checklist