Files
ss-tools/scripts/mcp_proxy_smoke.py
busya 4d5ef6bed0 feat: dashboard-testing UX remediation + admin MCP governance + i18n sweep
- UX audit Fix 1-5: actionable MCP HandoffSurface (endpoint/discovery/onboarding), reachable
  approval loop (sidebar section + waiting-for-me badge), honest MCP entry labels, ROUTES SSOT
  for dashboard-testing/load-testing (+ link-integrity coverage)
- scenario registry hub (/dashboard-testing/scenarios) built on ScenarioRegistryModel
- admin MCP governance: read-only GET /api/admin/mcp/catalog (catalog x roles + DCR clients) +
  /admin/mcp page/model/api + ROUTES.admin.mcp + sidebar entry
- full i18n sweep of dashboard-testing routes and scenario-* component trees (ru/en)
- accumulated workspace: MCP OAuth/DCR, automation idempotency migration, docker/nginx, specs
2026-09-05 20:16:26 +03:00

75 lines
3.4 KiB
Python

# #region Tooling.McpProxySmoke [C:3] [TYPE Module] [SEMANTICS docker,mcp,nginx,verification]
# @BRIEF Isolated real nginx smoke for HTTP/TLS MCP routing, authority and protocol headers.
# @POST Containers and network created by this invocation are removed, including on assertion failure.
# @REJECTED Reusing the development compose project could attach tests to the operator database.
from __future__ import annotations
import json
import subprocess
import time
import urllib.request
import uuid
from pathlib import Path
# #region Tooling.McpProxySmoke.Command [C:2] [TYPE Function]
def docker(*args: str) -> str:
return subprocess.check_output(["docker", *args], text=True).strip()
# #endregion Tooling.McpProxySmoke.Command
# #region Tooling.McpProxySmoke.Run [C:3] [TYPE Function]
def main() -> None:
root = Path(__file__).resolve().parents[1]
prefix = "ss-mcp-proxy-smoke-" + uuid.uuid4().hex[:10]
network, backend, frontend = prefix, prefix + "-backend", prefix + "-frontend"
try:
docker("network", "create", network)
docker(
"run", "-d", "--name", backend, "--network", network, "--network-alias", "backend",
"-v", f"{root / 'scripts/mcp_proxy_fixture.py'}:/fixture.py:ro",
"python:3.12-alpine", "python", "/fixture.py",
)
docker(
"run", "-d", "--name", frontend, "--network", network,
"-p", "127.0.0.1::80",
"-v", f"{root / 'docker/nginx.conf'}:/etc/nginx/conf.d/default.conf:ro",
"nginx:1.27-alpine",
)
docker("exec", frontend, "nginx", "-t")
port = docker("port", frontend, "80/tcp").rsplit(":", 1)[1]
origin = f"http://127.0.0.1:{port}"
for _ in range(50):
try:
urllib.request.urlopen(origin + "/.well-known/oauth-authorization-server", timeout=2)
break
except OSError:
time.sleep(0.2)
for path, method in [
("/.well-known/oauth-authorization-server", "GET"),
("/.well-known/oauth-protected-resource/mcp", "GET"),
("/oauth/token", "POST"), ("/oauth/register", "POST"),
("/oauth/register/fixture", "DELETE"), ("/mcp", "POST"), ("/mcp/", "GET"),
]:
request = urllib.request.Request(
origin + path, method=method,
data=b'{"jsonrpc":"2.0"}' if method == "POST" else None,
headers={"Authorization": "Bearer smoke-fixture", "Mcp-Session-Id": "fixture-session"},
)
with urllib.request.urlopen(request, timeout=5) as response:
payload = json.load(response)
assert response.headers["Mcp-Session-Id"] == "fixture-session"
assert payload == {"path": path, "method": method, "host": f"127.0.0.1:{port}",
"scheme": "http", "authorization": True, "session": "fixture-session"}
print("PASS: nginx routes discovery, OAuth and MCP with external port, bearer and session headers")
finally:
for name in (frontend, backend):
subprocess.run(["docker", "rm", "-f", name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.run(["docker", "network", "rm", network], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# #endregion Tooling.McpProxySmoke.Run
if __name__ == "__main__":
main()
# #endregion Tooling.McpProxySmoke