Skip to main content
Glama
smoke-local.ts5.05 kB
import { spawn } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; const __dirname = path.dirname(new URL(import.meta.url).pathname); type RpcMsg = any; function send(proc: any, msg: RpcMsg) { const line = JSON.stringify(msg); proc.stdin.write(line + '\n'); } function readLines(stream: NodeJS.ReadableStream, onLine: (l: string) => void) { let buf = ''; stream.on('data', (chunk) => { buf += chunk.toString(); let idx: number; while ((idx = buf.indexOf('\n')) >= 0) { const line = buf.slice(0, idx).trim(); buf = buf.slice(idx + 1); if (line) onLine(line); } }); } async function run() { const serverPath = path.resolve(__dirname, 'index.js'); const live = process.env.SMOKE_LIVE === '1'; const liveConfig = process.env.SMOKE_CONFIG || ''; let args: string[] = [serverPath]; let cfgPath: string | undefined; if (!live || liveConfig) { // Build a temporary config JSON with dummy values (no network usage) if (liveConfig && fs.existsSync(liveConfig)) { cfgPath = liveConfig; } else if (!live) { const cfg = { WP_BASE_URL: 'http://localhost:8888', WP_REST_API: 'http://localhost:8888/wp-json', WPNAV_BASE: 'http://localhost:8888/wp-json/wpnav/v1', WPNAV_INTROSPECT: 'http://localhost:8888/wp-json/wpnav/v1/introspect', WP_APP_USER: 'admin', WP_APP_PASS: 'password', }; cfgPath = path.join(os.tmpdir(), `wp-navigator-test-${Date.now()}.json`); fs.writeFileSync(cfgPath, JSON.stringify(cfg)); } args = [serverPath, cfgPath!]; } const proc = spawn(process.execPath, args, { stdio: ['pipe', 'pipe', 'pipe'], }); proc.stderr.on('data', (d) => { // echo server logs to console for visibility process.stderr.write(d); }); let nextId = 1; const pending = new Map<number, (resp: any) => void>(); readLines(proc.stdout, (line) => { try { const msg = JSON.parse(line); if (msg.id && pending.has(msg.id)) { const res = pending.get(msg.id)!; pending.delete(msg.id); res(msg); } } catch (e) { console.error('Parse error:', e, 'line=', line); } }); function rpc(method: string, params?: any): Promise<any> { const id = nextId++; const msg = { jsonrpc: '2.0', id, method, params }; send(proc, msg); return new Promise((resolve, reject) => { const t = setTimeout(() => { pending.delete(id); reject(new Error(`Timeout waiting for ${method} (id=${id})`)); }, 5000); pending.set(id, (resp) => { clearTimeout(t); resolve(resp); }); }); } // Initialize const init = await rpc('initialize', { protocolVersion: '2024-11-05', capabilities: { tools: {} }, clientInfo: { name: 'smoke-local', version: '0.0.1' }, }); console.log('initialize → ok'); // List tools const list = await rpc('tools/list'); const toolCount = Array.isArray(list.result?.tools) ? list.result.tools.length : 0; console.log(`tools/list → ${toolCount} tools`); // Call help const help = await rpc('tools/call', { name: 'wpnav_help', arguments: {} }); const helpText = help.result?.content?.[0]?.text || ''; if (!String(helpText).includes('WP Navigator MCP — Connection Help')) { throw new Error('help did not return expected banner'); } console.log('tools/call(wpnav_help) → ok'); // Call help and an update (writes disabled → should error before network) const upd = await rpc('tools/call', { name: 'wpnav_update_page', arguments: { id: 123, title: 'Hello' } }); const updIsError = !!upd.error || !!upd.result?.isError; const errText = upd.error?.message || upd.result?.content?.[0]?.text || ''; if (!updIsError || !String(errText).includes('WRITES_DISABLED')) { throw new Error('update did not produce WRITES_DISABLED error'); } console.log('tools/call(wpnav_update_page) → WRITES_DISABLED (expected)'); if (live) { // Live tests against WordPress: introspect + list pages const intro = await rpc('tools/call', { name: 'wpnav_introspect', arguments: {} }); const introText = intro.result?.content?.[0]?.text || ''; if (!introText || typeof introText !== 'string' || !introText.includes('plugin')) { throw new Error('introspect returned unexpected result'); } console.log('tools/call(wpnav_introspect) → ok'); const pages = await rpc('tools/call', { name: 'wpnav_list_pages', arguments: { per_page: 2 } }); const pagesText = pages.result?.content?.[0]?.text || ''; if (!pagesText || typeof pagesText !== 'string') { throw new Error('list_pages returned unexpected result'); } console.log('tools/call(wpnav_list_pages) → ok'); } proc.kill(); if (cfgPath && fs.existsSync(cfgPath) && cfgPath.includes('wp-navigator-test-')) fs.unlinkSync(cfgPath); console.log('Smoke tests passed.'); } run().catch((e) => { console.error('Smoke test failed:', e); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/littlebearapps/wp-navigator-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server