The hosted MCP end to end (README.md beside it): a real MCP client (the TypeScript SDK) does the OAuth dance against a running dev server with a virtual passkey in headless Chromium, lists tools, plays a site song into the signed-in tab (in its sandbox, charged to that listener's budget), publishes a song Jev screens, and a second listener cannot reach the first's tab. Exits nonzero on any failed check.
7import { browser, check, fail, log, MCP, person, SITE } from './person.mjs';
9try { 10 const link = await person('Link', 'e2ea1', 33401); 11 const tools = (await link.client.listTools()).tools.map((t) => t.name); 12 check(tools.includes('play_code') && tools.includes('publish_song') && tools.includes('list_sounds') && tools.length === 20, `tools listed: ${tools.join(', ')}`); 13 14 const status = await link.tool('get_status'); 15 check(status.text.includes('e2ea1'), `get_status lists the tab: ${status.text.replace(/\n/g, ' | ')}`); 16 17 const songs = JSON.parse((await link.tool('list_songs')).text); 18 check(songs.site.length > 5, `list_songs: ${songs.site.length} site songs, ${songs.listeners.length} listener songs`); 19 const dialUp = JSON.parse((await link.tool('get_song', { id: 'jev/dial-up' })).text); 20 check(dialUp.code.includes('jev('), `get_song jev/dial-up: ${dialUp.code.length} bytes of code, ${dialUp.spec.length} of spec`); 21 22 const before = await link.budget(); 23 const asks = []; 24 link.page.on('request', (r) => { 25 if (r.url().endsWith('/jev/v1/systemone')) asks.push(r); 26 }); 27 const played = await link.tool('play_code', { code: dialUp.code }); 28 check(!played.isError, `play_code: ${played.text}`); 29 const s1 = await link.tool('get_status'); 30 await link.page.waitForTimeout(6000); 31 const s2 = await link.tool('get_status'); 32 const cycle = (t) => Number(/cycle ([\d.]+)/.exec(t)?.[1]); 33 log('status then:', s1.text.split('\n')[1], '| now:', s2.text.split('\n')[1]); 34 check(cycle(s2.text) > cycle(s1.text), `the scheduler advances (${cycle(s1.text)} → ${cycle(s2.text)})`); 35 check(s2.text.includes("an AI's code (sandboxed)"), 'status says the AI code is sandboxed');
it is in the sandbox, not the page
38 const where = await link.page.evaluate(() => ({ 39 frames: [...document.querySelectorAll('iframe[sandbox]')].map((f) => f.getAttribute('sandbox')), 40 notice: document.querySelector('[data-sandbox]')?.textContent ?? null, 41 pageStarted: Boolean(window.strudelMirror?.repl?.scheduler?.started), 42 })); 43 check(where.frames.includes('allow-scripts') && !where.frames.some((s) => s.includes('allow-same-origin')), `plays in an opaque sandbox frame (${JSON.stringify(where.frames)})`); 44 check(where.notice?.includes('Your AI'), `the notice says so: ${where.notice?.slice(0, 90)}`); 45 check(!where.pageStarted, "the page's own scheduler is not playing it");
47 const logs = await link.tool('get_logs'); 48 log('get_logs:\n' + logs.text.split('\n').filter((l) => !l.includes('load sound')).slice(0, 25).join('\n')); 49 check(/\[sampler\] load sound/.test(logs.text), 'the logs show its samples loading'); 50 check(/\[jev\]/.test(logs.text), "the logs show Jev's answers"); 51 const after = await link.budget(); 52 const heads = await Promise.all(asks.map((r) => r.allHeaders())); 53 const answers = await Promise.all(asks.map(async (r) => (await r.response())?.headers() ?? {})); 54 check( 55 heads.length > 0 && heads.every((h) => /jev_session=/.test(h.cookie ?? '') && h.origin === SITE), 56 `the sandbox's ${heads.length} Jev calls went out from the page with Link's session`, 57 ); 58 log('their answers:', answers.map((h) => `cache ${h['jev-cache']}, budget ${h['jev-budget'] ?? '-'}`).join('; ')); 59 check(after >= before && answers.every((h) => h['jev-cache'] === 'hit' || /^\d+\/\d+$/.test(h['jev-budget'] ?? '')), `every forwarded call was charged to Link's budget (${before} → ${after}; cached answers are free)`); 60 61 const code = await link.tool('get_currently_playing_code'); 62 check(code.text === dialUp.code, 'get_currently_playing_code returns what it played'); 63 const bad = await link.tool('play_code', { code: 's("bd" ' }); 64 check(bad.isError, `a broken song's error comes back: ${bad.text.slice(0, 120)}`); 65 const stopped = await link.tool('stop_play'); 66 check(!stopped.isError, stopped.text); 67 68 const published = await link.tool('publish_song', { 69 title: 'E2E Heartbeat', 70 description: 'a kick and a hat, to prove the pipe', 71 spec: '# E2E Heartbeat\n\nA steady four on the floor with hats, published by an AI over MCP.', 72 code: 'setcps(120/60/4)\nstack(s("bd*4"), s("~ hh ~ hh"))', 73 }); 74 log('publish_song:', published.text); 75 const pub = JSON.parse(published.text); 76 check(!published.isError && ['public', 'pending', 'held'].includes(pub.status), `published ${pub.id}: ${pub.status}`); 77 const mine = JSON.parse((await link.tool('my_content')).text); 78 check(mine.revisions.some((r) => `listener:${r.song}` === pub.id), 'my_content lists it');
a second person
81 const zelda = await person('Zelda', 'e2eb2', 33402); 82 const zs = await zelda.tool('get_status'); 83 check(zs.text.includes('e2eb2') && !zs.text.includes('e2ea1'), `Zelda sees only her tab: ${zs.text.replace(/\n/g, ' | ')}`); 84 const ls = await link.tool('get_status'); 85 check(ls.text.includes('e2ea1') && !ls.text.includes('e2eb2'), `Link sees only his: ${ls.text.replace(/\n/g, ' | ')}`); 86 const reach = await zelda.tool('play_code', { code: 's("hh*8")', session_id: 'e2ea1' }); 87 check(reach.isError && /no open tab e2ea1/.test(reach.text), `Zelda cannot play in Link's tab: ${reach.text}`); 88 const linkCode = await link.tool('get_currently_playing_code'); 89 check(linkCode.text === 's("bd" ', "Link's tab is untouched (still his AI's last code)");
disconnecting in the account panel ends the token
92 const apps = await link.page.evaluate(() => fetch('/jev/me/apps').then((r) => r.json())); 93 check(apps.apps.length === 1 && apps.apps[0].app === 'E2E AI for Link', `connected apps: ${JSON.stringify(apps.apps.map((a) => a.app))}`); 94 await link.page.evaluate((id) => fetch(`/jev/me/apps/${id}`, { method: 'DELETE' }), apps.apps[0].id); 95 const raw = await fetch(MCP, { 96 method: 'POST', 97 headers: { Authorization: `Bearer ${link.provider.store.tokens.access_token}`, 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream' }, 98 body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' }), 99 }); 100 check(raw.status === 401, `after disconnecting, the token is refused (${raw.status})`); 101} catch (e) { 102 fail(e.stack ?? e); 103} finally { 104 await browser.close(); 105}