Skip to content

Commit cd70728

Browse files
committed
dev: add generic mcp_source.py helper to switch MCP package source (upstream/remote/local)
1 parent b6b8d47 commit cd70728

File tree

1 file changed

+241
-0
lines changed

1 file changed

+241
-0
lines changed

mcp_source.py

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Generic helper to switch the Unity MCP package source in a Unity project's
4+
Packages/manifest.json without embedding any personal paths.
5+
6+
Usage:
7+
python mcp_source.py [--manifest /abs/path/to/manifest.json] [--repo /abs/path/to/unity-mcp] [--choice 1|2|3]
8+
9+
Choices:
10+
1) Upstream main (CoplayDev/unity-mcp)
11+
2) Your remote current branch (derived from `origin` and current branch)
12+
3) Local repo workspace (file: URL to UnityMcpBridge in your checkout)
13+
"""
14+
15+
from __future__ import annotations
16+
17+
import argparse
18+
import json
19+
import os
20+
import pathlib
21+
import re
22+
import subprocess
23+
import sys
24+
from typing import Optional
25+
26+
PKG_NAME = "com.coplaydev.unity-mcp"
27+
BRIDGE_SUBPATH = "UnityMcpBridge"
28+
29+
30+
def run_git(repo: pathlib.Path, *args: str) -> str:
31+
result = subprocess.run([
32+
"git", "-C", str(repo), *args
33+
], capture_output=True, text=True)
34+
if result.returncode != 0:
35+
raise RuntimeError(result.stderr.strip() or f"git {' '.join(args)} failed")
36+
return result.stdout.strip()
37+
38+
39+
def normalize_origin_to_https(url: str) -> str:
40+
"""Map common SSH origin forms to https for Unity's git URL scheme."""
41+
if url.startswith("[email protected]:"):
42+
owner_repo = url.split(":", 1)[1]
43+
if owner_repo.endswith(".git"):
44+
owner_repo = owner_repo[:-4]
45+
return f"https://github.com/{owner_repo}.git"
46+
# already https or file: etc.
47+
return url
48+
49+
50+
def detect_repo_root(explicit: Optional[str]) -> pathlib.Path:
51+
if explicit:
52+
return pathlib.Path(explicit).resolve()
53+
# Prefer the git toplevel from the script's directory
54+
here = pathlib.Path(__file__).resolve().parent
55+
try:
56+
top = run_git(here, "rev-parse", "--show-toplevel")
57+
return pathlib.Path(top)
58+
except Exception:
59+
return here
60+
61+
62+
def detect_branch(repo: pathlib.Path) -> str:
63+
return run_git(repo, "rev-parse", "--abbrev-ref", "HEAD")
64+
65+
66+
def detect_origin(repo: pathlib.Path) -> str:
67+
url = run_git(repo, "remote", "get-url", "origin")
68+
return normalize_origin_to_https(url)
69+
70+
71+
def find_manifest(explicit: Optional[str]) -> pathlib.Path:
72+
if explicit:
73+
return pathlib.Path(explicit).resolve()
74+
# Walk up from CWD looking for Packages/manifest.json
75+
cur = pathlib.Path.cwd().resolve()
76+
for parent in [cur, *cur.parents]:
77+
candidate = parent / "Packages" / "manifest.json"
78+
if candidate.exists():
79+
return candidate
80+
raise FileNotFoundError("Could not find Packages/manifest.json from current directory. Use --manifest to specify a path.")
81+
82+
83+
def read_json(path: pathlib.Path) -> dict:
84+
with path.open("r", encoding="utf-8") as f:
85+
return json.load(f)
86+
87+
88+
def write_json(path: pathlib.Path, data: dict) -> None:
89+
with path.open("w", encoding="utf-8") as f:
90+
json.dump(data, f, indent=2)
91+
f.write("\n")
92+
93+
94+
def build_options(repo_root: pathlib.Path, branch: str, origin_https: str):
95+
upstream = "https://github.com/CoplayDev/unity-mcp.git?path=/UnityMcpBridge"
96+
# Ensure origin is https
97+
origin = origin_https
98+
# If origin is a local file path or non-https, try to coerce to https github if possible
99+
if origin.startswith("file:"):
100+
# Not meaningful for remote option; keep upstream
101+
origin_remote = upstream
102+
else:
103+
origin_remote = origin
104+
return [
105+
("[1] Upstream main", upstream),
106+
("[2] Remote current branch", f"{origin_remote}?path=/{BRIDGE_SUBPATH}#{branch}"),
107+
("[3] Local workspace", f"file:{(repo_root / BRIDGE_SUBPATH).as_posix()}"),
108+
]
109+
110+
111+
def parse_args() -> argparse.Namespace:
112+
p = argparse.ArgumentParser(description="Switch Unity MCP package source")
113+
p.add_argument("--manifest", help="Path to Packages/manifest.json")
114+
p.add_argument("--repo", help="Path to unity-mcp repo root (for local file option)")
115+
p.add_argument("--choice", choices=["1", "2", "3"], help="Pick option non-interactively")
116+
return p.parse_args()
117+
118+
119+
def main() -> None:
120+
args = parse_args()
121+
try:
122+
repo_root = detect_repo_root(args.repo)
123+
branch = detect_branch(repo_root)
124+
origin = detect_origin(repo_root)
125+
except Exception as e:
126+
print(f"Error: {e}", file=sys.stderr)
127+
sys.exit(1)
128+
129+
options = build_options(repo_root, branch, origin)
130+
131+
try:
132+
manifest_path = find_manifest(args.manifest)
133+
except Exception as e:
134+
print(f"Error: {e}", file=sys.stderr)
135+
sys.exit(1)
136+
137+
print("Select MCP package source by number:")
138+
for label, _ in options:
139+
print(label)
140+
141+
if args.choice:
142+
choice = args.choice
143+
else:
144+
choice = input("Enter 1-3: ").strip()
145+
146+
if choice not in {"1", "2", "3"}:
147+
print("Invalid selection.", file=sys.stderr)
148+
sys.exit(1)
149+
150+
idx = int(choice) - 1
151+
_, chosen = options[idx]
152+
153+
data = read_json(manifest_path)
154+
deps = data.get("dependencies", {})
155+
if PKG_NAME not in deps:
156+
print(f"Error: '{PKG_NAME}' not found in manifest dependencies.", file=sys.stderr)
157+
sys.exit(1)
158+
159+
print(f"\nUpdating {PKG_NAME}{chosen}")
160+
deps[PKG_NAME] = chosen
161+
data["dependencies"] = deps
162+
write_json(manifest_path, data)
163+
print(f"Done. Wrote to: {manifest_path}")
164+
print("Tip: In Unity, open Package Manager and Refresh to re-resolve packages.")
165+
166+
167+
if __name__ == "__main__":
168+
main()
169+
170+
#!/usr/bin/env python3
171+
import json
172+
import os
173+
import sys
174+
import subprocess
175+
176+
# Defaults for your environment
177+
UNITY_PROJECT = "/Users/davidsarno/ramble" # change if needed
178+
MANIFEST = os.path.join(UNITY_PROJECT, "Packages", "manifest.json")
179+
LOCAL_REPO = "/Users/davidsarno/unity-mcp" # local repo root
180+
PKG_NAME = "com.coplaydev.unity-mcp"
181+
182+
def get_current_branch(repo_path: str) -> str:
183+
result = subprocess.run(
184+
["git", "-C", repo_path, "rev-parse", "--abbrev-ref", "HEAD"],
185+
capture_output=True, text=True
186+
)
187+
if result.returncode != 0:
188+
print("Error: unable to detect current branch from local repo.", file=sys.stderr)
189+
sys.exit(1)
190+
return result.stdout.strip()
191+
192+
def build_options(branch: str):
193+
return [
194+
("[1] Upstream main", "https://github.com/CoplayDev/unity-mcp.git?path=/UnityMcpBridge"),
195+
(f"[2] Remote {branch}", f"https://github.com/dsarno/unity-mcp.git?path=/UnityMcpBridge#{branch}"),
196+
(f"[3] Local {branch}", f"file:{os.path.join(LOCAL_REPO, 'UnityMcpBridge')}"),
197+
]
198+
199+
def read_manifest(path: str):
200+
with open(path, "r", encoding="utf-8") as f:
201+
return json.load(f)
202+
203+
def write_manifest(path: str, data: dict):
204+
with open(path, "w", encoding="utf-8") as f:
205+
json.dump(data, f, indent=2)
206+
f.write("\n")
207+
208+
def main():
209+
# Allow overrides via args: python mcp_source.py [manifest.json] [local_repo]
210+
manifest_path = MANIFEST if len(sys.argv) < 2 else sys.argv[1]
211+
repo_path = LOCAL_REPO if len(sys.argv) < 3 else sys.argv[2]
212+
213+
branch = get_current_branch(repo_path)
214+
options = build_options(branch)
215+
216+
print("Select MCP package source by number:")
217+
for label, _ in options:
218+
print(label)
219+
choice = input("Enter 1-3: ").strip()
220+
if choice not in {"1", "2", "3"}:
221+
print("Invalid selection.", file=sys.stderr)
222+
sys.exit(1)
223+
224+
idx = int(choice) - 1
225+
_, chosen = options[idx]
226+
227+
data = read_manifest(manifest_path)
228+
deps = data.get("dependencies", {})
229+
if PKG_NAME not in deps:
230+
print(f"Error: '{PKG_NAME}' not found in manifest dependencies.", file=sys.stderr)
231+
sys.exit(1)
232+
233+
print(f"\nUpdating {PKG_NAME}{chosen}")
234+
deps[PKG_NAME] = chosen
235+
data["dependencies"] = deps
236+
write_manifest(manifest_path, data)
237+
print(f"Done. Wrote to: {manifest_path}")
238+
print("Tip: In Unity, open Package Manager and Refresh to re-resolve packages.")
239+
240+
if __name__ == "__main__":
241+
main()

0 commit comments

Comments
 (0)