/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import assert from 'node:assert'; import fs from 'node:fs'; import {describe, it} from 'node:test'; import {Client} from '@modelcontextprotocol/sdk/client/index.js'; import {StdioClientTransport} from '@modelcontextprotocol/sdk/client/stdio.js'; import {executablePath} from 'puppeteer'; import type {ToolDefinition} from '../src/tools/ToolDefinition'; describe('e2e', () => { async function withClient( cb: (client: Client) => Promise, extraArgs: string[] = [], ) { const transport = new StdioClientTransport({ command: 'node', args: [ 'build/src/index.js', '--headless', '--isolated', '--executable-path', executablePath(), ...extraArgs, ], }); const client = new Client( { name: 'e2e-test', version: '1.0.0', }, { capabilities: {}, }, ); try { await client.connect(transport); await cb(client); } finally { await client.close(); } } it('calls a tool', async () => { await withClient(async client => { const result = await client.callTool({ name: 'list_pages', arguments: {}, }); assert.deepStrictEqual(result, { content: [ { type: 'text', text: '# list_pages response\n## Pages\n1: about:blank [selected]', }, ], }); }); }); it('calls a tool multiple times', async () => { await withClient(async client => { let result = await client.callTool({ name: 'list_pages', arguments: {}, }); result = await client.callTool({ name: 'list_pages', arguments: {}, }); assert.deepStrictEqual(result, { content: [ { type: 'text', text: '# list_pages response\n## Pages\n1: about:blank [selected]', }, ], }); }); }); it('has all tools', async () => { await withClient(async client => { const {tools} = await client.listTools(); const exposedNames = tools.map(t => t.name).sort(); const files = fs.readdirSync('build/src/tools'); const definedNames = []; for (const file of files) { if (file === 'ToolDefinition.js') { continue; } const fileTools = await import(`../src/tools/${file}`); for (const maybeTool of Object.values(fileTools)) { if ('name' in maybeTool) { if (maybeTool.annotations?.conditions) { continue; } definedNames.push(maybeTool.name); } } } definedNames.sort(); assert.deepStrictEqual(exposedNames, definedNames); }); }); it('has experimental extensions tools', async () => { await withClient( async client => { const {tools} = await client.listTools(); const clickAt = tools.find(t => t.name === 'install_extension'); assert.ok(clickAt); }, ['--category-extensions'], ); }); it('has experimental vision tools', async () => { await withClient( async client => { const {tools} = await client.listTools(); const clickAt = tools.find(t => t.name === 'click_at'); assert.ok(clickAt); }, ['--experimental-vision'], ); }); it('has experimental interop tools', async () => { await withClient( async client => { const {tools} = await client.listTools(); const getTabId = tools.find(t => t.name === 'get_tab_id'); assert.ok(getTabId); }, ['--experimental-interop-tools'], ); }); });