| """Sandbox testability verifier for MatrixHub catalog entries. |
| |
| The public Matrix Hub API exposes *metadata only* (id, name, type, source_url, |
| …) — it does NOT return a runnable build (no ``packages`` / ``mcp_registration`` |
| / exec command). The no-secrets sandbox therefore can only trial servers for |
| which we have a known, packaged, credential-free run command. |
| |
| This module is the single source of truth for "can this entry be tested in our |
| sandbox?". It is mirrored in the matrixhub UI by ``lib/sandboxEligibility.ts`` |
| (keep the two registries in sync). See |
| ``matrixhub/docs/SANDBOX_TESTABILITY.md`` for the full design + reproduction. |
| |
| Verdict: |
| { |
| "testable": bool, |
| "reason": str, # human-readable, shown as the UI tag tooltip |
| "runtime": "node|python", # only when testable |
| "transport": "stdio", # only when testable |
| "start_command": "...", # only when testable |
| } |
| """ |
| from __future__ import annotations |
|
|
| import re |
| from typing import Any |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| SANDBOX_REGISTRY: list[dict[str, Any]] = [ |
| {"key": "filesystem", "pattern": r"(?:mcp[-_])?server[-_]filesystem", "word": r"\bfilesystem\b", |
| "runtime": "node", "start_command": "npx -y @modelcontextprotocol/server-filesystem /tmp"}, |
| {"key": "everything", "pattern": r"(?:mcp[-_])?server[-_]everything", "word": r"\beverything\b", |
| "runtime": "node", "start_command": "npx -y @modelcontextprotocol/server-everything"}, |
| {"key": "memory", "pattern": r"(?:mcp[-_])?server[-_]memory", "word": r"\bmemory\b", |
| "runtime": "node", "start_command": "npx -y @modelcontextprotocol/server-memory"}, |
| {"key": "sequentialthinking", "pattern": r"sequential[-_]?thinking", "word": r"sequential[-_]?thinking", |
| "runtime": "node", "start_command": "npx -y @modelcontextprotocol/server-sequential-thinking"}, |
| {"key": "time", "pattern": r"(?:mcp[-_])?server[-_]time", "word": r"\btime\b", |
| "runtime": "python", "start_command": "uvx mcp-server-time"}, |
| {"key": "fetch", "pattern": r"(?:mcp[-_])?server[-_]fetch", "word": r"\bfetch\b", |
| "runtime": "python", "start_command": "uvx mcp-server-fetch"}, |
| {"key": "git", "pattern": r"(?:mcp[-_])?server[-_]git\b", "word": r"\bgit\b", |
| "runtime": "python", "start_command": "uvx mcp-server-git"}, |
| ] |
|
|
| OFFICIAL_RE = r"modelcontextprotocol" |
|
|
| |
| |
| BLOCKERS: list[dict[str, str]] = [ |
| {"pattern": r"retell|talentlms|microsoft|\boffice\b|openai|anthropic|slack|" |
| r"salesforce|notion|gmail|google|stripe|twilio|hubspot|jira|" |
| r"confluence|aws|azure|\bgcp\b|databricks|snowflake|watsonx|" |
| r"sendgrid|sentry|datadog|pagerduty|zendesk|shopify", |
| "reason": "SaaS connector — requires API credentials"}, |
| ] |
|
|
| NOT_PACKAGED_REASON = ( |
| "No packaged sandbox build available " |
| "(the public catalog exposes no credential-free run command)" |
| ) |
|
|
|
|
| def _haystack(entity: dict[str, Any]) -> str: |
| return " ".join(str(entity.get(k) or "") for k in ("id", "name", "source_url", "homepage")).lower() |
|
|
|
|
| def verify(entity: dict[str, Any]) -> dict[str, Any]: |
| """Return a sandbox-testability verdict for a catalog entity dict.""" |
| hay = _haystack(entity) |
|
|
| for b in BLOCKERS: |
| if re.search(b["pattern"], hay): |
| return {"testable": False, "reason": b["reason"]} |
|
|
| official = bool(re.search(OFFICIAL_RE, hay)) |
| for entry in SANDBOX_REGISTRY: |
| if re.search(entry["pattern"], hay) or (official and re.search(entry["word"], hay)): |
| return { |
| "testable": True, |
| "reason": f"packaged reference server ({entry['key']})", |
| "runtime": entry["runtime"], |
| "transport": "stdio", |
| "start_command": entry["start_command"], |
| } |
|
|
| return {"testable": False, "reason": NOT_PACKAGED_REASON} |
|
|
|
|
| def is_testable(entity: dict[str, Any]) -> bool: |
| return verify(entity)["testable"] |
|
|