Skip to main content
Glama

Peekaboo MCP

by steipete
clean-tool.test.tsβ€’8.39 kB
import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { spawn, ChildProcess } from 'child_process'; import * as path from 'path'; const SERVER_PATH = path.resolve(process.cwd(), 'dist/index.js'); // Simple JSON-RPC client for testing class JSONRPCClient { private proc: ChildProcess; private messageId = 1; private responseHandlers = new Map<number, (response: any) => void>(); private buffer = ""; constructor() { this.proc = spawn("node", [SERVER_PATH], { stdio: ["pipe", "pipe", "inherit"], }); this.proc.stdout?.on("data", (data) => { this.buffer += data.toString(); this.processBuffer(); }); } private processBuffer() { const lines = this.buffer.split("\n"); this.buffer = lines.pop() || ""; for (const line of lines) { if (line.trim()) { try { const message = JSON.parse(line); if (message.id && this.responseHandlers.has(message.id)) { const handler = this.responseHandlers.get(message.id)!; this.responseHandlers.delete(message.id); handler(message); } } catch (e) { // Ignore non-JSON lines } } } } async request(method: string, params: any = {}): Promise<any> { const id = this.messageId++; const request = { jsonrpc: "2.0", id, method, params, }; return new Promise((resolve, reject) => { this.responseHandlers.set(id, (response) => { if (response.error) { reject(new Error(response.error.message)); } else { resolve(response.result); } }); this.proc.stdin?.write(JSON.stringify(request) + "\n"); }); } async close() { this.proc.kill(); await new Promise((resolve) => this.proc.on("exit", resolve)); } } // Clean tool integration tests disabled by default to prevent unintended filesystem modifications // These tests can delete session files and temporary data when run in full mode describe.skipIf(globalThis.shouldSkipFullTests)('[full] clean tool integration', () => { let client: JSONRPCClient; beforeAll(async () => { // Initialize the client client = new JSONRPCClient(); // Wait for server to be ready await new Promise(resolve => setTimeout(resolve, 1000)); }); afterAll(async () => { await client?.close(); }); describe('tool registration', () => { it('should list clean tool', async () => { const response = await client.request('tools/list', {}); const cleanTool = response.tools.find((t: any) => t.name === 'clean'); expect(cleanTool).toBeDefined(); expect(cleanTool.description).toContain('Cleans up session cache'); expect(cleanTool.inputSchema.type).toBe('object'); expect(cleanTool.inputSchema.properties).toHaveProperty('all_sessions'); expect(cleanTool.inputSchema.properties).toHaveProperty('older_than'); expect(cleanTool.inputSchema.properties).toHaveProperty('session'); expect(cleanTool.inputSchema.properties).toHaveProperty('dry_run'); }); }); describe('tool execution', () => { it('should handle dry run for all sessions', async () => { const response = await client.request('tools/call', { name: 'clean', arguments: { all_sessions: true, dry_run: true } }); expect(response.content).toBeDefined(); expect(response.content).toHaveLength(1); expect(response.content[0].type).toBe('text'); const text = response.content[0].text; expect(text).toContain('Dry run mode'); expect(text).toMatch(/(Would remove \d+ session|No sessions to clean)/); }); it('should clean sessions older than specified hours', async () => { const response = await client.request('tools/call', { name: 'clean', arguments: { older_than: 24, dry_run: true } }); expect(response.content).toBeDefined(); expect(response.content).toHaveLength(1); expect(response.content[0].type).toBe('text'); const text = response.content[0].text; expect(text).toContain('Dry run mode'); expect(text).toMatch(/(Would remove \d+ session|No sessions to clean)/); }); it('should clean specific session', async () => { const response = await client.request('tools/call', { name: 'clean', arguments: { session: '1751889198010-5978', // Use timestamp-based format dry_run: true } }); expect(response.content).toBeDefined(); expect(response.content).toHaveLength(1); expect(response.content[0].type).toBe('text'); const text = response.content[0].text; expect(text).toContain('Dry run mode'); }); it('should reject multiple cleanup options', async () => { const response = await client.request('tools/call', { name: 'clean', arguments: { all_sessions: true, older_than: 24 } }); expect(response.isError).toBe(true); expect(response.content[0].text).toContain('Invalid arguments'); }); it('should reject no cleanup options', async () => { const response = await client.request('tools/call', { name: 'clean', arguments: {} }); expect(response.isError).toBe(true); expect(response.content[0].text).toContain('Invalid arguments'); }); it('should handle invalid session ID', async () => { const response = await client.request('tools/call', { name: 'clean', arguments: { session: '9999999999999-9999', // Use invalid timestamp-based format dry_run: true } }); expect(response.content).toBeDefined(); const text = response.content[0].text; // Should indicate no sessions found or removed expect(text).toMatch(/(No sessions to clean|Would remove 0 session)/); }); it('should show execution time when available', async () => { const response = await client.request('tools/call', { name: 'clean', arguments: { all_sessions: true, dry_run: true } }); expect(response.content).toBeDefined(); const text = response.content[0].text; // Execution time may not always be returned by the Swift CLI // The test should pass if the command completes successfully expect(text).toContain('Dry run mode'); expect(response.isError).not.toBe(true); }); it('should format bytes correctly', async () => { const response = await client.request('tools/call', { name: 'clean', arguments: { all_sessions: true, dry_run: true } }); expect(response.content).toBeDefined(); const text = response.content[0].text; // Should show formatted bytes (B, KB, MB, or GB) expect(text).toMatch(/(Space to be freed: \d+\.\d+ (B|KB|MB|GB)|No sessions to clean)/); }); }); describe('edge cases', () => { it('should handle negative older_than value gracefully', async () => { try { await client.request('tools/call', { name: 'clean', arguments: { older_than: -1 } }); // If it doesn't throw, check the response } catch (error: any) { // Negative values might be rejected by validation expect(error.message).toBeDefined(); } }); it('should handle very large older_than value', async () => { const response = await client.request('tools/call', { name: 'clean', arguments: { older_than: 999999, dry_run: true } }); expect(response.content).toBeDefined(); const text = response.content[0].text; // With such a large value, no sessions should be old enough expect(text).toMatch(/(No sessions to clean|Would remove 0 session)/); }); it('should handle empty session ID', async () => { try { await client.request('tools/call', { name: 'clean', arguments: { session: '' } }); } catch (error: any) { // Empty string might be rejected expect(error.message).toBeDefined(); } }); }); });

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/steipete/Peekaboo'

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