Skip to main content
Glama
source-tree.md15.4 kB
# Source Tree - Reddit MCP Server **Version:** 1.0 MVP **Last Updated:** 2025-11-05 **Principles:** Clear separation of concerns, Apify Actor conventions, minimal structure --- ## Project Root Structure ``` reddit-mcp-server/ ├── .github/ # GitHub Actions CI/CD │ └── workflows/ │ └── test.yml # Automated testing │ ├── docs/ # Documentation │ ├── architecture/ # Technical docs (this file) │ │ ├── tech-stack.md │ │ ├── coding-standards.md │ │ └── source-tree.md │ ├── system-architecture.md # Detailed system design │ ├── feature-specifications.md │ └── README.md # User-facing documentation │ ├── src/ # Source code (main implementation) │ ├── main.py # Entry point │ ├── server.py # FastMCP server initialization │ │ │ ├── tools/ # MCP tool implementations │ │ ├── __init__.py │ │ ├── search.py # search_reddit │ │ ├── posts.py # get_subreddit_posts │ │ ├── comments.py # get_post_comments │ │ └── trending.py # get_trending_topics │ │ │ ├── reddit/ # Reddit API integration │ │ ├── __init__.py │ │ ├── client.py # PRAW client manager │ │ ├── auth.py # OAuth2 token handling │ │ ├── rate_limiter.py # Token bucket rate limiter │ │ └── normalizer.py # Response normalization │ │ │ ├── cache/ # Caching layer │ │ ├── __init__.py │ │ ├── manager.py # Redis operations │ │ ├── keys.py # Cache key generation │ │ └── ttl.py # TTL policies │ │ │ ├── models/ # Pydantic data models │ │ ├── __init__.py │ │ ├── inputs.py # Tool input schemas │ │ ├── outputs.py # Tool output schemas │ │ └── internal.py # Internal state models │ │ │ └── utils/ # Shared utilities │ ├── __init__.py │ ├── logger.py # Structured logging setup │ ├── errors.py # Custom exceptions │ └── config.py # Configuration management │ ├── tests/ # Test suite │ ├── unit/ # Unit tests │ │ ├── test_cache.py │ │ ├── test_rate_limiter.py │ │ └── test_normalizer.py │ ├── integration/ # Integration tests │ │ ├── test_reddit_api.py │ │ ├── test_mcp_tools.py │ │ └── test_caching_flow.py │ └── conftest.py # Pytest fixtures │ ├── .env.example # Environment variables template ├── .gitignore # Git ignore rules ├── actor.json # Apify Actor configuration ├── Dockerfile # Container definition ├── requirements.txt # Python dependencies ├── requirements-dev.txt # Development dependencies ├── pyproject.toml # Python project config (Black, Ruff, mypy) └── README.md # Project overview ``` --- ## Core Files Detail ### 1. Root Configuration Files #### `actor.json` (Apify Actor Manifest) ```json { "actorSpecification": 1, "name": "reddit-mcp-server", "title": "Reddit MCP Server", "version": "1.0.0", "description": "Enterprise-grade MCP server for Reddit integration", "usesStandbyMode": true, "webServerMcpPath": "/mcp", "environmentVariables": { "REDDIT_CLIENT_ID": { "type": "secret", "required": true, "description": "Reddit API client ID" }, "REDDIT_CLIENT_SECRET": { "type": "secret", "required": true, "description": "Reddit API client secret" }, "REDIS_URL": { "type": "string", "required": true, "description": "Redis connection URL" }, "LOG_LEVEL": { "type": "string", "required": false, "default": "INFO" } } } ``` #### `Dockerfile` (Apify Actor Container) ```dockerfile FROM apify/actor-python:3.11 # Copy requirements and install dependencies COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy source code COPY src ./src # Set working directory WORKDIR /usr/src/app # Run the MCP server CMD ["python", "-m", "src.main"] ``` #### `requirements.txt` ```txt # Core Framework mcp>=1.0.0 # Reddit Integration praw>=7.7.0 # Caching redis[asyncio]>=5.0.0 # Data Validation pydantic>=2.0.0 # Apify Platform apify>=1.6.0 # HTTP Server uvicorn>=0.25.0 # Logging structlog>=23.0.0 # Utilities python-dotenv>=1.0.0 ``` --- ## Source Code Structure ### `/src/main.py` - Entry Point **Purpose**: Application initialization and startup **Responsibilities**: - Load environment variables - Initialize logging - Start FastMCP server - Handle graceful shutdown ```python """ Reddit MCP Server - Main Entry Point Initializes and runs the FastMCP server with Redis caching and Reddit API integration. """ import asyncio import os from apify import Actor from src.server import create_mcp_server from src.utils.logger import setup_logging async def main(): """Main entry point for Apify Actor.""" async with Actor: # Setup logging setup_logging(level=os.getenv("LOG_LEVEL", "INFO")) # Create and run MCP server server = create_mcp_server() await server.run() if __name__ == "__main__": asyncio.run(main()) ``` ### `/src/server.py` - MCP Server Setup **Purpose**: FastMCP server configuration and tool registration **Responsibilities**: - Initialize FastMCP instance - Register all MCP tools - Configure middleware - Set up error handlers ### `/src/tools/` - MCP Tool Implementations #### Structure Each tool is a separate module for clarity: - `search.py` - search_reddit - `posts.py` - get_subreddit_posts - `comments.py` - get_post_comments - `trending.py` - get_trending_topics #### Example: `search.py` ```python """ search_reddit MCP tool implementation. """ from src.models.inputs import SearchRedditInput from src.cache.manager import cache_manager from src.reddit.client import reddit_client from mcp.server.fastmcp import FastMCP async def search_reddit(mcp: FastMCP, params: SearchRedditInput) -> dict: """ Search Reddit for posts matching query. See feature-specifications.md for full details. """ # Implementation here pass ``` ### `/src/reddit/` - Reddit API Layer #### `client.py` - PRAW Client Manager **Purpose**: Manage Reddit API client lifecycle **Key Classes**: - `RedditClientManager` - Singleton PRAW client **Responsibilities**: - Initialize PRAW with OAuth2 - Maintain connection pool - Handle client errors #### `auth.py` - OAuth2 Token Management **Purpose**: Handle Reddit OAuth2 token lifecycle **Key Classes**: - `OAuth2TokenManager` - Token refresh logic **Responsibilities**: - Token expiration detection - Automatic token refresh - Credential validation #### `rate_limiter.py` - Token Bucket Rate Limiter **Purpose**: Prevent exceeding Reddit API rate limits **Key Classes**: - `TokenBucketRateLimiter` - 100 QPM rate limiting **Responsibilities**: - Track API call timestamps - Wait/queue when limit reached - Priority request handling #### `normalizer.py` - Response Normalizer **Purpose**: Convert Reddit API responses to standard format **Key Functions**: - `normalize_post()` - Standardize submission objects - `normalize_comment()` - Standardize comment objects - `normalize_user()` - Standardize redditor objects ### `/src/cache/` - Caching Layer #### `manager.py` - Redis Cache Manager **Purpose**: All Redis operations **Key Classes**: - `CacheManager` - Redis client wrapper **Key Methods**: - `get(key)` - Retrieve cached value - `set(key, value, ttl)` - Store with expiration - `get_or_fetch(key, fetch_fn, ttl)` - Cache-aside pattern #### `keys.py` - Cache Key Generation **Purpose**: Generate consistent cache keys **Key Functions**: - `generate_cache_key(tool, params)` - Create hashed key - Pattern: `reddit:{tool}:{params_hash}:{version}` #### `ttl.py` - TTL Policies **Purpose**: Define cache expiration times **Key Classes**: - `CacheTTL` - Enum of TTL values per content type **TTL Values**: - NEW_POSTS = 120s - HOT_POSTS = 300s - TOP_POSTS = 3600s - SEARCH_RESULTS = 300s ### `/src/models/` - Data Models #### `inputs.py` - Tool Input Schemas **Purpose**: Pydantic models for tool inputs **Models**: - `SearchRedditInput` - `GetSubredditPostsInput` - `GetPostCommentsInput` - `GetTrendingTopicsInput` #### `outputs.py` - Tool Output Schemas **Purpose**: Pydantic models for tool outputs **Models**: - `RedditPost` - `RedditComment` - `ResponseMetadata` - `SearchRedditOutput` #### `internal.py` - Internal State Models **Purpose**: Models for internal system state **Models**: - `RateLimitState` - `CachedResponse` - `OAuthTokenState` ### `/src/utils/` - Shared Utilities #### `logger.py` - Logging Configuration **Purpose**: Setup structured logging **Key Functions**: - `setup_logging(level)` - Configure structlog #### `errors.py` - Custom Exceptions **Purpose**: Define exception hierarchy **Exceptions**: - `RedditMCPError` - Base exception - `ValidationError` - Input validation - `RateLimitError` - Rate limit exceeded - `RedditAPIError` - API errors - `CacheError` - Redis errors #### `config.py` - Configuration Management **Purpose**: Load and validate environment config **Key Functions**: - `load_config()` - Load from environment - `validate_config()` - Ensure required vars set --- ## Directory Responsibilities Summary | Directory | Purpose | Key Files | Dependencies | |-----------|---------|-----------|--------------| | `/src/tools/` | MCP tool implementations | `search.py`, `posts.py` | models, cache, reddit | | `/src/reddit/` | Reddit API integration | `client.py`, `rate_limiter.py` | praw, asyncio | | `/src/cache/` | Caching layer | `manager.py`, `keys.py` | redis | | `/src/models/` | Data validation | `inputs.py`, `outputs.py` | pydantic | | `/src/utils/` | Shared utilities | `logger.py`, `errors.py` | structlog | --- ## File Naming Conventions ### Python Modules - Use snake_case: `rate_limiter.py`, `cache_manager.py` - One module per major class or related functions - `__init__.py` for package exports ### Test Files - Prefix with `test_`: `test_cache.py` - Mirror source structure: `src/cache/manager.py` → `tests/unit/test_cache.py` ### Configuration Files - Use standard names: `requirements.txt`, `Dockerfile`, `actor.json` - Environment-specific: `.env.development`, `.env.production` --- ## Import Patterns ### Internal Imports Always use absolute imports from `src`: ```python # Good from src.cache.manager import CacheManager from src.models.inputs import SearchRedditInput # Bad from ..cache.manager import CacheManager # Relative imports ``` ### Module `__init__.py` Pattern Export main classes/functions: ```python # src/cache/__init__.py from src.cache.manager import CacheManager from src.cache.keys import generate_cache_key __all__ = ["CacheManager", "generate_cache_key"] ``` --- ## Testing Structure ### Test Organization ``` tests/ ├── unit/ # Fast, isolated tests │ ├── test_cache.py # Cache operations │ ├── test_keys.py # Key generation │ ├── test_normalizer.py # Response normalization │ └── test_rate_limiter.py # Rate limiting logic │ ├── integration/ # Slower, integration tests │ ├── test_reddit_api.py # Reddit API calls │ ├── test_mcp_tools.py # End-to-end tool tests │ └── test_cache_flow.py # Cache hit/miss flows │ └── conftest.py # Shared fixtures ``` ### Test File Naming - Unit tests: `test_{module}.py` - Integration tests: `test_{feature}_integration.py` --- ## Development Workflow ### Local Development Structure ``` # 1. Set up environment python -m venv venv source venv/bin/activate pip install -r requirements.txt -r requirements-dev.txt # 2. Set environment variables cp .env.example .env # Edit .env with your credentials # 3. Run tests pytest tests/ # 4. Run server locally python -m src.main # 5. Format and lint black src/ tests/ ruff check src/ tests/ mypy src/ ``` --- ## Deployment Structure (Apify) ### Apify Actor File Requirements **Required files for deployment:** 1. `actor.json` - Actor manifest 2. `Dockerfile` - Container definition 3. `requirements.txt` - Python dependencies 4. `src/` - Source code directory **Optional but recommended:** 1. `README.md` - User documentation 2. `INPUT_SCHEMA.json` - Actor input schema (auto-generated from Pydantic) 3. `.actor/` - Actor assets (screenshots, icons) ### Apify Directory Structure (Generated) ``` .actor/ ├── actor.json # Copy of actor.json ├── INPUT_SCHEMA.json # Auto-generated from Pydantic models ├── README.md # User-facing docs └── screenshots/ # Actor store screenshots ├── screenshot-1.png └── screenshot-2.png ``` --- ## Key Design Decisions ### Why This Structure? 1. **Flat Tool Organization**: Each tool is a separate file (not nested) for easy navigation 2. **Clear Layering**: tools → reddit/cache → models/utils (no circular dependencies) 3. **Minimal Nesting**: Max 2 levels deep (src/reddit/client.py) 4. **Apify-First**: Follows Apify Actor conventions (actor.json, Dockerfile at root) 5. **Test Mirroring**: Test structure mirrors source structure for clarity ### Anti-Patterns Avoided - No monolithic `utils.py` - utilities organized by domain - No deep nesting (src/lib/core/reddit/api/client.py) - No mixing concerns (Reddit logic separate from cache logic) - No circular imports (strict dependency hierarchy) --- ## Migration Path ### Adding New Tool (Week 3+) 1. Create `src/tools/new_tool.py` 2. Define input/output models in `src/models/inputs.py` and `outputs.py` 3. Register in `src/server.py` 4. Add tests in `tests/unit/test_new_tool.py` ### Adding New Feature Layer (v2.0+) If adding background jobs: ``` src/ └── jobs/ # Background job processing ├── __init__.py ├── scheduler.py # Job scheduling └── watch_keywords.py # Keyword monitoring job ``` --- ## Summary This source tree provides: - **Clarity**: Clear responsibility per directory - **Simplicity**: Minimal nesting, flat where possible - **Scalability**: Easy to add new tools/features - **Maintainability**: Obvious where to find things - **Testability**: Mirrored test structure **Golden Rule**: If you can't decide where a file goes, ask: "What is its primary responsibility?" Then place it in that domain's directory. **Next Steps**: See `tech-stack.md` for dependencies and `coding-standards.md` for implementation guidelines.

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/padak/apify-actor-reddit-mcp'

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