dev: add generic mcp_source.py helper to switch MCP package source (upstream/remote/local)
parent
b6b8d47dfe
commit
cd707284d7
|
|
@ -0,0 +1,241 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Generic helper to switch the Unity MCP package source in a Unity project's
|
||||||
|
Packages/manifest.json without embedding any personal paths.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python mcp_source.py [--manifest /abs/path/to/manifest.json] [--repo /abs/path/to/unity-mcp] [--choice 1|2|3]
|
||||||
|
|
||||||
|
Choices:
|
||||||
|
1) Upstream main (CoplayDev/unity-mcp)
|
||||||
|
2) Your remote current branch (derived from `origin` and current branch)
|
||||||
|
3) Local repo workspace (file: URL to UnityMcpBridge in your checkout)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
PKG_NAME = "com.coplaydev.unity-mcp"
|
||||||
|
BRIDGE_SUBPATH = "UnityMcpBridge"
|
||||||
|
|
||||||
|
|
||||||
|
def run_git(repo: pathlib.Path, *args: str) -> str:
|
||||||
|
result = subprocess.run([
|
||||||
|
"git", "-C", str(repo), *args
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise RuntimeError(result.stderr.strip() or f"git {' '.join(args)} failed")
|
||||||
|
return result.stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_origin_to_https(url: str) -> str:
|
||||||
|
"""Map common SSH origin forms to https for Unity's git URL scheme."""
|
||||||
|
if url.startswith("git@github.com:"):
|
||||||
|
owner_repo = url.split(":", 1)[1]
|
||||||
|
if owner_repo.endswith(".git"):
|
||||||
|
owner_repo = owner_repo[:-4]
|
||||||
|
return f"https://github.com/{owner_repo}.git"
|
||||||
|
# already https or file: etc.
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def detect_repo_root(explicit: Optional[str]) -> pathlib.Path:
|
||||||
|
if explicit:
|
||||||
|
return pathlib.Path(explicit).resolve()
|
||||||
|
# Prefer the git toplevel from the script's directory
|
||||||
|
here = pathlib.Path(__file__).resolve().parent
|
||||||
|
try:
|
||||||
|
top = run_git(here, "rev-parse", "--show-toplevel")
|
||||||
|
return pathlib.Path(top)
|
||||||
|
except Exception:
|
||||||
|
return here
|
||||||
|
|
||||||
|
|
||||||
|
def detect_branch(repo: pathlib.Path) -> str:
|
||||||
|
return run_git(repo, "rev-parse", "--abbrev-ref", "HEAD")
|
||||||
|
|
||||||
|
|
||||||
|
def detect_origin(repo: pathlib.Path) -> str:
|
||||||
|
url = run_git(repo, "remote", "get-url", "origin")
|
||||||
|
return normalize_origin_to_https(url)
|
||||||
|
|
||||||
|
|
||||||
|
def find_manifest(explicit: Optional[str]) -> pathlib.Path:
|
||||||
|
if explicit:
|
||||||
|
return pathlib.Path(explicit).resolve()
|
||||||
|
# Walk up from CWD looking for Packages/manifest.json
|
||||||
|
cur = pathlib.Path.cwd().resolve()
|
||||||
|
for parent in [cur, *cur.parents]:
|
||||||
|
candidate = parent / "Packages" / "manifest.json"
|
||||||
|
if candidate.exists():
|
||||||
|
return candidate
|
||||||
|
raise FileNotFoundError("Could not find Packages/manifest.json from current directory. Use --manifest to specify a path.")
|
||||||
|
|
||||||
|
|
||||||
|
def read_json(path: pathlib.Path) -> dict:
|
||||||
|
with path.open("r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def write_json(path: pathlib.Path, data: dict) -> None:
|
||||||
|
with path.open("w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
f.write("\n")
|
||||||
|
|
||||||
|
|
||||||
|
def build_options(repo_root: pathlib.Path, branch: str, origin_https: str):
|
||||||
|
upstream = "https://github.com/CoplayDev/unity-mcp.git?path=/UnityMcpBridge"
|
||||||
|
# Ensure origin is https
|
||||||
|
origin = origin_https
|
||||||
|
# If origin is a local file path or non-https, try to coerce to https github if possible
|
||||||
|
if origin.startswith("file:"):
|
||||||
|
# Not meaningful for remote option; keep upstream
|
||||||
|
origin_remote = upstream
|
||||||
|
else:
|
||||||
|
origin_remote = origin
|
||||||
|
return [
|
||||||
|
("[1] Upstream main", upstream),
|
||||||
|
("[2] Remote current branch", f"{origin_remote}?path=/{BRIDGE_SUBPATH}#{branch}"),
|
||||||
|
("[3] Local workspace", f"file:{(repo_root / BRIDGE_SUBPATH).as_posix()}"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
p = argparse.ArgumentParser(description="Switch Unity MCP package source")
|
||||||
|
p.add_argument("--manifest", help="Path to Packages/manifest.json")
|
||||||
|
p.add_argument("--repo", help="Path to unity-mcp repo root (for local file option)")
|
||||||
|
p.add_argument("--choice", choices=["1", "2", "3"], help="Pick option non-interactively")
|
||||||
|
return p.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
args = parse_args()
|
||||||
|
try:
|
||||||
|
repo_root = detect_repo_root(args.repo)
|
||||||
|
branch = detect_branch(repo_root)
|
||||||
|
origin = detect_origin(repo_root)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
options = build_options(repo_root, branch, origin)
|
||||||
|
|
||||||
|
try:
|
||||||
|
manifest_path = find_manifest(args.manifest)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print("Select MCP package source by number:")
|
||||||
|
for label, _ in options:
|
||||||
|
print(label)
|
||||||
|
|
||||||
|
if args.choice:
|
||||||
|
choice = args.choice
|
||||||
|
else:
|
||||||
|
choice = input("Enter 1-3: ").strip()
|
||||||
|
|
||||||
|
if choice not in {"1", "2", "3"}:
|
||||||
|
print("Invalid selection.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
idx = int(choice) - 1
|
||||||
|
_, chosen = options[idx]
|
||||||
|
|
||||||
|
data = read_json(manifest_path)
|
||||||
|
deps = data.get("dependencies", {})
|
||||||
|
if PKG_NAME not in deps:
|
||||||
|
print(f"Error: '{PKG_NAME}' not found in manifest dependencies.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"\nUpdating {PKG_NAME} → {chosen}")
|
||||||
|
deps[PKG_NAME] = chosen
|
||||||
|
data["dependencies"] = deps
|
||||||
|
write_json(manifest_path, data)
|
||||||
|
print(f"Done. Wrote to: {manifest_path}")
|
||||||
|
print("Tip: In Unity, open Package Manager and Refresh to re-resolve packages.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Defaults for your environment
|
||||||
|
UNITY_PROJECT = "/Users/davidsarno/ramble" # change if needed
|
||||||
|
MANIFEST = os.path.join(UNITY_PROJECT, "Packages", "manifest.json")
|
||||||
|
LOCAL_REPO = "/Users/davidsarno/unity-mcp" # local repo root
|
||||||
|
PKG_NAME = "com.coplaydev.unity-mcp"
|
||||||
|
|
||||||
|
def get_current_branch(repo_path: str) -> str:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "-C", repo_path, "rev-parse", "--abbrev-ref", "HEAD"],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print("Error: unable to detect current branch from local repo.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
return result.stdout.strip()
|
||||||
|
|
||||||
|
def build_options(branch: str):
|
||||||
|
return [
|
||||||
|
("[1] Upstream main", "https://github.com/CoplayDev/unity-mcp.git?path=/UnityMcpBridge"),
|
||||||
|
(f"[2] Remote {branch}", f"https://github.com/dsarno/unity-mcp.git?path=/UnityMcpBridge#{branch}"),
|
||||||
|
(f"[3] Local {branch}", f"file:{os.path.join(LOCAL_REPO, 'UnityMcpBridge')}"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def read_manifest(path: str):
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
def write_manifest(path: str, data: dict):
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
f.write("\n")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Allow overrides via args: python mcp_source.py [manifest.json] [local_repo]
|
||||||
|
manifest_path = MANIFEST if len(sys.argv) < 2 else sys.argv[1]
|
||||||
|
repo_path = LOCAL_REPO if len(sys.argv) < 3 else sys.argv[2]
|
||||||
|
|
||||||
|
branch = get_current_branch(repo_path)
|
||||||
|
options = build_options(branch)
|
||||||
|
|
||||||
|
print("Select MCP package source by number:")
|
||||||
|
for label, _ in options:
|
||||||
|
print(label)
|
||||||
|
choice = input("Enter 1-3: ").strip()
|
||||||
|
if choice not in {"1", "2", "3"}:
|
||||||
|
print("Invalid selection.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
idx = int(choice) - 1
|
||||||
|
_, chosen = options[idx]
|
||||||
|
|
||||||
|
data = read_manifest(manifest_path)
|
||||||
|
deps = data.get("dependencies", {})
|
||||||
|
if PKG_NAME not in deps:
|
||||||
|
print(f"Error: '{PKG_NAME}' not found in manifest dependencies.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"\nUpdating {PKG_NAME} → {chosen}")
|
||||||
|
deps[PKG_NAME] = chosen
|
||||||
|
data["dependencies"] = deps
|
||||||
|
write_manifest(manifest_path, data)
|
||||||
|
print(f"Done. Wrote to: {manifest_path}")
|
||||||
|
print("Tip: In Unity, open Package Manager and Refresh to re-resolve packages.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue