Skip to main content
Glama
Jing-yilin

Reddit MCP Server

by Jing-yilin
index.test.ts16.2 kB
import { describe, test, expect } from 'bun:test'; describe('Reddit MCP Server', () => { describe('Tool Schemas', () => { test('get_subreddit_hot requires subreddit parameter', () => { const requiredFields = ['subreddit']; const params = { subreddit: 'programming', limit: 10 }; for (const field of requiredFields) { expect(params).toHaveProperty(field); } }); test('get_subreddit_new requires subreddit parameter', () => { const params = { subreddit: 'news', limit: 25 }; expect(params).toHaveProperty('subreddit'); }); test('get_subreddit_top supports time filter', () => { const validTimeFilters = ['hour', 'day', 'week', 'month', 'year', 'all']; const params = { subreddit: 'askreddit', time: 'week', limit: 10 }; expect(validTimeFilters).toContain(params.time); }); test('get_post_content requires post_id', () => { const params = { post_id: 'abc123', comment_limit: 20, comment_depth: 3 }; expect(params).toHaveProperty('post_id'); expect(params.post_id).toBeTruthy(); }); test('get_post_content supports comment sort options', () => { const validSorts = ['confidence', 'top', 'new', 'controversial', 'old', 'qa']; expect(validSorts).toContain('confidence'); expect(validSorts).toContain('top'); expect(validSorts).toContain('new'); }); test('search_posts requires query parameter', () => { const params = { query: 'typescript', subreddit: 'programming', sort: 'relevance' }; expect(params).toHaveProperty('query'); expect(params.query).toBeTruthy(); }); test('search_posts supports sort options', () => { const validSorts = ['relevance', 'hot', 'top', 'new', 'comments']; const params = { query: 'test', sort: 'top' }; expect(validSorts).toContain(params.sort); }); test('get_user_info requires username', () => { const params = { username: 'spez' }; expect(params).toHaveProperty('username'); expect(params.username).toBeTruthy(); }); test('get_user_posts requires username', () => { const params = { username: 'spez', sort: 'new', limit: 10 }; expect(params).toHaveProperty('username'); }); test('get_user_posts supports sort options', () => { const validSorts = ['hot', 'new', 'top', 'controversial']; expect(validSorts).toContain('new'); expect(validSorts).toContain('top'); }); test('get_user_comments requires username', () => { const params = { username: 'automoderator', sort: 'new' }; expect(params).toHaveProperty('username'); }); test('get_subreddit_info requires subreddit', () => { const params = { subreddit: 'technology' }; expect(params).toHaveProperty('subreddit'); expect(params.subreddit).toBeTruthy(); }); }); describe('API Endpoint Paths', () => { test('subreddit hot endpoint is correct', () => { const subreddit = 'programming'; const endpoint = `/r/${subreddit}/hot.json`; expect(endpoint).toBe('/r/programming/hot.json'); }); test('subreddit new endpoint is correct', () => { const subreddit = 'news'; const endpoint = `/r/${subreddit}/new.json`; expect(endpoint).toBe('/r/news/new.json'); }); test('subreddit top endpoint is correct', () => { const subreddit = 'askreddit'; const endpoint = `/r/${subreddit}/top.json`; expect(endpoint).toBe('/r/askreddit/top.json'); }); test('post comments endpoint is correct', () => { const postId = 'abc123'; const endpoint = `/comments/${postId}.json`; expect(endpoint).toBe('/comments/abc123.json'); }); test('post comments with subreddit endpoint is correct', () => { const subreddit = 'programming'; const postId = 'abc123'; const endpoint = `/r/${subreddit}/comments/${postId}.json`; expect(endpoint).toBe('/r/programming/comments/abc123.json'); }); test('search endpoint is correct', () => { const endpoint = '/search.json'; expect(endpoint).toBe('/search.json'); }); test('search in subreddit endpoint is correct', () => { const subreddit = 'python'; const endpoint = `/r/${subreddit}/search.json`; expect(endpoint).toBe('/r/python/search.json'); }); test('user about endpoint is correct', () => { const username = 'spez'; const endpoint = `/user/${username}/about.json`; expect(endpoint).toBe('/user/spez/about.json'); }); test('user posts endpoint is correct', () => { const username = 'spez'; const endpoint = `/user/${username}/submitted.json`; expect(endpoint).toBe('/user/spez/submitted.json'); }); test('user comments endpoint is correct', () => { const username = 'spez'; const endpoint = `/user/${username}/comments.json`; expect(endpoint).toBe('/user/spez/comments.json'); }); test('subreddit about endpoint is correct', () => { const subreddit = 'technology'; const endpoint = `/r/${subreddit}/about.json`; expect(endpoint).toBe('/r/technology/about.json'); }); }); describe('Parameter Validation', () => { test('limit should default to 10', () => { const defaultLimit = 10; const params: { limit?: number } = {}; const actualLimit = params.limit ?? defaultLimit; expect(actualLimit).toBe(10); }); test('limit should not exceed 100', () => { const maxLimit = 100; const requestedLimit = 150; const actualLimit = Math.min(requestedLimit, maxLimit); expect(actualLimit).toBe(100); }); test('time filter should be valid enum value', () => { const validValues = ['hour', 'day', 'week', 'month', 'year', 'all']; const testValue = 'week'; expect(validValues).toContain(testValue); }); test('comment_depth should default to 3', () => { const defaultDepth = 3; const params: { comment_depth?: number } = {}; const actualDepth = params.comment_depth ?? defaultDepth; expect(actualDepth).toBe(3); }); test('comment_limit should default to 20', () => { const defaultLimit = 20; const params: { comment_limit?: number } = {}; const actualLimit = params.comment_limit ?? defaultLimit; expect(actualLimit).toBe(20); }); test('max_items should default to 10', () => { const defaultMaxItems = 10; const params: { max_items?: number } = {}; const actualMaxItems = params.max_items ?? defaultMaxItems; expect(actualMaxItems).toBe(10); }); }); describe('Response Format Validation', () => { test('post response has expected fields', () => { const mockPost = { id: 'abc123', title: 'Test Post', subreddit: 'test', author: 'testuser', score: 100, upvoteRatio: 0.95, comments: 50, created: '2024-01-15T00:00:00.000Z', url: 'https://example.com', permalink: 'https://reddit.com/r/test/comments/abc123/test_post/', isVideo: false, isSelf: true, }; expect(mockPost).toHaveProperty('id'); expect(mockPost).toHaveProperty('title'); expect(mockPost).toHaveProperty('subreddit'); expect(mockPost).toHaveProperty('author'); expect(mockPost).toHaveProperty('score'); expect(mockPost).toHaveProperty('comments'); }); test('comment response has expected fields', () => { const mockComment = { id: 'comment123', author: 'commenter', body: 'This is a comment', score: 25, created: '2024-01-15T00:00:00.000Z', depth: 0, edited: false, }; expect(mockComment).toHaveProperty('id'); expect(mockComment).toHaveProperty('author'); expect(mockComment).toHaveProperty('body'); expect(mockComment).toHaveProperty('score'); expect(mockComment).toHaveProperty('depth'); }); test('user response has expected fields', () => { const mockUser = { id: 'user123', name: 'testuser', created: '2020-01-01T00:00:00.000Z', linkKarma: 1000, commentKarma: 5000, totalKarma: 6000, verified: true, premium: false, }; expect(mockUser).toHaveProperty('id'); expect(mockUser).toHaveProperty('name'); expect(mockUser).toHaveProperty('linkKarma'); expect(mockUser).toHaveProperty('commentKarma'); }); test('subreddit response has expected fields', () => { const mockSubreddit = { id: 'sub123', name: 'programming', title: 'Programming', description: 'A subreddit about programming', subscribers: 5000000, activeUsers: 10000, created: '2008-01-01T00:00:00.000Z', over18: false, url: 'https://reddit.com/r/programming/', }; expect(mockSubreddit).toHaveProperty('id'); expect(mockSubreddit).toHaveProperty('name'); expect(mockSubreddit).toHaveProperty('subscribers'); }); test('pagination response includes nextCursor when available', () => { const mockResponse = { data: [{ id: '1' }, { id: '2' }], pagination: { nextCursor: 't3_xyz789', }, }; expect(mockResponse).toHaveProperty('pagination'); expect(mockResponse.pagination).toHaveProperty('nextCursor'); }); }); describe('Error Handling', () => { test('missing subreddit for get_subreddit_hot should throw error', () => { const hasSubreddit = false; if (!hasSubreddit) { expect(() => { throw new Error('subreddit is required'); }).toThrow('subreddit is required'); } }); test('missing post_id for get_post_content should throw error', () => { const hasPostId = false; if (!hasPostId) { expect(() => { throw new Error('post_id is required'); }).toThrow('post_id is required'); } }); test('missing query for search_posts should throw error', () => { const hasQuery = false; if (!hasQuery) { expect(() => { throw new Error('query is required'); }).toThrow('query is required'); } }); test('missing username for get_user_info should throw error', () => { const hasUsername = false; if (!hasUsername) { expect(() => { throw new Error('username is required'); }).toThrow('username is required'); } }); }); describe('Data Cleaners', () => { test('cleanPost handles null input', () => { const result = null; expect(result).toBeNull(); }); test('cleanPost extracts correct fields from raw data', () => { const rawPost = { data: { id: 'test123', title: 'Test Title', subreddit: 'test', author: 'testauthor', score: 100, upvote_ratio: 0.95, num_comments: 50, created_utc: 1705276800, url: 'https://example.com', permalink: '/r/test/comments/test123/test_title/', selftext: 'Test content', is_video: false, is_self: true, total_awards_received: 2, over_18: false, }, }; const expected = { id: 'test123', title: 'Test Title', subreddit: 'test', author: 'testauthor', score: 100, }; expect(rawPost.data.id).toBe(expected.id); expect(rawPost.data.title).toBe(expected.title); }); test('cleanComment handles nested replies', () => { const mockComment = { kind: 't1', data: { id: 'comment1', author: 'user1', body: 'Parent comment', score: 10, created_utc: 1705276800, replies: { data: { children: [ { kind: 't1', data: { id: 'comment2', author: 'user2', body: 'Reply comment', score: 5, created_utc: 1705276900, }, }, ], }, }, }, }; expect(mockComment.data.replies.data.children.length).toBe(1); expect(mockComment.data.replies.data.children[0].data.body).toBe('Reply comment'); }); test('cleanUser extracts karma fields', () => { const rawUser = { data: { id: 'user123', name: 'testuser', link_karma: 1000, comment_karma: 5000, total_karma: 6000, created_utc: 1577836800, }, }; expect(rawUser.data.link_karma).toBe(1000); expect(rawUser.data.comment_karma).toBe(5000); expect(rawUser.data.total_karma).toBe(6000); }); }); }); describe('MCP Protocol Compliance', () => { test('tools/list returns valid tool array', () => { const toolsResponse = { tools: [ { name: 'get_subreddit_hot', description: 'Get hot posts from a subreddit' }, { name: 'get_subreddit_new', description: 'Get new posts from a subreddit' }, { name: 'get_subreddit_top', description: 'Get top posts from a subreddit' }, { name: 'get_post_content', description: 'Get post details with comments' }, { name: 'search_posts', description: 'Search Reddit posts' }, { name: 'get_user_info', description: 'Get user profile information' }, { name: 'get_user_posts', description: 'Get posts by a user' }, { name: 'get_user_comments', description: 'Get comments by a user' }, { name: 'get_subreddit_info', description: 'Get subreddit information' }, ], }; expect(toolsResponse.tools).toBeInstanceOf(Array); expect(toolsResponse.tools.length).toBe(9); expect(toolsResponse.tools[0]).toHaveProperty('name'); expect(toolsResponse.tools[0]).toHaveProperty('description'); }); test('tool count should be 9', () => { const expectedTools = [ 'get_subreddit_hot', 'get_subreddit_new', 'get_subreddit_top', 'get_post_content', 'search_posts', 'get_user_info', 'get_user_posts', 'get_user_comments', 'get_subreddit_info', ]; expect(expectedTools.length).toBe(9); }); test('tool call returns CallToolResult format', () => { const result = { content: [ { type: 'text', text: 'data:[]', }, ], }; expect(result).toHaveProperty('content'); expect(result.content[0]).toHaveProperty('type'); expect(result.content[0]).toHaveProperty('text'); expect(result.content[0].type).toBe('text'); }); test('error response uses McpError format', () => { const errorResponse = { code: -32602, message: 'Missing arguments', }; expect(errorResponse).toHaveProperty('code'); expect(errorResponse).toHaveProperty('message'); }); test('error codes are correct', () => { const ErrorCodes = { ParseError: -32700, InvalidRequest: -32600, MethodNotFound: -32601, InvalidParams: -32602, InternalError: -32603, }; expect(ErrorCodes.InvalidParams).toBe(-32602); expect(ErrorCodes.MethodNotFound).toBe(-32601); expect(ErrorCodes.InternalError).toBe(-32603); }); }); describe('Base URL Configuration', () => { test('base URL is correct', () => { const baseURL = 'https://www.reddit.com'; expect(baseURL).toBe('https://www.reddit.com'); }); test('User-Agent header is set', () => { const headers = { 'User-Agent': 'Reddit-MCP-Server/1.0.0 (by /u/mcp-bot)', 'Accept': 'application/json', }; expect(headers).toHaveProperty('User-Agent'); expect(headers['User-Agent']).toContain('Reddit-MCP-Server'); }); test('proxy support is available', () => { const proxyEnvVars = ['PROXY_URL', 'HTTP_PROXY', 'HTTPS_PROXY']; for (const envVar of proxyEnvVars) { const value = process.env[envVar]; expect(value === undefined || typeof value === 'string').toBe(true); } }); });

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