├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── pyproject.toml ├── smithery.yaml ├── src └── mcp_server_reddit │ ├── __init__.py │ ├── __main__.py │ └── server.py └── uv.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-generated files 2 | .DS_Store 3 | __pycache__/ 4 | *.py[oc] 5 | build/ 6 | dist/ 7 | wheels/ 8 | *.egg-info 9 | 10 | # Virtual environments 11 | .venv 12 | .env 13 | .python-version 14 | 15 | # Others 16 | .windsurfrules 17 | .idea/ -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | # Use a Python base image with hatchling for building 3 | FROM python:3.11-slim as builder 4 | 5 | # Set work directory 6 | WORKDIR /app 7 | 8 | # Install build system 9 | RUN pip install hatchling 10 | 11 | # Copy project files 12 | COPY pyproject.toml README.md /app/ 13 | COPY src /app/src 14 | 15 | # Build the project wheel 16 | RUN python -m build 17 | 18 | # Use a separate environment for the final image 19 | FROM python:3.11-slim 20 | 21 | WORKDIR /app 22 | 23 | # Install the package 24 | COPY --from=builder /app/dist/*.whl /app/ 25 | RUN pip install /app/*.whl 26 | 27 | # Define default command 28 | ENTRYPOINT ["python", "-m", "mcp_server_reddit"] 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Hawstein 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MCP Server Reddit 2 | [![smithery badge](https://smithery.ai/badge/@Hawstein/mcp-server-reddit)](https://smithery.ai/server/@Hawstein/mcp-server-reddit) 3 | 4 | A Model Context Protocol server providing access to Reddit public API for LLMs. This server enables LLMs to interact with Reddit's content, including browsing frontpage posts, accessing subreddit information, and reading post comments. 5 | 6 | This server uses [redditwarp](https://github.com/Pyprohly/redditwarp) to interact with Reddit's public API and exposes the functionality through MCP protocol. 7 | 8 | Server Reddit MCP server 9 | 10 | ## Video Demo (Click to Watch) 11 | 12 | A demo in Clinde 👇 13 | 14 | [![MCP Server Reddit - Clinde](https://img.youtube.com/vi/1Gdx1jWFbCM/maxresdefault.jpg)](https://youtu.be/1Gdx1jWFbCM) 15 | 16 | 17 | ## Available Tools 18 | 19 | - `get_frontpage_posts` - Get hot posts from Reddit frontpage 20 | - Optional arguments: 21 | - `limit` (integer): Number of posts to return (default: 10, range: 1-100) 22 | 23 | - `get_subreddit_info` - Get information about a subreddit 24 | - Required arguments: 25 | - `subreddit_name` (string): Name of the subreddit (e.g. 'Python', 'news') 26 | 27 | - `get_subreddit_hot_posts` - Get hot posts from a specific subreddit 28 | - Required arguments: 29 | - `subreddit_name` (string): Name of the subreddit (e.g. 'Python', 'news') 30 | - Optional arguments: 31 | - `limit` (integer): Number of posts to return (default: 10, range: 1-100) 32 | 33 | - `get_subreddit_new_posts` - Get new posts from a specific subreddit 34 | - Required arguments: 35 | - `subreddit_name` (string): Name of the subreddit (e.g. 'Python', 'news') 36 | - Optional arguments: 37 | - `limit` (integer): Number of posts to return (default: 10, range: 1-100) 38 | 39 | - `get_subreddit_top_posts` - Get top posts from a specific subreddit 40 | - Required arguments: 41 | - `subreddit_name` (string): Name of the subreddit (e.g. 'Python', 'news') 42 | - Optional arguments: 43 | - `limit` (integer): Number of posts to return (default: 10, range: 1-100) 44 | - `time` (string): Time filter for top posts (default: '', options: 'hour', 'day', 'week', 'month', 'year', 'all') 45 | 46 | - `get_subreddit_rising_posts` - Get rising posts from a specific subreddit 47 | - Required arguments: 48 | - `subreddit_name` (string): Name of the subreddit (e.g. 'Python', 'news') 49 | - Optional arguments: 50 | - `limit` (integer): Number of posts to return (default: 10, range: 1-100) 51 | 52 | - `get_post_content` - Get detailed content of a specific post 53 | - Required arguments: 54 | - `post_id` (string): ID of the post 55 | - Optional arguments: 56 | - `comment_limit` (integer): Number of top-level comments to return (default: 10, range: 1-100) 57 | - `comment_depth` (integer): Maximum depth of comment tree (default: 3, range: 1-10) 58 | 59 | - `get_post_comments` - Get comments from a post 60 | - Required arguments: 61 | - `post_id` (string): ID of the post 62 | - Optional arguments: 63 | - `limit` (integer): Number of comments to return (default: 10, range: 1-100) 64 | 65 | 66 | ## Installation 67 | 68 | ### Using [Clinde](https://clinde.ai/) (recommended) 69 | 70 | The easiest way to use MCP Server Reddit is through the Clinde desktop app. Simply download and install Clinde, then: 71 | 72 | 1. Open the Clinde app 73 | 2. Navigate to the Servers page 74 | 3. Find mcp-server-reddit and click Install 75 | 76 | That's it! No technical knowledge required - Clinde handles all the installation and configuration for you seamlessly. 77 | 78 | ### Using uv (recommended) 79 | 80 | When using [`uv`](https://docs.astral.sh/uv/) no specific installation is needed. We will 81 | use [`uvx`](https://docs.astral.sh/uv/guides/tools/) to directly run *mcp-server-reddit*. 82 | 83 | ### Using PIP 84 | 85 | Alternatively you can install `mcp-server-reddit` via pip: 86 | 87 | ```bash 88 | pip install mcp-server-reddit 89 | ``` 90 | 91 | After installation, you can run it as a script using: 92 | 93 | ```bash 94 | python -m mcp_server_reddit 95 | ``` 96 | 97 | ### Installing via Smithery 98 | 99 | To install MCP Server Reddit for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@Hawstein/mcp-server-reddit): 100 | 101 | ```bash 102 | npx -y @smithery/cli install @Hawstein/mcp-server-reddit --client claude 103 | ``` 104 | 105 | ## Configuration 106 | 107 | ### Configure for Claude.app 108 | 109 | Add to your Claude settings: 110 | 111 |
112 | Using uvx 113 | 114 | ```json 115 | "mcpServers": { 116 | "reddit": { 117 | "command": "uvx", 118 | "args": ["mcp-server-reddit"] 119 | } 120 | } 121 | ``` 122 |
123 | 124 |
125 | Using pip installation 126 | 127 | ```json 128 | "mcpServers": { 129 | "reddit": { 130 | "command": "python", 131 | "args": ["-m", "mcp_server_reddit"] 132 | } 133 | } 134 | ``` 135 |
136 | 137 | ### Configure for Zed 138 | 139 | Add to your Zed settings.json: 140 | 141 |
142 | Using uvx 143 | 144 | ```json 145 | "context_servers": [ 146 | "mcp-server-reddit": { 147 | "command": "uvx", 148 | "args": ["mcp-server-reddit"] 149 | } 150 | ], 151 | ``` 152 |
153 | 154 |
155 | Using pip installation 156 | 157 | ```json 158 | "context_servers": { 159 | "mcp-server-reddit": { 160 | "command": "python", 161 | "args": ["-m", "mcp_server_reddit"] 162 | } 163 | }, 164 | ``` 165 |
166 | 167 | ## Examples of Questions 168 | 169 | - "What are the current hot posts on Reddit's frontpage?" (get_frontpage_posts) 170 | - "Tell me about the r/ClaudeAI subreddit" (get_subreddit_info) 171 | - "What are the hot posts in the r/ClaudeAI subreddit?" (get_subreddit_hot_posts) 172 | - "Show me the newest posts from r/ClaudeAI" (get_subreddit_new_posts) 173 | - "What are the top posts of all time in r/ClaudeAI?" (get_subreddit_top_posts) 174 | - "What posts are trending in r/ClaudeAI right now?" (get_subreddit_rising_posts) 175 | - "Get the full content and comments of this Reddit post: [post_url]" (get_post_content) 176 | - "Summarize the comments on this Reddit post: [post_url]" (get_post_comments) 177 | 178 | ## Debugging 179 | 180 | You can use the MCP inspector to debug the server. For uvx installations: 181 | 182 | ```bash 183 | npx @modelcontextprotocol/inspector uvx mcp-server-reddit 184 | ``` 185 | 186 | Or if you've installed the package in a specific directory or are developing on it: 187 | 188 | ```bash 189 | cd path/to/mcp_server_reddit 190 | npx @modelcontextprotocol/inspector uv run mcp-server-reddit 191 | ``` 192 | 193 | ## License 194 | 195 | mcp-server-reddit is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository. 196 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "mcp-server-reddit" 3 | version = "0.2.0" 4 | description = "A Model Context Protocol (MCP) server that provides tools for fetching Reddit content, including frontpage posts, subreddit information and hot posts, post details, and comments." 5 | readme = "README.md" 6 | requires-python = ">=3.11" 7 | authors = [ 8 | { name = "Hawstein", email = "hawstein@claudemind.com" }, 9 | ] 10 | keywords = ["reddit", "mcp", "llm"] 11 | license = { text = "MIT" } 12 | classifiers = [ 13 | "Development Status :: 4 - Beta", 14 | "Intended Audience :: Developers", 15 | "License :: OSI Approved :: MIT License", 16 | "Programming Language :: Python :: 3", 17 | "Programming Language :: Python :: 3.11", 18 | ] 19 | dependencies = [ 20 | "mcp>=1.0.0", 21 | "pydantic>=2.0.0", 22 | "redditwarp>=1.3.0", 23 | ] 24 | 25 | [project.scripts] 26 | mcp-server-reddit = "mcp_server_reddit:main" 27 | 28 | [build-system] 29 | requires = ["hatchling"] 30 | build-backend = "hatchling.build" 31 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | properties: {} 9 | commandFunction: 10 | # A function that produces the CLI command to start the MCP on stdio. 11 | |- 12 | config => ({ command: 'python', args: ['-m', 'mcp_server_reddit'] }) 13 | -------------------------------------------------------------------------------- /src/mcp_server_reddit/__init__.py: -------------------------------------------------------------------------------- 1 | from .server import serve 2 | 3 | 4 | def main(): 5 | """MCP Reddit Server - Reddit API functionality for MCP""" 6 | import argparse 7 | import asyncio 8 | 9 | parser = argparse.ArgumentParser( 10 | description="give a model the ability to access Reddit public API" 11 | ) 12 | 13 | args = parser.parse_args() 14 | asyncio.run(serve()) 15 | 16 | 17 | if __name__ == "__main__": 18 | main() 19 | -------------------------------------------------------------------------------- /src/mcp_server_reddit/__main__.py: -------------------------------------------------------------------------------- 1 | from mcp_server_reddit import main 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /src/mcp_server_reddit/server.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | import json 3 | from typing import Sequence 4 | import redditwarp.SYNC 5 | from mcp.server import Server 6 | from mcp.server.stdio import stdio_server 7 | from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource 8 | from mcp.shared.exceptions import McpError 9 | from pydantic import BaseModel 10 | 11 | 12 | class PostType(str, Enum): 13 | LINK = "link" 14 | TEXT = "text" 15 | GALLERY = "gallery" 16 | UNKNOWN = "unknown" 17 | 18 | 19 | class RedditTools(str, Enum): 20 | GET_FRONTPAGE_POSTS = "get_frontpage_posts" 21 | GET_SUBREDDIT_INFO = "get_subreddit_info" 22 | GET_SUBREDDIT_HOT_POSTS = "get_subreddit_hot_posts" 23 | GET_SUBREDDIT_NEW_POSTS = "get_subreddit_new_posts" 24 | GET_SUBREDDIT_TOP_POSTS = "get_subreddit_top_posts" 25 | GET_SUBREDDIT_RISING_POSTS = "get_subreddit_rising_posts" 26 | GET_POST_CONTENT = "get_post_content" 27 | GET_POST_COMMENTS = "get_post_comments" 28 | 29 | 30 | class SubredditInfo(BaseModel): 31 | name: str 32 | subscriber_count: int 33 | description: str | None 34 | 35 | 36 | class Post(BaseModel): 37 | id: str 38 | title: str 39 | author: str 40 | score: int 41 | subreddit: str 42 | url: str 43 | created_at: str 44 | comment_count: int 45 | post_type: PostType 46 | content: str | None 47 | 48 | 49 | class Comment(BaseModel): 50 | id: str 51 | author: str 52 | body: str 53 | score: int 54 | replies: list['Comment'] = [] 55 | 56 | 57 | class Moderator(BaseModel): 58 | name: str 59 | 60 | 61 | class PostDetail(BaseModel): 62 | post: Post 63 | comments: list[Comment] 64 | 65 | 66 | class RedditServer: 67 | def __init__(self): 68 | self.client = redditwarp.SYNC.Client() 69 | 70 | def _get_post_type(self, submission) -> PostType: 71 | """Helper method to determine post type""" 72 | if isinstance(submission, redditwarp.models.submission_SYNC.LinkPost): 73 | return PostType.LINK 74 | elif isinstance(submission, redditwarp.models.submission_SYNC.TextPost): 75 | return PostType.TEXT 76 | elif isinstance(submission, redditwarp.models.submission_SYNC.GalleryPost): 77 | return PostType.GALLERY 78 | return PostType.UNKNOWN 79 | 80 | # The type can actually be determined by submission.post_hint 81 | # - self for text 82 | # - image for image 83 | # - hosted:video for video 84 | def _get_post_content(self, submission) -> str | None: 85 | """Helper method to extract post content based on type""" 86 | if isinstance(submission, redditwarp.models.submission_SYNC.LinkPost): 87 | return submission.permalink 88 | elif isinstance(submission, redditwarp.models.submission_SYNC.TextPost): 89 | return submission.body 90 | elif isinstance(submission, redditwarp.models.submission_SYNC.GalleryPost): 91 | return str(submission.gallery_link) 92 | return None 93 | 94 | def _build_post(self, submission) -> Post: 95 | """Helper method to build Post object from submission""" 96 | return Post( 97 | id=submission.id36, 98 | title=submission.title, 99 | author=submission.author_display_name or '[deleted]', 100 | score=submission.score, 101 | subreddit=submission.subreddit.name, 102 | url=submission.permalink, 103 | created_at=submission.created_at.astimezone().isoformat(), 104 | comment_count=submission.comment_count, 105 | post_type=self._get_post_type(submission), 106 | content=self._get_post_content(submission) 107 | ) 108 | 109 | def get_frontpage_posts(self, limit: int = 10) -> list[Post]: 110 | """Get hot posts from Reddit frontpage""" 111 | posts = [] 112 | for subm in self.client.p.front.pull.hot(limit): 113 | posts.append(self._build_post(subm)) 114 | return posts 115 | 116 | def get_subreddit_info(self, subreddit_name: str) -> SubredditInfo: 117 | """Get information about a subreddit""" 118 | subr = self.client.p.subreddit.fetch_by_name(subreddit_name) 119 | return SubredditInfo( 120 | name=subr.name, 121 | subscriber_count=subr.subscriber_count, 122 | description=subr.public_description 123 | ) 124 | 125 | def _build_comment_tree(self, node, depth: int = 3) -> Comment | None: 126 | """Helper method to recursively build comment tree""" 127 | if depth <= 0 or not node: 128 | return None 129 | 130 | comment = node.value 131 | replies = [] 132 | for child in node.children: 133 | child_comment = self._build_comment_tree(child, depth - 1) 134 | if child_comment: 135 | replies.append(child_comment) 136 | 137 | return Comment( 138 | id=comment.id36, 139 | author=comment.author_display_name or '[deleted]', 140 | body=comment.body, 141 | score=comment.score, 142 | replies=replies 143 | ) 144 | 145 | def get_subreddit_hot_posts(self, subreddit_name: str, limit: int = 10) -> list[Post]: 146 | """Get hot posts from a specific subreddit""" 147 | posts = [] 148 | for subm in self.client.p.subreddit.pull.hot(subreddit_name, limit): 149 | posts.append(self._build_post(subm)) 150 | return posts 151 | 152 | def get_subreddit_new_posts(self, subreddit_name: str, limit: int = 10) -> list[Post]: 153 | """Get new posts from a specific subreddit""" 154 | posts = [] 155 | for subm in self.client.p.subreddit.pull.new(subreddit_name, limit): 156 | posts.append(self._build_post(subm)) 157 | return posts 158 | 159 | def get_subreddit_top_posts(self, subreddit_name: str, limit: int = 10, time: str = '') -> list[Post]: 160 | """Get top posts from a specific subreddit""" 161 | posts = [] 162 | for subm in self.client.p.subreddit.pull.top(subreddit_name, limit, time=time): 163 | posts.append(self._build_post(subm)) 164 | return posts 165 | 166 | def get_subreddit_rising_posts(self, subreddit_name: str, limit: int = 10) -> list[Post]: 167 | """Get rising posts from a specific subreddit""" 168 | posts = [] 169 | for subm in self.client.p.subreddit.pull.rising(subreddit_name, limit): 170 | posts.append(self._build_post(subm)) 171 | return posts 172 | 173 | def get_post_content(self, post_id: str, comment_limit: int = 10, comment_depth: int = 3) -> PostDetail: 174 | """Get detailed content of a specific post including comments""" 175 | submission = self.client.p.submission.fetch(post_id) 176 | post = self._build_post(submission) 177 | 178 | # Fetch comments 179 | comments = self.get_post_comments(post_id, comment_limit) 180 | 181 | return PostDetail(post=post, comments=comments) 182 | 183 | def get_post_comments(self, post_id: str, limit: int = 10) -> list[Comment]: 184 | """Get comments from a post""" 185 | comments = [] 186 | tree_node = self.client.p.comment_tree.fetch(post_id, sort='top', limit=limit) 187 | for node in tree_node.children: 188 | comment = self._build_comment_tree(node) 189 | if comment: 190 | comments.append(comment) 191 | return comments 192 | 193 | 194 | async def serve() -> None: 195 | server = Server("mcp-reddit") 196 | reddit_server = RedditServer() 197 | 198 | @server.list_tools() 199 | async def list_tools() -> list[Tool]: 200 | """List available Reddit tools.""" 201 | return [ 202 | Tool( 203 | name=RedditTools.GET_FRONTPAGE_POSTS.value, 204 | description="Get hot posts from Reddit frontpage", 205 | inputSchema={ 206 | "type": "object", 207 | "properties": { 208 | "limit": { 209 | "type": "integer", 210 | "description": "Number of posts to return (default: 10)", 211 | "default": 10, 212 | "minimum": 1, 213 | "maximum": 100 214 | } 215 | } 216 | } 217 | ), 218 | Tool( 219 | name=RedditTools.GET_SUBREDDIT_INFO.value, 220 | description="Get information about a subreddit", 221 | inputSchema={ 222 | "type": "object", 223 | "properties": { 224 | "subreddit_name": { 225 | "type": "string", 226 | "description": "Name of the subreddit (e.g. 'Python', 'news')", 227 | } 228 | }, 229 | "required": ["subreddit_name"] 230 | } 231 | ), 232 | Tool( 233 | name=RedditTools.GET_SUBREDDIT_HOT_POSTS.value, 234 | description="Get hot posts from a specific subreddit", 235 | inputSchema={ 236 | "type": "object", 237 | "properties": { 238 | "subreddit_name": { 239 | "type": "string", 240 | "description": "Name of the subreddit (e.g. 'Python', 'news')", 241 | }, 242 | "limit": { 243 | "type": "integer", 244 | "description": "Number of posts to return (default: 10)", 245 | "default": 10, 246 | "minimum": 1, 247 | "maximum": 100 248 | } 249 | }, 250 | "required": ["subreddit_name"] 251 | } 252 | ), 253 | Tool( 254 | name=RedditTools.GET_SUBREDDIT_NEW_POSTS.value, 255 | description="Get new posts from a specific subreddit", 256 | inputSchema={ 257 | "type": "object", 258 | "properties": { 259 | "subreddit_name": { 260 | "type": "string", 261 | "description": "Name of the subreddit (e.g. 'Python', 'news')", 262 | }, 263 | "limit": { 264 | "type": "integer", 265 | "description": "Number of posts to return (default: 10)", 266 | "default": 10, 267 | "minimum": 1, 268 | "maximum": 100 269 | } 270 | }, 271 | "required": ["subreddit_name"] 272 | } 273 | ), 274 | Tool( 275 | name=RedditTools.GET_SUBREDDIT_TOP_POSTS.value, 276 | description="Get top posts from a specific subreddit", 277 | inputSchema={ 278 | "type": "object", 279 | "properties": { 280 | "subreddit_name": { 281 | "type": "string", 282 | "description": "Name of the subreddit (e.g. 'Python', 'news')", 283 | }, 284 | "limit": { 285 | "type": "integer", 286 | "description": "Number of posts to return (default: 10)", 287 | "default": 10, 288 | "minimum": 1, 289 | "maximum": 100 290 | }, 291 | "time": { 292 | "type": "string", 293 | "description": "Time filter for top posts (e.g. 'hour', 'day', 'week', 'month', 'year', 'all')", 294 | "default": "", 295 | "enum": ["", "hour", "day", "week", "month", "year", "all"] 296 | } 297 | }, 298 | "required": ["subreddit_name"] 299 | } 300 | ), 301 | Tool( 302 | name=RedditTools.GET_SUBREDDIT_RISING_POSTS.value, 303 | description="Get rising posts from a specific subreddit", 304 | inputSchema={ 305 | "type": "object", 306 | "properties": { 307 | "subreddit_name": { 308 | "type": "string", 309 | "description": "Name of the subreddit (e.g. 'Python', 'news')", 310 | }, 311 | "limit": { 312 | "type": "integer", 313 | "description": "Number of posts to return (default: 10)", 314 | "default": 10, 315 | "minimum": 1, 316 | "maximum": 100 317 | } 318 | }, 319 | "required": ["subreddit_name"] 320 | } 321 | ), 322 | Tool( 323 | name=RedditTools.GET_POST_CONTENT.value, 324 | description="Get detailed content of a specific post", 325 | inputSchema={ 326 | "type": "object", 327 | "properties": { 328 | "post_id": { 329 | "type": "string", 330 | "description": "ID of the post", 331 | }, 332 | "comment_limit": { 333 | "type": "integer", 334 | "description": "Number of top-level comments to return (default: 10)", 335 | "default": 10, 336 | "minimum": 1, 337 | "maximum": 100 338 | }, 339 | "comment_depth": { 340 | "type": "integer", 341 | "description": "Maximum depth of comment tree (default: 3)", 342 | "default": 3, 343 | "minimum": 1, 344 | "maximum": 10 345 | } 346 | }, 347 | "required": ["post_id"] 348 | } 349 | ), 350 | Tool( 351 | name=RedditTools.GET_POST_COMMENTS.value, 352 | description="Get comments from a post", 353 | inputSchema={ 354 | "type": "object", 355 | "properties": { 356 | "post_id": { 357 | "type": "string", 358 | "description": "ID of the post", 359 | }, 360 | "limit": { 361 | "type": "integer", 362 | "description": "Number of comments to return (default: 10)", 363 | "default": 10, 364 | "minimum": 1, 365 | "maximum": 100 366 | } 367 | }, 368 | "required": ["post_id"] 369 | } 370 | ), 371 | ] 372 | 373 | @server.call_tool() 374 | async def call_tool( 375 | name: str, arguments: dict 376 | ) -> Sequence[TextContent | ImageContent | EmbeddedResource]: 377 | """Handle tool calls for Reddit API.""" 378 | try: 379 | match name: 380 | case RedditTools.GET_FRONTPAGE_POSTS.value: 381 | limit = arguments.get("limit", 10) 382 | result = reddit_server.get_frontpage_posts(limit) 383 | 384 | case RedditTools.GET_SUBREDDIT_INFO.value: 385 | subreddit_name = arguments.get("subreddit_name") 386 | if not subreddit_name: 387 | raise ValueError("Missing required argument: subreddit_name") 388 | result = reddit_server.get_subreddit_info(subreddit_name) 389 | 390 | case RedditTools.GET_SUBREDDIT_HOT_POSTS.value: 391 | subreddit_name = arguments.get("subreddit_name") 392 | if not subreddit_name: 393 | raise ValueError("Missing required argument: subreddit_name") 394 | limit = arguments.get("limit", 10) 395 | result = reddit_server.get_subreddit_hot_posts(subreddit_name, limit) 396 | 397 | case RedditTools.GET_SUBREDDIT_NEW_POSTS.value: 398 | subreddit_name = arguments.get("subreddit_name") 399 | if not subreddit_name: 400 | raise ValueError("Missing required argument: subreddit_name") 401 | limit = arguments.get("limit", 10) 402 | result = reddit_server.get_subreddit_new_posts(subreddit_name, limit) 403 | 404 | case RedditTools.GET_SUBREDDIT_TOP_POSTS.value: 405 | subreddit_name = arguments.get("subreddit_name") 406 | if not subreddit_name: 407 | raise ValueError("Missing required argument: subreddit_name") 408 | limit = arguments.get("limit", 10) 409 | time = arguments.get("time", "") 410 | result = reddit_server.get_subreddit_top_posts(subreddit_name, limit, time) 411 | 412 | case RedditTools.GET_SUBREDDIT_RISING_POSTS.value: 413 | subreddit_name = arguments.get("subreddit_name") 414 | if not subreddit_name: 415 | raise ValueError("Missing required argument: subreddit_name") 416 | limit = arguments.get("limit", 10) 417 | result = reddit_server.get_subreddit_rising_posts(subreddit_name, limit) 418 | 419 | case RedditTools.GET_POST_CONTENT.value: 420 | post_id = arguments.get("post_id") 421 | if not post_id: 422 | raise ValueError("Missing required argument: post_id") 423 | comment_limit = arguments.get("comment_limit", 10) 424 | comment_depth = arguments.get("comment_depth", 3) 425 | result = reddit_server.get_post_content(post_id, comment_limit, comment_depth) 426 | 427 | case RedditTools.GET_POST_COMMENTS.value: 428 | post_id = arguments.get("post_id") 429 | if not post_id: 430 | raise ValueError("Missing required argument: post_id") 431 | limit = arguments.get("limit", 10) 432 | result = reddit_server.get_post_comments(post_id, limit) 433 | 434 | case _: 435 | raise ValueError(f"Unknown tool: {name}") 436 | 437 | return [ 438 | TextContent(type="text", text=json.dumps(result, default=lambda x: x.model_dump(), indent=2)) 439 | ] 440 | 441 | except Exception as e: 442 | raise ValueError(f"Error processing mcp-server-reddit query: {str(e)}") 443 | 444 | options = server.create_initialization_options() 445 | async with stdio_server() as (read_stream, write_stream): 446 | await server.run(read_stream, write_stream, options) 447 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.11" 3 | 4 | [[package]] 5 | name = "annotated-types" 6 | version = "0.7.0" 7 | source = { registry = "https://pypi.org/simple" } 8 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 9 | wheels = [ 10 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 11 | ] 12 | 13 | [[package]] 14 | name = "anyio" 15 | version = "4.8.0" 16 | source = { registry = "https://pypi.org/simple" } 17 | dependencies = [ 18 | { name = "idna" }, 19 | { name = "sniffio" }, 20 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 21 | ] 22 | sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } 23 | wheels = [ 24 | { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, 25 | ] 26 | 27 | [[package]] 28 | name = "certifi" 29 | version = "2025.1.31" 30 | source = { registry = "https://pypi.org/simple" } 31 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } 32 | wheels = [ 33 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, 34 | ] 35 | 36 | [[package]] 37 | name = "click" 38 | version = "8.1.8" 39 | source = { registry = "https://pypi.org/simple" } 40 | dependencies = [ 41 | { name = "colorama", marker = "platform_system == 'Windows'" }, 42 | ] 43 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 44 | wheels = [ 45 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 46 | ] 47 | 48 | [[package]] 49 | name = "colorama" 50 | version = "0.4.6" 51 | source = { registry = "https://pypi.org/simple" } 52 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 53 | wheels = [ 54 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 55 | ] 56 | 57 | [[package]] 58 | name = "h11" 59 | version = "0.14.0" 60 | source = { registry = "https://pypi.org/simple" } 61 | sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } 62 | wheels = [ 63 | { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, 64 | ] 65 | 66 | [[package]] 67 | name = "httpcore" 68 | version = "1.0.7" 69 | source = { registry = "https://pypi.org/simple" } 70 | dependencies = [ 71 | { name = "certifi" }, 72 | { name = "h11" }, 73 | ] 74 | sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } 75 | wheels = [ 76 | { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, 77 | ] 78 | 79 | [[package]] 80 | name = "httpx" 81 | version = "0.28.1" 82 | source = { registry = "https://pypi.org/simple" } 83 | dependencies = [ 84 | { name = "anyio" }, 85 | { name = "certifi" }, 86 | { name = "httpcore" }, 87 | { name = "idna" }, 88 | ] 89 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 90 | wheels = [ 91 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 92 | ] 93 | 94 | [[package]] 95 | name = "httpx-sse" 96 | version = "0.4.0" 97 | source = { registry = "https://pypi.org/simple" } 98 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 99 | wheels = [ 100 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 101 | ] 102 | 103 | [[package]] 104 | name = "idna" 105 | version = "3.10" 106 | source = { registry = "https://pypi.org/simple" } 107 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 108 | wheels = [ 109 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 110 | ] 111 | 112 | [[package]] 113 | name = "mcp" 114 | version = "1.2.1" 115 | source = { registry = "https://pypi.org/simple" } 116 | dependencies = [ 117 | { name = "anyio" }, 118 | { name = "httpx" }, 119 | { name = "httpx-sse" }, 120 | { name = "pydantic" }, 121 | { name = "pydantic-settings" }, 122 | { name = "sse-starlette" }, 123 | { name = "starlette" }, 124 | { name = "uvicorn" }, 125 | ] 126 | sdist = { url = "https://files.pythonhosted.org/packages/fc/30/51e4555826126e3954fa2ab1e934bf74163c5fe05e98f38ca4d0f8abbf63/mcp-1.2.1.tar.gz", hash = "sha256:c9d43dbfe943aa1530e2be8f54b73af3ebfb071243827b4483d421684806cb45", size = 103968 } 127 | wheels = [ 128 | { url = "https://files.pythonhosted.org/packages/4c/0d/6770742a84c8aa1d36c0d628896a380584c5759612e66af7446af07d8775/mcp-1.2.1-py3-none-any.whl", hash = "sha256:579bf9c9157850ebb1344f3ca6f7a3021b0123c44c9f089ef577a7062522f0fd", size = 66453 }, 129 | ] 130 | 131 | [[package]] 132 | name = "mcp-server-reddit" 133 | version = "0.1.0" 134 | source = { editable = "." } 135 | dependencies = [ 136 | { name = "mcp" }, 137 | { name = "pydantic" }, 138 | { name = "redditwarp" }, 139 | ] 140 | 141 | [package.metadata] 142 | requires-dist = [ 143 | { name = "mcp", specifier = ">=1.0.0" }, 144 | { name = "pydantic", specifier = ">=2.0.0" }, 145 | { name = "redditwarp", specifier = ">=1.3.0" }, 146 | ] 147 | 148 | [[package]] 149 | name = "pydantic" 150 | version = "2.10.6" 151 | source = { registry = "https://pypi.org/simple" } 152 | dependencies = [ 153 | { name = "annotated-types" }, 154 | { name = "pydantic-core" }, 155 | { name = "typing-extensions" }, 156 | ] 157 | sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 } 158 | wheels = [ 159 | { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 }, 160 | ] 161 | 162 | [[package]] 163 | name = "pydantic-core" 164 | version = "2.27.2" 165 | source = { registry = "https://pypi.org/simple" } 166 | dependencies = [ 167 | { name = "typing-extensions" }, 168 | ] 169 | sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } 170 | wheels = [ 171 | { url = "https://files.pythonhosted.org/packages/c2/89/f3450af9d09d44eea1f2c369f49e8f181d742f28220f88cc4dfaae91ea6e/pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", size = 1893421 }, 172 | { url = "https://files.pythonhosted.org/packages/9e/e3/71fe85af2021f3f386da42d291412e5baf6ce7716bd7101ea49c810eda90/pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", size = 1814998 }, 173 | { url = "https://files.pythonhosted.org/packages/a6/3c/724039e0d848fd69dbf5806894e26479577316c6f0f112bacaf67aa889ac/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", size = 1826167 }, 174 | { url = "https://files.pythonhosted.org/packages/2b/5b/1b29e8c1fb5f3199a9a57c1452004ff39f494bbe9bdbe9a81e18172e40d3/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", size = 1865071 }, 175 | { url = "https://files.pythonhosted.org/packages/89/6c/3985203863d76bb7d7266e36970d7e3b6385148c18a68cc8915fd8c84d57/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", size = 2036244 }, 176 | { url = "https://files.pythonhosted.org/packages/0e/41/f15316858a246b5d723f7d7f599f79e37493b2e84bfc789e58d88c209f8a/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", size = 2737470 }, 177 | { url = "https://files.pythonhosted.org/packages/a8/7c/b860618c25678bbd6d1d99dbdfdf0510ccb50790099b963ff78a124b754f/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", size = 1992291 }, 178 | { url = "https://files.pythonhosted.org/packages/bf/73/42c3742a391eccbeab39f15213ecda3104ae8682ba3c0c28069fbcb8c10d/pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", size = 1994613 }, 179 | { url = "https://files.pythonhosted.org/packages/94/7a/941e89096d1175d56f59340f3a8ebaf20762fef222c298ea96d36a6328c5/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", size = 2002355 }, 180 | { url = "https://files.pythonhosted.org/packages/6e/95/2359937a73d49e336a5a19848713555605d4d8d6940c3ec6c6c0ca4dcf25/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", size = 2126661 }, 181 | { url = "https://files.pythonhosted.org/packages/2b/4c/ca02b7bdb6012a1adef21a50625b14f43ed4d11f1fc237f9d7490aa5078c/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", size = 2153261 }, 182 | { url = "https://files.pythonhosted.org/packages/72/9d/a241db83f973049a1092a079272ffe2e3e82e98561ef6214ab53fe53b1c7/pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", size = 1812361 }, 183 | { url = "https://files.pythonhosted.org/packages/e8/ef/013f07248041b74abd48a385e2110aa3a9bbfef0fbd97d4e6d07d2f5b89a/pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", size = 1982484 }, 184 | { url = "https://files.pythonhosted.org/packages/10/1c/16b3a3e3398fd29dca77cea0a1d998d6bde3902fa2706985191e2313cc76/pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", size = 1867102 }, 185 | { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 }, 186 | { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 }, 187 | { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 }, 188 | { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 }, 189 | { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 }, 190 | { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 }, 191 | { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 }, 192 | { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 }, 193 | { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 }, 194 | { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 }, 195 | { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 }, 196 | { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 }, 197 | { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 }, 198 | { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 }, 199 | { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 }, 200 | { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 }, 201 | { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 }, 202 | { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 }, 203 | { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 }, 204 | { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 }, 205 | { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 }, 206 | { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 }, 207 | { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 }, 208 | { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 }, 209 | { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 }, 210 | { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, 211 | { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, 212 | { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, 213 | ] 214 | 215 | [[package]] 216 | name = "pydantic-settings" 217 | version = "2.7.1" 218 | source = { registry = "https://pypi.org/simple" } 219 | dependencies = [ 220 | { name = "pydantic" }, 221 | { name = "python-dotenv" }, 222 | ] 223 | sdist = { url = "https://files.pythonhosted.org/packages/73/7b/c58a586cd7d9ac66d2ee4ba60ca2d241fa837c02bca9bea80a9a8c3d22a9/pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93", size = 79920 } 224 | wheels = [ 225 | { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 }, 226 | ] 227 | 228 | [[package]] 229 | name = "python-dotenv" 230 | version = "1.0.1" 231 | source = { registry = "https://pypi.org/simple" } 232 | sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } 233 | wheels = [ 234 | { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, 235 | ] 236 | 237 | [[package]] 238 | name = "redditwarp" 239 | version = "1.3.0" 240 | source = { registry = "https://pypi.org/simple" } 241 | sdist = { url = "https://files.pythonhosted.org/packages/03/f8/48d632fa5d21f805d0298dbdf70e6c802c9f5df048b5fe10074dd279ac47/redditwarp-1.3.0.tar.gz", hash = "sha256:962a092ae0e8e40f703af40996a6b7d896b718893e4c619d16740aa2426c7901", size = 207632 } 242 | wheels = [ 243 | { url = "https://files.pythonhosted.org/packages/0b/c4/a62b72c184e2d98bd0a16a4fa7d693e82cb40ae2342c74d8e7acb66eebc5/redditwarp-1.3.0-py3-none-any.whl", hash = "sha256:752139e961965524e534295ddef363316ffcfafc9abb6e8a1cb08cc90645db97", size = 483990 }, 244 | ] 245 | 246 | [[package]] 247 | name = "sniffio" 248 | version = "1.3.1" 249 | source = { registry = "https://pypi.org/simple" } 250 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 251 | wheels = [ 252 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 253 | ] 254 | 255 | [[package]] 256 | name = "sse-starlette" 257 | version = "2.2.1" 258 | source = { registry = "https://pypi.org/simple" } 259 | dependencies = [ 260 | { name = "anyio" }, 261 | { name = "starlette" }, 262 | ] 263 | sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 } 264 | wheels = [ 265 | { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 }, 266 | ] 267 | 268 | [[package]] 269 | name = "starlette" 270 | version = "0.45.3" 271 | source = { registry = "https://pypi.org/simple" } 272 | dependencies = [ 273 | { name = "anyio" }, 274 | ] 275 | sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 } 276 | wheels = [ 277 | { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 }, 278 | ] 279 | 280 | [[package]] 281 | name = "typing-extensions" 282 | version = "4.12.2" 283 | source = { registry = "https://pypi.org/simple" } 284 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } 285 | wheels = [ 286 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, 287 | ] 288 | 289 | [[package]] 290 | name = "uvicorn" 291 | version = "0.34.0" 292 | source = { registry = "https://pypi.org/simple" } 293 | dependencies = [ 294 | { name = "click" }, 295 | { name = "h11" }, 296 | ] 297 | sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } 298 | wheels = [ 299 | { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, 300 | ] 301 | --------------------------------------------------------------------------------