import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
import { createHash, randomBytes } from 'node:crypto';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { createServer } from 'node:net';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { setTimeout as delay } from 'node:timers/promises';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
async function unusedPort() {
const server = createServer();
await new Promise((ok, fail) => {
server.once('error', fail);
server.listen(0, '127.0.0.1', ok);
});
const port = server.address().port;
await new Promise((ok) => server.close(ok));
return port;
}
async function smoke(command, args) {
const data = await mkdtemp(join(tmpdir(), 'colab-hosted-'));
const port = await unusedPort();
const origin = `http://127.0.0.1:${port}`;
const env = {
HOME: data,
PATH: process.env.PATH,
DATA_PATH: data,
EXTERNAL_URL: origin,
LISTEN: `127.0.0.1:${port}`,
PASSWORD: 'local-smoke-password',
MCP_ALLOWED_SUBJECT: 'password_user',
COLAB_GOOGLE_CLIENT_ID: 'mock-google-web-client',
COLAB_GOOGLE_CLIENT_SECRET: 'mock-google-web-secret',
};
const child = spawn(command, [...args, 'serve'], { env, stdio: ['ignore', 'pipe', 'pipe'] });
const closed = new Promise((ok, fail) => {
child.once('error', fail);
child.once('exit', (code) => ok(code));
});
let logs = '';
for (const stream of [child.stdout, child.stderr])
stream.on('data', (chunk) => {
logs = (logs + chunk).slice(-128 * 1024);
});
const cookies = new Map();
const request = async (path, options = {}) => {
const res = await fetch(new URL(path, origin), {
...options,
redirect: 'manual',
signal: AbortSignal.timeout(10_000),
headers: { cookie: [...cookies].map(([k, v]) => `${k}=${v}`).join('; '), ...options.headers },
});
for (const cookie of res.headers.getSetCookie()) {
const pair = cookie.split(';')[0],
at = pair.indexOf('=');
cookies.set(pair.slice(0, at), pair.slice(at + 1));
}
return res;
};
const form = (body) => ({
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams(body),
});
let client;
try {
let ready = false;
for (let attempt = 0; attempt < 100; attempt++) {
if (child.exitCode !== null) throw new Error('hosted gateway exited during startup');
try {
ready = (await fetch(`${origin}/.well-known/oauth-authorization-server`)).status === 200;
} catch {}
if (ready) break;
await delay(50);
}
assert.ok(ready, 'gateway must become ready');
assert.equal((await request('/mcp', { method: 'POST' })).status, 401);
const gated = await request('/.auth/colab');
assert.equal(gated.status, 302);
assert.equal(gated.headers.get('location'), '/.auth/login');
assert.equal(gated.headers.get('cache-control'), 'no-store');
assert.equal((await request('/.auth/login', form({ password: env.PASSWORD }))).status, 302);
const page = await request('/.auth/colab');
assert.equal(page.status, 200);
assert.equal(page.headers.get('referrer-policy'), 'strict-origin');
const csrf = (await page.text()).match(/name="csrf" value="([^"]+)"/)[1];
assert.equal((await request('/.auth/colab/login', form({ csrf: 'wrong' }))).status, 403);
const google = await request('/.auth/colab/login', form({ csrf }));
assert.equal(google.status, 302);
const googleURL = new URL(google.headers.get('location'));
assert.equal(googleURL.origin, 'https://accounts.google.com');
assert.equal(googleURL.searchParams.get('client_id'), env.COLAB_GOOGLE_CLIENT_ID);
assert.equal(googleURL.searchParams.get('redirect_uri'), `${origin}/.auth/colab/callback`);
assert.equal(googleURL.searchParams.get('code_challenge_method'), 'S256');
const callback = 'https://oauth-redirect.googleusercontent.com/other-callback/smoke';
const registration = await request('/.idp/register', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
redirect_uris: [callback],
token_endpoint_auth_method: 'client_secret_basic',
grant_types: ['authorization_code', 'refresh_token'],
response_types: ['code'],
}),
});
assert.equal(registration.status, 201);
const registered = await registration.json();
const verifier = randomBytes(32).toString('base64url');
const challenge = createHash('sha256').update(verifier).digest('base64url');
const authorization = await request(
'/.idp/auth?' +
new URLSearchParams({
client_id: registered.client_id,
redirect_uri: callback,
response_type: 'code',
state: 'hosted-smoke-state',
code_challenge: challenge,
code_challenge_method: 'S256',
resource: `${origin}/mcp`,
}),
);
assert.equal(authorization.status, 302);
const consentPath = authorization.headers.get('location');
assert.equal((await request(consentPath)).status, 200);
const approved = await request(consentPath, { method: 'POST' });
assert.equal(approved.status, 303);
const code = new URL(approved.headers.get('location')).searchParams.get('code');
assert.ok(code);
const exchanged = await request('/.idp/token', {
...form({
grant_type: 'authorization_code',
code,
code_verifier: verifier,
redirect_uri: callback,
resource: `${origin}/mcp`,
}),
headers: {
'content-type': 'application/x-www-form-urlencoded',
authorization:
'Basic ' +
Buffer.from(`${registered.client_id}:${registered.client_secret}`).toString('base64'),
},
});
assert.equal(exchanged.status, 200);
const tokens = await exchanged.json();
client = new Client({ name: 'hosted-smoke', version: '1.0.0' });
await client.connect(
new StreamableHTTPClientTransport(new URL(`${origin}/mcp`), {
requestInit: { headers: { authorization: `Bearer ${tokens.access_token}` } },
}),
);
const { tools } = await client.listTools();
assert.equal(tools.length, 11);
assert.ok(tools.every((tool) => tool.annotations.readOnlyHint === true));
const result = await client.callTool({ name: 'colab_list', arguments: {} });
assert.equal(result.isError, true);
assert.match(result.content[0].text, new RegExp(`${origin}/\.auth/colab`));
const accounts = [0, 1].map((i) => ({
account_id: `acct_${randomBytes(32).toString('base64url')}`,
email: `fixture-${i}@example.com`,
google_subject: `fixture-subject-${i}`,
client_id: 'fixture-client',
client_secret: 'fixture-secret',
refresh_token: 'fixture-refresh',
}));
await writeFile(
join(data, 'colab-google.json.accounts.json'),
JSON.stringify({ version: 1, accounts }),
{ mode: 0o600 },
);
const discovered = await client.callTool({ name: 'colab_accounts', arguments: {} });
assert.deepEqual(JSON.parse(discovered.content[0].text), {
accounts: accounts.map(({ account_id }) => ({ account_id })),
});
for (const account of accounts) assert.ok(!discovered.content[0].text.includes(account.email));
const ambiguous = await client.callTool({ name: 'colab_launch', arguments: {} });
assert.equal(ambiguous.isError, true);
assert.match(ambiguous.content[0].text, /choose an account_id/);
const management = await (await request('/.auth/colab')).text();
for (const account of accounts) assert.ok(management.includes(account.email));
assert.ok(!management.includes('fixture-refresh'));
for (const secret of [registered.client_secret, code, tokens.access_token])
assert.ok(!logs.includes(secret), 'gateway logs must not contain credentials');
console.log(
`hosted smoke passed (${args.length ? 'node' : 'native'}): owner gate, google redirect, DCR, consent, S256 token exchange, 11 tools, setup link, live pool discovery and explicit selection; no google calls`,
);
} finally {
await client?.close();
child.kill('SIGTERM');
const timer = setTimeout(() => child.kill('SIGKILL'), 10_000);
try {
assert.equal(await closed, 0, 'gateway and worker must stop cleanly');
} finally {
clearTimeout(timer);
await rm(data, { recursive: true, force: true });
}
}
}
await smoke(process.execPath, [resolve('src/index.js')]);
await smoke(resolve('dist/colab-mcp'), []);