import urllib.request, urllib.parse, json, hashlib, base64, re, sys, pathlib, datetime, subprocess
side, checkout, log, output_root = sys.argv[1:]
out=pathlib.Path(output_root)/(side+'-'+datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ'));out.mkdir()
logtext=pathlib.Path(log).read_text(); port=re.search(r'serverPort=(\d+)',logtext)[1]; origin='http://127.0.0.1:'+port
issued=json.loads(subprocess.check_output(['node','apps/server/src/bin.ts','auth','pairing','create','--base-dir',str(pathlib.Path(checkout)/'.t3'),'--json'],cwd=checkout,text=True))
pairing=issued.get('credential',issued.get('token'))
session=None
def post(path, body, token=None, form=False):
    global session
    headers={'Content-Type':'application/x-www-form-urlencoded' if form else 'application/json','Accept':'application/json, text/event-stream'}
    if token: headers['Authorization']='Bearer '+token
    if token and session: headers.update({'mcp-session-id':session,'mcp-protocol-version':'2025-06-18'})
    data=urllib.parse.urlencode(body).encode() if form else json.dumps(body).encode()
    with urllib.request.urlopen(urllib.request.Request(origin+path,data=data,headers=headers),timeout=90) as r:
        if r.headers.get('mcp-session-id'): session=r.headers.get('mcp-session-id')
        txt=r.read().decode()
        if txt.startswith('event:') or txt.startswith('data:'):
            return json.loads(next(x[6:] for x in txt.splitlines() if x.startswith('data: ')))
        return json.loads(txt)
client=post('/oauth/mcp/register',{'client_name':'External MCP proof','redirect_uris':['http://localhost:51234/callback'],'grant_types':['authorization_code','refresh_token'],'token_endpoint_auth_method':'none'})
verifier='proof-external-client-'+'a'*43
challenge=base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).decode().rstrip('=')
auth={'response_type':'code','client_id':client['client_id'],'redirect_uri':'http://localhost:51234/callback','code_challenge':challenge,'code_challenge_method':'S256','state':'proof','resource':origin+'/mcp'}
approval=post('/oauth/mcp/decision',{'authorization':auth,'decision':{'_tag':'pairing-code','access':'full-access','code':pairing}})
code=urllib.parse.parse_qs(urllib.parse.urlparse(approval['redirectTo']).query)['code'][0]
tokens=post('/oauth/mcp/token',{'grant_type':'authorization_code','code':code,'client_id':client['client_id'],'redirect_uri':auth['redirect_uri'],'code_verifier':verifier,'resource':origin+'/mcp'},form=True)
token=tokens['access_token']; i=0
records=[]
def rpc(method, params):
    global i
    i+=1
    return post('/mcp',{'jsonrpc':'2.0','id':i,'method':method,'params':params},token)
init=rpc('initialize',{'protocolVersion':'2025-06-18','capabilities':{},'clientInfo':{'name':'External MCP proof','version':'1.0'}})
tools=rpc('tools/list',{})['result']['tools']
names=['t3_thread_list','delegate_task','preview_status','device_list','t3_thread_launch','t3_thread_wait','t3_thread_read']
records.append({'action':'OAuth approval','access':'full-access','scopes':tokens.get('scope'),'callingThread':None,'transport':'HTTP MCP to real development server','database':'fresh, no seed'})
records.append({'action':'tools/list','advertised': [t['name'] for t in tools if t['name'] in names]})
for name,args in [('t3_thread_list',{}),('t3_thread_list',{'allProjects':True}),('delegate_task',{'task':'Return a short greeting.'}),('preview_status',{}),('device_list',{})]:
    result=rpc('tools/call',{'name':name,'arguments':args})
    records.append({'tool':name,'arguments':args,'response':result})
guide=(pathlib.Path(checkout)/'docs/user/outside-agents.md').read_text()
start=guide.find('## What outside agents can call')
excerpt=guide[start:guide.find('\n## ',start+3)] if start>=0 else '(No “What outside agents can call” section on this commit.)'
sha=subprocess.check_output(['git','-C',checkout,'rev-parse','HEAD'],text=True).strip()
def redact(value):
    if isinstance(value, dict):
        return {k: ('[private model identifier removed]' if k == 'model' else redact(v)) for k,v in value.items()}
    if isinstance(value, list): return [redact(x) for x in value]
    if isinstance(value, str) and value.startswith('{'):
        try: return json.dumps(redact(json.loads(value)),separators=(',',':'))
        except json.JSONDecodeError: pass
    return value
records=redact(records)
payload={'side':side,'commit':sha,'environment':'Linux; real dev server; fresh isolated database; HTTP OAuth external client; Full access; no calling thread','records':records,'outsideAgentGuide':excerpt}
(out/'transcript.json').write_text(json.dumps(payload,indent=2)+'\n')
(out/'transcript.txt').write_text('EXTERNAL MCP RUNTIME CAPTURE\nCommit: '+sha+'\nEnvironment: '+payload['environment']+'\n\n'+ '\n\n'.join(json.dumps(x,indent=2) for x in records)+'\n\nOUTSIDE-AGENT GUIDE\n'+excerpt+'\n')
print(out)
print((out/'transcript.txt').read_text())
