Skip to main content
Glama
Jing-yilin

Reddit MCP Server

by Jing-yilin
integration.test.ts13.5 kB
/** * MCP Server Integration Tests for Reddit MCP * * These tests verify the MCP server protocol compliance and tool schemas. * They spawn the actual MCP server and test via JSON-RPC. */ import { describe, test, expect } from 'bun:test'; import { spawn } from 'child_process'; import * as path from 'path'; describe('Reddit MCP Server Integration Tests', () => { const sendRequest = (request: object): Promise<any> => { return new Promise((resolve, reject) => { const proc = spawn('node', [path.join(__dirname, '../build/index.js')], { env: { ...process.env }, stdio: ['pipe', 'pipe', 'pipe'], }); let stdout = ''; let stderr = ''; proc.stdout?.on('data', (data) => { stdout += data.toString(); }); proc.stderr?.on('data', (data) => { stderr += data.toString(); }); proc.on('close', () => { try { const lines = stdout.trim().split('\n'); for (const line of lines) { if (line.startsWith('{')) { const response = JSON.parse(line); resolve(response); return; } } resolve({ stdout, stderr }); } catch (e) { resolve({ stdout, stderr, error: e }); } }); proc.on('error', reject); proc.stdin?.write(JSON.stringify(request) + '\n'); proc.stdin?.end(); setTimeout(() => { proc.kill(); reject(new Error('Request timeout')); }, 30000); }); }; describe('tools/list endpoint', () => { test('returns all 9 tools', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); expect(response.result).toBeDefined(); expect(response.result.tools).toBeInstanceOf(Array); expect(response.result.tools.length).toBe(9); }); test('includes get_subreddit_hot tool with correct schema', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const tool = tools.find((t: any) => t.name === 'get_subreddit_hot'); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('subreddit'); expect(tool.inputSchema.properties).toHaveProperty('subreddit'); expect(tool.inputSchema.properties).toHaveProperty('limit'); expect(tool.inputSchema.properties).toHaveProperty('after'); expect(tool.inputSchema.properties).toHaveProperty('save_dir'); expect(tool.inputSchema.properties).toHaveProperty('max_items'); }); test('includes get_subreddit_new tool', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const tool = tools.find((t: any) => t.name === 'get_subreddit_new'); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('subreddit'); }); test('includes get_subreddit_top tool with time filter', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const tool = tools.find((t: any) => t.name === 'get_subreddit_top'); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('subreddit'); expect(tool.inputSchema.properties).toHaveProperty('time'); expect(tool.inputSchema.properties.time.enum).toContain('hour'); expect(tool.inputSchema.properties.time.enum).toContain('day'); expect(tool.inputSchema.properties.time.enum).toContain('week'); expect(tool.inputSchema.properties.time.enum).toContain('month'); expect(tool.inputSchema.properties.time.enum).toContain('year'); expect(tool.inputSchema.properties.time.enum).toContain('all'); }); test('includes get_post_content tool with comment options', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const tool = tools.find((t: any) => t.name === 'get_post_content'); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('post_id'); expect(tool.inputSchema.properties).toHaveProperty('subreddit'); expect(tool.inputSchema.properties).toHaveProperty('comment_limit'); expect(tool.inputSchema.properties).toHaveProperty('comment_depth'); expect(tool.inputSchema.properties).toHaveProperty('sort'); expect(tool.inputSchema.properties.sort.enum).toContain('confidence'); expect(tool.inputSchema.properties.sort.enum).toContain('top'); expect(tool.inputSchema.properties.sort.enum).toContain('new'); }); test('includes search_posts tool with filters', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const tool = tools.find((t: any) => t.name === 'search_posts'); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('query'); expect(tool.inputSchema.properties).toHaveProperty('subreddit'); expect(tool.inputSchema.properties).toHaveProperty('sort'); expect(tool.inputSchema.properties).toHaveProperty('time'); expect(tool.inputSchema.properties).toHaveProperty('limit'); expect(tool.inputSchema.properties.sort.enum).toContain('relevance'); expect(tool.inputSchema.properties.sort.enum).toContain('hot'); expect(tool.inputSchema.properties.sort.enum).toContain('top'); expect(tool.inputSchema.properties.sort.enum).toContain('new'); expect(tool.inputSchema.properties.sort.enum).toContain('comments'); }); test('includes get_user_info tool', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const tool = tools.find((t: any) => t.name === 'get_user_info'); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('username'); }); test('includes get_user_posts tool with sort options', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const tool = tools.find((t: any) => t.name === 'get_user_posts'); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('username'); expect(tool.inputSchema.properties).toHaveProperty('sort'); expect(tool.inputSchema.properties.sort.enum).toContain('hot'); expect(tool.inputSchema.properties.sort.enum).toContain('new'); expect(tool.inputSchema.properties.sort.enum).toContain('top'); expect(tool.inputSchema.properties.sort.enum).toContain('controversial'); }); test('includes get_user_comments tool', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const tool = tools.find((t: any) => t.name === 'get_user_comments'); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('username'); expect(tool.inputSchema.properties).toHaveProperty('sort'); expect(tool.inputSchema.properties).toHaveProperty('time'); expect(tool.inputSchema.properties).toHaveProperty('limit'); }); test('includes get_subreddit_info tool', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const tool = tools.find((t: any) => t.name === 'get_subreddit_info'); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('subreddit'); }); test('all tools have name and description', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; for (const tool of tools) { expect(tool).toHaveProperty('name'); expect(tool).toHaveProperty('description'); expect(tool).toHaveProperty('inputSchema'); expect(tool.name).toBeTruthy(); expect(tool.description).toBeTruthy(); } }); test('tool names follow snake_case convention', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; const snakeCaseRegex = /^[a-z]+(_[a-z]+)*$/; for (const tool of tools) { expect(snakeCaseRegex.test(tool.name)).toBe(true); } }); test('all tools mention TOON format in description', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const tools = response.result.tools; for (const tool of tools) { expect(tool.description.toLowerCase()).toContain('toon'); } }); }); describe('Error handling', () => { test('returns error for unknown tool', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 2, method: 'tools/call', params: { name: 'unknown_tool', arguments: {}, }, }); expect(response.error || response.result).toBeDefined(); }); test('returns error for missing arguments', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 3, method: 'tools/call', params: { name: 'get_subreddit_hot', }, }); expect(response.error || response.result).toBeDefined(); }); }); }); describe('Tool Schema Validation', () => { const sendRequest = (request: object): Promise<any> => { return new Promise((resolve, reject) => { const proc = spawn('node', [path.join(__dirname, '../build/index.js')], { env: { ...process.env }, stdio: ['pipe', 'pipe', 'pipe'], }); let stdout = ''; proc.stdout?.on('data', (data) => { stdout += data.toString(); }); proc.on('close', () => { try { const lines = stdout.trim().split('\n'); for (const line of lines) { if (line.startsWith('{')) { resolve(JSON.parse(line)); return; } } resolve({}); } catch (e) { resolve({}); } }); proc.on('error', reject); proc.stdin?.write(JSON.stringify(request) + '\n'); proc.stdin?.end(); setTimeout(() => { proc.kill(); reject(new Error('Timeout')); }, 30000); }); }; test('subreddit tools have consistent schema structure', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const subredditTools = ['get_subreddit_hot', 'get_subreddit_new', 'get_subreddit_top']; const tools = response.result.tools; for (const toolName of subredditTools) { const tool = tools.find((t: any) => t.name === toolName); expect(tool).toBeDefined(); expect(tool.inputSchema.properties).toHaveProperty('subreddit'); expect(tool.inputSchema.properties).toHaveProperty('limit'); expect(tool.inputSchema.properties).toHaveProperty('after'); expect(tool.inputSchema.properties).toHaveProperty('save_dir'); expect(tool.inputSchema.properties).toHaveProperty('max_items'); } }); test('user tools have consistent schema structure', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const userTools = ['get_user_info', 'get_user_posts', 'get_user_comments']; const tools = response.result.tools; for (const toolName of userTools) { const tool = tools.find((t: any) => t.name === toolName); expect(tool).toBeDefined(); expect(tool.inputSchema.required).toContain('username'); expect(tool.inputSchema.properties).toHaveProperty('username'); } }); test('pagination tools have after parameter', async () => { const response = await sendRequest({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {}, }); const paginatedTools = [ 'get_subreddit_hot', 'get_subreddit_new', 'get_subreddit_top', 'search_posts', 'get_user_posts', 'get_user_comments', ]; const tools = response.result.tools; for (const toolName of paginatedTools) { const tool = tools.find((t: any) => t.name === toolName); expect(tool).toBeDefined(); expect(tool.inputSchema.properties).toHaveProperty('after'); } }); });

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/Jing-yilin/reddit-mcp'

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