├── .python-version ├── .dockerignore ├── .gitignore ├── src └── arxiv_server │ ├── __init__.py │ └── server.py ├── pyproject.toml ├── Dockerfile ├── smithery.yaml ├── LICENSE ├── README.md └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.13 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/__pycache__/ 2 | **/.DS_Store 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | __pycache__ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /src/arxiv_server/__init__.py: -------------------------------------------------------------------------------- 1 | from . import server 2 | 3 | def main(): 4 | """Main entry point for the package.""" 5 | server.main() 6 | 7 | # Optionally expose other important items at package level 8 | __all__ = ['main', 'server'] 9 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "arxiv-mcp-server" 3 | version = "0.1.0" 4 | description = "MCP server for accesing arXiv API" 5 | readme = "README.md" 6 | requires-python = ">=3.13" 7 | license = "MIT" 8 | authors = [ 9 | {name = "Prashal Ruchiranga"} 10 | ] 11 | dependencies = [ 12 | "feedparser>=6.0.11", 13 | "httpx>=0.28.1", 14 | "mcp[cli]>=1.6.0", 15 | "pymupdf>=1.25.5", 16 | ] 17 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.13-slim 2 | 3 | # Set working directory 4 | WORKDIR /app 5 | 6 | # Create download directory 7 | ENV DOWNLOAD_PATH=/data 8 | RUN mkdir -p ${DOWNLOAD_PATH} 9 | 10 | # Copy project metadata first (to install dependencies) 11 | COPY pyproject.toml uv.lock ./ 12 | 13 | # Install dependencies 14 | RUN pip install uv 15 | RUN uv sync 16 | 17 | # Copy source code 18 | COPY src ./src 19 | 20 | # Activate virtual environment 21 | ENV PATH="/app/.venv/bin:$PATH" 22 | 23 | # Start the MCP server 24 | CMD ["python", "src/arxiv_server/server.py"] 25 | -------------------------------------------------------------------------------- /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 | required: [] 9 | properties: 10 | downloadPath: 11 | type: string 12 | default: /data 13 | description: Path to save downloaded PDFs 14 | commandFunction: 15 | # A JS function that produces the CLI command based on the given config to start the MCP on stdio. 16 | |- 17 | (config) => ({ command: 'python', args: ['src/arxiv_server/server.py'], env: { DOWNLOAD_PATH: config.downloadPath } }) 18 | exampleConfig: 19 | downloadPath: /data 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Prashal Ruchiranga 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 | # arXiv MCP Server 2 | 3 | [![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-purple.svg)](https://modelcontextprotocol.io) 4 | [![Python](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/) 5 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) 6 | [![smithery badge](https://smithery.ai/badge/@prashalruchiranga/arxiv-mcp-server)](https://smithery.ai/server/@prashalruchiranga/arxiv-mcp-server) 7 | 8 | A Model Context Protocol (MCP) server that enables interacting with the arXiv API using natural language. 9 | 10 | ## Features 11 | - Retrieve metadata about scholarly articles hosted on arXiv.org 12 | - Download articles in PDF format to the local machine 13 | - Search arXiv database for a particular query 14 | - Retrieve articles and load them into a large language model (LLM) context 15 | 16 | ## Tools 17 | - **get_article_url** 18 | - Retrieve the URL of an article hosted on arXiv.org based on its title 19 | - `title` (String): Article title 20 | - **download_article** 21 | - Download the article hosted on arXiv.org as a PDF file 22 | - `title` (String): Article title 23 | - **load_article_to_context** 24 | - Load the article hosted on arXiv.org into context of a LLM 25 | - `title` (String): Article title 26 | - **get_details** 27 | - Retrieve metadata of an article hosted on arXiv.org based on its title 28 | - `title` (String): Article title 29 | - **search_arxiv** 30 | - Performs a search query on the arXiv API based on specified parameters and returns matching article metadata 31 | - `all_fields` (String): General keyword search across all metadata fields 32 | - `title` (String): Keyword(s) to search for within the titles of articles 33 | - `author` (String): Author name(s) to filter results by 34 | - `abstract` (String): Keyword(s) to search for within article abstracts 35 | - `start` (int): Index of the first result to return 36 | 37 | ## Setup 38 | 39 | ### MacOS 40 | 41 | Clone the repository 42 | ``` 43 | git clone https://github.com/prashalruchiranga/arxiv-mcp-server.git 44 | cd arxiv-mcp-server 45 | ``` 46 | Install `uv` package manager. For more details on installing, visit the [official uv documentation](https://docs.astral.sh/uv/getting-started/installation/). 47 | ``` 48 | # Using Homebrew 49 | brew install uv 50 | 51 | # or 52 | curl -LsSf https://astral.sh/uv/install.sh | sh 53 | ``` 54 | 55 | Create and activate virtual environment. 56 | ``` 57 | uv venv --python=python3.13 58 | source .venv/bin/activate 59 | ``` 60 | 61 | Install development dependencies. 62 | ``` 63 | uv sync 64 | ``` 65 | 66 | ### Windows 67 | 68 | Install `uv` package manager. For more details on installing, visit the [official uv documentation](https://docs.astral.sh/uv/getting-started/installation/). 69 | ``` 70 | # Use irm to download the script and execute it with iex 71 | powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 72 | ``` 73 | Close and reopen the shell, then clone the repository. 74 | ``` 75 | git clone https://github.com/prashalruchiranga/arxiv-mcp-server.git 76 | cd arxiv-mcp-server 77 | ``` 78 | 79 | Create and activate virtual environment. 80 | ``` 81 | uv venv --python=python3.13 82 | source .venv\Scripts\activate 83 | ``` 84 | 85 | Install development dependencies. 86 | ``` 87 | uv sync 88 | ``` 89 | 90 | ## Usage with Claude Desktop 91 | To enable this integration, add the server configuration to your `claude_desktop_config.json` file. Make sure to create the file if it doesn’t exist. 92 | 93 | On MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json` On Windows: `%APPDATA%/Roaming/Claude/claude_desktop_config.json` 94 | 95 | ``` 96 | { 97 | "mcpServers": { 98 | "arxiv-server": { 99 | "command": "uv", 100 | "args": [ 101 | "--directory", 102 | "/ABSOLUTE/PATH/TO/PARENT/FOLDER/arxiv-mcp-server/src/arxiv_server", 103 | "run", 104 | "server.py" 105 | ], 106 | "env": { 107 | "DOWNLOAD_PATH": "/ABSOLUTE/PATH/TO/DOWNLOADS/FOLDER" 108 | } 109 | } 110 | } 111 | } 112 | ``` 113 | 114 | You may need to put the full path to the uv executable in the command field. You can get this by running `which uv` on MacOS or `where uv` on Windows. 115 | 116 | ## Example Prompts 117 | ``` 118 | Can you get the details of 'Reasoning to Learn from Latent Thoughts' paper? 119 | ``` 120 | ``` 121 | Get the papers authored or co-authored by Yann Lecun on convolutional neural networks 122 | ``` 123 | ``` 124 | Download the attention is all you need paper 125 | ``` 126 | ``` 127 | Can you get the papers by Andrew NG which have 'convolutional neural networks' in title? 128 | ``` 129 | ``` 130 | Can you display the paper? 131 | ``` 132 | ``` 133 | List the titles of papers by Yann LeCun. Paginate through the API until there are 30 titles 134 | ``` 135 | 136 | ## License 137 | 138 | Licensed under MIT. See the [LICENSE](https://github.com/prashalruchiranga/arxiv-mcp-server/blob/main/LICENSE). 139 | -------------------------------------------------------------------------------- /src/arxiv_server/server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import difflib 4 | import json 5 | from typing import Optional 6 | import httpx 7 | from mcp.server.fastmcp import Context, FastMCP 8 | import feedparser 9 | import fitz 10 | 11 | mcp = FastMCP("arxiv-server") 12 | 13 | USER_AGENT = "arxiv-app/1.0" 14 | ARXIV_API_BASE = "https://export.arxiv.org/api" 15 | DOWNLOAD_PATH = os.getenv("DOWNLOAD_PATH") 16 | 17 | async def make_api_call(url: str, params: dict[str, str]) -> str | None: 18 | """Make a request to the arXiv API.""" 19 | headers = { 20 | "User-Agent": USER_AGENT, 21 | "Accept": "application/atom+xml" 22 | } 23 | async with httpx.AsyncClient() as client: 24 | try: 25 | response = await client.get(url, params=params, headers=headers, timeout=30.0) 26 | response.raise_for_status() 27 | return response.text 28 | except Exception: 29 | return None 30 | 31 | async def get_pdf(url: str) -> bytes | None: 32 | """Get PDF document as bytes from arXiv.org.""" 33 | headers = { 34 | "User-Agent": USER_AGENT, 35 | "Accept": "application/pdf" 36 | } 37 | async with httpx.AsyncClient() as client: 38 | try: 39 | response = await client.get(url, headers=headers, timeout=30.0) 40 | response.raise_for_status() 41 | return response.content 42 | except Exception: 43 | return None 44 | 45 | def find_best_match(target_title: str, entries: list, threshold: float = 0.8): 46 | """Find the entry whose title best matches the target title.""" 47 | target_title_lower = target_title.lower() 48 | best_entry = None 49 | best_score = 0.0 50 | for entry in entries: 51 | entry_title_lower = entry.title.lower() 52 | score = difflib.SequenceMatcher(None, target_title_lower, entry_title_lower).ratio() 53 | if score > best_score: 54 | best_score = score 55 | best_entry = entry 56 | if best_score >= threshold: 57 | return best_entry 58 | return None 59 | 60 | async def fetch_information(title: str): 61 | """Get information about the article.""" 62 | formatted_title = format_text(title) 63 | url = f"{ARXIV_API_BASE}/query" 64 | params = { 65 | "search_query": f'ti:{formatted_title}', 66 | "start": 0, 67 | "max_results": 25 68 | } 69 | data = await make_api_call(url, params=params) 70 | if data is None: 71 | return "Unable to retrieve data from arXiv.org." 72 | feed = feedparser.parse(data) 73 | error_msg = ( 74 | "Unable to extract information for the provided title. " 75 | "This issue may stem from an incorrect or incomplete title, " 76 | "or because the work has not been published on arXiv." 77 | ) 78 | if not feed.entries: 79 | return error_msg 80 | best_match = find_best_match(target_title=formatted_title, entries=feed.entries) 81 | if best_match is None: 82 | return str(error_msg) 83 | return best_match 84 | 85 | async def get_url_and_arxiv_id(title: str) -> tuple[str, str] | str: 86 | """Get URL of the article hosted on arXiv.org.""" 87 | info = await fetch_information(title) 88 | if isinstance(info, str): 89 | return info 90 | arxiv_id = info.id.split("/abs/")[-1] 91 | direct_pdf_url = f"https://arxiv.org/pdf/{arxiv_id}" 92 | return (direct_pdf_url, arxiv_id) 93 | 94 | def format_text(text: str) -> str: 95 | """Clean a given text string by removing escape sequences and leading and trailing whitespaces.""" 96 | # Remove common escape sequences 97 | text_without_escapes = re.sub(r'\\[ntr]', ' ', text) 98 | # Replace colon with space 99 | text_without_colon = text_without_escapes.replace(':', ' ') 100 | # Remove both single quotes and double quotes 101 | text_without_quotes = re.sub(r'[\'"]', '', text_without_colon) 102 | # Collapse multiple spaces into one 103 | text_single_spaced = re.sub(r'\s+', ' ', text_without_quotes) 104 | # Trim leading and trailing spaces 105 | cleaned_text = text_single_spaced.strip() 106 | return cleaned_text 107 | 108 | @mcp.tool() 109 | async def get_article_url(title: str) -> str: 110 | """ 111 | Retrieve the URL of an article hosted on arXiv.org based on its title. Use this tool only 112 | for retrieving the URL. This tool searches for the article based on its title, and then 113 | fetches the corresponding URL from arXiv.org. 114 | 115 | Args: 116 | title: Article title. 117 | 118 | Returns: 119 | URL that can be used to retrieve the article. 120 | """ 121 | result = await get_url_and_arxiv_id(title) 122 | if isinstance(result, str): 123 | return result 124 | article_url, _ = result 125 | return article_url 126 | 127 | @mcp.tool() 128 | async def download_article(title: str) -> str: 129 | """ 130 | Download the article hosted on arXiv.org as a PDF file. This tool searches for the article based on its 131 | title, retrieves the article's PDF, and saves it to a specified download location using the arXiv ID as 132 | the filename. 133 | 134 | Args: 135 | title: Article title. 136 | 137 | Returns: 138 | Success or error message. 139 | """ 140 | result = await get_url_and_arxiv_id(title) 141 | if isinstance(result, str): 142 | return result 143 | article_url, arxiv_id = result 144 | pdf_doc = await get_pdf(article_url) 145 | if pdf_doc is None: 146 | return "Unable to retrieve the article from arXiv.org." 147 | file_path = os.path.join(DOWNLOAD_PATH, f"{arxiv_id}.pdf") 148 | try: 149 | with open(file_path, "wb") as file: 150 | file.write(pdf_doc) 151 | return f"Download successful. Find the PDF at {DOWNLOAD_PATH}" 152 | except Exception: 153 | return f"Unable to save the article to local directory." 154 | 155 | @mcp.tool() 156 | async def load_article_to_context(title: str) -> str: 157 | """ 158 | Load the article hosted on arXiv.org into context. This tool searches for the article based on its 159 | title, retrieves the article content, and loads text content into LLM context. 160 | 161 | Args: 162 | title: Article title. 163 | 164 | Returns: 165 | Article as a text string or error message. 166 | """ 167 | result = await get_url_and_arxiv_id(title) 168 | if isinstance(result, str): 169 | return result 170 | article_url, _ = result 171 | pdf_doc = await get_pdf(article_url) 172 | if pdf_doc is None: 173 | return "Unable to retrieve the article from arXiv.org." 174 | pymupdf_doc = fitz.open(stream=pdf_doc, filetype="pdf") 175 | content = "" 176 | for page in pymupdf_doc: 177 | content += page.get_text() 178 | return content 179 | 180 | @mcp.tool() 181 | async def get_details(title: str) -> str: 182 | """ 183 | Retrieve information of an article hosted on arXiv.org based on its title. This tool searches for the article 184 | based on its title and retrieves arXiv ID, title, authors, link, direct PDF URL, published timestamp, last 185 | updated timestamp, and summary. 186 | 187 | Args: 188 | title: Article title. 189 | 190 | Returns: 191 | A JSON-formatted string containing article details if retrieval is successful; 192 | otherwise, a plain error message string. 193 | """ 194 | info = await fetch_information(title) 195 | if isinstance(info, str): 196 | return info 197 | id = info.id 198 | link = info.link 199 | article_title = info.title 200 | authors = [author['name'] for author in info.authors] 201 | arxiv_id = id.split("/abs/")[-1] 202 | direct_pdf_url = f"https://arxiv.org/pdf/{arxiv_id}" 203 | updated = getattr(info, "updated", "Unknown") 204 | published = getattr(info, "published", "Unknown") 205 | summary = getattr(info, "summary", "Unknown") 206 | info_dict = { 207 | "arXiv ID": arxiv_id, 208 | "Title": article_title, 209 | "Authors": authors, 210 | "Link": link, 211 | "Direct PDF URL": direct_pdf_url, 212 | "Published": published, 213 | "Updated": updated, 214 | "Summary": summary 215 | } 216 | return json.dumps(info_dict) 217 | 218 | @mcp.tool() 219 | async def search_arxiv(ctx: Context, all_fields: Optional[str]=None, title: Optional[str]=None, 220 | author: Optional[str]=None, abstract: Optional[str]=None, start: int=0) -> str: 221 | """ 222 | Performs a search query on the arXiv API based on specified parameters and returns matching article metadata. 223 | This function allows for flexible querying of the arXiv database. Only parameters that are explicitly provided 224 | will be included in the final search query. Results are returned in a JSON-formatted string with article titles 225 | as keys and their corresponding arXiv IDs as values. 226 | 227 | Args: 228 | all_fields: General keyword search across all metadata fields including title, abstract, authors, comments, and categories. 229 | title: Keyword(s) to search for within the titles of articles. 230 | author: Author name(s) to filter results by. 231 | abstract: Keyword(s) to search for within article abstracts. 232 | start: Index of the first result to return; used for paginating through search results. Defaults to 0. 233 | 234 | Returns: 235 | A JSON-formatted string containing article titles and their associated arXiv IDs; 236 | otherwise, a plain error message string. 237 | """ 238 | prefixed_params = [] 239 | if author: 240 | author = format_text(author) 241 | prefixed_params.append(f'au:{author}') 242 | if all_fields: 243 | all_fields = format_text(all_fields) 244 | prefixed_params.append(f'all:{all_fields}') 245 | if title: 246 | title = format_text(title) 247 | prefixed_params.append(f'ti:{title}') 248 | if abstract: 249 | abstract = format_text(abstract) 250 | prefixed_params.append(f'abs:{abstract}') 251 | # Construct search query 252 | search_query = " AND ".join(prefixed_params) 253 | params = { 254 | "search_query": search_query, 255 | "start": start, 256 | "max_results": 10 257 | } 258 | await ctx.info("Calling the API") 259 | response = await make_api_call(f"{ARXIV_API_BASE}/query", params=params) 260 | if response is None: 261 | return "Unable to retrieve data from arXiv.org." 262 | feed = feedparser.parse(response) 263 | error_msg = ( 264 | "Unable to extract information for your query. " 265 | "This issue may stem from an incorrect search query." 266 | ) 267 | if not feed.entries: 268 | return error_msg 269 | entries = {} 270 | await ctx.info("Extracting information") 271 | for entry in feed.entries: 272 | id = entry.id 273 | article_title = entry.title 274 | arxiv_id = id.split("/abs/")[-1] 275 | authors = [author['name'] for author in entry.authors] 276 | entries[article_title] = {"arXiv ID": arxiv_id, "Authors": authors} 277 | return entries 278 | 279 | def main(): 280 | mcp.run(transport="stdio") 281 | 282 | if __name__ == "__main__": 283 | main() 284 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 1 3 | requires-python = ">=3.13" 4 | 5 | [[package]] 6 | name = "annotated-types" 7 | version = "0.7.0" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 10 | wheels = [ 11 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 12 | ] 13 | 14 | [[package]] 15 | name = "anyio" 16 | version = "4.9.0" 17 | source = { registry = "https://pypi.org/simple" } 18 | dependencies = [ 19 | { name = "idna" }, 20 | { name = "sniffio" }, 21 | ] 22 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } 23 | wheels = [ 24 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, 25 | ] 26 | 27 | [[package]] 28 | name = "arxiv-mcp-server" 29 | version = "0.1.0" 30 | source = { virtual = "." } 31 | dependencies = [ 32 | { name = "feedparser" }, 33 | { name = "httpx" }, 34 | { name = "mcp", extra = ["cli"] }, 35 | { name = "pymupdf" }, 36 | ] 37 | 38 | [package.metadata] 39 | requires-dist = [ 40 | { name = "feedparser", specifier = ">=6.0.11" }, 41 | { name = "httpx", specifier = ">=0.28.1" }, 42 | { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, 43 | { name = "pymupdf", specifier = ">=1.25.5" }, 44 | ] 45 | 46 | [[package]] 47 | name = "certifi" 48 | version = "2025.4.26" 49 | source = { registry = "https://pypi.org/simple" } 50 | sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } 51 | wheels = [ 52 | { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, 53 | ] 54 | 55 | [[package]] 56 | name = "click" 57 | version = "8.1.8" 58 | source = { registry = "https://pypi.org/simple" } 59 | dependencies = [ 60 | { name = "colorama", marker = "sys_platform == 'win32'" }, 61 | ] 62 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 63 | wheels = [ 64 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 65 | ] 66 | 67 | [[package]] 68 | name = "colorama" 69 | version = "0.4.6" 70 | source = { registry = "https://pypi.org/simple" } 71 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 72 | wheels = [ 73 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 74 | ] 75 | 76 | [[package]] 77 | name = "feedparser" 78 | version = "6.0.11" 79 | source = { registry = "https://pypi.org/simple" } 80 | dependencies = [ 81 | { name = "sgmllib3k" }, 82 | ] 83 | sdist = { url = "https://files.pythonhosted.org/packages/ff/aa/7af346ebeb42a76bf108027fe7f3328bb4e57a3a96e53e21fd9ef9dd6dd0/feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5", size = 286197 } 84 | wheels = [ 85 | { url = "https://files.pythonhosted.org/packages/7c/d4/8c31aad9cc18f451c49f7f9cfb5799dadffc88177f7917bc90a66459b1d7/feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45", size = 81343 }, 86 | ] 87 | 88 | [[package]] 89 | name = "h11" 90 | version = "0.16.0" 91 | source = { registry = "https://pypi.org/simple" } 92 | sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } 93 | wheels = [ 94 | { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, 95 | ] 96 | 97 | [[package]] 98 | name = "httpcore" 99 | version = "1.0.9" 100 | source = { registry = "https://pypi.org/simple" } 101 | dependencies = [ 102 | { name = "certifi" }, 103 | { name = "h11" }, 104 | ] 105 | sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } 106 | wheels = [ 107 | { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, 108 | ] 109 | 110 | [[package]] 111 | name = "httpx" 112 | version = "0.28.1" 113 | source = { registry = "https://pypi.org/simple" } 114 | dependencies = [ 115 | { name = "anyio" }, 116 | { name = "certifi" }, 117 | { name = "httpcore" }, 118 | { name = "idna" }, 119 | ] 120 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 121 | wheels = [ 122 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 123 | ] 124 | 125 | [[package]] 126 | name = "httpx-sse" 127 | version = "0.4.0" 128 | source = { registry = "https://pypi.org/simple" } 129 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 130 | wheels = [ 131 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 132 | ] 133 | 134 | [[package]] 135 | name = "idna" 136 | version = "3.10" 137 | source = { registry = "https://pypi.org/simple" } 138 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 139 | wheels = [ 140 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 141 | ] 142 | 143 | [[package]] 144 | name = "markdown-it-py" 145 | version = "3.0.0" 146 | source = { registry = "https://pypi.org/simple" } 147 | dependencies = [ 148 | { name = "mdurl" }, 149 | ] 150 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } 151 | wheels = [ 152 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, 153 | ] 154 | 155 | [[package]] 156 | name = "mcp" 157 | version = "1.6.0" 158 | source = { registry = "https://pypi.org/simple" } 159 | dependencies = [ 160 | { name = "anyio" }, 161 | { name = "httpx" }, 162 | { name = "httpx-sse" }, 163 | { name = "pydantic" }, 164 | { name = "pydantic-settings" }, 165 | { name = "sse-starlette" }, 166 | { name = "starlette" }, 167 | { name = "uvicorn" }, 168 | ] 169 | sdist = { url = "https://files.pythonhosted.org/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723", size = 200031 } 170 | wheels = [ 171 | { url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077 }, 172 | ] 173 | 174 | [package.optional-dependencies] 175 | cli = [ 176 | { name = "python-dotenv" }, 177 | { name = "typer" }, 178 | ] 179 | 180 | [[package]] 181 | name = "mdurl" 182 | version = "0.1.2" 183 | source = { registry = "https://pypi.org/simple" } 184 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } 185 | wheels = [ 186 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, 187 | ] 188 | 189 | [[package]] 190 | name = "pydantic" 191 | version = "2.11.3" 192 | source = { registry = "https://pypi.org/simple" } 193 | dependencies = [ 194 | { name = "annotated-types" }, 195 | { name = "pydantic-core" }, 196 | { name = "typing-extensions" }, 197 | { name = "typing-inspection" }, 198 | ] 199 | sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513 } 200 | wheels = [ 201 | { url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591 }, 202 | ] 203 | 204 | [[package]] 205 | name = "pydantic-core" 206 | version = "2.33.1" 207 | source = { registry = "https://pypi.org/simple" } 208 | dependencies = [ 209 | { name = "typing-extensions" }, 210 | ] 211 | sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 } 212 | wheels = [ 213 | { url = "https://files.pythonhosted.org/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551 }, 214 | { url = "https://files.pythonhosted.org/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785 }, 215 | { url = "https://files.pythonhosted.org/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758 }, 216 | { url = "https://files.pythonhosted.org/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109 }, 217 | { url = "https://files.pythonhosted.org/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159 }, 218 | { url = "https://files.pythonhosted.org/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222 }, 219 | { url = "https://files.pythonhosted.org/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980 }, 220 | { url = "https://files.pythonhosted.org/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840 }, 221 | { url = "https://files.pythonhosted.org/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518 }, 222 | { url = "https://files.pythonhosted.org/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025 }, 223 | { url = "https://files.pythonhosted.org/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991 }, 224 | { url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 }, 225 | { url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 }, 226 | { url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 }, 227 | { url = "https://files.pythonhosted.org/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963 }, 228 | { url = "https://files.pythonhosted.org/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896 }, 229 | { url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 }, 230 | ] 231 | 232 | [[package]] 233 | name = "pydantic-settings" 234 | version = "2.9.1" 235 | source = { registry = "https://pypi.org/simple" } 236 | dependencies = [ 237 | { name = "pydantic" }, 238 | { name = "python-dotenv" }, 239 | { name = "typing-inspection" }, 240 | ] 241 | sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234 } 242 | wheels = [ 243 | { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356 }, 244 | ] 245 | 246 | [[package]] 247 | name = "pygments" 248 | version = "2.19.1" 249 | source = { registry = "https://pypi.org/simple" } 250 | sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } 251 | wheels = [ 252 | { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, 253 | ] 254 | 255 | [[package]] 256 | name = "pymupdf" 257 | version = "1.25.5" 258 | source = { registry = "https://pypi.org/simple" } 259 | sdist = { url = "https://files.pythonhosted.org/packages/f9/af/3d5d363241b9a74470273cf1534436f13a0a61fc5ef6efd19e5afe9de812/pymupdf-1.25.5.tar.gz", hash = "sha256:5f96311cacd13254c905f6654a004a0a2025b71cabc04fda667f5472f72c15a0", size = 69812626 } 260 | wheels = [ 261 | { url = "https://files.pythonhosted.org/packages/85/5f/153d6c338291448e182648844849d13938a62a82a3e4a9b0907d9b381148/pymupdf-1.25.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cde4e1c9cfb09c0e1e9c2b7f4b787dd6bb34a32cfe141a4675e24af7c0c25dd3", size = 19364722 }, 262 | { url = "https://files.pythonhosted.org/packages/4e/55/43b64fa6cd048d2ea4574c045b5ac05d023254b91c2c703185f6f8a77b30/pymupdf-1.25.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5a35e2725fae0ab57f058dff77615c15eb5961eac50ba04f41ebc792cd8facad", size = 18606161 }, 263 | { url = "https://files.pythonhosted.org/packages/8b/22/29edb3236aed2f99a7922699fd71183e2f6cdde3c3884670158ae4dcf3ea/pymupdf-1.25.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d94b800e9501929c42283d39bc241001dd87fdeea297b5cb40d5b5714534452f", size = 19467121 }, 264 | { url = "https://files.pythonhosted.org/packages/18/12/95e2ebe2933f94800fdeafd87bc281a790e1dc947b147c3d101df4f73703/pymupdf-1.25.5-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee22155d3a634642d76553204867d862ae1bdd9f7cf70c0797d8127ebee6bed5", size = 20030310 }, 265 | { url = "https://files.pythonhosted.org/packages/bd/db/b4edec9e731ea7c2b74bf28b9091ed4e919d5c7f889ef86352b7fd416197/pymupdf-1.25.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6ed7fc25271004d6d3279c20a80cb2bb4cda3efa9f9088dcc07cd790eca0bc63", size = 21293562 }, 266 | { url = "https://files.pythonhosted.org/packages/ec/47/682a8ddce650e09f5de6809c9bce926b2493a19b7f9537d80d4646989670/pymupdf-1.25.5-cp39-abi3-win32.whl", hash = "sha256:65e18ddb37fe8ec4edcdbebe9be3a8486b6a2f42609d0a142677e42f3a0614f8", size = 15110464 }, 267 | { url = "https://files.pythonhosted.org/packages/71/c2/a9059607f80dcaf2392f991748cfc53456820392c0220cff02572653512a/pymupdf-1.25.5-cp39-abi3-win_amd64.whl", hash = "sha256:7f44bc3d03ea45b2f68c96464f96105e8c7908896f2fb5e8c04f1fb8dae7981e", size = 16579671 }, 268 | ] 269 | 270 | [[package]] 271 | name = "python-dotenv" 272 | version = "1.1.0" 273 | source = { registry = "https://pypi.org/simple" } 274 | sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } 275 | wheels = [ 276 | { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, 277 | ] 278 | 279 | [[package]] 280 | name = "rich" 281 | version = "14.0.0" 282 | source = { registry = "https://pypi.org/simple" } 283 | dependencies = [ 284 | { name = "markdown-it-py" }, 285 | { name = "pygments" }, 286 | ] 287 | sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 } 288 | wheels = [ 289 | { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 }, 290 | ] 291 | 292 | [[package]] 293 | name = "sgmllib3k" 294 | version = "1.0.0" 295 | source = { registry = "https://pypi.org/simple" } 296 | sdist = { url = "https://files.pythonhosted.org/packages/9e/bd/3704a8c3e0942d711c1299ebf7b9091930adae6675d7c8f476a7ce48653c/sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9", size = 5750 } 297 | 298 | [[package]] 299 | name = "shellingham" 300 | version = "1.5.4" 301 | source = { registry = "https://pypi.org/simple" } 302 | sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } 303 | wheels = [ 304 | { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, 305 | ] 306 | 307 | [[package]] 308 | name = "sniffio" 309 | version = "1.3.1" 310 | source = { registry = "https://pypi.org/simple" } 311 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 312 | wheels = [ 313 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 314 | ] 315 | 316 | [[package]] 317 | name = "sse-starlette" 318 | version = "2.3.3" 319 | source = { registry = "https://pypi.org/simple" } 320 | dependencies = [ 321 | { name = "anyio" }, 322 | { name = "starlette" }, 323 | ] 324 | sdist = { url = "https://files.pythonhosted.org/packages/86/35/7d8d94eb0474352d55f60f80ebc30f7e59441a29e18886a6425f0bccd0d3/sse_starlette-2.3.3.tar.gz", hash = "sha256:fdd47c254aad42907cfd5c5b83e2282be15be6c51197bf1a9b70b8e990522072", size = 17499 } 325 | wheels = [ 326 | { url = "https://files.pythonhosted.org/packages/5d/20/52fdb5ebb158294b0adb5662235dd396fc7e47aa31c293978d8d8942095a/sse_starlette-2.3.3-py3-none-any.whl", hash = "sha256:8b0a0ced04a329ff7341b01007580dd8cf71331cc21c0ccea677d500618da1e0", size = 10235 }, 327 | ] 328 | 329 | [[package]] 330 | name = "starlette" 331 | version = "0.46.2" 332 | source = { registry = "https://pypi.org/simple" } 333 | dependencies = [ 334 | { name = "anyio" }, 335 | ] 336 | sdist = { url = "https://files.pythonhosted.org/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846 } 337 | wheels = [ 338 | { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037 }, 339 | ] 340 | 341 | [[package]] 342 | name = "typer" 343 | version = "0.15.2" 344 | source = { registry = "https://pypi.org/simple" } 345 | dependencies = [ 346 | { name = "click" }, 347 | { name = "rich" }, 348 | { name = "shellingham" }, 349 | { name = "typing-extensions" }, 350 | ] 351 | sdist = { url = "https://files.pythonhosted.org/packages/8b/6f/3991f0f1c7fcb2df31aef28e0594d8d54b05393a0e4e34c65e475c2a5d41/typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5", size = 100711 } 352 | wheels = [ 353 | { url = "https://files.pythonhosted.org/packages/7f/fc/5b29fea8cee020515ca82cc68e3b8e1e34bb19a3535ad854cac9257b414c/typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc", size = 45061 }, 354 | ] 355 | 356 | [[package]] 357 | name = "typing-extensions" 358 | version = "4.13.2" 359 | source = { registry = "https://pypi.org/simple" } 360 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } 361 | wheels = [ 362 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, 363 | ] 364 | 365 | [[package]] 366 | name = "typing-inspection" 367 | version = "0.4.0" 368 | source = { registry = "https://pypi.org/simple" } 369 | dependencies = [ 370 | { name = "typing-extensions" }, 371 | ] 372 | sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } 373 | wheels = [ 374 | { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, 375 | ] 376 | 377 | [[package]] 378 | name = "uvicorn" 379 | version = "0.34.2" 380 | source = { registry = "https://pypi.org/simple" } 381 | dependencies = [ 382 | { name = "click" }, 383 | { name = "h11" }, 384 | ] 385 | sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815 } 386 | wheels = [ 387 | { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483 }, 388 | ] 389 | --------------------------------------------------------------------------------