|
| 1 | +import assert from 'node:assert/strict'; |
| 2 | +import { test } from 'vitest'; |
| 3 | +import { handleMcpMessage } from '../router.ts'; |
| 4 | +import { handleMcpPayload } from '../server.ts'; |
| 5 | + |
| 6 | +test('MCP router exposes status install and help tools only', () => { |
| 7 | + const response = handleMcpMessage({ |
| 8 | + jsonrpc: '2.0', |
| 9 | + id: 1, |
| 10 | + method: 'tools/list', |
| 11 | + }); |
| 12 | + |
| 13 | + assert.equal(response?.jsonrpc, '2.0'); |
| 14 | + assert.ok(response && 'result' in response); |
| 15 | + const tools = (response.result as { tools: Array<{ name: string }> }).tools; |
| 16 | + assert.deepEqual( |
| 17 | + tools.map((tool) => tool.name), |
| 18 | + ['status', 'install', 'help'], |
| 19 | + ); |
| 20 | +}); |
| 21 | + |
| 22 | +test('MCP help tool returns versioned workflow guidance', () => { |
| 23 | + const response = handleMcpMessage({ |
| 24 | + jsonrpc: '2.0', |
| 25 | + id: 2, |
| 26 | + method: 'tools/call', |
| 27 | + params: { |
| 28 | + name: 'help', |
| 29 | + arguments: { topic: 'workflow' }, |
| 30 | + }, |
| 31 | + }); |
| 32 | + |
| 33 | + assert.ok(response && 'result' in response); |
| 34 | + const result = response.result as { content: Array<{ text: string }>; isError: boolean }; |
| 35 | + assert.equal(result.isError, false); |
| 36 | + assert.match(result.content[0]?.text ?? '', /agent-device help workflow/); |
| 37 | + assert.match(result.content[0]?.text ?? '', /snapshot -i/); |
| 38 | +}); |
| 39 | + |
| 40 | +test('MCP install tool can return npx client config', () => { |
| 41 | + const response = handleMcpMessage({ |
| 42 | + jsonrpc: '2.0', |
| 43 | + id: 3, |
| 44 | + method: 'tools/call', |
| 45 | + params: { |
| 46 | + name: 'install', |
| 47 | + arguments: { global: false, client: 'Cline' }, |
| 48 | + }, |
| 49 | + }); |
| 50 | + |
| 51 | + assert.ok(response && 'result' in response); |
| 52 | + const result = response.result as { content: Array<{ text: string }> }; |
| 53 | + const text = result.content[0]?.text ?? ''; |
| 54 | + assert.match(text, /npx -y agent-device mcp/); |
| 55 | + assert.match(text, /"args": \["-y","agent-device","mcp"\]/); |
| 56 | + assert.match(text, /Client hint: Cline/); |
| 57 | +}); |
| 58 | + |
| 59 | +test('MCP exposes help resources and workflow prompts', () => { |
| 60 | + const resources = handleMcpMessage({ |
| 61 | + jsonrpc: '2.0', |
| 62 | + id: 4, |
| 63 | + method: 'resources/list', |
| 64 | + }); |
| 65 | + assert.ok(resources && 'result' in resources); |
| 66 | + const resourceUris = (resources.result as { resources: Array<{ uri: string }> }).resources.map( |
| 67 | + (resource) => resource.uri, |
| 68 | + ); |
| 69 | + assert.ok(resourceUris.includes('agent-device://help/workflow')); |
| 70 | + |
| 71 | + const prompt = handleMcpMessage({ |
| 72 | + jsonrpc: '2.0', |
| 73 | + id: 5, |
| 74 | + method: 'prompts/get', |
| 75 | + params: { |
| 76 | + name: 'agent-device-dogfood', |
| 77 | + arguments: { target: 'SampleApp on iOS' }, |
| 78 | + }, |
| 79 | + }); |
| 80 | + assert.ok(prompt && 'result' in prompt); |
| 81 | + const result = prompt.result as { messages: Array<{ content: { text: string } }> }; |
| 82 | + assert.match(result.messages[0]?.content.text ?? '', /dogfood/); |
| 83 | + assert.match(result.messages[0]?.content.text ?? '', /SampleApp on iOS/); |
| 84 | +}); |
| 85 | + |
| 86 | +test('MCP initialize returns supported protocol version and unknown methods use JSON-RPC code', () => { |
| 87 | + const initialized = handleMcpMessage({ |
| 88 | + jsonrpc: '2.0', |
| 89 | + id: 6, |
| 90 | + method: 'initialize', |
| 91 | + params: { |
| 92 | + protocolVersion: '2099-01-01', |
| 93 | + }, |
| 94 | + }); |
| 95 | + assert.ok(initialized && 'result' in initialized); |
| 96 | + assert.equal((initialized.result as { protocolVersion: string }).protocolVersion, '2025-11-25'); |
| 97 | + |
| 98 | + const unknown = handleMcpMessage({ |
| 99 | + jsonrpc: '2.0', |
| 100 | + id: 7, |
| 101 | + method: 'unknown/method', |
| 102 | + }); |
| 103 | + assert.ok(unknown && 'error' in unknown); |
| 104 | + assert.equal(unknown.error.code, -32601); |
| 105 | +}); |
| 106 | + |
| 107 | +test('MCP batch requests return one JSON-RPC array response', () => { |
| 108 | + const response = handleMcpPayload([ |
| 109 | + { |
| 110 | + jsonrpc: '2.0', |
| 111 | + id: 8, |
| 112 | + method: 'ping', |
| 113 | + }, |
| 114 | + { |
| 115 | + jsonrpc: '2.0', |
| 116 | + method: 'notifications/initialized', |
| 117 | + }, |
| 118 | + { |
| 119 | + jsonrpc: '2.0', |
| 120 | + id: 9, |
| 121 | + method: 'tools/list', |
| 122 | + }, |
| 123 | + ]); |
| 124 | + |
| 125 | + assert.ok(Array.isArray(response)); |
| 126 | + assert.equal(response.length, 2); |
| 127 | + assert.equal(response[0]?.id, 8); |
| 128 | + assert.equal(response[1]?.id, 9); |
| 129 | +}); |
0 commit comments