import { Cli as incur_Cli, z } from 'incur' import { Cli } from 'tapimo' import * as TestApp from '../../test/App.js' import * as Runtime from '../../test/runtime.js' const apiUrl = 'https://api.tempo-api.test' describe('from', () => { test('creates a Tempo API incur CLI', async () => { const fetch = mockHostedApi() try { const cli = Cli.from(apiUrl, { docs: false, version: '1.2.3' }) await serve(cli, ['--help']) expect(cli.name).toMatchInlineSnapshot(`"tapimo"`) expect(cli.description).toMatchInlineSnapshot(`"Tempo API CLI"`) expect((await initialize(cli)).result).toMatchObject({ instructions: expect.stringContaining('Tempo MCP provides access'), serverInfo: { name: 'tempo', title: 'Tempo MCP', version: '1.2.3', }, }) } finally { fetch.mockRestore() } }) test('serves the hosted Tempo API command', async () => { // Verified tokens are database-backed (no bundled fallback), so seed the // db for the default chain to exercise the `--verified` listing. const db = TestApp.database() await TestApp.verifiedSeed(db, Runtime.get().chainId) const fetch = mockHostedApi(TestApp.create({ db })) try { const cli = Cli.from(apiUrl, { docs: false }) // `/v1/tokens?verified=true` now serves the curated verified list through // the standard listing endpoint, so the CLI surfaces the filter as a // `--verified` flag on the `v1 tokens get` command instead of routing // through a separate `tokens verified` subcommand. Data routes are mounted // under the `/v1` prefix, which incur namespaces as a top-level `v1` group. const output = await serve(cli, [ 'v1', 'tokens', 'get', '--verified', 'true', '--format', 'json', ]) const body = JSON.parse(output) expect(body.nextCursor).toBeNull() expect(body.data.length).toBeGreaterThan(0) expect(body.data.every((token: { verified: boolean }) => token.verified === true)).toBe(true) } finally { fetch.mockRestore() } }) test('mounts the docs MCP source as a command group', async () => { const fetch = mockHostedApi() // Stub remote docs MCP server: an in-process incur CLI serving `/mcp`. const remote = incur_Cli.create('docs-remote', { version: '1.0.0' }).command('search', { description: 'Search docs', options: z.object({ query: z.string().describe('Search query') }), run: (context) => ({ hits: [context.options.query] }), }) try { const cli = Cli.from(apiUrl, { docs: { fetch: (request) => remote.fetch(request), url: 'http://docs.local/mcp' }, }) const output = await serve(cli, ['docs', 'search', '--query', 'fees', '--format', 'json']) expect(JSON.parse(output)).toEqual({ hits: ['fees'] }) } finally { fetch.mockRestore() } }) }) function mockHostedApi(app = TestApp.create()) { const fetch = globalThis.fetch return vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => { const request = input instanceof Request ? input : new Request(input, init) const url = new URL(request.url) if (url.origin !== apiUrl) return fetch(input, init) // `Cli.from(url)` uses incur's hosted request source, which ultimately // calls global fetch. Route the fake hosted origin into the local test app // so the test covers the URL-backed path without real network I/O. return app.fetch(new Request(url, request)) }) } async function initialize(cli: ReturnType) { const response = await cli.fetch( new Request('http://localhost/mcp', { body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'initialize', params: { capabilities: {}, clientInfo: { name: 'test-client', version: '1.0.0' }, protocolVersion: '2025-03-26', }, }), headers: { accept: 'application/json, text/event-stream', 'content-type': 'application/json', }, method: 'POST', }), ) return (await response.json()) as { result: { instructions: string serverInfo: { name: string; title: string; version: string } } } } async function serve(cli: ReturnType, argv: string[]) { const output: string[] = [] await cli.serve(argv, { exit(code) { throw new Error(`unexpected exit ${code}: ${output.join('')}`) }, stdout(value) { output.push(value) }, }) return output.join('') }