#!/usr/bin/env bun /** * Smoke test: verify MCP tool calls work against a real database. * Usage: DATABASE_URL=... bun run scripts/smoke-test-mcp.ts */ import { PostgresEngine } from '../src/core/postgres-engine.ts'; import { handleToolCall } from '../src/mcp/server.ts'; const DB_URL = process.env.DATABASE_URL; if (!DB_URL) { console.error('Set DATABASE_URL'); process.exit(1); } const eng = new PostgresEngine(); await eng.connect({ database_url: DB_URL }); let passed = 0; let failed = 0; async function test(name: string, fn: () => Promise) { try { await fn(); console.log(` āœ“ ${name}`); passed++; } catch (e: any) { console.log(` āœ— ${name}: ${e.message}`); failed++; } } console.log('MCP Smoke Test\n'); await test('get_stats returns counts', async () => { const stats = await handleToolCall(eng, 'get_stats', {}) as any; if (typeof stats.page_count !== 'number') throw new Error('page_count missing'); }); await test('put_page creates a page', async () => { await handleToolCall(eng, 'put_page', { slug: 'smoke/test-page', content: '---\ntitle: Smoke Test Page\ntype: note\n---\n\nThis page was created by the MCP smoke test.', }); }); await test('get_page retrieves the page', async () => { const page = await handleToolCall(eng, 'get_page', { slug: 'smoke/test-page' }) as any; if (page.title !== 'Smoke Test Page') throw new Error(`Wrong title: ${page.title}`); }); await test('dry_run prevents mutation', async () => { const result = await handleToolCall(eng, 'put_page', { slug: 'smoke/should-not-exist', content: '---\ntitle: Should Not Exist\ntype: note\n---\n\ndry run test', dry_run: true, }) as any; if (!result.dry_run) throw new Error('dry_run flag not returned'); // Verify page was NOT created try { await handleToolCall(eng, 'get_page', { slug: 'smoke/should-not-exist' }); throw new Error('Page was created despite dry_run'); } catch (e: any) { if (!e.message.includes('not found') && !e.code) throw e; } }); await test('search finds the page', async () => { const results = await handleToolCall(eng, 'search', { query: 'smoke test' }) as any[]; if (!Array.isArray(results)) throw new Error('search should return array'); // Keyword search may or may not find it depending on search_vector trigger }); await test('list_pages includes our page', async () => { const pages = await handleToolCall(eng, 'list_pages', { limit: 100 }) as any[]; const found = pages.find((p: any) => p.slug === 'smoke/test-page'); if (!found) throw new Error('smoke/test-page not in list'); }); await test('add_tag and get_tags work', async () => { await handleToolCall(eng, 'add_tag', { slug: 'smoke/test-page', tag: 'smoke-test' }); const tags = await handleToolCall(eng, 'get_tags', { slug: 'smoke/test-page' }) as string[]; if (!tags.includes('smoke-test')) throw new Error('tag not found'); }); await test('delete_page cleans up', async () => { await handleToolCall(eng, 'delete_page', { slug: 'smoke/test-page' }); try { await handleToolCall(eng, 'get_page', { slug: 'smoke/test-page' }); throw new Error('Page still exists after delete'); } catch (e: any) { if (!e.message.includes('not found') && !e.code) throw e; } }); await eng.disconnect(); console.log(`\n${passed} passed, ${failed} failed`); if (failed > 0) process.exit(1); console.log('\n🧠 MCP smoke test passed!');