├── .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 | [](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 |
9 |
10 | ## Video Demo (Click to Watch)
11 |
12 | A demo in Clinde 👇
13 |
14 | [](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 |
--------------------------------------------------------------------------------