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"\n Updating { 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"\n Updating { 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