import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { spawn } from 'node:child_process'; function rpc(proc, obj) { return new Promise((resolve) => { const onData = (buf) => { const lines = String(buf).split('\n').filter(Boolean); for (const l of lines) { try { const msg = JSON.parse(l); if (msg.id === obj.id) { proc.stdout.off('data', onData); resolve(msg); return; } } catch {} } }; proc.stdout.on('data', onData); proc.stdin.write(JSON.stringify(obj) + '\n'); }); } describe('NewTube MCP server (stdio)', () => { it('initialize + tools/list expose 11 outils dont transcript', async () => { const proc = spawn(process.execPath, ['./mcp/server.mjs'], { env: { ...process.env, NEWTUBE_API_URL: 'http://127.0.0.1:9/api' } }); try { const init = await rpc(proc, { jsonrpc: '2.0', id: 1, method: 'initialize', params: {} }); assert.equal(init.result.serverInfo.name, 'newtube'); assert.ok(init.result.capabilities.tools); const list = await rpc(proc, { jsonrpc: '2.0', id: 2, method: 'tools/list', params: {} }); const names = list.result.tools.map((t) => t.name); for (const expected of ['search_videos', 'suggest_queries', 'get_video_details', 'get_trending', 'get_transcript', 'get_transcript_text', 'health_check', 'list_playlists', 'list_likes', 'list_subscriptions', 'list_watch_history']) { assert.ok(names.includes(expected), `outil manquant: ${expected}`); } const tr = list.result.tools.find((t) => t.name === 'get_transcript'); assert.ok(tr.inputSchema.properties.provider); assert.ok(tr.inputSchema.properties.videoId); const ping = await rpc(proc, { jsonrpc: '2.0', id: 3, method: 'ping', params: {} }); assert.deepEqual(ping.result, {}); } finally { proc.kill(); } }); it('validation locale sans API (q trop court -> isError)', async () => { const proc = spawn(process.execPath, ['./mcp/server.mjs'], { env: { ...process.env, NEWTUBE_API_URL: 'http://127.0.0.1:9/api' } }); try { const call = await rpc(proc, { jsonrpc: '2.0', id: 10, method: 'tools/call', params: { name: 'search_videos', arguments: { q: 'x' } }, }); assert.equal(call.result.isError, true); assert.match(call.result.content[0].text, /min 2/); } finally { proc.kill(); } }); });