← Files SocialBuARCHIVED FILE

scripts/validate.py

5.46 KB · Nov 3, 2026 · 18:53 UTC

↓ Download file

#!/usr/bin/env python3
"""Validate SocialBu agent manifests without third-party dependencies."""

from __future__ import annotations

import argparse
import json
import sys
import urllib.error
import urllib.request
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
MCP_URL = "https://socialbu.com/mcp"


def load_json(path: str) -> dict:
    with (ROOT / path).open(encoding="utf-8") as handle:
        return json.load(handle)


def validate_files(release_tag: str = "") -> None:
    manifests = {
        path: load_json(path)
        for path in (
            "plugin.json",
            "mcp.json",
            ".mcp.json",
            "server.json",
            "gemini-extension.json",
            ".codex-plugin/plugin.json",
            ".claude-plugin/plugin.json",
            ".claude-plugin/marketplace.json",
            ".grok-plugin/plugin.json",
            ".grok-plugin/marketplace.json",
        )
    }

    version = manifests["plugin.json"]["version"]
    for path in (
        "plugin.json",
        "server.json",
        "gemini-extension.json",
        ".codex-plugin/plugin.json",
        ".claude-plugin/plugin.json",
        ".grok-plugin/plugin.json",
    ):
        assert manifests[path]["version"] == version, f"Version mismatch in {path}"

    assert manifests[".claude-plugin/marketplace.json"]["metadata"]["version"] == version
    if release_tag:
        assert release_tag == f"v{version}", "Release tag must match manifest versions"

    for path in (
        "plugin.json",
        ".codex-plugin/plugin.json",
        ".claude-plugin/plugin.json",
        ".grok-plugin/plugin.json",
    ):
        assert manifests[path]["name"] == "socialbu", f"Plugin name mismatch in {path}"

    for path in (".claude-plugin/marketplace.json", ".grok-plugin/marketplace.json"):
        assert manifests[path]["name"] == "socialbu-agent", f"Marketplace name mismatch in {path}"
        assert manifests[path]["plugins"][0]["name"] == "socialbu"

    assert manifests["gemini-extension.json"]["name"] == "socialbu-agent"
    assert manifests["gemini-extension.json"]["contextFileName"] == "GEMINI.md"
    assert manifests["mcp.json"]["mcpServers"]["socialbu"]["url"] == MCP_URL
    assert manifests[".mcp.json"]["mcpServers"]["socialbu"]["url"] == MCP_URL
    assert manifests[".codex-plugin/plugin.json"]["mcpServers"] == "./.mcp.json"
    assert manifests[".claude-plugin/plugin.json"]["mcpServers"] == "./.mcp.json"
    assert manifests["server.json"]["remotes"] == [
        {"type": "streamable-http", "url": MCP_URL}
    ]
    assert manifests["gemini-extension.json"]["mcpServers"]["socialbu"]["httpUrl"] == MCP_URL
    assert manifests[".grok-plugin/plugin.json"]["mcpServers"]["socialbu"]["url"] == MCP_URL

    plugin = manifests["plugin.json"]
    interface = plugin["extensions"]["com.openai"]["interface"]
    for field in ("displayName", "shortDescription"):
        assert 0 < len(interface[field]) <= 30, f"Invalid OpenAI listing length: {field}"
    for field in ("websiteURL", "supportURL", "privacyPolicyURL", "termsOfServiceURL"):
        assert interface[field].startswith("https://"), f"Missing HTTPS listing URL: {field}"
    for claude_field, openai_field in (
        ("documentationUrl", "websiteURL"),
        ("supportUrl", "supportURL"),
        ("privacyPolicyUrl", "privacyPolicyURL"),
        ("termsOfServiceUrl", "termsOfServiceURL"),
    ):
        assert manifests[".claude-plugin/plugin.json"][claude_field] == interface[openai_field]
    for asset in (interface["composerIcon"], interface["logo"]):
        assert (ROOT / asset).is_file(), f"Missing asset: {asset}"

    skill = (ROOT / "skills/socialbu/SKILL.md").read_text(encoding="utf-8")
    assert skill.startswith("---\nname: socialbu\n")
    assert "description:" in skill.split("---", 2)[1]
    assert "TODO" not in skill
    assert (ROOT / "GEMINI.md").is_file()
    assert (ROOT / "docs/publishing.md").is_file()
    assert (ROOT / "LICENSE").read_text(encoding="utf-8").startswith("MIT License\n")


def validate_live_endpoint() -> None:
    request = urllib.request.Request(MCP_URL, headers={"User-Agent": "SocialBu-Agent-Validation/1.0"}, method="GET")
    try:
        urllib.request.urlopen(request, timeout=15)
    except urllib.error.HTTPError as error:
        assert error.code == 401, f"Unexpected MCP status: {error.code}"
        assert 'resource_metadata="https://socialbu.com/.well-known/oauth-protected-resource"' in error.headers.get("WWW-Authenticate", ""), "Missing SocialBu OAuth challenge"
    except urllib.error.URLError as error:
        raise AssertionError(f"Could not reach {MCP_URL}: {error.reason}") from error
    else:
        raise AssertionError(f"{MCP_URL} unexpectedly allowed an unauthenticated GET")


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--live", action="store_true", help="also check the hosted MCP connection")
    parser.add_argument("--release-tag", default="", help="require manifests to match this v-prefixed tag")
    args = parser.parse_args()

    try:
        validate_files(args.release_tag)
        if args.live:
            validate_live_endpoint()
    except (AssertionError, KeyError, json.JSONDecodeError) as error:
        print(f"validation failed: {error}", file=sys.stderr)
        return 1

    print("SocialBu agent package is valid.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

SHA-256: 032289a535982f1054ea991dd6e045916c48c751410b74b72649c00f17a6b552