From fe45356543b277bbee2b7d86b0181b4c7b2e25e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 07:55:22 +0100 Subject: [PATCH 01/31] Bump urllib3 from 2.6.2 to 2.6.3 (#37) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.2 to 2.6.3. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.6.2...2.6.3) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.6.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index ce3ef34..e5ffa38 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "certifi" @@ -272,14 +272,14 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "urllib3" -version = "2.6.2" +version = "2.6.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd"}, - {file = "urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797"}, + {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, + {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, ] [package.extras] From 327736887ce85495fd0e1e2f257f913a8db3dd6d Mon Sep 17 00:00:00 2001 From: Ahmad Ragab Date: Sat, 7 Mar 2026 10:07:48 -0500 Subject: [PATCH 02/31] fix: parse_inline drops links at position 0 and marks() loses href (#36) * fix: parse_inline drops links at position 0 and marks() loses href Two bugs in inline link handling: 1. parse_inline(): Links at the start of a line (position 0) were silently dropped. The image-exclusion check `match.start() > 0 and ...` would short-circuit to False when the link started at position 0, causing it to be skipped entirely. Fixed by checking `match.start() == 0 or text[match.start() - 1] != '!'` instead. 2. marks(): All link hrefs were set to null. parse_inline() returns marks with the structure `{'type': 'link', 'attrs': {'href': url}}`, but marks() only checked `mark.get('href')` at the top level. Added fallback to `mark.get('attrs', {}).get('href')` to handle both the attrs-nested format from parse_inline and the top-level format used in the README examples. Added tests covering both bugs plus regression tests for image exclusion. * chore: verify pre-commit formatting * fix: keep links at pos 0; preserve link href (no reformat) --------- Co-authored-by: cellarius --- substack/post.py | 29 ++++++------- tests/substack/test_post.py | 82 +++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 14 deletions(-) create mode 100644 tests/substack/test_post.py diff --git a/substack/post.py b/substack/post.py index de262ee..792e5b2 100644 --- a/substack/post.py +++ b/substack/post.py @@ -39,40 +39,41 @@ def parse_inline(text: str) -> List[Dict]: tokens = [] # Process text character by character to handle nested formatting # We'll use regex to find all markdown patterns, then process them in order - + # Find all markdown patterns: links, bold, italic # Pattern order: links first (to avoid conflicts), then bold, then italic link_pattern = r'\[([^\]]+)\]\(([^)]+)\)' bold_pattern = r'\*\*([^*]+)\*\*' italic_pattern = r'(? 0 and text[match.start()-1:match.start()+1] != "![": + # But do NOT skip normal links at position 0. + if match.start() == 0 or text[match.start()-1:match.start()+1] != "![": matches.append((match.start(), match.end(), "link", match.group(1), match.group(2))) - + for match in re.finditer(bold_pattern, text): # Check if this range is already covered by a link if not any(start <= match.start() < end for start, end, _, _, _ in matches): matches.append((match.start(), match.end(), "bold", match.group(1), None)) - + for match in re.finditer(italic_pattern, text): # Check if this range is already covered by a link or bold if not any(start <= match.start() < end for start, end, _, _, _ in matches): matches.append((match.start(), match.end(), "italic", match.group(1), None)) - + # Sort matches by position matches.sort(key=lambda x: x[0]) - + # Build tokens last_pos = 0 for start, end, match_type, content, url in matches: # Add text before this match if start > last_pos: tokens.append({"content": text[last_pos:start]}) - + # Add the formatted content if match_type == "link": tokens.append({ @@ -89,16 +90,16 @@ def parse_inline(text: str) -> List[Dict]: "content": content, "marks": [{"type": "em"}] }) - + last_pos = end - + # Add remaining text if last_pos < len(text): tokens.append({"content": text[last_pos:]}) - + # Filter out empty tokens tokens = [t for t in tokens if t.get("content")] - + return tokens @@ -351,7 +352,7 @@ def marks(self, marks): for mark in marks: new_mark = {"type": mark.get("type")} if mark.get("type") == "link": - href = mark.get("href") + href = mark.get("href") or mark.get("attrs", {}).get("href") new_mark.update({"attrs": {"href": href}}) content_marks.append(new_mark) content["marks"] = content_marks @@ -572,7 +573,7 @@ def from_markdown(self, markdown_content: str, api=None): alt_text = linked_image_match.group(1) image_url = linked_image_match.group(2) link_url = linked_image_match.group(3) - + # Adjust image URL if it starts with a slash image_url = image_url[1:] if image_url.startswith("/") else image_url diff --git a/tests/substack/test_post.py b/tests/substack/test_post.py new file mode 100644 index 0000000..c8e1512 --- /dev/null +++ b/tests/substack/test_post.py @@ -0,0 +1,82 @@ +"""Tests for Post and parse_inline.""" + +import json + +from substack.post import Post, parse_inline + + +class TestParseInline: + """Tests for parse_inline link handling.""" + + def test_link_at_start_of_text(self): + """Links at position 0 should be parsed correctly.""" + result = parse_inline("[GPT](https://openai.com/)") + assert len(result) == 1 + assert result[0]["content"] == "GPT" + assert result[0]["marks"][0]["attrs"]["href"] == "https://openai.com/" + + def test_multiple_links_on_same_line(self): + """All links on the same line should be parsed.""" + result = parse_inline( + "[GPT](https://openai.com/) and [Claude](https://anthropic.com/)" + ) + links = [r for r in result if r.get("marks")] + assert len(links) == 2 + assert links[0]["content"] == "GPT" + assert links[0]["marks"][0]["attrs"]["href"] == "https://openai.com/" + assert links[1]["content"] == "Claude" + assert links[1]["marks"][0]["attrs"]["href"] == "https://anthropic.com/" + + def test_image_not_parsed_as_link(self): + """Image syntax ![alt](url) should not be parsed as a link.""" + result = parse_inline("![alt](https://example.com/img.png)") + links = [r for r in result if r.get("marks")] + assert len(links) == 0 + + def test_link_mid_text(self): + """Links in the middle of text should work.""" + result = parse_inline("Check [this](https://example.com) out") + links = [r for r in result if r.get("marks")] + assert len(links) == 1 + assert links[0]["marks"][0]["attrs"]["href"] == "https://example.com" + + +class TestPostMarks: + """Tests for Post.marks() link href handling.""" + + def test_marks_preserves_href_from_attrs(self): + """marks() should read href from attrs when present.""" + post = Post(title="Test", subtitle="", user_id=1) + post.from_markdown("[Example](https://example.com)") + body = json.loads(post.get_draft()["draft_body"]) + # Find the link mark + for block in body["content"]: + for node in block.get("content", []): + for mark in node.get("marks", []): + if mark.get("type") == "link": + assert mark["attrs"]["href"] == "https://example.com" + return + raise AssertionError("No link mark found in output") + + def test_marks_preserves_href_from_top_level(self): + """marks() should also work when href is at top level (legacy format).""" + post = Post(title="Test", subtitle="", user_id=1) + post.add( + { + "type": "paragraph", + "content": [ + { + "content": "Link", + "marks": [{"type": "link", "href": "https://example.com"}], + } + ], + } + ) + body = json.loads(post.get_draft()["draft_body"]) + for block in body["content"]: + for node in block.get("content", []): + for mark in node.get("marks", []): + if mark.get("type") == "link": + assert mark["attrs"]["href"] == "https://example.com" + return + raise AssertionError("No link mark found in output") From a37540ea4b46399ae867cfb938cea91a94b98e8b Mon Sep 17 00:00:00 2001 From: YF Chau <82932032+yf-chau@users.noreply.github.com> Date: Sat, 7 Mar 2026 15:10:50 +0000 Subject: [PATCH 03/31] Render bullet lists as proper ProseMirror nodes in from_markdown() (#35) * Render bullet lists as proper ProseMirror nodes in from_markdown() from_markdown() stripped bullet markers (* / -) but emitted each item as an independent paragraph node. Substack's ProseMirror editor expects a single bullet_list node wrapping list_item children. Without this structure, bullet lists render as disconnected paragraphs in the published post. Consecutive bullet lines are now accumulated and flushed as a single bullet_list node with proper list_item > paragraph nesting. Non-bullet lines and blank lines flush the pending list, so mixed content works correctly. Co-Authored-By: Claude Opus 4.6 * Add before/after screenshots for PR Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: YF Chau Co-authored-by: Claude Opus 4.6 --- docs/after.png | Bin 0 -> 64559 bytes docs/before.png | Bin 0 -> 50279 bytes substack/post.py | 41 ++++++++++++++++++++++++++++++++++------- 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 docs/after.png create mode 100644 docs/before.png diff --git a/docs/after.png b/docs/after.png new file mode 100644 index 0000000000000000000000000000000000000000..1a8e0dc42c0ce6f5e3164e62938c89b3ddce2897 GIT binary patch literal 64559 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%0D$UT*Ae5ana2*_=Df=I7PrN2izjiIiez?2{^1p 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

NwvC91PDwKIKgSf}B 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 H``~{7>B!R+ literal 0 HcmV?d00001 diff --git a/docs/before.png b/docs/before.png new file mode 100644 index 0000000000000000000000000000000000000000..626f0c374da8388e37316f1f52a4b55736cde134 GIT binary patch literal 50279 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 Date: Sat, 7 Mar 2026 17:14:20 +0100 Subject: [PATCH 04/31] Bump version to 0.1.18 in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index cec993c..f28ced6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-substack" -version = "0.1.17" +version = "0.1.18" description = "A Python wrapper around the Substack API." authors = ["Paolo Mazza "] license = "MIT" From 7ede8344b74ba0f1e18d657eace56c198b7b7230 Mon Sep 17 00:00:00 2001 From: Ahmad Ragab Date: Wed, 25 Mar 2026 10:14:51 -0400 Subject: [PATCH 05/31] feat: add blockquote support to from_markdown() and Post.blockquote() (#40) Co-authored-by: Ahmad Ragab --- substack/post.py | 90 ++++++++++++++++++++++++++++++-- tests/substack/test_post.py | 100 ++++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 5 deletions(-) diff --git a/substack/post.py b/substack/post.py index 3d9c01f..60feaaf 100644 --- a/substack/post.py +++ b/substack/post.py @@ -227,6 +227,42 @@ def heading(self, content=None, level: int = 1): item["level"] = level return self.add(item) + def blockquote(self, content=None): + """ + Add a blockquote to the post. + + The blockquote wraps one or more paragraph nodes. + + Args: + content: Text string or list of inline token dicts. When a plain + string is provided it is wrapped in a single paragraph node. + + Returns: + Self for method chaining. + """ + paragraphs: List[Dict] = [] + if content is not None: + if isinstance(content, str): + tokens = parse_inline(content) + text_nodes = [ + {"type": "text", "text": t["content"]} for t in tokens if t + ] + if text_nodes: + paragraphs.append({"type": "paragraph", "content": text_nodes}) + elif isinstance(content, list): + for item in content: + if isinstance(item, dict) and item.get("type") == "paragraph": + paragraphs.append(item) + elif isinstance(item, dict): + text_nodes = [{"type": "text", "text": item.get("content", "")}] + paragraphs.append({"type": "paragraph", "content": text_nodes}) + + node: Dict = {"type": "blockquote"} + if paragraphs: + node["content"] = paragraphs + self.draft_body["content"] = self.draft_body.get("content", []) + [node] + return self + def horizontal_rule(self): """ @@ -464,6 +500,7 @@ def from_markdown(self, markdown_content: str, api=None): - Linked images: [![Alt](image_url)](link_url) - images that are also links - Links: [text](url) - inline links in paragraphs - Code blocks: Fenced code blocks with ```language or ``` + - Blockquotes: Lines starting with '>' (consecutive lines grouped) - Paragraphs: Regular text blocks - Bullet lists: Lines starting with '*' or '-' - Inline formatting: **bold** and *italic* within paragraphs @@ -611,12 +648,14 @@ def from_markdown(self, markdown_content: str, api=None): self.add({"type": "captionedImage", "src": image_url}) - # Process paragraphs or bullet lists + # Process paragraphs, bullet lists, or blockquotes else: if "\n" in text_content: # Process each line, grouping consecutive bullets - # into a single bullet_list node + # into a single bullet_list node and consecutive + # blockquote lines into a single blockquote node. pending_bullets: List[List[Dict]] = [] + pending_quotes: List[str] = [] def flush_bullets(): if not pending_bullets: @@ -632,10 +671,36 @@ def flush_bullets(): ) pending_bullets.clear() + def flush_quotes(): + if not pending_quotes: + return + paragraphs: List[Dict] = [] + for quote_line in pending_quotes: + tokens = parse_inline(quote_line) + text_nodes = [ + {"type": "text", "text": t["content"]} + for t in tokens if t + ] + if text_nodes: + paragraphs.append({"type": "paragraph", "content": text_nodes}) + node: Dict = {"type": "blockquote"} + if paragraphs: + node["content"] = paragraphs + self.draft_body["content"].append(node) + pending_quotes.clear() + for line in text_content.split("\n"): line = line.strip() if not line: flush_bullets() + flush_quotes() + continue + + # Check for blockquote marker + if line.startswith("> ") or line == ">": + flush_bullets() + quote_text = line[2:] if line.startswith("> ") else "" + pending_quotes.append(quote_text) continue # Check for bullet marker @@ -648,18 +713,33 @@ def flush_bullets(): bullet_text = line[1:].strip() if bullet_text is not None: + flush_quotes() tokens = parse_inline(bullet_text) if tokens: pending_bullets.append(tokens) else: flush_bullets() + flush_quotes() tokens = parse_inline(line) self.add({"type": "paragraph", "content": tokens}) flush_bullets() + flush_quotes() else: - # Single paragraph - tokens = parse_inline(text_content) - self.add({"type": "paragraph", "content": tokens}) + # Single line — could be a blockquote or paragraph + if text_content.startswith("> ") or text_content == ">": + quote_text = text_content[2:] if text_content.startswith("> ") else "" + tokens = parse_inline(quote_text) + text_nodes = [ + {"type": "text", "text": t["content"]} + for t in tokens if t + ] + para = {"type": "paragraph", "content": text_nodes} if text_nodes else {"type": "paragraph"} + self.draft_body["content"] = self.draft_body.get("content", []) + [ + {"type": "blockquote", "content": [para]} + ] + else: + tokens = parse_inline(text_content) + self.add({"type": "paragraph", "content": tokens}) return self diff --git a/tests/substack/test_post.py b/tests/substack/test_post.py index c8e1512..701c2a2 100644 --- a/tests/substack/test_post.py +++ b/tests/substack/test_post.py @@ -80,3 +80,103 @@ def test_marks_preserves_href_from_top_level(self): assert mark["attrs"]["href"] == "https://example.com" return raise AssertionError("No link mark found in output") + + +class TestBlockquoteFromMarkdown: + """Tests for blockquote parsing in from_markdown().""" + + def test_single_blockquote_line(self): + """A single '> text' line produces a blockquote with one paragraph.""" + post = Post(title="T", subtitle="S", user_id=1) + post.from_markdown("> This is a quote") + body = json.loads(post.get_draft()["draft_body"]) + bq = body["content"][0] + assert bq["type"] == "blockquote" + assert len(bq["content"]) == 1 + assert bq["content"][0]["type"] == "paragraph" + assert bq["content"][0]["content"][0]["text"] == "This is a quote" + + def test_multiline_blockquote_grouped(self): + """Consecutive '>' lines become a single blockquote with multiple paragraphs.""" + post = Post(title="T", subtitle="S", user_id=1) + post.from_markdown("> Line one\n> Line two\n> Line three") + body = json.loads(post.get_draft()["draft_body"]) + bq = body["content"][0] + assert bq["type"] == "blockquote" + assert len(bq["content"]) == 3 + texts = [p["content"][0]["text"] for p in bq["content"]] + assert texts == ["Line one", "Line two", "Line three"] + + def test_blockquote_separated_by_blank_line(self): + """A blank line between '>' groups creates two separate blockquotes.""" + post = Post(title="T", subtitle="S", user_id=1) + post.from_markdown("> First block\n\n> Second block") + body = json.loads(post.get_draft()["draft_body"]) + blockquotes = [n for n in body["content"] if n["type"] == "blockquote"] + assert len(blockquotes) == 2 + + def test_blockquote_then_paragraph(self): + """A blockquote followed by a regular paragraph produces both node types.""" + post = Post(title="T", subtitle="S", user_id=1) + post.from_markdown("> A quote\n\nA regular paragraph") + body = json.loads(post.get_draft()["draft_body"]) + assert body["content"][0]["type"] == "blockquote" + assert body["content"][1]["type"] == "paragraph" + + def test_paragraph_blockquote_paragraph(self): + """Blockquote sandwiched between paragraphs preserves order.""" + post = Post(title="T", subtitle="S", user_id=1) + post.from_markdown("Before\n\n> The quote\n\nAfter") + body = json.loads(post.get_draft()["draft_body"]) + types = [n["type"] for n in body["content"]] + assert types == ["paragraph", "blockquote", "paragraph"] + + def test_blockquote_with_inline_link(self): + """Links inside blockquotes are parsed as marks.""" + post = Post(title="T", subtitle="S", user_id=1) + post.from_markdown("> See [example](https://example.com)") + body = json.loads(post.get_draft()["draft_body"]) + bq = body["content"][0] + assert bq["type"] == "blockquote" + para = bq["content"][0] + assert para["type"] == "paragraph" + + def test_blockquote_adjacent_to_bullet_list(self): + """Blockquote followed immediately by bullets flushes correctly.""" + post = Post(title="T", subtitle="S", user_id=1) + post.from_markdown("> A quote\n- bullet one\n- bullet two") + body = json.loads(post.get_draft()["draft_body"]) + types = [n["type"] for n in body["content"]] + assert types == ["blockquote", "bullet_list"] + + def test_empty_continuation_line(self): + """A bare '>' between quoted lines keeps them in one blockquote.""" + post = Post(title="T", subtitle="S", user_id=1) + post.from_markdown("> First\n>\n> Third") + body = json.loads(post.get_draft()["draft_body"]) + blockquotes = [n for n in body["content"] if n["type"] == "blockquote"] + assert len(blockquotes) == 1 + paras_with_content = [p for p in blockquotes[0]["content"] if p.get("content")] + assert len(paras_with_content) == 2 + + +class TestBlockquoteMethod: + """Tests for the Post.blockquote() convenience method.""" + + def test_blockquote_string(self): + """blockquote('text') wraps text in a blockquote node.""" + post = Post(title="T", subtitle="S", user_id=1) + post.blockquote("Hello world") + body = json.loads(post.get_draft()["draft_body"]) + bq = body["content"][0] + assert bq["type"] == "blockquote" + assert bq["content"][0]["content"][0]["text"] == "Hello world" + + def test_blockquote_chaining(self): + """blockquote() returns self for method chaining.""" + post = Post(title="T", subtitle="S", user_id=1) + result = post.blockquote("one").blockquote("two") + assert result is post + body = json.loads(post.get_draft()["draft_body"]) + blockquotes = [n for n in body["content"] if n["type"] == "blockquote"] + assert len(blockquotes) == 2 From 88cf3b53101195cebf1908b85135165fe1df447f Mon Sep 17 00:00:00 2001 From: ma2za Date: Wed, 25 Mar 2026 16:22:27 +0100 Subject: [PATCH 06/31] feat(api): add methods for managing post tags - Incremented version to 0.1.19 in pyproject.toml. - Added `add_tags_to_post` method to allow adding multiple tags to a post. - Implemented `get_publication_post_tags` to retrieve all tags for the current publication. - Created `add_tag_to_post` to check for existing tags before creating new ones and applying them to posts. --- .gitignore | 1 + examples/draft.yaml | 5 +- examples/publish_post.py | 2 + poetry.lock | 256 +++++++++++++++++++++------------------ pyproject.toml | 4 +- substack/api.py | 61 ++++++++++ 6 files changed, 206 insertions(+), 123 deletions(-) diff --git a/.gitignore b/.gitignore index b97131e..cfeb20e 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,4 @@ dmypy.json .pyre/ .idea/ +.vscode/ \ No newline at end of file diff --git a/examples/draft.yaml b/examples/draft.yaml index cb0c54b..9346f36 100644 --- a/examples/draft.yaml +++ b/examples/draft.yaml @@ -8,6 +8,9 @@ write_comment_permissions: "none" # none, only_paid, everyone section: "rick" +tags: + - "python" + - "api" body: 0: type: "heading" @@ -43,7 +46,7 @@ body: content: "Set the EMAIL, PASSWORD, PUBLICATION_URL and USER_ID environment variables." 7: type: "captionedImage" - src: "rickroll_4k.jpg" + src: "examples/rickroll_4k.jpg" 8: type: "youtube2" src: "EnDg65ISswg" diff --git a/examples/publish_post.py b/examples/publish_post.py index a614201..34f966c 100644 --- a/examples/publish_post.py +++ b/examples/publish_post.py @@ -69,6 +69,8 @@ # post.set_section(post_data.get("section"), api.get_sections()) api.put_draft(draft.get("id"), draft_section_id=post.draft_section_id) + api.add_tags_to_post(draft.get("id"), post_data.get("tags", [])) + if args.publish: api.prepublish_draft(draft.get("id")) diff --git a/poetry.lock b/poetry.lock index e5ffa38..36092e0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,138 +1,154 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "certifi" -version = "2025.11.12" +version = "2026.2.25" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, - {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, + {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, + {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, ] [[package]] name = "charset-normalizer" -version = "3.4.4" +version = "3.4.6" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, - {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, - {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:659a1e1b500fac8f2779dd9e1570464e012f43e580371470b45277a27baa7532"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f61aa92e4aad0be58eb6eb4e0c21acf32cf8065f4b2cae5665da756c4ceef982"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f50498891691e0864dc3da965f340fada0771f6142a378083dc4608f4ea513e2"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bf625105bb9eef28a56a943fec8c8a98aeb80e7d7db99bd3c388137e6eb2d237"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2bd9d128ef93637a5d7a6af25363cf5dec3fa21cf80e68055aad627f280e8afa"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:d08ec48f0a1c48d75d0356cea971921848fb620fdeba805b28f937e90691209f"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ed80ff870ca6de33f4d953fda4d55654b9a2b340ff39ab32fa3adbcd718f264"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f98059e4fcd3e3e4e2d632b7cf81c2faae96c43c60b569e9c621468082f1d104"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:ab30e5e3e706e3063bc6de96b118688cb10396b70bb9864a430f67df98c61ecc"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:d5f5d1e9def3405f60e3ca8232d56f35c98fb7bf581efcc60051ebf53cb8b611"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:461598cd852bfa5a61b09cae2b1c02e2efcd166ee5516e243d540ac24bfa68a7"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:71be7e0e01753a89cf024abf7ecb6bca2c81738ead80d43004d9b5e3f1244e64"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:df01808ee470038c3f8dc4f48620df7225c49c2d6639e38f96e6d6ac6e6f7b0e"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-win32.whl", hash = "sha256:69dd852c2f0ad631b8b60cfbe25a28c0058a894de5abb566619c205ce0550eae"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:517ad0e93394ac532745129ceabdf2696b609ec9f87863d337140317ebce1c14"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win32.whl", hash = "sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8"}, + {file = "charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69"}, + {file = "charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6"}, ] [[package]] @@ -290,5 +306,5 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [metadata] lock-version = "2.1" -python-versions = "^3.9" -content-hash = "eca02f6ded24311e6a3f6249289dfb5a139fdf1239637acbc90a5b771953df34" +python-versions = ">=3.9" +content-hash = "7ee6412342a43d2f52d5815d281847619086753f2c68b7444d3c2e04cf7a3b25" diff --git a/pyproject.toml b/pyproject.toml index f28ced6..4ee23d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-substack" -version = "0.1.18" +version = "0.1.19" description = "A Python wrapper around the Substack API." authors = ["Paolo Mazza "] license = "MIT" @@ -16,7 +16,7 @@ homepage = "https://github.com/ma2za/python-substack" keywords = ["substack"] [tool.poetry.dependencies] -python = "^3.9" +python = ">=3.9" requests = "^2.31.0" python-dotenv = "^0.21.0" diff --git a/substack/api.py b/substack/api.py index a4b51cb..676d86e 100644 --- a/substack/api.py +++ b/substack/api.py @@ -514,6 +514,67 @@ def get_image(self, image: str): data={"image": image}, ) return Api._handle_response(response=response) + + def add_tags_to_post(self, post_id: int, tag_names: list) -> dict: + """ + Add multiple tags to a post. + + Args: + post_id: The ID of the post to tag. + tag_names: A list of tag names to add. + + Returns: + A dictionary with the results of applying all tags. + """ + results = [] + for tag_name in tag_names: + result = self.add_tag_to_post(post_id, tag_name) + results.append(result) + return {"tags_added": results} + + def get_publication_post_tags(self) -> list: + """ + Retrieve all post tags for the current publication. + + Returns: + List of tag dicts as returned by Substack API. + """ + response = self._session.get(f"{self.publication_url}/publication/post-tag") + return Api._handle_response(response=response) + + def add_tag_to_post(self, post_id: int, tag_name: str) -> dict: + """ + Add a tag to a post by first checking published tags and creating only if needed. + + Args: + post_id: The ID of the post to tag. + tag_name: The name of the tag to add. + + Returns: + The response from applying the tag to the post. + """ + # Fetch existing publication tags first (avoid re-creating an already existing tag) + existing_tags = self.get_publication_post_tags() or [] + existing_tag = next( + (tag for tag in existing_tags if tag.get("name") == tag_name), + None, + ) + + if existing_tag is not None: + tag_id = existing_tag["id"] + else: + create_tag_response = self._session.post( + f"{self.publication_url}/publication/post-tag", + json={"name": tag_name}, + ) + tag_data = Api._handle_response(create_tag_response) + tag_id = tag_data["id"] + + apply_tag_response = self._session.post( + f"{self.publication_url}/post/{post_id}/tag/{tag_id}", + ) + return Api._handle_response(apply_tag_response) + def get_categories(self): """ From 1979fff67b4c4948f44b0f0dde6f7aa39b456ea5 Mon Sep 17 00:00:00 2001 From: ma2za Date: Wed, 25 Mar 2026 16:38:59 +0100 Subject: [PATCH 07/31] feat: enhance draft publishing with additional metadata and update version to 0.1.20 --- README.md | 9 ++++++++- examples/draft.yaml | 6 ++++++ examples/publish_post.py | 17 ++++++++++++++--- pyproject.toml | 2 +- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9f6844b..5029d51 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,14 @@ for _, item in body.items(): post.add(item) draft = api.post_draft(post.get_draft()) -api.put_draft(draft.get("id"), draft_section_id=post.draft_section_id) +put_draft_kwargs = { + "draft_section_id": post.draft_section_id, + "search_engine_title": post_data.get("search_engine_title"), + "search_engine_description": post_data.get("search_engine_description"), + "slug": post_data.get("slug"), +} +put_draft_kwargs = {k: v for k, v in put_draft_kwargs.items() if v is not None} +api.put_draft(draft.get("id"), **put_draft_kwargs) # Publish the draft api.prepublish_draft(draft.get("id")) diff --git a/examples/draft.yaml b/examples/draft.yaml index 9346f36..6866afa 100644 --- a/examples/draft.yaml +++ b/examples/draft.yaml @@ -6,6 +6,12 @@ audience: "everyone" # everyone, only_paid, founding, only_free write_comment_permissions: "none" # none, only_paid, everyone +search_engine_title: + "This is a short SEO title set via the Python API wrapper." +search_engine_description: + "This is a short SEO description set via the Python API wrapper." +slug: + "this-is-a-draft-post-2" section: "rick" tags: diff --git a/examples/publish_post.py b/examples/publish_post.py index 34f966c..573c3d9 100644 --- a/examples/publish_post.py +++ b/examples/publish_post.py @@ -20,7 +20,7 @@ type=str, ) parser.add_argument( - "--publish", help="Publish the draft.", action="store_true", default=True + "--publish", help="Publish the draft.", action="store_false", default=False ) parser.add_argument( "--cookies", @@ -38,7 +38,9 @@ api = Api( email=os.getenv("EMAIL") if not cookies_path and not cookies_string else None, - password=os.getenv("PASSWORD") if not cookies_path and not cookies_string else None, + password=os.getenv("PASSWORD") + if not cookies_path and not cookies_string + else None, cookies_path=cookies_path, cookies_string=cookies_string, publication_url=os.getenv("PUBLICATION_URL"), @@ -67,7 +69,16 @@ draft = api.post_draft(post.get_draft()) # post.set_section(post_data.get("section"), api.get_sections()) - api.put_draft(draft.get("id"), draft_section_id=post.draft_section_id) + put_draft_kwargs = { + "draft_section_id": post.draft_section_id, + "search_engine_title": post_data.get("search_engine_title"), + "search_engine_description": post_data.get("search_engine_description"), + "slug": post_data.get("slug"), + } + # Remove None values so we only send fields that were explicitly provided + put_draft_kwargs = {k: v for k, v in put_draft_kwargs.items() if v is not None} + + api.put_draft(draft.get("id"), **put_draft_kwargs) api.add_tags_to_post(draft.get("id"), post_data.get("tags", [])) diff --git a/pyproject.toml b/pyproject.toml index 4ee23d1..ab65b50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-substack" -version = "0.1.19" +version = "0.1.20" description = "A Python wrapper around the Substack API." authors = ["Paolo Mazza "] license = "MIT" From ee1c781a44105c1a07b86f66568d57a2c8ed4e19 Mon Sep 17 00:00:00 2001 From: ma2za Date: Wed, 25 Mar 2026 18:20:00 +0100 Subject: [PATCH 08/31] Bump version to 0.1.21, update dependencies, and add MCP server functionality --- README.md | 22 + poetry.lock | 2088 ++++++++++++++++++++++++++++++++++-- pyproject.toml | 15 +- substack/__init__.py | 2 +- substack_mcp/mcp_server.py | 293 +++++ 5 files changed, 2295 insertions(+), 125 deletions(-) create mode 100644 substack_mcp/mcp_server.py diff --git a/README.md b/README.md index 5029d51..c4f57fa 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,12 @@ You can install python-substack using: $ pip install python-substack +For the MCP server tools, install the extra dependency set: + + $ poetry install --with mcp + +> NOTE: We had to upgrade the package requirements to support Python 3.10 because 3.9 is basically vintage now. If you still run 3.9, please join us in the future (or bring snacks). + --- # Setup @@ -247,6 +253,22 @@ body: src: "local_image.jpg" # Local images will be uploaded automatically ``` +## MCP FastMCP server + +This package now includes a FastMCP server in `substack/mcp_fastmcp.py` with the following tools: + +- `post_draft_from_markdown(...)`: create draft from markdown, optional tag/add/prepublish/publish, and control send/share_automatically. +- `put_draft(draft_id, update_payload)`: update draft fields. +- `add_tags(draft_id, tags)`: add tags to a draft/post. +- `prepublish_draft(draft_id)`: prepublish a draft. +- `publish_draft(draft_id, send=True, share_automatically=False)`: publish a draft. + +Use via stdio transport: + +```bash +python -c "from substack.mcp_fastmcp import main; main()" +``` + # Contributing Install pre-commit: diff --git a/poetry.lock b/poetry.lock index 36092e0..816d899 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,17 +1,273 @@ # This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. +[[package]] +name = "aiofile" +version = "3.9.0" +description = "Asynchronous file operations." +optional = false +python-versions = "<4,>=3.8" +groups = ["mcp"] +files = [ + {file = "aiofile-3.9.0-py3-none-any.whl", hash = "sha256:ce2f6c1571538cbdfa0143b04e16b208ecb0e9cb4148e528af8a640ed51cc8aa"}, + {file = "aiofile-3.9.0.tar.gz", hash = "sha256:e5ad718bb148b265b6df1b3752c4d1d83024b93da9bd599df74b9d9ffcf7919b"}, +] + +[package.dependencies] +caio = ">=0.9.0,<0.10.0" + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["mcp"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.13.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"}, + {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +trio = ["trio (>=0.32.0)"] + +[[package]] +name = "attrs" +version = "26.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"}, + {file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"}, +] + +[[package]] +name = "authlib" +version = "1.6.9" +description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "authlib-1.6.9-py2.py3-none-any.whl", hash = "sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3"}, + {file = "authlib-1.6.9.tar.gz", hash = "sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04"}, +] + +[package.dependencies] +cryptography = "*" + +[[package]] +name = "backports-tarfile" +version = "1.2.0" +description = "Backport of CPython tarfile module" +optional = false +python-versions = ">=3.8" +groups = ["mcp"] +markers = "python_version < \"3.12\"" +files = [ + {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, + {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)"] + +[[package]] +name = "beartype" +version = "0.22.9" +description = "Unbearably fast near-real-time pure-Python runtime-static type-checker." +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2"}, + {file = "beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f"}, +] + +[package.extras] +dev = ["autoapi (>=0.9.0)", "celery", "click", "coverage (>=5.5)", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pydata-sphinx-theme (<=0.7.2)", "pygments", "pyinstaller", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "setuptools", "sphinx", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "tox (>=3.20.1)", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""] +doc-ghp = ["mkdocs-material[imaging] (>=9.6.0)", "mkdocstrings-python (>=1.16.0)", "mkdocstrings-python-xref (>=1.16.0)"] +doc-rtd = ["autoapi (>=0.9.0)", "pydata-sphinx-theme (<=0.7.2)", "setuptools", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)"] +test = ["celery", "click", "coverage (>=5.5)", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pygments", "pyinstaller", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "sphinx", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "tox (>=3.20.1)", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""] +test-tox = ["celery", "click", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pygments", "pyinstaller", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "sphinx", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""] +test-tox-coverage = ["coverage (>=5.5)"] + +[[package]] +name = "cachetools" +version = "7.0.5" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "cachetools-7.0.5-py3-none-any.whl", hash = "sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114"}, + {file = "cachetools-7.0.5.tar.gz", hash = "sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990"}, +] + +[[package]] +name = "caio" +version = "0.9.25" +description = "Asynchronous file IO for Linux MacOS or Windows." +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "caio-0.9.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca6c8ecda611478b6016cb94d23fd3eb7124852b985bdec7ecaad9f3116b9619"}, + {file = "caio-0.9.25-cp310-cp310-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db9b5681e4af8176159f0d6598e73b2279bb661e718c7ac23342c550bd78c241"}, + {file = "caio-0.9.25-cp310-cp310-manylinux_2_34_aarch64.whl", hash = "sha256:bf61d7d0c4fd10ffdd98ca47f7e8db4d7408e74649ffaf4bef40b029ada3c21b"}, + {file = "caio-0.9.25-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:ab52e5b643f8bbd64a0605d9412796cd3464cb8ca88593b13e95a0f0b10508ae"}, + {file = "caio-0.9.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d6956d9e4a27021c8bd6c9677f3a59eb1d820cc32d0343cea7961a03b1371965"}, + {file = "caio-0.9.25-cp311-cp311-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bf84bfa039f25ad91f4f52944452a5f6f405e8afab4d445450978cd6241d1478"}, + {file = "caio-0.9.25-cp311-cp311-manylinux_2_34_aarch64.whl", hash = "sha256:ae3d62587332bce600f861a8de6256b1014d6485cfd25d68c15caf1611dd1f7c"}, + {file = "caio-0.9.25-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:fc220b8533dcf0f238a6b1a4a937f92024c71e7b10b5a2dfc1c73604a25709bc"}, + {file = "caio-0.9.25-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb7ff95af4c31ad3f03179149aab61097a71fd85e05f89b4786de0359dffd044"}, + {file = "caio-0.9.25-cp312-cp312-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:97084e4e30dfa598449d874c4d8e0c8d5ea17d2f752ef5e48e150ff9d240cd64"}, + {file = "caio-0.9.25-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:4fa69eba47e0f041b9d4f336e2ad40740681c43e686b18b191b6c5f4c5544bfb"}, + {file = "caio-0.9.25-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:6bebf6f079f1341d19f7386db9b8b1f07e8cc15ae13bfdaff573371ba0575d69"}, + {file = "caio-0.9.25-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d6c2a3411af97762a2b03840c3cec2f7f728921ff8adda53d7ea2315a8563451"}, + {file = "caio-0.9.25-cp313-cp313-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0998210a4d5cd5cb565b32ccfe4e53d67303f868a76f212e002a8554692870e6"}, + {file = "caio-0.9.25-cp313-cp313-manylinux_2_34_aarch64.whl", hash = "sha256:1a177d4777141b96f175fe2c37a3d96dec7911ed9ad5f02bac38aaa1c936611f"}, + {file = "caio-0.9.25-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:9ed3cfb28c0e99fec5e208c934e5c157d0866aa9c32aa4dc5e9b6034af6286b7"}, + {file = "caio-0.9.25-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fab6078b9348e883c80a5e14b382e6ad6aabbc4429ca034e76e730cf464269db"}, + {file = "caio-0.9.25-cp314-cp314-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:44a6b58e52d488c75cfaa5ecaa404b2b41cc965e6c417e03251e868ecd5b6d77"}, + {file = "caio-0.9.25-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:628a630eb7fb22381dd8e3c8ab7f59e854b9c806639811fc3f4310c6bd711d79"}, + {file = "caio-0.9.25-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:0ba16aa605ccb174665357fc729cf500679c2d94d5f1458a6f0d5ca48f2060a7"}, + {file = "caio-0.9.25-py3-none-any.whl", hash = "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40"}, + {file = "caio-0.9.25.tar.gz", hash = "sha256:16498e7f81d1d0f5a4c0ad3f2540e65fe25691376e0a5bd367f558067113ed10"}, +] + +[package.extras] +develop = ["aiomisc-pytest", "coveralls", "pylama[toml]", "pytest", "pytest-cov", "setuptools"] + [[package]] name = "certifi" version = "2026.2.25" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "mcp"] files = [ {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, ] +[[package]] +name = "cffi" +version = "2.0.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, +] + +[package.dependencies] +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + [[package]] name = "charset-normalizer" version = "3.4.6" @@ -152,159 +408,1753 @@ files = [ ] [[package]] -name = "idna" -version = "3.11" -description = "Internationalized Domain Names in Applications (IDNA)" +name = "click" +version = "8.3.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, + {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["mcp"] +markers = "platform_system == \"Windows\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "cryptography" +version = "46.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = "!=3.9.0,!=3.9.1,>=3.8" +groups = ["mcp"] +files = [ + {file = "cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1"}, + {file = "cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48"}, + {file = "cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4"}, + {file = "cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d"}, + {file = "cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a"}, + {file = "cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4"}, + {file = "cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9"}, + {file = "cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72"}, + {file = "cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7"}, + {file = "cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d"}, +] + +[package.dependencies] +cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} +typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] +docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] +nox = ["nox[uv] (>=2024.4.15)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] +sdist = ["build (>=1.0.0)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "cyclopts" +version = "4.10.1" +description = "Intuitive, easy CLIs based on type hints." +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "cyclopts-4.10.1-py3-none-any.whl", hash = "sha256:35f37257139380a386d9fe4475e1e7c87ca7795765ef4f31abba579fcfcb6ecd"}, + {file = "cyclopts-4.10.1.tar.gz", hash = "sha256:ad4e4bb90576412d32276b14a76f55d43353753d16217f2c3cd5bdceba7f15a0"}, +] + +[package.dependencies] +attrs = ">=23.1.0" +docstring-parser = ">=0.15,<4.0" +rich = ">=13.6.0" +rich-rst = ">=1.3.1,<2.0.0" +tomli = {version = ">=2.0.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.8.0", markers = "python_version < \"3.11\""} + +[package.extras] +debug = ["ipdb (>=0.13.9)", "line-profiler (>=3.5.1)"] +dev = ["coverage[toml] (>=5.1)", "mkdocs (>=1.4.0)", "pre-commit (>=2.16.0)", "pydantic (>=2.11.2,<3.0.0)", "pytest (>=8.2.0)", "pytest-cov (>=3.0.0)", "pytest-mock (>=3.7.0)", "pyyaml (>=6.0.1)", "syrupy (>=4.0.0)", "toml (>=0.10.2,<1.0.0)", "trio (>=0.10.0)"] +docs = ["gitpython (>=3.1.31)", "myst-parser[linkify] (>=3.0.1,<5.0.0)", "sphinx (>=7.4.7,<8.2.0)", "sphinx-autodoc-typehints (>=1.25.2,<4.0.0)", "sphinx-copybutton (>=0.5,<1.0)", "sphinx-rtd-dark-mode (>=1.3.0,<2.0.0)", "sphinx-rtd-theme (>=3.0.0,<4.0.0)"] +mkdocs = ["markdown (>=3.3)", "mkdocs (>=1.4.0)", "pymdown-extensions (>=10.0)"] +toml = ["tomli (>=2.0.0) ; python_version < \"3.11\""] +trio = ["trio (>=0.10.0)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "dnspython" +version = "2.8.0" +description = "DNS toolkit" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, + {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, +] + +[package.extras] +dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"] +dnssec = ["cryptography (>=45)"] +doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"] +doq = ["aioquic (>=1.2.0)"] +idna = ["idna (>=3.10)"] +trio = ["trio (>=0.30)"] +wmi = ["wmi (>=1.5.1) ; platform_system == \"Windows\""] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +description = "Parse Python docstrings in reST, Google and Numpydoc format" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["mcp"] files = [ - {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, - {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, + {file = "docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708"}, + {file = "docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912"}, ] [package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +dev = ["pre-commit (>=2.16.0) ; python_version >= \"3.9\"", "pydoctor (>=25.4.0)", "pytest"] +docs = ["pydoctor (>=25.4.0)"] +test = ["pytest"] [[package]] -name = "python-dotenv" -version = "0.21.1" -description = "Read key-value pairs from a .env file and set them as environment variables" +name = "docutils" +version = "0.22.4" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"}, + {file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"}, +] + +[[package]] +name = "email-validator" +version = "2.3.0" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +groups = ["mcp"] +files = [ + {file = "email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4"}, + {file = "email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["mcp"] files = [ - {file = "python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49"}, - {file = "python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a"}, + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + [package.extras] -cli = ["click (>=5.0)"] +test = ["pytest (>=6)"] [[package]] -name = "pyyaml" -version = "6.0.3" -description = "YAML parser and emitter for Python" +name = "fastmcp" +version = "3.1.1" +description = "The fast, Pythonic way to build MCP servers and clients." +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "fastmcp-3.1.1-py3-none-any.whl", hash = "sha256:8132ba069d89f14566b3266919d6d72e2ec23dd45d8944622dca407e9beda7eb"}, + {file = "fastmcp-3.1.1.tar.gz", hash = "sha256:db184b5391a31199323766a3abf3a8bfbb8010479f77eca84c0e554f18655c48"}, +] + +[package.dependencies] +authlib = ">=1.6.5" +cyclopts = ">=4.0.0" +exceptiongroup = ">=1.2.2" +httpx = ">=0.28.1,<1.0" +jsonref = ">=1.1.0" +jsonschema-path = ">=0.3.4" +mcp = ">=1.24.0,<2.0" +openapi-pydantic = ">=0.5.1" +opentelemetry-api = ">=1.20.0" +packaging = ">=24.0" +platformdirs = ">=4.0.0" +py-key-value-aio = {version = ">=0.4.4,<0.5.0", extras = ["filetree", "keyring", "memory"]} +pydantic = {version = ">=2.11.7", extras = ["email"]} +pyperclip = ">=1.9.0" +python-dotenv = ">=1.1.0" +pyyaml = ">=6.0,<7.0" +rich = ">=13.9.4" +uncalled-for = ">=0.2.0" +uvicorn = ">=0.35" +watchfiles = ">=1.0.0" +websockets = ">=15.0.1" + +[package.extras] +anthropic = ["anthropic (>=0.40.0)"] +apps = ["prefab-ui (>=0.6.0)"] +azure = ["azure-identity (>=1.16.0)"] +code-mode = ["pydantic-monty (>=0.0.7,<0.0.8)"] +gemini = ["google-genai (>=1.18.0)"] +openai = ["openai (>=1.102.0)"] +tasks = ["pydocket (>=0.18.0)"] + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["mcp"] files = [ - {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, - {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, - {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, - {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, - {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, - {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, - {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, - {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, - {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, - {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, - {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, - {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, - {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, - {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, - {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, - {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, - {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, - {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, - {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, - {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, - {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, - {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, - {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, - {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, - {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, - {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, - {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, - {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, - {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, - {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, - {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, - {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, - {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, - {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, - {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, - {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, - {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, - {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, - {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, - {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, - {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, - {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, - {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, - {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, - {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, - {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, - {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, - {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, - {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, - {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, - {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, - {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, - {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, - {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, - {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, - {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, - {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, - {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, - {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, - {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, - {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, - {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, - {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, - {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, - {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, - {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, - {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, - {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, - {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, - {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, - {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, - {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, - {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] [[package]] -name = "requests" -version = "2.32.5" -description = "Python HTTP for Humans." +name = "httpcore" +version = "1.0.9" +description = "A minimal low-level HTTP client." optional = false -python-versions = ">=3.9" -groups = ["main"] +python-versions = ">=3.8" +groups = ["mcp"] files = [ - {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, - {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, ] [package.dependencies] -certifi = ">=2017.4.17" -charset_normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" +certifi = "*" +h11 = ">=0.16" [package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] [[package]] -name = "urllib3" -version = "2.6.3" -description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." optional = false -python-versions = ">=3.9" -groups = ["main"] +python-versions = ">=3.8" +groups = ["mcp"] files = [ - {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, - {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + [package.extras] -brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] -[metadata] -lock-version = "2.1" +[[package]] +name = "httpx-sse" +version = "0.4.3" +description = "Consume Server-Sent Event (SSE) messages with HTTPX." +optional = false python-versions = ">=3.9" -content-hash = "7ee6412342a43d2f52d5815d281847619086753f2c68b7444d3c2e04cf7a3b25" +groups = ["mcp"] +files = [ + {file = "httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc"}, + {file = "httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d"}, +] + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +groups = ["main", "mcp"] +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "importlib-metadata" +version = "8.7.1" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"}, + {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +perf = ["ipython"] +test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +description = "Utility functions for Python class constructs" +optional = false +python-versions = ">=3.8" +groups = ["mcp"] +files = [ + {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, + {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "jaraco-context" +version = "6.1.2" +description = "Useful decorators and context managers" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535"}, + {file = "jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3"}, +] + +[package.dependencies] +"backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.14)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +test = ["jaraco.test (>=5.6.0)", "portend", "pytest (>=6,!=8.1.*)"] +type = ["pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""] + +[[package]] +name = "jaraco-functools" +version = "4.4.0" +description = "Functools like those found in stdlib" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176"}, + {file = "jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb"}, +] + +[package.dependencies] +more_itertools = "*" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +test = ["jaraco.classes", "pytest (>=6,!=8.1.*)"] +type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] + +[[package]] +name = "jeepney" +version = "0.9.0" +description = "Low-level, pure Python DBus protocol wrapper." +optional = false +python-versions = ">=3.7" +groups = ["mcp"] +markers = "sys_platform == \"linux\"" +files = [ + {file = "jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"}, + {file = "jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"}, +] + +[package.extras] +test = ["async-timeout ; python_version < \"3.11\"", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["trio"] + +[[package]] +name = "jsonref" +version = "1.1.0" +description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." +optional = false +python-versions = ">=3.7" +groups = ["mcp"] +files = [ + {file = "jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9"}, + {file = "jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552"}, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce"}, + {file = "jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.3.6" +referencing = ">=0.28.4" +rpds-py = ">=0.25.0" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-path" +version = "0.4.5" +description = "JSONSchema Spec with object-oriented paths" +optional = false +python-versions = "<4.0.0,>=3.10" +groups = ["mcp"] +files = [ + {file = "jsonschema_path-0.4.5-py3-none-any.whl", hash = "sha256:7d77a2c3f3ec569a40efe5c5f942c44c1af2a6f96fe0866794c9ef5b8f87fd65"}, + {file = "jsonschema_path-0.4.5.tar.gz", hash = "sha256:c6cd7d577ae290c7defd4f4029e86fdb248ca1bd41a07557795b3c95e5144918"}, +] + +[package.dependencies] +pathable = ">=0.5.0,<0.6.0" +PyYAML = ">=5.1" +referencing = "<0.38.0" + +[package.extras] +requests = ["requests (>=2.31.0,<3.0.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"}, + {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "keyring" +version = "25.7.0" +description = "Store and access your passwords safely." +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f"}, + {file = "keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b"}, +] + +[package.dependencies] +importlib_metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +"jaraco.classes" = "*" +"jaraco.context" = "*" +"jaraco.functools" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +completion = ["shtab (>=1.1.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] +type = ["pygobject-stubs", "pytest-mypy (>=1.0.1)", "shtab", "types-pywin32"] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, + {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins (>=0.5.0)"] +profiling = ["gprof2dot"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] + +[[package]] +name = "mcp" +version = "1.26.0" +description = "Model Context Protocol SDK" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca"}, + {file = "mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66"}, +] + +[package.dependencies] +anyio = ">=4.5" +httpx = ">=0.27.1" +httpx-sse = ">=0.4" +jsonschema = ">=4.20.0" +pydantic = ">=2.11.0,<3.0.0" +pydantic-settings = ">=2.5.2" +pyjwt = {version = ">=2.10.1", extras = ["crypto"]} +python-multipart = ">=0.0.9" +pywin32 = {version = ">=310", markers = "sys_platform == \"win32\""} +sse-starlette = ">=1.6.1" +starlette = ">=0.27" +typing-extensions = ">=4.9.0" +typing-inspection = ">=0.4.1" +uvicorn = {version = ">=0.31.1", markers = "sys_platform != \"emscripten\""} + +[package.extras] +cli = ["python-dotenv (>=1.0.0)", "typer (>=0.16.0)"] +rich = ["rich (>=13.9.4)"] +ws = ["websockets (>=15.0.1)"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +groups = ["mcp"] +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "more-itertools" +version = "10.8.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b"}, + {file = "more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd"}, +] + +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +description = "Pydantic OpenAPI schema implementation" +optional = false +python-versions = "<4.0,>=3.8" +groups = ["mcp"] +files = [ + {file = "openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146"}, + {file = "openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d"}, +] + +[package.dependencies] +pydantic = ">=1.8" + +[[package]] +name = "opentelemetry-api" +version = "1.40.0" +description = "OpenTelemetry Python API" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9"}, + {file = "opentelemetry_api-1.40.0.tar.gz", hash = "sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f"}, +] + +[package.dependencies] +importlib-metadata = ">=6.0,<8.8.0" +typing-extensions = ">=4.5.0" + +[[package]] +name = "packaging" +version = "26.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["mcp"] +files = [ + {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, + {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, +] + +[[package]] +name = "pathable" +version = "0.5.0" +description = "Object-oriented paths" +optional = false +python-versions = "<4.0,>=3.10" +groups = ["mcp"] +files = [ + {file = "pathable-0.5.0-py3-none-any.whl", hash = "sha256:646e3d09491a6351a0c82632a09c02cdf70a252e73196b36d8a15ba0a114f0a6"}, + {file = "pathable-0.5.0.tar.gz", hash = "sha256:d81938348a1cacb525e7c75166270644782c0fb9c8cecc16be033e71427e0ef1"}, +] + +[[package]] +name = "platformdirs" +version = "4.9.4" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868"}, + {file = "platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934"}, +] + +[[package]] +name = "py-key-value-aio" +version = "0.4.4" +description = "Async Key-Value Store - A pluggable interface for KV Stores" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "py_key_value_aio-0.4.4-py3-none-any.whl", hash = "sha256:18e17564ecae61b987f909fc2cd41ee2012c84b4b1dcb8c055cf8b4bc1bf3f5d"}, + {file = "py_key_value_aio-0.4.4.tar.gz", hash = "sha256:e3012e6243ed7cc09bb05457bd4d03b1ba5c2b1ca8700096b3927db79ffbbe55"}, +] + +[package.dependencies] +aiofile = {version = ">=3.5.0", optional = true, markers = "extra == \"filetree\""} +anyio = {version = ">=4.4.0", optional = true, markers = "extra == \"filetree\""} +beartype = ">=0.20.0" +cachetools = {version = ">=5.0.0", optional = true, markers = "extra == \"memory\""} +keyring = {version = ">=25.6.0", optional = true, markers = "extra == \"keyring\""} +typing-extensions = ">=4.15.0" + +[package.extras] +aerospike = ["aerospike (>=16.0.0)"] +disk = ["diskcache (>=5.0.0)", "pathvalidate (>=3.3.1)"] +docs = ["mkdocs (>=1.6.0)", "mkdocs-material (>=9.5.0)", "mkdocstrings-python (>=1.10.0)", "mkdocstrings[python] (>=0.30.0)"] +duckdb = ["duckdb (>=1.1.1)", "pytz (>=2025.2)"] +dynamodb = ["aioboto3 (>=13.3.0)", "types-aiobotocore-dynamodb (>=2.16.0)"] +elasticsearch = ["aiohttp (>=3.12)", "elasticsearch (>=8.0.0)"] +filetree = ["aiofile (>=3.5.0)", "anyio (>=4.4.0)"] +firestore = ["google-auth (>=2.24.0)", "google-cloud-firestore (>=2.13.0)"] +keyring = ["keyring (>=25.6.0)"] +keyring-linux = ["dbus-python (>=1.4.0)", "keyring (>=25.6.0)"] +memcached = ["aiomcache (>=0.8.0)"] +memory = ["cachetools (>=5.0.0)"] +mongodb = ["pymongo (>=4.0.0)"] +opensearch = ["opensearch-py[async] (>=2.0.0)"] +postgresql = ["asyncpg (>=0.30.0)"] +pydantic = ["pydantic (>=2.11.9)"] +redis = ["redis (>=4.3.0)"] +rocksdb = ["rocksdict (>=0.3.2) ; python_full_version < \"3.12.0\"", "rocksdict (>=0.3.24) ; python_full_version >= \"3.12.0\""] +s3 = ["aioboto3 (>=13.3.0)", "types-aiobotocore-s3 (>=2.16.0)"] +valkey = ["valkey-glide (>=2.1.0)"] +vault = ["hvac (>=2.3.0)", "types-hvac (>=2.3.0)"] +wrappers-encryption = ["cryptography (>=45.0.0)"] + +[[package]] +name = "pycparser" +version = "3.0" +description = "C parser in Python" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" +files = [ + {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, + {file = "pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"}, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"}, + {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} +pydantic-core = "2.41.5" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"}, + {file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c"}, + {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2"}, + {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556"}, + {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49"}, + {file = "pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba"}, + {file = "pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9"}, + {file = "pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6"}, + {file = "pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284"}, + {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594"}, + {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e"}, + {file = "pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe"}, + {file = "pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f"}, + {file = "pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7"}, + {file = "pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5"}, + {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c"}, + {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294"}, + {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1"}, + {file = "pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d"}, + {file = "pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815"}, + {file = "pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3"}, + {file = "pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9"}, + {file = "pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d"}, + {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740"}, + {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e"}, + {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858"}, + {file = "pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36"}, + {file = "pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11"}, + {file = "pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd"}, + {file = "pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a"}, + {file = "pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553"}, + {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90"}, + {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07"}, + {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb"}, + {file = "pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23"}, + {file = "pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf"}, + {file = "pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008"}, + {file = "pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf"}, + {file = "pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425"}, + {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504"}, + {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5"}, + {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3"}, + {file = "pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460"}, + {file = "pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51"}, + {file = "pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e"}, +] + +[package.dependencies] +typing-extensions = ">=4.14.1" + +[[package]] +name = "pydantic-settings" +version = "2.13.1" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237"}, + {file = "pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025"}, +] + +[package.dependencies] +pydantic = ">=2.7.0" +python-dotenv = ">=0.21.0" +typing-inspection = ">=0.4.0" + +[package.extras] +aws-secrets-manager = ["boto3 (>=1.35.0)", "boto3-stubs[secretsmanager]"] +azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] +gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["mcp"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyjwt" +version = "2.12.1" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c"}, + {file = "pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b"}, +] + +[package.dependencies] +cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} +typing_extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==7.10.7)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=8.4.2,<9.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==7.10.7)", "pytest (>=8.4.2,<9.0.0)"] + +[[package]] +name = "pyperclip" +version = "1.11.0" +description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" +optional = false +python-versions = "*" +groups = ["mcp"] +files = [ + {file = "pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273"}, + {file = "pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6"}, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.10" +groups = ["main", "mcp"] +files = [ + {file = "python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a"}, + {file = "python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.22" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155"}, + {file = "python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58"}, +] + +[[package]] +name = "pywin32" +version = "311" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +groups = ["mcp"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"}, + {file = "pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b"}, + {file = "pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b"}, + {file = "pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151"}, + {file = "pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503"}, + {file = "pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2"}, + {file = "pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31"}, + {file = "pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067"}, + {file = "pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852"}, + {file = "pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d"}, + {file = "pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d"}, + {file = "pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a"}, + {file = "pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee"}, + {file = "pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87"}, + {file = "pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42"}, + {file = "pywin32-311-cp38-cp38-win32.whl", hash = "sha256:6c6f2969607b5023b0d9ce2541f8d2cbb01c4f46bc87456017cf63b73f1e2d8c"}, + {file = "pywin32-311-cp38-cp38-win_amd64.whl", hash = "sha256:c8015b09fb9a5e188f83b7b04de91ddca4658cee2ae6f3bc483f0b21a77ef6cd"}, + {file = "pywin32-311-cp39-cp39-win32.whl", hash = "sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b"}, + {file = "pywin32-311-cp39-cp39-win_amd64.whl", hash = "sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91"}, + {file = "pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d"}, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +groups = ["mcp"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, + {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main", "mcp"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "referencing" +version = "0.37.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, + {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" +typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} + +[[package]] +name = "requests" +version = "2.33.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b"}, + {file = "requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652"}, +] + +[package.dependencies] +certifi = ">=2023.5.7" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.26,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +test = ["PySocks (>=1.5.6,!=1.5.7)", "pytest (>=3)", "pytest-cov", "pytest-httpbin (==2.1.0)", "pytest-mock", "pytest-xdist"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] + +[[package]] +name = "rich" +version = "14.3.3" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +groups = ["mcp"] +files = [ + {file = "rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d"}, + {file = "rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "rich-rst" +version = "1.3.2" +description = "A beautiful reStructuredText renderer for rich" +optional = false +python-versions = "*" +groups = ["mcp"] +files = [ + {file = "rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a"}, + {file = "rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4"}, +] + +[package.dependencies] +docutils = "*" +rich = ">=12.0.0" + +[package.extras] +docs = ["sphinx"] + +[[package]] +name = "rpds-py" +version = "0.30.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288"}, + {file = "rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139"}, + {file = "rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464"}, + {file = "rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169"}, + {file = "rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425"}, + {file = "rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85"}, + {file = "rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c"}, + {file = "rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825"}, + {file = "rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229"}, + {file = "rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad"}, + {file = "rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394"}, + {file = "rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf"}, + {file = "rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b"}, + {file = "rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e"}, + {file = "rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2"}, + {file = "rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95"}, + {file = "rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d"}, + {file = "rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15"}, + {file = "rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1"}, + {file = "rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a"}, + {file = "rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27"}, + {file = "rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6"}, + {file = "rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d"}, + {file = "rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0"}, + {file = "rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53"}, + {file = "rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed"}, + {file = "rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950"}, + {file = "rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6"}, + {file = "rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb"}, + {file = "rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40"}, + {file = "rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0"}, + {file = "rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e"}, + {file = "rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84"}, +] + +[[package]] +name = "secretstorage" +version = "3.5.0" +description = "Python bindings to FreeDesktop.org Secret Service API" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +markers = "sys_platform == \"linux\"" +files = [ + {file = "secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137"}, + {file = "secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[[package]] +name = "sse-starlette" +version = "3.3.3" +description = "SSE plugin for Starlette" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "sse_starlette-3.3.3-py3-none-any.whl", hash = "sha256:c5abb5082a1cc1c6294d89c5290c46b5f67808cfdb612b7ec27e8ba061c22e8d"}, + {file = "sse_starlette-3.3.3.tar.gz", hash = "sha256:72a95d7575fd5129bd0ae15275ac6432bb35ac542fdebb82889c24bb9f3f4049"}, +] + +[package.dependencies] +anyio = ">=4.7.0" +starlette = ">=0.49.1" + +[package.extras] +daphne = ["daphne (>=4.2.0)"] +examples = ["aiosqlite (>=0.21.0)", "fastapi (>=0.115.12)", "sqlalchemy[asyncio] (>=2.0.41)", "uvicorn (>=0.34.0)"] +granian = ["granian (>=2.3.1)"] +uvicorn = ["uvicorn (>=0.34.0)"] + +[[package]] +name = "starlette" +version = "1.0.0" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b"}, + {file = "starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149"}, +] + +[package.dependencies] +anyio = ">=3.6.2,<5" +typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\""} + +[package.extras] +full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] + +[[package]] +name = "tomli" +version = "2.4.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["mcp"] +markers = "python_version == \"3.10\"" +files = [ + {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"}, + {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"}, + {file = "tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95"}, + {file = "tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76"}, + {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d"}, + {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576"}, + {file = "tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a"}, + {file = "tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa"}, + {file = "tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614"}, + {file = "tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1"}, + {file = "tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8"}, + {file = "tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a"}, + {file = "tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1"}, + {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b"}, + {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51"}, + {file = "tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729"}, + {file = "tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da"}, + {file = "tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3"}, + {file = "tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0"}, + {file = "tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e"}, + {file = "tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4"}, + {file = "tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e"}, + {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c"}, + {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f"}, + {file = "tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86"}, + {file = "tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87"}, + {file = "tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132"}, + {file = "tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6"}, + {file = "tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc"}, + {file = "tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66"}, + {file = "tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d"}, + {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702"}, + {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8"}, + {file = "tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776"}, + {file = "tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475"}, + {file = "tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2"}, + {file = "tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9"}, + {file = "tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0"}, + {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df"}, + {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d"}, + {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f"}, + {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b"}, + {file = "tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087"}, + {file = "tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd"}, + {file = "tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4"}, + {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"}, + {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "uncalled-for" +version = "0.2.0" +description = "Async dependency injection for Python functions" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "uncalled_for-0.2.0-py3-none-any.whl", hash = "sha256:2c0bd338faff5f930918f79e7eb9ff48290df2cb05fcc0b40a7f334e55d4d85f"}, + {file = "uncalled_for-0.2.0.tar.gz", hash = "sha256:b4f8fdbcec328c5a113807d653e041c5094473dd4afa7c34599ace69ccb7e69f"}, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, + {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, +] + +[package.extras] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] + +[[package]] +name = "uvicorn" +version = "0.42.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "uvicorn-0.42.0-py3-none-any.whl", hash = "sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359"}, + {file = "uvicorn-0.42.0.tar.gz", hash = "sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.20)", "websockets (>=10.4)"] + +[[package]] +name = "watchfiles" +version = "1.1.1" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c"}, + {file = "watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43"}, + {file = "watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31"}, + {file = "watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac"}, + {file = "watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d"}, + {file = "watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d"}, + {file = "watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863"}, + {file = "watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab"}, + {file = "watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82"}, + {file = "watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4"}, + {file = "watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844"}, + {file = "watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e"}, + {file = "watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5"}, + {file = "watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741"}, + {file = "watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6"}, + {file = "watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b"}, + {file = "watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14"}, + {file = "watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d"}, + {file = "watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff"}, + {file = "watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606"}, + {file = "watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701"}, + {file = "watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10"}, + {file = "watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849"}, + {file = "watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4"}, + {file = "watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e"}, + {file = "watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d"}, + {file = "watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610"}, + {file = "watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af"}, + {file = "watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6"}, + {file = "watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce"}, + {file = "watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa"}, + {file = "watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb"}, + {file = "watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803"}, + {file = "watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94"}, + {file = "watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43"}, + {file = "watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9"}, + {file = "watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9"}, + {file = "watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404"}, + {file = "watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18"}, + {file = "watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a"}, + {file = "watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219"}, + {file = "watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428"}, + {file = "watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0"}, + {file = "watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150"}, + {file = "watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae"}, + {file = "watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d"}, + {file = "watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b"}, + {file = "watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374"}, + {file = "watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0"}, + {file = "watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42"}, + {file = "watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18"}, + {file = "watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da"}, + {file = "watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051"}, + {file = "watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e"}, + {file = "watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70"}, + {file = "watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261"}, + {file = "watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620"}, + {file = "watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04"}, + {file = "watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77"}, + {file = "watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef"}, + {file = "watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf"}, + {file = "watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5"}, + {file = "watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd"}, + {file = "watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb"}, + {file = "watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5"}, + {file = "watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3"}, + {file = "watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33"}, + {file = "watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510"}, + {file = "watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05"}, + {file = "watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6"}, + {file = "watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81"}, + {file = "watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b"}, + {file = "watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a"}, + {file = "watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02"}, + {file = "watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21"}, + {file = "watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5"}, + {file = "watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7"}, + {file = "watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101"}, + {file = "watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44"}, + {file = "watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c"}, + {file = "watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc"}, + {file = "watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c"}, + {file = "watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099"}, + {file = "watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01"}, + {file = "watchfiles-1.1.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c882d69f6903ef6092bedfb7be973d9319940d56b8427ab9187d1ecd73438a70"}, + {file = "watchfiles-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d6ff426a7cb54f310d51bfe83fe9f2bbe40d540c741dc974ebc30e6aa238f52e"}, + {file = "watchfiles-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79ff6c6eadf2e3fc0d7786331362e6ef1e51125892c75f1004bd6b52155fb956"}, + {file = "watchfiles-1.1.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c1f5210f1b8fc91ead1283c6fd89f70e76fb07283ec738056cf34d51e9c1d62c"}, + {file = "watchfiles-1.1.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9c4702f29ca48e023ffd9b7ff6b822acdf47cb1ff44cb490a3f1d5ec8987e9c"}, + {file = "watchfiles-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acb08650863767cbc58bca4813b92df4d6c648459dcaa3d4155681962b2aa2d3"}, + {file = "watchfiles-1.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08af70fd77eee58549cd69c25055dc344f918d992ff626068242259f98d598a2"}, + {file = "watchfiles-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c3631058c37e4a0ec440bf583bc53cdbd13e5661bb6f465bc1d88ee9a0a4d02"}, + {file = "watchfiles-1.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cf57a27fb986c6243d2ee78392c503826056ffe0287e8794503b10fb51b881be"}, + {file = "watchfiles-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d7e7067c98040d646982daa1f37a33d3544138ea155536c2e0e63e07ff8a7e0f"}, + {file = "watchfiles-1.1.1-cp39-cp39-win32.whl", hash = "sha256:6c9c9262f454d1c4d8aaa7050121eb4f3aea197360553699520767daebf2180b"}, + {file = "watchfiles-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:74472234c8370669850e1c312490f6026d132ca2d396abfad8830b4f1c096957"}, + {file = "watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3"}, + {file = "watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2"}, + {file = "watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d"}, + {file = "watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b"}, + {file = "watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88"}, + {file = "watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336"}, + {file = "watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24"}, + {file = "watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49"}, + {file = "watchfiles-1.1.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdab464fee731e0884c35ae3588514a9bcf718d0e2c82169c1c4a85cc19c3c7f"}, + {file = "watchfiles-1.1.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3dbd8cbadd46984f802f6d479b7e3afa86c42d13e8f0f322d669d79722c8ec34"}, + {file = "watchfiles-1.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5524298e3827105b61951a29c3512deb9578586abf3a7c5da4a8069df247cccc"}, + {file = "watchfiles-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b943d3668d61cfa528eb949577479d3b077fd25fb83c641235437bc0b5bc60e"}, + {file = "watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "16.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.10" +groups = ["mcp"] +files = [ + {file = "websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a"}, + {file = "websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0"}, + {file = "websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957"}, + {file = "websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72"}, + {file = "websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde"}, + {file = "websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3"}, + {file = "websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3"}, + {file = "websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9"}, + {file = "websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35"}, + {file = "websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8"}, + {file = "websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad"}, + {file = "websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d"}, + {file = "websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe"}, + {file = "websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b"}, + {file = "websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5"}, + {file = "websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64"}, + {file = "websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6"}, + {file = "websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac"}, + {file = "websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00"}, + {file = "websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79"}, + {file = "websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39"}, + {file = "websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c"}, + {file = "websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f"}, + {file = "websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1"}, + {file = "websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2"}, + {file = "websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89"}, + {file = "websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea"}, + {file = "websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9"}, + {file = "websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230"}, + {file = "websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c"}, + {file = "websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5"}, + {file = "websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82"}, + {file = "websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8"}, + {file = "websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f"}, + {file = "websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a"}, + {file = "websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156"}, + {file = "websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0"}, + {file = "websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904"}, + {file = "websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4"}, + {file = "websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e"}, + {file = "websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4"}, + {file = "websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1"}, + {file = "websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3"}, + {file = "websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8"}, + {file = "websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d"}, + {file = "websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244"}, + {file = "websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e"}, + {file = "websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641"}, + {file = "websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8"}, + {file = "websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e"}, + {file = "websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944"}, + {file = "websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206"}, + {file = "websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6"}, + {file = "websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd"}, + {file = "websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d"}, + {file = "websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03"}, + {file = "websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da"}, + {file = "websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c"}, + {file = "websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767"}, + {file = "websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec"}, + {file = "websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5"}, +] + +[[package]] +name = "zipp" +version = "3.23.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +groups = ["mcp"] +files = [ + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + +[metadata] +lock-version = "2.1" +python-versions = "<4.0,>=3.10" +content-hash = "add336e7fc3c6fa9a72fb7e0b3e2950d49783e2eeb82e1783c61de0c6f09054f" diff --git a/pyproject.toml b/pyproject.toml index ab65b50..b6196f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-substack" -version = "0.1.20" +version = "0.1.21" description = "A Python wrapper around the Substack API." authors = ["Paolo Mazza "] license = "MIT" @@ -16,15 +16,20 @@ homepage = "https://github.com/ma2za/python-substack" keywords = ["substack"] [tool.poetry.dependencies] -python = ">=3.9" +python = "<4.0,>=3.10" -requests = "^2.31.0" -python-dotenv = "^0.21.0" +requests = "^2.32.0" +python-dotenv = "^1.2.1" PyYAML = "^6.0" - [tool.poetry.group.dev.dependencies] +[tool.poetry.group.mcp] + +optional = true + +[tool.poetry.group.mcp.dependencies] +fastmcp = "^3.1.1" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/substack/__init__.py b/substack/__init__.py index 0d8268f..4963dbe 100644 --- a/substack/__init__.py +++ b/substack/__init__.py @@ -3,7 +3,7 @@ __author__ = "Paolo Mazza" __email__ = "mazzapaolo2019@gmail.com" __license__ = "MIT License" -__version__ = "1.0" +__version__ = "0.1.21" __url__ = "https://github.com/ma2za/python-substack" __download_url__ = "https://pypi.python.org/pypi/python-substack" __description__ = "A Python wrapper around the Substack API" diff --git a/substack_mcp/mcp_server.py b/substack_mcp/mcp_server.py new file mode 100644 index 0000000..df0ea8c --- /dev/null +++ b/substack_mcp/mcp_server.py @@ -0,0 +1,293 @@ +from __future__ import annotations + +import os +from typing import Any, Dict, List, Optional + +try: + from dotenv import load_dotenv +except ImportError: + load_dotenv = None + +from mcp.server.fastmcp import FastMCP + +from substack.api import Api +from substack.post import Post + +if load_dotenv is not None: + load_dotenv() + + +def get_api() -> Api: + email = os.getenv("EMAIL") + password = os.getenv("PASSWORD") + cookies_path = os.getenv("COOKIES_PATH") + cookies_string = os.getenv("COOKIES_STRING") + publication_url = os.getenv("PUBLICATION_URL") + + if cookies_path or cookies_string: + return Api( + cookies_path=cookies_path, + cookies_string=cookies_string, + publication_url=publication_url, + ) + + if email and password: + return Api( + email=email, + password=password, + publication_url=publication_url, + ) + + raise ValueError( + "Missing Substack auth configuration: set EMAIL/PASSWORD or COOKIES_PATH/COOKIES_STRING" + ) + + +def _normalize_tags(tags: Optional[Any]) -> List[str]: + if tags is None: + return [] + if isinstance(tags, str): + return [tags] + if isinstance(tags, list): + return [str(tag) for tag in tags] + raise ValueError("tags must be a string or a list of strings") + + +mcp = FastMCP("substack") + + +@mcp.tool() +async def post_draft_from_markdown( + title: str, + markdown: str, + subtitle: Optional[str] = "", + audience: str = "everyone", + write_comment_permissions: str = "everyone", + search_engine_title: Optional[str] = None, + search_engine_description: Optional[str] = None, + slug: Optional[str] = None, + draft_section_id: Optional[int] = None, + tags: Optional[Any] = None, + prepublish: bool = False, + publish: bool = False, + send: bool = True, + share_automatically: bool = False, +) -> Dict[str, Any]: + """Create or update a Substack draft from Markdown. + + This tool builds a Substack `Post` from markdown content and posts a draft. + It supports optional tag assignment, prepublish (setup check), and publishing. + + Args: + title: Draft title. + markdown: Markdown body content. + subtitle: Optional subtitle text. + audience: One of `everyone`, `only_paid`, `founding`, `only_free`. + write_comment_permissions: One of `none`, `only_paid`, `everyone`. + search_engine_title: Optional title for search engine optimization. + search_engine_description: Optional description for search engine optimization. + slug: Optional URL slug for the post. + draft_section_id: Optional section ID for the draft. + tags: Tag or list of tags to attach to the post. + prepublish: If true, calls `prepublish_draft` after creation. + publish: If true, calls `publish_draft` after creation (and optionally prepublish). + send: Passed to `publish_draft` for newsletter delivery. + share_automatically: Passed to `publish_draft`. + + Returns: + dict containing drafted post (`draft`), optional `tags`, `prepublish`, `publish` results. + + Examples: + With the YAML structure from the README, a caller can map fields like: + + ```yaml + title: "My Post Title" + subtitle: "My Post Subtitle" + audience: "everyone" + write_comment_permissions: "everyone" + markdown: | + # Hello + + This is the body. + + tags: + - python + - substack + prepublish: true + publish: true + send: false + share_automatically: true + ``` + + Then invoke via MCP directly: + + ```python + from substack_mcp.mcp_server import post_draft_from_markdown + + result = await post_draft_from_markdown( + title='My Post Title', + markdown='# Hello\n\nThis is the body.', + subtitle='My Post Subtitle', + audience='everyone', + write_comment_permissions='everyone', + tags=['python', 'substack'], + prepublish=True, + publish=False, # set true when ready + ) + print(result) + ``` + + A longer process with manual prepublish/publish calls: + + ```python + from substack_mcp.mcp_server import ( + post_draft_from_markdown, + prepublish_draft, + publish_draft, + add_tags, + ) + + d = await post_draft_from_markdown( + title='Long flow', + markdown='Content', + tags=['a','b'], + publish=False, + ) + draft_id = d['draft']['id'] + + await add_tags(draft_id, ['post-tag', 'news']) + await prepublish_draft(draft_id) + await publish_draft(draft_id, send=True, share_automatically=True) + ``` + + This docstring example is meant to mirror the YAML-driven workflow and show how to decompose the same operations into explicit tool calls. + """ + client = get_api() + user_id = client.get_user_id() + + post = Post( + title=title, + subtitle=subtitle or "", + user_id=user_id, + audience=audience, + write_comment_permissions=write_comment_permissions, + ) + + post.from_markdown(markdown, api=client) + + draft = client.post_draft(post.get_draft()) + + update_payload: Dict[str, Any] = {} + if search_engine_title: + update_payload["search_engine_title"] = search_engine_title + if search_engine_description: + update_payload["search_engine_description"] = search_engine_description + if slug: + update_payload["slug"] = slug + if draft_section_id is not None: + update_payload["draft_section_id"] = draft_section_id + + if update_payload: + draft = client.put_draft(draft.get("id"), **update_payload) + + tags_list = _normalize_tags(tags) + tags_result = None + if tags_list: + tags_result = client.add_tags_to_post(draft.get("id"), tags_list) + + prepublish_result = None + if prepublish: + prepublish_result = client.prepublish_draft(draft.get("id")) + + publish_result = None + if publish: + publish_result = client.publish_draft( + draft.get("id"), send=send, share_automatically=share_automatically + ) + + return { + "draft": draft, + "tags": tags_result, + "prepublish": prepublish_result, + "publish": publish_result, + } + + +@mcp.tool() +async def put_draft( + draft_id: int, + update_payload: Dict[str, Any], +) -> Dict[str, Any]: + """Update an existing draft by draft ID. + + Args: + draft_id: target draft identifier. + update_payload: dict of fields supported by Substack `put_draft` (e.g. `slug`, `draft_section_id`). + + Returns: + API response dict for the updated draft. + """ + client = get_api() + return client.put_draft(draft_id, **update_payload) + + +@mcp.tool() +async def add_tags(draft_id: int, tags: Any) -> Dict[str, Any]: + """Add tags to a specific draft/post. + + Args: + draft_id: target draft identifier. + tags: string or list of tag names (e.g. `"tech"` or `["tech", "python"]`). + + Returns: + Response from `add_tags_to_post` (tag IDs + names). + """ + client = get_api() + tags_list = _normalize_tags(tags) + if not tags_list: + raise ValueError("tags is required and cannot be empty") + return client.add_tags_to_post(draft_id, tags_list) + + +@mcp.tool() +async def prepublish_draft(draft_id: int) -> Dict[str, Any]: + """Invoke prepublish checks for a draft. + + Args: + draft_id: target draft identifier. + + Returns: + Prepublish response dict from Substack API. + """ + client = get_api() + return client.prepublish_draft(draft_id) + + +@mcp.tool() +async def publish_draft( + draft_id: int, + send: bool = True, + share_automatically: bool = False, +) -> Dict[str, Any]: + """Publish a draft to live post state. + + Args: + draft_id: target draft identifier. + send: if False then do not send email to subscribers. + share_automatically: whether to auto-share (e.g. social propagation). + + Returns: + Response from Substack `publish_draft`. + """ + client = get_api() + return client.publish_draft( + draft_id, send=send, share_automatically=share_automatically + ) + + +def main() -> None: + mcp.run(transport="stdio") + + +if __name__ == "__main__": + main() From 380ce82ff1d5c61e46748a8f1174e2856824629c Mon Sep 17 00:00:00 2001 From: Paolo Mazza <59370937+ma2za@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:56:59 +0200 Subject: [PATCH 09/31] Update ci_publish.yml --- .github/workflows/ci_publish.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml index f04374c..7c6e6a6 100644 --- a/.github/workflows/ci_publish.yml +++ b/.github/workflows/ci_publish.yml @@ -1,8 +1,10 @@ name: Publish on: - push: - branches: - - main + release: + types: + - created + workflow_dispatch: + jobs: deploy: runs-on: ubuntu-latest From c20c57322387afd7059f3cc975a3f1a79147f9ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:59:28 +0200 Subject: [PATCH 10/31] Bump cryptography from 46.0.5 to 46.0.6 (#41) Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.5 to 46.0.6. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.5...46.0.6) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 117 ++++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/poetry.lock b/poetry.lock index 816d899..6a72eff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aiofile" @@ -176,7 +176,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, @@ -437,61 +437,61 @@ files = [ [[package]] name = "cryptography" -version = "46.0.5" +version = "46.0.6" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" -groups = ["mcp"] -files = [ - {file = "cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731"}, - {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82"}, - {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1"}, - {file = "cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48"}, - {file = "cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4"}, - {file = "cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663"}, - {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826"}, - {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d"}, - {file = "cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a"}, - {file = "cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4"}, - {file = "cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c"}, - {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4"}, - {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9"}, - {file = "cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72"}, - {file = "cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7"}, - {file = "cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d"}, +groups = ["dev", "mcp"] +files = [ + {file = "cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738"}, + {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c"}, + {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f"}, + {file = "cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2"}, + {file = "cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124"}, + {file = "cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a"}, + {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d"}, + {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736"}, + {file = "cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed"}, + {file = "cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4"}, + {file = "cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58"}, + {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb"}, + {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72"}, + {file = "cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c"}, + {file = "cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e"}, + {file = "cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759"}, ] [package.dependencies] @@ -505,7 +505,7 @@ nox = ["nox[uv] (>=2024.4.15)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.6)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -883,7 +883,7 @@ files = [ [package.dependencies] attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.3.6" +jsonschema-specifications = ">=2023.03.6" referencing = ">=0.28.4" rpds-py = ">=0.25.0" @@ -1154,7 +1154,7 @@ version = "3.0" description = "C parser in Python" optional = false python-versions = ">=3.10" -groups = ["mcp"] +groups = ["dev", "mcp"] markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, @@ -1870,11 +1870,12 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +markers = {dev = "python_version == \"3.10\""} [[package]] name = "typing-inspection" From f2431b43925b8f22a5f4a12674fc5e2cd3908348 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:59:27 +0200 Subject: [PATCH 11/31] Bump fastmcp from 3.1.1 to 3.2.0 (#42) Bumps [fastmcp](https://github.com/PrefectHQ/fastmcp) from 3.1.1 to 3.2.0. - [Release notes](https://github.com/PrefectHQ/fastmcp/releases) - [Changelog](https://github.com/PrefectHQ/fastmcp/blob/main/docs/changelog.mdx) - [Commits](https://github.com/PrefectHQ/fastmcp/compare/v3.1.1...v3.2.0) --- updated-dependencies: - dependency-name: fastmcp dependency-version: 3.2.0 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6a72eff..ff300a6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -176,7 +176,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, @@ -441,7 +441,7 @@ version = "46.0.6" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8"}, {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30"}, @@ -623,14 +623,14 @@ test = ["pytest (>=6)"] [[package]] name = "fastmcp" -version = "3.1.1" +version = "3.2.0" description = "The fast, Pythonic way to build MCP servers and clients." optional = false python-versions = ">=3.10" groups = ["mcp"] files = [ - {file = "fastmcp-3.1.1-py3-none-any.whl", hash = "sha256:8132ba069d89f14566b3266919d6d72e2ec23dd45d8944622dca407e9beda7eb"}, - {file = "fastmcp-3.1.1.tar.gz", hash = "sha256:db184b5391a31199323766a3abf3a8bfbb8010479f77eca84c0e554f18655c48"}, + {file = "fastmcp-3.2.0-py3-none-any.whl", hash = "sha256:e71aba3df16f86f546a4a9e513261d3233bcc92bef0dfa647bac3fa33623f681"}, + {file = "fastmcp-3.2.0.tar.gz", hash = "sha256:d4830b8ffc3592d3d9c76dc0f398904cf41f04910e41a0de38cc1004e0903bef"}, ] [package.dependencies] @@ -657,10 +657,10 @@ watchfiles = ">=1.0.0" websockets = ">=15.0.1" [package.extras] -anthropic = ["anthropic (>=0.40.0)"] -apps = ["prefab-ui (>=0.6.0)"] -azure = ["azure-identity (>=1.16.0)"] -code-mode = ["pydantic-monty (>=0.0.7,<0.0.8)"] +anthropic = ["anthropic (>=0.48.0)"] +apps = ["prefab-ui (>=0.18.0)"] +azure = ["azure-identity (>=1.16.0)", "pyjwt (>=2.12.0)"] +code-mode = ["pydantic-monty (==0.0.9)"] gemini = ["google-genai (>=1.18.0)"] openai = ["openai (>=1.102.0)"] tasks = ["pydocket (>=0.18.0)"] @@ -1154,7 +1154,7 @@ version = "3.0" description = "C parser in Python" optional = false python-versions = ">=3.10" -groups = ["dev", "mcp"] +groups = ["mcp"] markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, @@ -1870,12 +1870,11 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {dev = "python_version == \"3.10\""} [[package]] name = "typing-inspection" From e659a9e77d5b364d7ebaa054101636f6646112b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:34:19 +0200 Subject: [PATCH 12/31] Bump pygments from 2.19.2 to 2.20.0 (#43) Bumps [pygments](https://github.com/pygments/pygments) from 2.19.2 to 2.20.0. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](https://github.com/pygments/pygments/compare/2.19.2...2.20.0) --- updated-dependencies: - dependency-name: pygments dependency-version: 2.20.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index ff300a6..0322d7f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1344,14 +1344,14 @@ yaml = ["pyyaml (>=6.0.1)"] [[package]] name = "pygments" -version = "2.19.2" +version = "2.20.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.8" -groups = ["mcp"] +python-versions = ">=3.9" +groups = ["dev", "mcp"] files = [ - {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, - {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, ] [package.extras] From ed224c8860c1815b5d7e1492bf58097cc9c223a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:22:38 +0200 Subject: [PATCH 13/31] Bump cryptography from 46.0.6 to 46.0.7 (#48) Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.6 to 46.0.7. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.6...46.0.7) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.7 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 113 ++++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0322d7f..732757b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -176,7 +176,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, @@ -437,61 +437,61 @@ files = [ [[package]] name = "cryptography" -version = "46.0.6" +version = "46.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ - {file = "cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738"}, - {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c"}, - {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f"}, - {file = "cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2"}, - {file = "cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124"}, - {file = "cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a"}, - {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d"}, - {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736"}, - {file = "cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed"}, - {file = "cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4"}, - {file = "cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58"}, - {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb"}, - {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72"}, - {file = "cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c"}, - {file = "cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e"}, - {file = "cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759"}, + {file = "cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b"}, + {file = "cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85"}, + {file = "cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e"}, + {file = "cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457"}, + {file = "cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b"}, + {file = "cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2"}, + {file = "cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e"}, + {file = "cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee"}, + {file = "cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298"}, + {file = "cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb"}, + {file = "cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0"}, + {file = "cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85"}, + {file = "cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e"}, + {file = "cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246"}, + {file = "cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4"}, + {file = "cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5"}, ] [package.dependencies] @@ -505,7 +505,7 @@ nox = ["nox[uv] (>=2024.4.15)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.6)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.7)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -1154,7 +1154,7 @@ version = "3.0" description = "C parser in Python" optional = false python-versions = ">=3.10" -groups = ["mcp"] +groups = ["dev", "mcp"] markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, @@ -1348,7 +1348,7 @@ version = "2.20.0" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, @@ -1870,11 +1870,12 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +markers = {dev = "python_version == \"3.10\""} [[package]] name = "typing-inspection" From eb30662c4928457f7d946d692816b36f3ebb152b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:25:18 +0200 Subject: [PATCH 14/31] Bump authlib from 1.6.9 to 1.6.11 (#49) Bumps [authlib](https://github.com/authlib/authlib) from 1.6.9 to 1.6.11. - [Release notes](https://github.com/authlib/authlib/releases) - [Changelog](https://github.com/authlib/authlib/blob/v1.6.11/docs/changelog.rst) - [Commits](https://github.com/authlib/authlib/compare/v1.6.9...v1.6.11) --- updated-dependencies: - dependency-name: authlib dependency-version: 1.6.11 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 732757b..ecdbeff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -61,14 +61,14 @@ files = [ [[package]] name = "authlib" -version = "1.6.9" +version = "1.6.11" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ - {file = "authlib-1.6.9-py2.py3-none-any.whl", hash = "sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3"}, - {file = "authlib-1.6.9.tar.gz", hash = "sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04"}, + {file = "authlib-1.6.11-py2.py3-none-any.whl", hash = "sha256:c8687a9a26451c51a34a06fa17bb97cb15bba46a6a626755e2d7f50da8bff3e3"}, + {file = "authlib-1.6.11.tar.gz", hash = "sha256:64db35b9b01aeccb4715a6c9a6613a06f2bd7be2ab9d2eb89edd1dfc7580a38f"}, ] [package.dependencies] From d94cd5f1ad8d135e5873a681289c8672dbc9a53f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:28:32 +0200 Subject: [PATCH 15/31] Bump python-multipart from 0.0.22 to 0.0.26 (#50) Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.22 to 0.0.26. - [Release notes](https://github.com/Kludex/python-multipart/releases) - [Changelog](https://github.com/Kludex/python-multipart/blob/main/CHANGELOG.md) - [Commits](https://github.com/Kludex/python-multipart/compare/0.0.22...0.0.26) --- updated-dependencies: - dependency-name: python-multipart dependency-version: 0.0.26 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/poetry.lock b/poetry.lock index ecdbeff..6f6022c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -65,7 +65,7 @@ version = "1.6.11" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "authlib-1.6.11-py2.py3-none-any.whl", hash = "sha256:c8687a9a26451c51a34a06fa17bb97cb15bba46a6a626755e2d7f50da8bff3e3"}, {file = "authlib-1.6.11.tar.gz", hash = "sha256:64db35b9b01aeccb4715a6c9a6613a06f2bd7be2ab9d2eb89edd1dfc7580a38f"}, @@ -176,7 +176,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, @@ -441,7 +441,7 @@ version = "46.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4"}, {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325"}, @@ -1154,7 +1154,7 @@ version = "3.0" description = "C parser in Python" optional = false python-versions = ">=3.10" -groups = ["dev", "mcp"] +groups = ["mcp"] markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, @@ -1408,14 +1408,14 @@ cli = ["click (>=5.0)"] [[package]] name = "python-multipart" -version = "0.0.22" +version = "0.0.26" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.10" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ - {file = "python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155"}, - {file = "python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58"}, + {file = "python_multipart-0.0.26-py3-none-any.whl", hash = "sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185"}, + {file = "python_multipart-0.0.26.tar.gz", hash = "sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17"}, ] [[package]] @@ -1870,12 +1870,11 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {dev = "python_version == \"3.10\""} [[package]] name = "typing-inspection" From 610f2471652e08f7c8b04bff32d120731586b00e Mon Sep 17 00:00:00 2001 From: Adam Rummer <31801759+cyclingwithelephants@users.noreply.github.com> Date: Sat, 2 May 2026 10:43:16 +0100 Subject: [PATCH 16/31] feat: add inline code, ordered lists, horizontal rules, bold+italic, strikethrough (#47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add inline code, ordered lists, horizontal rules, bold+italic, strikethrough Extend parse_inline() with new inline formatting patterns: - Inline code (`code`) with correct pattern ordering to prevent backtick content from being parsed as bold/italic - Bold+italic combo (***text***) matched before bold/italic - Strikethrough (~~text~~) Extend from_markdown() with new block-level elements: - Ordered lists (1. item) mirroring existing bullet list pattern - Horizontal rules (---, ***, ___) using existing horizontal_rule() method Fix parse_inline() token format in list items and blockquotes: - Add tokens_to_text_nodes() helper that converts parse_inline() tokens ({"content": "text"}) to valid ProseMirror text nodes ({"type": "text", "text": "text"}) with marks preserved - Apply to flush_bullets, flush_ordered, flush_quotes, and single-line variants — fixes Substack rendering empty body for lists/blockquotes All features verified against live Substack API. Includes 33 new unit tests covering all additions plus regression tests. * refactor: address PR review comments - Fix duplicate "Post Utilities" in module docstring - Remove numbering from parse_inline section comments - Flatten single-line blockquote/ordered/paragraph branch with elif --- substack/post.py | 152 ++++++++++--- tests/substack/test_post_extended.py | 306 +++++++++++++++++++++++++++ 2 files changed, 433 insertions(+), 25 deletions(-) create mode 100644 tests/substack/test_post_extended.py diff --git a/substack/post.py b/substack/post.py index 60feaaf..8a9d55d 100644 --- a/substack/post.py +++ b/substack/post.py @@ -8,19 +8,40 @@ import re from typing import Dict, List -__all__ = ["Post", "parse_inline"] +__all__ = ["Post", "parse_inline", "tokens_to_text_nodes"] from substack.exceptions import SectionNotExistsException +def tokens_to_text_nodes(tokens: List[Dict]) -> List[Dict]: + """Convert parse_inline() tokens to ProseMirror text nodes. + + parse_inline() returns {"content": "text", "marks": [...]}. + ProseMirror expects {"type": "text", "text": "text", "marks": [...]}. + """ + nodes = [] + for token in tokens: + if not token or not token.get("content"): + continue + node = {"type": "text", "text": token["content"]} + marks = token.get("marks") + if marks: + node["marks"] = marks + nodes.append(node) + return nodes + + def parse_inline(text: str) -> List[Dict]: """ Convert inline Markdown in a text string into a list of tokens for use in the post content. Supported formatting: + - `code`: Text wrapped in backticks. - **Bold**: Text wrapped in double asterisks. - *Italic*: Text wrapped in single asterisks. + - ***Bold+Italic***: Text wrapped in triple asterisks. + - ~~Strikethrough~~: Text wrapped in double tildes. - [Links]: Text wrapped in square brackets followed by URL in parentheses. Args: @@ -37,33 +58,50 @@ def parse_inline(text: str) -> List[Dict]: return [] tokens = [] - # Process text character by character to handle nested formatting - # We'll use regex to find all markdown patterns, then process them in order - # Find all markdown patterns: links, bold, italic - # Pattern order: links first (to avoid conflicts), then bold, then italic + # Pattern order matters: code > links > bold+italic > bold > italic > strikethrough + code_pattern = r'`([^`]+)`' link_pattern = r'\[([^\]]+)\]\(([^)]+)\)' + bold_italic_pattern = r'\*\*\*([^*]+)\*\*\*' bold_pattern = r'\*\*([^*]+)\*\*' italic_pattern = r'(? List[Dict]: tokens.append({"content": text[last_pos:start]}) # Add the formatted content - if match_type == "link": + if match_type == "code": + tokens.append({ + "content": content, + "marks": [{"type": "code"}] + }) + elif match_type == "link": tokens.append({ "content": content, "marks": [{"type": "link", "attrs": {"href": url}}] }) + elif match_type == "bold_italic": + tokens.append({ + "content": content, + "marks": [{"type": "strong"}, {"type": "em"}] + }) elif match_type == "bold": tokens.append({ "content": content, @@ -90,6 +138,11 @@ def parse_inline(text: str) -> List[Dict]: "content": content, "marks": [{"type": "em"}] }) + elif match_type == "strikethrough": + tokens.append({ + "content": content, + "marks": [{"type": "strikethrough"}] + }) last_pos = end @@ -503,7 +556,9 @@ def from_markdown(self, markdown_content: str, api=None): - Blockquotes: Lines starting with '>' (consecutive lines grouped) - Paragraphs: Regular text blocks - Bullet lists: Lines starting with '*' or '-' - - Inline formatting: **bold** and *italic* within paragraphs + - Ordered lists: Lines starting with '1.', '2.', etc. + - Horizontal rules: Lines with ---, ***, or ___ + - Inline formatting: **bold**, *italic*, ***bold+italic***, `code`, ~~strikethrough~~ Args: markdown_content: Markdown string to parse and add to the post. @@ -593,6 +648,11 @@ def from_markdown(self, markdown_content: str, api=None): if not text_content: continue + # Check for horizontal rule: ---, ***, ___ + if re.match(r'^(\*{3,}|-{3,}|_{3,})\s*$', text_content): + self.horizontal_rule() + continue + # Process headings (lines starting with '#' characters) if text_content.startswith("#"): level = len(text_content) - len(text_content.lstrip("#")) @@ -648,14 +708,15 @@ def from_markdown(self, markdown_content: str, api=None): self.add({"type": "captionedImage", "src": image_url}) - # Process paragraphs, bullet lists, or blockquotes + # Process paragraphs, bullet lists, ordered lists, or blockquotes else: if "\n" in text_content: - # Process each line, grouping consecutive bullets - # into a single bullet_list node and consecutive - # blockquote lines into a single blockquote node. + # Process each line, grouping consecutive bullets/ordered items + # into list nodes and consecutive blockquote lines into a + # single blockquote node. pending_bullets: List[List[Dict]] = [] pending_quotes: List[str] = [] + pending_ordered: List[List[Dict]] = [] def flush_bullets(): if not pending_bullets: @@ -677,10 +738,7 @@ def flush_quotes(): paragraphs: List[Dict] = [] for quote_line in pending_quotes: tokens = parse_inline(quote_line) - text_nodes = [ - {"type": "text", "text": t["content"]} - for t in tokens if t - ] + text_nodes = tokens_to_text_nodes(tokens) if text_nodes: paragraphs.append({"type": "paragraph", "content": text_nodes}) node: Dict = {"type": "blockquote"} @@ -689,20 +747,48 @@ def flush_quotes(): self.draft_body["content"].append(node) pending_quotes.clear() + def flush_ordered(): + if not pending_ordered: + return + list_items = [] + for item_nodes in pending_ordered: + list_items.append({ + "type": "list_item", + "content": [{"type": "paragraph", "content": item_nodes}], + }) + self.draft_body["content"].append( + {"type": "ordered_list", "content": list_items} + ) + pending_ordered.clear() + for line in text_content.split("\n"): line = line.strip() if not line: flush_bullets() + flush_ordered() flush_quotes() continue # Check for blockquote marker if line.startswith("> ") or line == ">": flush_bullets() + flush_ordered() quote_text = line[2:] if line.startswith("> ") else "" pending_quotes.append(quote_text) continue + # Check for ordered list marker + ordered_match = re.match(r'^(\d+)\.\s+(.*)', line) + if ordered_match: + flush_bullets() + flush_quotes() + item_text = ordered_match.group(2).strip() + tokens = parse_inline(item_text) + text_nodes = tokens_to_text_nodes(tokens) + if text_nodes: + pending_ordered.append(text_nodes) + continue + # Check for bullet marker bullet_text = None if line.startswith("* "): @@ -713,31 +799,47 @@ def flush_quotes(): bullet_text = line[1:].strip() if bullet_text is not None: + flush_ordered() flush_quotes() tokens = parse_inline(bullet_text) - if tokens: - pending_bullets.append(tokens) + text_nodes = tokens_to_text_nodes(tokens) + if text_nodes: + pending_bullets.append(text_nodes) else: flush_bullets() + flush_ordered() flush_quotes() tokens = parse_inline(line) self.add({"type": "paragraph", "content": tokens}) flush_bullets() + flush_ordered() flush_quotes() else: - # Single line — could be a blockquote or paragraph + # Single line — blockquote, ordered list, or paragraph if text_content.startswith("> ") or text_content == ">": quote_text = text_content[2:] if text_content.startswith("> ") else "" tokens = parse_inline(quote_text) - text_nodes = [ - {"type": "text", "text": t["content"]} - for t in tokens if t - ] + text_nodes = tokens_to_text_nodes(tokens) para = {"type": "paragraph", "content": text_nodes} if text_nodes else {"type": "paragraph"} self.draft_body["content"] = self.draft_body.get("content", []) + [ {"type": "blockquote", "content": [para]} ] + + elif re.match(r'^(\d+)\.\s+(.*)', text_content): + ordered_match = re.match(r'^(\d+)\.\s+(.*)', text_content) + item_text = ordered_match.group(2).strip() + tokens = parse_inline(item_text) + text_nodes = tokens_to_text_nodes(tokens) + if text_nodes: + list_item = { + "type": "list_item", + "content": [{"type": "paragraph", "content": text_nodes}], + } + self.draft_body["content"].append( + {"type": "ordered_list", "content": [list_item]} + ) + else: tokens = parse_inline(text_content) self.add({"type": "paragraph", "content": tokens}) diff --git a/tests/substack/test_post_extended.py b/tests/substack/test_post_extended.py new file mode 100644 index 0000000..df5fd36 --- /dev/null +++ b/tests/substack/test_post_extended.py @@ -0,0 +1,306 @@ +"""Tests for extended post.py -- inline code, ordered lists, horizontal rules, bold+italic, strikethrough.""" + +import json + +import pytest +from substack.post import Post, parse_inline + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def make_post(): + """Create a fresh Post instance for testing.""" + return Post(title="Test", subtitle="Sub", user_id=1) + + +def body_content(post): + """Return the content list from the post's draft body.""" + return post.draft_body["content"] + + +# --------------------------------------------------------------------------- +# TestParseInlineCode +# --------------------------------------------------------------------------- + +class TestParseInlineCode: + def test_inline_code_basic(self): + tokens = parse_inline("`code`") + assert len(tokens) == 1 + assert tokens[0]["content"] == "code" + assert tokens[0]["marks"] == [{"type": "code"}] + + def test_inline_code_in_sentence(self): + tokens = parse_inline("Use `foo()` here") + assert len(tokens) == 3 + assert tokens[0] == {"content": "Use "} + assert tokens[1] == {"content": "foo()", "marks": [{"type": "code"}]} + assert tokens[2] == {"content": " here"} + + def test_inline_code_preserves_special_chars(self): + tokens = parse_inline("`**not bold**`") + assert len(tokens) == 1 + assert tokens[0]["content"] == "**not bold**" + assert tokens[0]["marks"] == [{"type": "code"}] + + def test_inline_code_multiple(self): + tokens = parse_inline("`a` and `b`") + assert len(tokens) == 3 + assert tokens[0] == {"content": "a", "marks": [{"type": "code"}]} + assert tokens[1] == {"content": " and "} + assert tokens[2] == {"content": "b", "marks": [{"type": "code"}]} + + def test_inline_code_empty_backticks(self): + tokens = parse_inline("``") + # Empty backticks should not match the code pattern + assert len(tokens) == 1 + assert tokens[0] == {"content": "``"} + + +# --------------------------------------------------------------------------- +# TestParseInlineBoldItalic +# --------------------------------------------------------------------------- + +class TestParseInlineBoldItalic: + def test_bold_italic_combo(self): + tokens = parse_inline("***text***") + assert len(tokens) == 1 + assert tokens[0]["content"] == "text" + assert {"type": "strong"} in tokens[0]["marks"] + assert {"type": "em"} in tokens[0]["marks"] + + def test_bold_italic_in_sentence(self): + tokens = parse_inline("This is ***important*** stuff") + assert len(tokens) == 3 + assert tokens[0] == {"content": "This is "} + assert tokens[1]["content"] == "important" + assert {"type": "strong"} in tokens[1]["marks"] + assert {"type": "em"} in tokens[1]["marks"] + assert tokens[2] == {"content": " stuff"} + + def test_bold_italic_does_not_break_bold(self): + tokens = parse_inline("**bold** and ***both***") + assert len(tokens) == 3 + assert tokens[0]["content"] == "bold" + assert tokens[0]["marks"] == [{"type": "strong"}] + assert tokens[2]["content"] == "both" + assert {"type": "strong"} in tokens[2]["marks"] + assert {"type": "em"} in tokens[2]["marks"] + + def test_bold_italic_does_not_break_italic(self): + tokens = parse_inline("*italic* and ***both***") + assert len(tokens) == 3 + assert tokens[0]["content"] == "italic" + assert tokens[0]["marks"] == [{"type": "em"}] + assert tokens[2]["content"] == "both" + assert {"type": "strong"} in tokens[2]["marks"] + assert {"type": "em"} in tokens[2]["marks"] + + +# --------------------------------------------------------------------------- +# TestParseInlineStrikethrough +# --------------------------------------------------------------------------- + +class TestParseInlineStrikethrough: + def test_strikethrough_basic(self): + tokens = parse_inline("~~struck~~") + assert len(tokens) == 1 + assert tokens[0]["content"] == "struck" + assert tokens[0]["marks"] == [{"type": "strikethrough"}] + + def test_strikethrough_in_sentence(self): + tokens = parse_inline("This is ~~wrong~~ right") + assert len(tokens) == 3 + assert tokens[0] == {"content": "This is "} + assert tokens[1] == {"content": "wrong", "marks": [{"type": "strikethrough"}]} + assert tokens[2] == {"content": " right"} + + def test_strikethrough_with_other_marks(self): + tokens = parse_inline("**bold** and ~~struck~~") + assert len(tokens) == 3 + assert tokens[0]["marks"] == [{"type": "strong"}] + assert tokens[2]["marks"] == [{"type": "strikethrough"}] + + +# --------------------------------------------------------------------------- +# TestOrderedListFromMarkdown +# --------------------------------------------------------------------------- + +class TestOrderedListFromMarkdown: + def test_basic_ordered_list(self): + post = make_post() + post.from_markdown("1. a\n2. b\n3. c") + content = body_content(post) + assert len(content) == 1 + ol = content[0] + assert ol["type"] == "ordered_list" + assert len(ol["content"]) == 3 + for item in ol["content"]: + assert item["type"] == "list_item" + + def test_ordered_list_with_inline_formatting(self): + post = make_post() + post.from_markdown("1. **bold item**\n2. normal") + content = body_content(post) + ol = content[0] + first_item_tokens = ol["content"][0]["content"][0]["content"] + assert any( + t.get("marks") == [{"type": "strong"}] + for t in first_item_tokens + ) + + def test_ordered_list_single_item(self): + post = make_post() + post.from_markdown("1. only") + content = body_content(post) + assert len(content) == 1 + assert content[0]["type"] == "ordered_list" + assert len(content[0]["content"]) == 1 + + def test_mixed_list_types(self): + post = make_post() + post.from_markdown("- bullet a\n- bullet b\n\n1. ordered a\n2. ordered b") + content = body_content(post) + types = [node["type"] for node in content] + assert "bullet_list" in types + assert "ordered_list" in types + + def test_ordered_list_non_sequential_numbers(self): + post = make_post() + post.from_markdown("1. a\n5. b\n3. c") + content = body_content(post) + ol = content[0] + assert ol["type"] == "ordered_list" + assert len(ol["content"]) == 3 + + def test_ordered_list_with_links(self): + post = make_post() + post.from_markdown("1. [link](https://example.com)\n2. plain") + content = body_content(post) + ol = content[0] + first_item_tokens = ol["content"][0]["content"][0]["content"] + assert any( + any(m.get("type") == "link" for m in t.get("marks", [])) + for t in first_item_tokens + ) + + def test_single_line_ordered_item(self): + """A single ordered list item as its own block (no newlines in block).""" + post = make_post() + # Surround with blank lines so it's its own block + post.from_markdown("\n1. item\n") + content = body_content(post) + assert any(node["type"] == "ordered_list" for node in content) + + +# --------------------------------------------------------------------------- +# TestHorizontalRuleFromMarkdown +# --------------------------------------------------------------------------- + +class TestHorizontalRuleFromMarkdown: + def test_horizontal_rule_dashes(self): + post = make_post() + post.from_markdown("text\n\n---\n\nmore text") + content = body_content(post) + types = [node["type"] for node in content] + assert "horizontal_rule" in types + + def test_horizontal_rule_asterisks(self): + post = make_post() + post.from_markdown("text\n\n***\n\nmore text") + content = body_content(post) + types = [node["type"] for node in content] + assert "horizontal_rule" in types + + def test_horizontal_rule_underscores(self): + post = make_post() + post.from_markdown("text\n\n___\n\nmore text") + content = body_content(post) + types = [node["type"] for node in content] + assert "horizontal_rule" in types + + def test_horizontal_rule_extended(self): + post = make_post() + post.from_markdown("text\n\n-----\n\nmore text") + content = body_content(post) + types = [node["type"] for node in content] + assert "horizontal_rule" in types + + def test_horizontal_rule_between_paragraphs(self): + post = make_post() + post.from_markdown("above\n\n---\n\nbelow") + content = body_content(post) + assert len(content) == 3 + assert content[0]["type"] == "paragraph" + assert content[1]["type"] == "horizontal_rule" + assert content[2]["type"] == "paragraph" + + def test_horizontal_rule_not_in_code_block(self): + post = make_post() + post.from_markdown("```\n---\n```") + content = body_content(post) + types = [node["type"] for node in content] + assert "horizontal_rule" not in types + assert "codeBlock" in types + + +# --------------------------------------------------------------------------- +# TestExistingFeaturesUnchanged +# --------------------------------------------------------------------------- + +class TestExistingFeaturesUnchanged: + def test_bold(self): + tokens = parse_inline("**bold**") + assert len(tokens) == 1 + assert tokens[0]["content"] == "bold" + assert tokens[0]["marks"] == [{"type": "strong"}] + + def test_italic(self): + tokens = parse_inline("*italic*") + assert len(tokens) == 1 + assert tokens[0]["content"] == "italic" + assert tokens[0]["marks"] == [{"type": "em"}] + + def test_links(self): + tokens = parse_inline("[text](https://example.com)") + assert len(tokens) == 1 + assert tokens[0]["content"] == "text" + assert tokens[0]["marks"] == [{"type": "link", "attrs": {"href": "https://example.com"}}] + + def test_bullet_list(self): + post = make_post() + post.from_markdown("- a\n- b\n- c") + content = body_content(post) + assert len(content) == 1 + assert content[0]["type"] == "bullet_list" + assert len(content[0]["content"]) == 3 + + def test_code_block(self): + post = make_post() + post.from_markdown("```python\nprint('hello')\n```") + content = body_content(post) + assert len(content) == 1 + assert content[0]["type"] == "codeBlock" + + def test_blockquote(self): + post = make_post() + post.from_markdown("> quote text") + content = body_content(post) + assert len(content) == 1 + assert content[0]["type"] == "blockquote" + + def test_heading(self): + post = make_post() + post.from_markdown("## Heading Two") + content = body_content(post) + assert len(content) == 1 + assert content[0]["type"] == "heading" + assert content[0]["attrs"]["level"] == 2 + + def test_image(self): + post = make_post() + post.from_markdown("![alt](https://example.com/img.png)") + content = body_content(post) + assert len(content) == 1 + assert content[0]["type"] == "captionedImage" From fa6f5f2d93a319f8ece92fe619a2d2e794c5d9cd Mon Sep 17 00:00:00 2001 From: Paolo Mazza <59370937+ma2za@users.noreply.github.com> Date: Sat, 2 May 2026 11:43:42 +0200 Subject: [PATCH 17/31] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b6196f9..9a96686 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-substack" -version = "0.1.21" +version = "0.1.22" description = "A Python wrapper around the Substack API." authors = ["Paolo Mazza "] license = "MIT" From ed9220c1b34bdd94d66cdd7429bc0f1db47a2410 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 17:52:25 +0200 Subject: [PATCH 18/31] Bump python-multipart from 0.0.26 to 0.0.27 (#51) Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.26 to 0.0.27. - [Release notes](https://github.com/Kludex/python-multipart/releases) - [Changelog](https://github.com/Kludex/python-multipart/blob/main/CHANGELOG.md) - [Commits](https://github.com/Kludex/python-multipart/compare/0.0.26...0.0.27) --- updated-dependencies: - dependency-name: python-multipart dependency-version: 0.0.27 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6f6022c..7256877 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1408,14 +1408,14 @@ cli = ["click (>=5.0)"] [[package]] name = "python-multipart" -version = "0.0.26" +version = "0.0.27" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.10" groups = ["dev", "mcp"] files = [ - {file = "python_multipart-0.0.26-py3-none-any.whl", hash = "sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185"}, - {file = "python_multipart-0.0.26.tar.gz", hash = "sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17"}, + {file = "python_multipart-0.0.27-py3-none-any.whl", hash = "sha256:6fccfad17a27334bd0193681b369f476eda3409f17381a2d65aa7df3f7275645"}, + {file = "python_multipart-0.0.27.tar.gz", hash = "sha256:9870a6a8c5a20a5bf4f07c017bd1489006ff8836cff097b6933355ee2b49b602"}, ] [[package]] From dca3ad65c107d4700f7534c7c756cc82535c3752 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 20:59:03 +0200 Subject: [PATCH 19/31] Bump urllib3 from 2.6.3 to 2.7.0 (#52) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.7.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7256877..46d1a20 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. [[package]] name = "aiofile" @@ -883,7 +883,7 @@ files = [ [package.dependencies] attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" +jsonschema-specifications = ">=2023.3.6" referencing = ">=0.28.4" rpds-py = ">=0.25.0" @@ -1412,7 +1412,7 @@ version = "0.0.27" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.10" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "python_multipart-0.0.27-py3-none-any.whl", hash = "sha256:6fccfad17a27334bd0193681b369f476eda3409f17381a2d65aa7df3f7275645"}, {file = "python_multipart-0.0.27.tar.gz", hash = "sha256:9870a6a8c5a20a5bf4f07c017bd1489006ff8836cff097b6933355ee2b49b602"}, @@ -1905,14 +1905,14 @@ files = [ [[package]] name = "urllib3" -version = "2.6.3" +version = "2.7.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, - {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, + {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, + {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, ] [package.extras] From f2440af43636c3b336d62886bbd1b80f6107e69b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 21:02:26 +0200 Subject: [PATCH 20/31] Bump idna from 3.11 to 3.15 (#53) Bumps [idna](https://github.com/kjd/idna) from 3.11 to 3.15. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.md) - [Commits](https://github.com/kjd/idna/compare/v3.11...v3.15) --- updated-dependencies: - dependency-name: idna dependency-version: '3.15' dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 46d1a20..5554754 100644 --- a/poetry.lock +++ b/poetry.lock @@ -738,18 +738,18 @@ files = [ [[package]] name = "idna" -version = "3.11" +version = "3.15" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" groups = ["main", "mcp"] files = [ - {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, - {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, + {file = "idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8"}, + {file = "idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc"}, ] [package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "importlib-metadata" From c1dc6caf02d4e6ee65fb96221356ded2b44b433e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 21:33:09 +0200 Subject: [PATCH 21/31] Bump authlib from 1.6.11 to 1.6.12 (#54) Bumps [authlib](https://github.com/authlib/authlib) from 1.6.11 to 1.6.12. - [Release notes](https://github.com/authlib/authlib/releases) - [Changelog](https://github.com/authlib/authlib/blob/1.6.12/docs/changelog.rst) - [Commits](https://github.com/authlib/authlib/compare/v1.6.11...1.6.12) --- updated-dependencies: - dependency-name: authlib dependency-version: 1.6.12 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5554754..025d557 100644 --- a/poetry.lock +++ b/poetry.lock @@ -61,14 +61,14 @@ files = [ [[package]] name = "authlib" -version = "1.6.11" +version = "1.6.12" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ - {file = "authlib-1.6.11-py2.py3-none-any.whl", hash = "sha256:c8687a9a26451c51a34a06fa17bb97cb15bba46a6a626755e2d7f50da8bff3e3"}, - {file = "authlib-1.6.11.tar.gz", hash = "sha256:64db35b9b01aeccb4715a6c9a6613a06f2bd7be2ab9d2eb89edd1dfc7580a38f"}, + {file = "authlib-1.6.12-py2.py3-none-any.whl", hash = "sha256:e9229ad7fde610b139dd12f5edbe97eab9ee78bfb85691247e767727850b99ab"}, + {file = "authlib-1.6.12.tar.gz", hash = "sha256:0656d8482f28fc8221929d5f35b2bde5d13e10555ebc06b4561b0d622e83b1bd"}, ] [package.dependencies] @@ -176,7 +176,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, @@ -441,7 +441,7 @@ version = "46.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4"}, {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325"}, @@ -1154,7 +1154,7 @@ version = "3.0" description = "C parser in Python" optional = false python-versions = ">=3.10" -groups = ["mcp"] +groups = ["dev", "mcp"] markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, @@ -1870,11 +1870,12 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +markers = {dev = "python_version == \"3.10\""} [[package]] name = "typing-inspection" From c49e7c475cb3b0bfa8b915e870249021e59afdb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:29:16 +0200 Subject: [PATCH 22/31] Bump starlette from 1.0.0 to 1.0.1 (#55) Bumps [starlette](https://github.com/Kludex/starlette) from 1.0.0 to 1.0.1. - [Release notes](https://github.com/Kludex/starlette/releases) - [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md) - [Commits](https://github.com/Kludex/starlette/compare/1.0.0...1.0.1) --- updated-dependencies: - dependency-name: starlette dependency-version: 1.0.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/poetry.lock b/poetry.lock index 025d557..44f58ea 100644 --- a/poetry.lock +++ b/poetry.lock @@ -33,7 +33,7 @@ version = "4.13.0" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.10" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"}, {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"}, @@ -65,7 +65,7 @@ version = "1.6.12" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "authlib-1.6.12-py2.py3-none-any.whl", hash = "sha256:e9229ad7fde610b139dd12f5edbe97eab9ee78bfb85691247e767727850b99ab"}, {file = "authlib-1.6.12.tar.gz", hash = "sha256:0656d8482f28fc8221929d5f35b2bde5d13e10555ebc06b4561b0d622e83b1bd"}, @@ -176,7 +176,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, @@ -441,7 +441,7 @@ version = "46.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4"}, {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325"}, @@ -609,11 +609,12 @@ version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] +markers = {dev = "python_version == \"3.10\""} [package.dependencies] typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} @@ -742,7 +743,7 @@ version = "3.15" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" -groups = ["main", "mcp"] +groups = ["main", "dev", "mcp"] files = [ {file = "idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8"}, {file = "idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc"}, @@ -1154,7 +1155,7 @@ version = "3.0" description = "C parser in Python" optional = false python-versions = ">=3.10" -groups = ["dev", "mcp"] +groups = ["mcp"] markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, @@ -1789,14 +1790,14 @@ uvicorn = ["uvicorn (>=0.34.0)"] [[package]] name = "starlette" -version = "1.0.0" +version = "1.0.1" description = "The little ASGI library that shines." optional = false python-versions = ">=3.10" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ - {file = "starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b"}, - {file = "starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149"}, + {file = "starlette-1.0.1-py3-none-any.whl", hash = "sha256:7c0e69b2ee1c848bd54669d908500117a3ee13de603a21427e5c6fc1adf98dcd"}, + {file = "starlette-1.0.1.tar.gz", hash = "sha256:512399c5f1de7fac99c88572212ded9ddeddef2fb32afa82d724000e88b38f4f"}, ] [package.dependencies] @@ -1875,7 +1876,7 @@ files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {dev = "python_version == \"3.10\""} +markers = {dev = "python_version < \"3.13\""} [[package]] name = "typing-inspection" From 567c8a7c952187594368e19ad7c2c122cac6b5f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 12:10:37 +0200 Subject: [PATCH 23/31] Bump starlette from 1.0.1 to 1.3.1 (#57) Bumps [starlette](https://github.com/Kludex/starlette) from 1.0.1 to 1.3.1. - [Release notes](https://github.com/Kludex/starlette/releases) - [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md) - [Commits](https://github.com/Kludex/starlette/compare/1.0.1...1.3.1) --- updated-dependencies: - dependency-name: starlette dependency-version: 1.3.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 44f58ea..ca546e0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1790,14 +1790,14 @@ uvicorn = ["uvicorn (>=0.34.0)"] [[package]] name = "starlette" -version = "1.0.1" +version = "1.3.1" description = "The little ASGI library that shines." optional = false python-versions = ">=3.10" groups = ["dev", "mcp"] files = [ - {file = "starlette-1.0.1-py3-none-any.whl", hash = "sha256:7c0e69b2ee1c848bd54669d908500117a3ee13de603a21427e5c6fc1adf98dcd"}, - {file = "starlette-1.0.1.tar.gz", hash = "sha256:512399c5f1de7fac99c88572212ded9ddeddef2fb32afa82d724000e88b38f4f"}, + {file = "starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6"}, + {file = "starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0"}, ] [package.dependencies] @@ -1805,7 +1805,7 @@ anyio = ">=3.6.2,<5" typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\""} [package.extras] -full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] +full = ["httpx (>=0.27.0,<0.29.0)", "httpx2 (>=2.0.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] [[package]] name = "tomli" From 1c5174b93f4539686ec97e1dac92f208d30c4305 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 12:21:48 +0200 Subject: [PATCH 24/31] Bump cryptography from 46.0.7 to 48.0.1 (#58) Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.7 to 48.0.1. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.7...48.0.1) --- updated-dependencies: - dependency-name: cryptography dependency-version: 48.0.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 130 ++++++++++++++++++++++++---------------------------- 1 file changed, 61 insertions(+), 69 deletions(-) diff --git a/poetry.lock b/poetry.lock index ca546e0..9a475ff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -33,7 +33,7 @@ version = "4.13.0" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.10" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"}, {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"}, @@ -176,7 +176,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, @@ -437,76 +437,69 @@ files = [ [[package]] name = "cryptography" -version = "46.0.7" +version = "48.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false -python-versions = "!=3.9.0,!=3.9.1,>=3.8" -groups = ["mcp"] -files = [ - {file = "cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4"}, - {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325"}, - {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308"}, - {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77"}, - {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1"}, - {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef"}, - {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de"}, - {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83"}, - {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb"}, - {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b"}, - {file = "cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85"}, - {file = "cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e"}, - {file = "cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457"}, - {file = "cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b"}, - {file = "cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842"}, - {file = "cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c"}, - {file = "cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902"}, - {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d"}, - {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022"}, - {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce"}, - {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f"}, - {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99"}, - {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1"}, - {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2"}, - {file = "cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e"}, - {file = "cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee"}, - {file = "cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298"}, - {file = "cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb"}, - {file = "cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4"}, - {file = "cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7"}, - {file = "cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832"}, - {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163"}, - {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2"}, - {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067"}, - {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0"}, - {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba"}, - {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006"}, - {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0"}, - {file = "cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85"}, - {file = "cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e"}, - {file = "cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246"}, - {file = "cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3"}, - {file = "cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f"}, - {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15"}, - {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455"}, - {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65"}, - {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968"}, - {file = "cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4"}, - {file = "cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5"}, +python-versions = "!=3.9.0,!=3.9.1,>=3.9" +groups = ["dev", "mcp"] +files = [ + {file = "cryptography-48.0.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3e4a1a3232eef2e6c732827d5722db29a0cc8b27af2a4d865b094cf954be9ca1"}, + {file = "cryptography-48.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:32143b24adb918f078134e1e230f1eb8cc04886b92c28b5f0041aaf3e5699225"}, + {file = "cryptography-48.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0d27a5696721ef7a672b8c810f6aded391058e0b9486e63e6d93baf765da691"}, + {file = "cryptography-48.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb86ce1af36fe65041b6db9a8bb064ee621a7e5fded0f80d475ec243477cd242"}, + {file = "cryptography-48.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b024e784ad6c077ee0147b35ea9cbfc1e34e1fd4c1dcca214c2794d73a12df08"}, + {file = "cryptography-48.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3752f2dbc8f07a30aad2932c986cea495b03bb554887828225da104f732852b6"}, + {file = "cryptography-48.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:bd81490cd5801d755cf97bb68ac191f14b708470b1c7cf4580f669b9c9264cd8"}, + {file = "cryptography-48.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:66fd0771e7b9c6dcd44cf1120690d2338d16d72795cf40cae2786a39eba65429"}, + {file = "cryptography-48.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:3fd2ca57062b241c856670b073487d2e86c4637937ca5601e48f97bf8e11fc8f"}, + {file = "cryptography-48.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:0ee6ea481db1ab889cba043ec1eda17bb9c1ea79db6722f779c3667f9f70322f"}, + {file = "cryptography-48.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2ceef93cb096aa3c4cc4b5c94ca6131f9196d28c64d6111533402a9b2054d41"}, + {file = "cryptography-48.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bd3f92d76217892b15df84ca256c2c113d386fdda7a7d8691aeeced976507c6"}, + {file = "cryptography-48.0.1-cp311-abi3-win32.whl", hash = "sha256:b9a32b876490d66c8bcc9963ef220199569748434ab01a9d6aaeabf88e7f5158"}, + {file = "cryptography-48.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:39489bfca54c7a1f6b297efcd8bc608ab92d16c4ca631b0cad4da46724588b24"}, + {file = "cryptography-48.0.1-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:f817adc181390bd54f2f700107a7419040fb7c1bdf2fc26f36551a06a68c3345"}, + {file = "cryptography-48.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d5d30989c6917b478b5817902e85fddaea2261efa8648383d965381ccb9e1ac4"}, + {file = "cryptography-48.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:df637c05205ea7c1d7fbcbe54bbfea648a52951155f997af13d895d0ecc96991"}, + {file = "cryptography-48.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:869c3b8a53bfe27147832df48b32adadf558249d50e76cb3769d40e986b13265"}, + {file = "cryptography-48.0.1-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:e361afba8918070d376df76f408a4f67fec0ee9cff81a99e48fe9a233ef59e17"}, + {file = "cryptography-48.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d069066deead00ac7f090be101be875a06855908f7ec004c27b8fefb4acfb411"}, + {file = "cryptography-48.0.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:09f73a725d582cef64b91281a322cd798d14a33b2b6f2b7ad9531dc336d84c02"}, + {file = "cryptography-48.0.1-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:15254441469dd6bf027039453288e2072124f8b6603563f5d759e1c9b69273fa"}, + {file = "cryptography-48.0.1-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:8ace4507d1e6533c125f4fac754f8bb8b6a74c08e92179dabd7e16571a3efbf3"}, + {file = "cryptography-48.0.1-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b4e391975f038e66432328639620a4aff2d307513b004f1ca06d6225bced815c"}, + {file = "cryptography-48.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42fcd8e26fe555d9b3577a135f5091fefa0aa4e99129c23fb56787a1bd4ada72"}, + {file = "cryptography-48.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1400da5e32a43253392277eac7490a60e497d810a63dd5608d71bbd7af507c9"}, + {file = "cryptography-48.0.1-cp314-cp314t-win32.whl", hash = "sha256:0df56b056bc17c1b7d6821dfa65216e62bd232d8ab05eb3db44e71d235651471"}, + {file = "cryptography-48.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:9de21387aa95e2a895823d0745b430bed4f33503ba9ab5e0b5311f33e37d66d2"}, + {file = "cryptography-48.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:4fdc69f8e4316bcf0c8c8ec1f26f285d12e8142d88d96c876a59a03be3f6ae67"}, + {file = "cryptography-48.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48fe40804d4caa2288f24e70ca8c64c42dd826da0ad7e4f1b41b2128d679e6c8"}, + {file = "cryptography-48.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:86be3b1b0b6bf09482fb50a979c508d2950ed95f5621ec77f4e385962006b83a"}, + {file = "cryptography-48.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4ab0a343c807bbcd90c971cd1ecf072937cd01847a9e002bef88fb47ac6be577"}, + {file = "cryptography-48.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9621de99d2da096006b629979efd8ae7eb2d8b822488d0c89ee4000c306c59b1"}, + {file = "cryptography-48.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:88c852a0ae366e262e5a1744b685e6a433dc8788dd2a277e418bf4904203609d"}, + {file = "cryptography-48.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:43c5835e2cb98c8733d86f57d6fc879b613f5c3478607281c3e36daffc6dd8a6"}, + {file = "cryptography-48.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:fe0180af5bf9236518a087e35bf2d9a347d5f5f51e63c579d683ddff424e3d46"}, + {file = "cryptography-48.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:b7a2d1a937a738a881737cec135a38bb61470589b17515b9f73f571d0ae10401"}, + {file = "cryptography-48.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b74ca3b8e5ecdd833bf6a002ca41b4793bb27fb8f1c06ffaf2643c9e9140e31b"}, + {file = "cryptography-48.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c37f2461406063b417837f5f3daab668652acd82423efcd7f0a9f04be972de1"}, + {file = "cryptography-48.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86fe77abb1bd87afb251d4d02ada7ecf53a32cee9b67d976abb2e45a13297475"}, + {file = "cryptography-48.0.1-cp39-abi3-win32.whl", hash = "sha256:6b2c0c3e6ccf3ade7750f836ef3ee36eea250cc467d45c256895573ac08cc6f1"}, + {file = "cryptography-48.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:9a49ca6c81417f6a5edb50375a60cccdd70fa0a91a5211829dbea74eba94d2ac"}, + {file = "cryptography-48.0.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:08a597acce1ff37f347400087776599e2348a3a8bc53b44120e463cd274efe4a"}, + {file = "cryptography-48.0.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:735824ec41b7f74a7c45fb1591349333e4c696cb6c044e5f46356e560143e4cd"}, + {file = "cryptography-48.0.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:92a46e1d638daa264ba2971c0b0489c9409787943efae4d60ffda3d091ef832c"}, + {file = "cryptography-48.0.1-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:7e234ac052af99f2700826a5c29ea99d9c1b1f80341cde62d11c8154dc8e0bd9"}, + {file = "cryptography-48.0.1-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:33842cf0888951cef5bc7ac724ab844a42044c1727b967b7f8997289a0464f92"}, + {file = "cryptography-48.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6184ca7b174f28d7c703f1290d4b297217c45355f77a98f67e9b7f14549ac54a"}, + {file = "cryptography-48.0.1.tar.gz", hash = "sha256:266f4ee051abb2f725b74ef8072b521ce1feacf685a3364fa6a6b45548db791a"}, ] [package.dependencies] -cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} +cffi = {version = ">=2.0.0", markers = "platform_python_implementation != \"PyPy\""} typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] -docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox[uv] (>=2024.4.15)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] -sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.7)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] -test-randomorder = ["pytest-randomly"] [[package]] name = "cyclopts" @@ -609,12 +602,11 @@ version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] -markers = {dev = "python_version == \"3.10\""} [package.dependencies] typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} @@ -743,7 +735,7 @@ version = "3.15" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "mcp"] +groups = ["main", "mcp"] files = [ {file = "idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8"}, {file = "idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc"}, @@ -1155,7 +1147,7 @@ version = "3.0" description = "C parser in Python" optional = false python-versions = ">=3.10" -groups = ["mcp"] +groups = ["dev", "mcp"] markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, @@ -1794,7 +1786,7 @@ version = "1.3.1" description = "The little ASGI library that shines." optional = false python-versions = ">=3.10" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6"}, {file = "starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0"}, @@ -1876,7 +1868,7 @@ files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {dev = "python_version < \"3.13\""} +markers = {dev = "python_version == \"3.10\""} [[package]] name = "typing-inspection" From 3baa4b94cc051bee46a34f39b15f486b1d34b59b Mon Sep 17 00:00:00 2001 From: ma2za Date: Thu, 18 Jun 2026 12:23:09 +0200 Subject: [PATCH 25/31] fix --- substack/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substack/__init__.py b/substack/__init__.py index 4963dbe..0cfe23b 100644 --- a/substack/__init__.py +++ b/substack/__init__.py @@ -3,7 +3,7 @@ __author__ = "Paolo Mazza" __email__ = "mazzapaolo2019@gmail.com" __license__ = "MIT License" -__version__ = "0.1.21" +__version__ = "0.1.22" __url__ = "https://github.com/ma2za/python-substack" __download_url__ = "https://pypi.python.org/pypi/python-substack" __description__ = "A Python wrapper around the Substack API" From 6aa9bd78469c412e037979258c5a47103681c420 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 12:25:58 +0200 Subject: [PATCH 26/31] Bump python-multipart from 0.0.27 to 0.0.31 (#59) Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.27 to 0.0.31. - [Release notes](https://github.com/Kludex/python-multipart/releases) - [Changelog](https://github.com/Kludex/python-multipart/blob/main/CHANGELOG.md) - [Commits](https://github.com/Kludex/python-multipart/compare/0.0.27...0.0.31) --- updated-dependencies: - dependency-name: python-multipart dependency-version: 0.0.31 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9a475ff..c4d2eae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -176,7 +176,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, @@ -441,7 +441,7 @@ version = "48.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "cryptography-48.0.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3e4a1a3232eef2e6c732827d5722db29a0cc8b27af2a4d865b094cf954be9ca1"}, {file = "cryptography-48.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:32143b24adb918f078134e1e230f1eb8cc04886b92c28b5f0041aaf3e5699225"}, @@ -1147,7 +1147,7 @@ version = "3.0" description = "C parser in Python" optional = false python-versions = ">=3.10" -groups = ["dev", "mcp"] +groups = ["mcp"] markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, @@ -1401,14 +1401,14 @@ cli = ["click (>=5.0)"] [[package]] name = "python-multipart" -version = "0.0.27" +version = "0.0.31" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.10" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ - {file = "python_multipart-0.0.27-py3-none-any.whl", hash = "sha256:6fccfad17a27334bd0193681b369f476eda3409f17381a2d65aa7df3f7275645"}, - {file = "python_multipart-0.0.27.tar.gz", hash = "sha256:9870a6a8c5a20a5bf4f07c017bd1489006ff8836cff097b6933355ee2b49b602"}, + {file = "python_multipart-0.0.31-py3-none-any.whl", hash = "sha256:8408153d68a9773291fc1da39a8b85a50044bddbabd2dd72e9229776b7b15e28"}, + {file = "python_multipart-0.0.31.tar.gz", hash = "sha256:fc631183bb13e56db3158a4909908dfb2e23565286744e798241e63750e5d680"}, ] [[package]] @@ -1863,12 +1863,11 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {dev = "python_version == \"3.10\""} [[package]] name = "typing-inspection" From 21a8e9c3ae28aacdd44ac2f2e29fa6543b882746 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 18:19:41 +0300 Subject: [PATCH 27/31] Bump pyjwt from 2.12.1 to 2.13.0 (#60) Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.12.1 to 2.13.0. - [Release notes](https://github.com/jpadilla/pyjwt/releases) - [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst) - [Commits](https://github.com/jpadilla/pyjwt/compare/2.12.1...2.13.0) --- updated-dependencies: - dependency-name: pyjwt dependency-version: 2.13.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/poetry.lock b/poetry.lock index c4d2eae..b11fbd7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1352,14 +1352,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyjwt" -version = "2.12.1" +version = "2.13.0" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ - {file = "pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c"}, - {file = "pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b"}, + {file = "pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728"}, + {file = "pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423"}, ] [package.dependencies] @@ -1368,9 +1368,6 @@ typing_extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==7.10.7)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=8.4.2,<9.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==7.10.7)", "pytest (>=8.4.2,<9.0.0)"] [[package]] name = "pyperclip" @@ -1405,7 +1402,7 @@ version = "0.0.31" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.10" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "python_multipart-0.0.31-py3-none-any.whl", hash = "sha256:8408153d68a9773291fc1da39a8b85a50044bddbabd2dd72e9229776b7b15e28"}, {file = "python_multipart-0.0.31.tar.gz", hash = "sha256:fc631183bb13e56db3158a4909908dfb2e23565286744e798241e63750e5d680"}, @@ -1863,11 +1860,12 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +markers = {dev = "python_version == \"3.10\""} [[package]] name = "typing-inspection" From 22110694238aeafd79f475e18e207853a87388de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2026 14:54:03 +0300 Subject: [PATCH 28/31] Bump pydantic-settings from 2.13.1 to 2.14.2 (#62) Bumps [pydantic-settings](https://github.com/pydantic/pydantic-settings) from 2.13.1 to 2.14.2. - [Release notes](https://github.com/pydantic/pydantic-settings/releases) - [Commits](https://github.com/pydantic/pydantic-settings/compare/v2.13.1...v2.14.2) --- updated-dependencies: - dependency-name: pydantic-settings dependency-version: 2.14.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/poetry.lock b/poetry.lock index b11fbd7..787b983 100644 --- a/poetry.lock +++ b/poetry.lock @@ -21,7 +21,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -1160,7 +1160,7 @@ version = "2.12.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"}, {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"}, @@ -1183,7 +1183,7 @@ version = "2.41.5" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"}, {file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"}, @@ -1313,14 +1313,14 @@ typing-extensions = ">=4.14.1" [[package]] name = "pydantic-settings" -version = "2.13.1" +version = "2.14.2" description = "Settings management using Pydantic" optional = false python-versions = ">=3.10" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ - {file = "pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237"}, - {file = "pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025"}, + {file = "pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440"}, + {file = "pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f"}, ] [package.dependencies] @@ -1329,7 +1329,7 @@ python-dotenv = ">=0.21.0" typing-inspection = ">=0.4.0" [package.extras] -aws-secrets-manager = ["boto3 (>=1.35.0)", "boto3-stubs[secretsmanager]"] +aws-secrets-manager = ["boto3 (>=1.35.0)", "types-boto3[secretsmanager]"] azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"] toml = ["tomli (>=2.0.1)"] @@ -1356,7 +1356,7 @@ version = "2.13.0" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.9" -groups = ["dev", "mcp"] +groups = ["mcp"] files = [ {file = "pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728"}, {file = "pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423"}, @@ -1387,7 +1387,7 @@ version = "1.2.2" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.10" -groups = ["main", "mcp"] +groups = ["main", "dev", "mcp"] files = [ {file = "python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a"}, {file = "python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3"}, @@ -1865,7 +1865,6 @@ files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {dev = "python_version == \"3.10\""} [[package]] name = "typing-inspection" @@ -1873,7 +1872,7 @@ version = "0.4.2" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" -groups = ["mcp"] +groups = ["dev", "mcp"] files = [ {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, From fb7c3b6fcc3c66531c6818f093bed7662dd68ed3 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Sat, 27 Jun 2026 19:18:51 +1000 Subject: [PATCH 29/31] Add Markdown footnote support to from_markdown (#56) * Add Markdown footnote support to from_markdown Parse standard Markdown footnotes (`text[^label]` references and `[^label]: definition` lines) into Substack's footnoteAnchor inline nodes and footnote blocks. Footnotes are numbered by order of first reference and labels may be numeric or named. Also adds Post.footnote_anchor() and Post.footnote() helpers for building footnotes manually, plus tests. * Keep footnote handling out of code blocks and inline code Footnote definition extraction now skips fenced code blocks, and anchor injection skips codeBlock nodes and text marked as inline code. This fixes footnote-like text inside code being removed or rewritten. Adds regression tests for fenced and inline code cases. * Support multi-paragraph footnote definitions Footnote definitions can now contain multiple paragraphs (a blank line followed by an indented block); previously a second paragraph leaked into the post body and only the first was kept. Extraction preserves paragraph breaks and Post.footnote() splits blank-line-separated content into multiple paragraph nodes (verified accepted/rendered by Substack). Adds regression tests. --- README.md | 15 ++ substack/post.py | 202 +++++++++++++++++++++++++ tests/substack/test_footnotes.py | 246 +++++++++++++++++++++++++++++++ 3 files changed, 463 insertions(+) create mode 100644 tests/substack/test_footnotes.py diff --git a/README.md b/README.md index c4f57fa..cfe0611 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,21 @@ This is a paragraph with **bold** and *italic* text. """ post.from_markdown(markdown_content, api=api) +# Markdown footnotes are supported too. References become inline anchors and +# definitions become footnote blocks, numbered by order of first appearance. +# Labels can be numbers or names (e.g. [^1] or [^source]). +footnote_markdown = """ +A claim that needs support.[^1] Another, with a named label.[^source] + +[^1]: The supporting detail, with a [link](https://example.com). +[^source]: Author, *Title* (2025). +""" +post.from_markdown(footnote_markdown, api=api) + +# Or build footnotes manually: +post.paragraph(content=[{"content": "Some claim."}]).footnote_anchor(1) +post.footnote(1, "The note text, with **formatting** allowed.") + draft = api.post_draft(post.get_draft()) # set section (can only be done after first posting the draft) diff --git a/substack/post.py b/substack/post.py index 8a9d55d..690b402 100644 --- a/substack/post.py +++ b/substack/post.py @@ -12,6 +12,10 @@ from substack.exceptions import SectionNotExistsException +# Markdown footnotes: ``text.[^label]`` references and ``[^label]: definition`` lines. +FOOTNOTE_REFERENCE_PATTERN = re.compile(r"\[\^([^\]]+)\]") +FOOTNOTE_DEFINITION_PATTERN = re.compile(r"^\[\^([^\]]+)\]:\s?(.*)$") + def tokens_to_text_nodes(tokens: List[Dict]) -> List[Dict]: """Convert parse_inline() tokens to ProseMirror text nodes. @@ -543,6 +547,186 @@ def code_block(self, content, attrs=None): return self + def footnote_anchor(self, number: int): + """ + + Add an inline footnote reference (the superscript marker) to the last block. + + Args: + number: The footnote number this anchor points to. + + Returns: + Self for method chaining. + + """ + content = self.draft_body["content"][-1].get("content", []) + content += [{"type": "footnoteAnchor", "attrs": {"number": number}}] + self.draft_body["content"][-1]["content"] = content + return self + + def footnote(self, number: int, content=None): + """ + + Append a footnote block (the note shown at the foot of the post). + + Args: + number: The footnote number, matching a footnote_anchor. + content: Text string or list of inline token dicts. A plain string is + parsed for inline Markdown and may contain blank-line-separated + paragraphs; a parse_inline() token list or a list of ready text + nodes is also accepted (single paragraph). + + Returns: + Self for method chaining. + + """ + paragraphs: List[Dict] = [] + if isinstance(content, str): + # Blank lines separate paragraphs within the footnote. + for chunk in re.split(r"\n\s*\n", content): + chunk = chunk.strip() + if chunk: + paragraphs.append( + {"type": "paragraph", "content": tokens_to_text_nodes(parse_inline(chunk))} + ) + elif isinstance(content, list): + # Accept either parse_inline tokens ({"content": ...}) or text nodes. + if content and content[0].get("type") == "text": + text_nodes = content + else: + text_nodes = tokens_to_text_nodes(content) + paragraphs.append({"type": "paragraph", "content": text_nodes}) + + if not paragraphs: + paragraphs = [{"type": "paragraph", "content": []}] + + node: Dict = { + "type": "footnote", + "attrs": {"number": number}, + "content": paragraphs, + } + self.draft_body["content"] = self.draft_body.get("content", []) + [node] + return self + + @staticmethod + def _extract_footnote_definitions(markdown_content: str): + """ + + Pull ``[^label]: definition`` lines out of the Markdown. + + Definitions may wrap onto indented continuation lines and may contain + multiple paragraphs (blank line followed by an indented block). Returns + the body with definitions removed plus a {label: definition_text} mapping, + where paragraphs are separated by a blank line. + + """ + lines = markdown_content.split("\n") + body_lines: List[str] = [] + definitions: Dict[str, str] = {} + in_code_fence = False + i = 0 + while i < len(lines): + # Track fenced code blocks so footnote-like lines inside them are + # left untouched. + if lines[i].lstrip().startswith("```"): + in_code_fence = not in_code_fence + body_lines.append(lines[i]) + i += 1 + continue + match = None if in_code_fence else FOOTNOTE_DEFINITION_PATTERN.match(lines[i]) + if match: + label, first = match.group(1), match.group(2) + paragraphs: List[str] = [] + current = [first.strip()] if first.strip() else [] + i += 1 + while i < len(lines): + line = lines[i] + if line.strip() == "": + # A blank line stays in the footnote only if the next + # non-empty line is indented (a further paragraph). + nxt = i + 1 + if ( + nxt < len(lines) + and lines[nxt].strip() + and lines[nxt][:1] in (" ", "\t") + ): + if current: + paragraphs.append(" ".join(current)) + current = [] + i += 1 + continue + break + if line[:1] in (" ", "\t"): + current.append(line.strip()) + i += 1 + else: + break + if current: + paragraphs.append(" ".join(current)) + definitions[label] = "\n\n".join(paragraphs) + else: + body_lines.append(lines[i]) + i += 1 + return "\n".join(body_lines), definitions + + @staticmethod + def _number_footnotes(markdown_content: str, definitions: Dict[str, str]): + """Number footnotes by order of first inline reference in the body.""" + order: List[str] = [] + for match in FOOTNOTE_REFERENCE_PATTERN.finditer(markdown_content): + label = match.group(1) + if label in definitions and label not in order: + order.append(label) + # Defined-but-unreferenced footnotes go last, in definition order. + for label in definitions: + if label not in order: + order.append(label) + return {label: index + 1 for index, label in enumerate(order)} + + def _inject_footnote_anchors(self, node: Dict, numbers_by_label: Dict[str, int]): + """Recursively replace ``[^label]`` in text nodes with footnoteAnchor nodes.""" + # Never rewrite the contents of a code block. + if node.get("type") == "codeBlock": + return + content = node.get("content") + if not isinstance(content, list): + return + new_content: List[Dict] = [] + for child in content: + text = child.get("text", "") + has_code_mark = any( + mark.get("type") == "code" for mark in (child.get("marks") or []) + ) + if ( + child.get("type") == "text" + and not has_code_mark + and FOOTNOTE_REFERENCE_PATTERN.search(text) + ): + marks = child.get("marks") + last = 0 + for match in FOOTNOTE_REFERENCE_PATTERN.finditer(text): + label = match.group(1) + if label not in numbers_by_label: + continue # Unknown label: leave the literal text in place. + if match.start() > last: + segment = {"type": "text", "text": text[last:match.start()]} + if marks: + segment["marks"] = marks + new_content.append(segment) + new_content.append( + {"type": "footnoteAnchor", "attrs": {"number": numbers_by_label[label]}} + ) + last = match.end() + if last < len(text): + segment = {"type": "text", "text": text[last:]} + if marks: + segment["marks"] = marks + new_content.append(segment) + else: + self._inject_footnote_anchors(child, numbers_by_label) + new_content.append(child) + node["content"] = new_content + def from_markdown(self, markdown_content: str, api=None): """ Parse Markdown content and add it to the post. @@ -559,6 +743,10 @@ def from_markdown(self, markdown_content: str, api=None): - Ordered lists: Lines starting with '1.', '2.', etc. - Horizontal rules: Lines with ---, ***, or ___ - Inline formatting: **bold**, *italic*, ***bold+italic***, `code`, ~~strikethrough~~ + - Footnotes: ``text.[^label]`` references plus ``[^label]: definition`` + lines. References become inline anchors and definitions become + footnote blocks, numbered by order of first appearance. Labels may be + numbers or names (e.g. ``[^1]`` or ``[^agi-book]``). Args: markdown_content: Markdown string to parse and add to the post. @@ -572,6 +760,13 @@ def from_markdown(self, markdown_content: str, api=None): >>> post = Post("Title", "Subtitle", user_id) >>> post.from_markdown("# Heading\\n\\nThis is **bold** text with [a link](https://example.com).") """ + # Footnotes: extract ``[^label]: ...`` definitions and number them by + # order of first reference before parsing the rest of the body. + markdown_content, footnote_definitions = self._extract_footnote_definitions( + markdown_content + ) + footnote_numbers = self._number_footnotes(markdown_content, footnote_definitions) + lines = markdown_content.split("\n") blocks = [] current_block: List[str] = [] @@ -844,4 +1039,11 @@ def flush_ordered(): tokens = parse_inline(text_content) self.add({"type": "paragraph", "content": tokens}) + # Footnotes: turn ``[^label]`` references into inline anchors, then append + # the footnote blocks in numbered order. + if footnote_numbers: + self._inject_footnote_anchors(self.draft_body, footnote_numbers) + for label, number in sorted(footnote_numbers.items(), key=lambda item: item[1]): + self.footnote(number, footnote_definitions[label]) + return self diff --git a/tests/substack/test_footnotes.py b/tests/substack/test_footnotes.py new file mode 100644 index 0000000..172d152 --- /dev/null +++ b/tests/substack/test_footnotes.py @@ -0,0 +1,246 @@ +"""Tests for Markdown footnote support in post.py.""" + +from substack.post import Post + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def make_post(): + """Create a fresh Post instance for testing.""" + return Post(title="Test", subtitle="Sub", user_id=1) + + +def body_content(post): + """Return the content list from the post's draft body.""" + return post.draft_body["content"] + + +def find_nodes(node, node_type, acc=None): + """Recursively collect every node of a given type from a doc tree.""" + if acc is None: + acc = [] + if isinstance(node, dict): + if node.get("type") == node_type: + acc.append(node) + for value in node.values(): + find_nodes(value, node_type, acc) + elif isinstance(node, list): + for value in node: + find_nodes(value, node_type, acc) + return acc + + +def anchors(post): + return find_nodes(post.draft_body, "footnoteAnchor") + + +def footnotes(post): + return find_nodes(post.draft_body, "footnote") + + +# --------------------------------------------------------------------------- +# TestFootnoteHelpers +# --------------------------------------------------------------------------- + +class TestFootnoteHelpers: + def test_footnote_anchor_added_inline(self): + post = make_post() + post.paragraph(content=[{"content": "See here."}]) + post.footnote_anchor(1) + para = body_content(post)[0] + assert para["content"][-1] == {"type": "footnoteAnchor", "attrs": {"number": 1}} + + def test_footnote_block_from_string(self): + post = make_post() + post.footnote(1, "A simple note.") + block = body_content(post)[-1] + assert block["type"] == "footnote" + assert block["attrs"] == {"number": 1} + assert block["content"][0]["type"] == "paragraph" + assert block["content"][0]["content"][0]["text"] == "A simple note." + + def test_footnote_block_parses_inline_markdown(self): + post = make_post() + post.footnote(2, "See [the source](https://example.com).") + block = footnotes(post)[0] + text_nodes = block["content"][0]["content"] + link_node = next(n for n in text_nodes if n.get("marks")) + assert link_node["text"] == "the source" + assert link_node["marks"] == [{"type": "link", "attrs": {"href": "https://example.com"}}] + + +# --------------------------------------------------------------------------- +# TestFromMarkdownFootnotes +# --------------------------------------------------------------------------- + +class TestFromMarkdownFootnotes: + def test_basic_reference_and_definition(self): + post = make_post() + post.from_markdown("A claim.[^1]\n\n[^1]: The supporting detail.") + assert len(anchors(post)) == 1 + assert anchors(post)[0]["attrs"]["number"] == 1 + blocks = footnotes(post) + assert len(blocks) == 1 + assert blocks[0]["attrs"]["number"] == 1 + assert blocks[0]["content"][0]["content"][0]["text"] == "The supporting detail." + + def test_definition_removed_from_body(self): + post = make_post() + post.from_markdown("A claim.[^1]\n\n[^1]: The note.") + # The definition line must not leak into a paragraph. + paragraphs = find_nodes(post.draft_body, "paragraph") + body_text = " ".join( + n.get("text", "") + for p in paragraphs + for n in p.get("content", []) + ) + assert "[^1]:" not in body_text + + def test_anchor_injected_mid_sentence(self): + post = make_post() + post.from_markdown("Before[^1] and after.\n\n[^1]: Note.") + para = find_nodes(post.draft_body, "paragraph")[0] + types = [c["type"] for c in para["content"]] + assert types == ["text", "footnoteAnchor", "text"] + assert para["content"][0]["text"] == "Before" + assert para["content"][2]["text"] == " and after." + + def test_named_labels_numbered_by_first_appearance(self): + post = make_post() + md = ( + "First[^book] then second[^study].\n\n" + "[^study]: Second definition.\n" + "[^book]: First definition.\n" + ) + post.from_markdown(md) + nums = [a["attrs"]["number"] for a in anchors(post)] + assert nums == [1, 2] # order of reference, not of definition + blocks = sorted(footnotes(post), key=lambda b: b["attrs"]["number"]) + assert blocks[0]["content"][0]["content"][0]["text"] == "First definition." + assert blocks[1]["content"][0]["content"][0]["text"] == "Second definition." + + def test_repeated_reference_reuses_number(self): + post = make_post() + post.from_markdown("One[^a] two[^a].\n\n[^a]: Note.") + nums = [a["attrs"]["number"] for a in anchors(post)] + assert nums == [1, 1] + assert len(footnotes(post)) == 1 + + def test_link_inside_definition_preserved(self): + post = make_post() + post.from_markdown("Claim.[^1]\n\n[^1]: See [docs](https://example.com).") + block = footnotes(post)[0] + link_node = next( + n for n in block["content"][0]["content"] if n.get("marks") + ) + assert link_node["marks"][0]["attrs"]["href"] == "https://example.com" + + def test_multiline_definition(self): + post = make_post() + md = "Claim.[^1]\n\n[^1]: First line\n continued on the next line." + post.from_markdown(md) + text = footnotes(post)[0]["content"][0]["content"][0]["text"] + assert text == "First line continued on the next line." + + def test_unreferenced_definition_still_appended(self): + post = make_post() + post.from_markdown("No references here.\n\n[^1]: Orphan note.") + assert len(anchors(post)) == 0 + assert len(footnotes(post)) == 1 + + def test_reference_without_definition_left_as_text(self): + post = make_post() + post.from_markdown("A dangling[^missing] reference.") + assert len(anchors(post)) == 0 + assert len(footnotes(post)) == 0 + para = find_nodes(post.draft_body, "paragraph")[0] + assert "[^missing]" in para["content"][0]["text"] + + def test_definition_in_middle_moves_to_end(self): + post = make_post() + md = ( + "First paragraph.[^1]\n\n" + "[^1]: First footnote.\n\n" + "Second paragraph." + ) + post.from_markdown(md) + + types = [node["type"] for node in body_content(post)] + # Both paragraphs come first; the footnote block is last regardless of + # where the definition appeared in the source. + assert types == ["paragraph", "paragraph", "footnote"] + + paragraphs = find_nodes(post.draft_body, "paragraph") + assert paragraphs[0]["content"][0]["text"] == "First paragraph." + # The definition line did not become a paragraph in the body. + assert paragraphs[1]["content"][0]["text"] == "Second paragraph." + + assert len(anchors(post)) == 1 + block = footnotes(post)[0] + assert block["content"][0]["content"][0]["text"] == "First footnote." + + def test_footnote_definition_inside_fenced_code_stays_code(self): + post = make_post() + post.from_markdown("```\n[^1]: not a footnote\n```") + content = body_content(post) + assert len(content) == 1 + assert content[0]["type"] == "codeBlock" + assert content[0]["content"][0]["text"] == "[^1]: not a footnote" + + def test_footnote_reference_inside_fenced_code_stays_text(self): + post = make_post() + post.from_markdown("```\ncode [^1]\n```\n\n[^1]: note") + content = body_content(post) + assert content[0]["type"] == "codeBlock" + assert content[0]["content"][0]["text"] == "code [^1]" + + def test_footnote_reference_inside_inline_code_stays_text(self): + post = make_post() + post.from_markdown("`code [^1]`\n\n[^1]: note") + content = body_content(post) + assert content[0]["type"] == "paragraph" + assert content[0]["content"][0]["text"] == "code [^1]" + assert content[0]["content"][0]["marks"] == [{"type": "code"}] + + def test_multiparagraph_definition(self): + post = make_post() + md = "Claim.[^1]\n\n[^1]: First para.\n\n Second para." + post.from_markdown(md) + # The second paragraph must stay in the footnote, not leak into the body. + assert [n["type"] for n in body_content(post)] == ["paragraph", "footnote"] + block = footnotes(post)[0] + assert len(block["content"]) == 2 + assert block["content"][0]["content"][0]["text"] == "First para." + assert block["content"][1]["content"][0]["text"] == "Second para." + + def test_multiparagraph_definition_in_middle(self): + post = make_post() + md = ( + "First.[^1]\n\n" + "[^1]: Note para one.\n\n" + " Note para two.\n\n" + "Back to the body." + ) + post.from_markdown(md) + types = [n["type"] for n in body_content(post)] + assert types == ["paragraph", "paragraph", "footnote"] + assert body_content(post)[1]["content"][0]["text"] == "Back to the body." + assert len(footnotes(post)[0]["content"]) == 2 + + def test_footnote_helper_splits_paragraphs(self): + post = make_post() + post.footnote(1, "Para one.\n\nPara two.") + block = footnotes(post)[0] + assert len(block["content"]) == 2 + assert block["content"][1]["content"][0]["text"] == "Para two." + + def test_no_footnotes_is_unchanged(self): + post = make_post() + post.from_markdown("Just a plain paragraph.") + assert len(anchors(post)) == 0 + assert len(footnotes(post)) == 0 + assert find_nodes(post.draft_body, "paragraph")[0]["content"][0]["text"] == ( + "Just a plain paragraph." + ) From 667de4eb6868065f87f003f882f6d08c1145b9c0 Mon Sep 17 00:00:00 2001 From: Paolo Mazza <59370937+ma2za@users.noreply.github.com> Date: Sat, 27 Jun 2026 12:25:27 +0300 Subject: [PATCH 30/31] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9a96686..67f5a0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-substack" -version = "0.1.22" +version = "0.1.23" description = "A Python wrapper around the Substack API." authors = ["Paolo Mazza "] license = "MIT" From 0fc190bc45103d0d999434bdb08c0462c12cf272 Mon Sep 17 00:00:00 2001 From: Paolo Mazza <59370937+ma2za@users.noreply.github.com> Date: Sat, 27 Jun 2026 12:25:43 +0300 Subject: [PATCH 31/31] Update __init__.py --- substack/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substack/__init__.py b/substack/__init__.py index 0cfe23b..739f101 100644 --- a/substack/__init__.py +++ b/substack/__init__.py @@ -3,7 +3,7 @@ __author__ = "Paolo Mazza" __email__ = "mazzapaolo2019@gmail.com" __license__ = "MIT License" -__version__ = "0.1.22" +__version__ = "0.1.23" __url__ = "https://github.com/ma2za/python-substack" __download_url__ = "https://pypi.python.org/pypi/python-substack" __description__ = "A Python wrapper around the Substack API"