mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-29 07:23:25 +08:00
* feat(mcp): add version-aware wire profile metadata Signed-off-by: Shiju <shiju@nvidia.com> * feat(policy): canonicalize MCP version allowlists Signed-off-by: Shiju <shiju@nvidia.com> * fix(policy): align MCP policy tests with current main Signed-off-by: Shiju <shiju@nvidia.com> * fix(policy): canonicalize supervisor protobuf ingress Materialize defaultable MCP revisions before ambiguity checks, OPA construction, and sidecar delivery. Reject invalid sidecar policies with bounded errors. Signed-off-by: Shiju <shiju@nvidia.com> --------- Signed-off-by: Shiju <shiju@nvidia.com>
88 lines
3.1 KiB
Python
88 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""Render the OpenShell client policy for a conformance server URL.
|
|
|
|
Parses the server URL into host/port/path, substitutes them into
|
|
policy-template.yaml, writes the rendered policy, and prints the (possibly
|
|
rewritten) URL the client should connect to. Used both to seed the reusable
|
|
client sandbox with a placeholder policy and to install the per-scenario policy
|
|
in client-through-openshell.sh.
|
|
|
|
Usage: render-policy.py <server-url> <policy-file> <policy-template> <mcp-version>
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import string
|
|
import sys
|
|
from ipaddress import ip_address
|
|
from pathlib import Path
|
|
from urllib.parse import urlparse, urlunparse
|
|
|
|
if len(sys.argv) != 5:
|
|
raise SystemExit(
|
|
"usage: render-policy.py <server-url> <policy-file> "
|
|
"<policy-template> <mcp-version>"
|
|
)
|
|
|
|
raw_url, policy_file, policy_template, mcp_version = sys.argv[1:5]
|
|
# Keep this boundary check synchronized with McpProtocolVersion::ALL. The
|
|
# renderer is standalone Python, so it cannot import the Rust registry.
|
|
supported_mcp_versions = {"2025-03-26", "2025-06-18", "2025-11-25"}
|
|
if mcp_version not in supported_mcp_versions:
|
|
raise SystemExit(f"unsupported MCP protocol version: {mcp_version!r}")
|
|
parsed = urlparse(raw_url)
|
|
|
|
if parsed.scheme not in ("http", "https"):
|
|
raise SystemExit(f"unsupported conformance server URL scheme: {parsed.scheme!r}")
|
|
|
|
host = parsed.hostname
|
|
if not host:
|
|
raise SystemExit(f"conformance server URL is missing a host: {raw_url}")
|
|
|
|
expected_host = os.environ.get("OPENSHELL_MCP_CONFORMANCE_EXPECTED_SERVER_HOST")
|
|
if expected_host:
|
|
try:
|
|
actual_ip = ip_address(host)
|
|
actual_ip = getattr(actual_ip, "ipv4_mapped", None) or actual_ip
|
|
expected_ip = ip_address(expected_host)
|
|
expected_ip = getattr(expected_ip, "ipv4_mapped", None) or expected_ip
|
|
except ValueError as err:
|
|
raise SystemExit(
|
|
"conformance server URL host must be an IP address when "
|
|
"OPENSHELL_MCP_CONFORMANCE_EXPECTED_SERVER_HOST is set"
|
|
) from err
|
|
if actual_ip != expected_ip:
|
|
raise SystemExit(
|
|
f"conformance server URL host {actual_ip} does not match expected "
|
|
f"runner host {expected_ip}"
|
|
)
|
|
|
|
target_host = (
|
|
"host.openshell.internal" if host in {"localhost", "127.0.0.1", "::1"} else host
|
|
)
|
|
port = parsed.port or (443 if parsed.scheme == "https" else 80)
|
|
path = parsed.path or "/"
|
|
netloc_host = (
|
|
f"[{target_host}]"
|
|
if ":" in target_host and not target_host.startswith("[")
|
|
else target_host
|
|
)
|
|
netloc = f"{netloc_host}:{port}"
|
|
rewritten = urlunparse(
|
|
(parsed.scheme, netloc, path, parsed.params, parsed.query, parsed.fragment)
|
|
)
|
|
|
|
template = string.Template(Path(policy_template).read_text(encoding="utf-8"))
|
|
policy = template.substitute(
|
|
host_spec=f"host: {json.dumps(target_host)}",
|
|
port_spec=f" port: {port}",
|
|
path=json.dumps(path),
|
|
mcp_version=json.dumps(mcp_version),
|
|
)
|
|
Path(policy_file).write_text(policy, encoding="utf-8")
|
|
|
|
print(rewritten)
|