RohanKarthikeyan commited on
Commit
855e188
·
verified ·
1 Parent(s): 49901a6

Upload updated code

Browse files
Files changed (8) hide show
  1. .gitignore +4 -0
  2. README.md +16 -18
  3. app.py +84 -22
  4. demo_app.py +27 -0
  5. mcp_client.py +35 -0
  6. oaklib_utils.py +5 -36
  7. pyproject.toml +11 -2
  8. uv.lock +255 -1
.gitignore CHANGED
@@ -7,4 +7,8 @@ wheels/
7
  *.egg-info
8
 
9
  # Virtual environments
 
10
  .venv
 
 
 
 
7
  *.egg-info
8
 
9
  # Virtual environments
10
+ .env
11
  .venv
12
+
13
+ # Ruff configuration file
14
+ ruff.toml
README.md CHANGED
@@ -11,43 +11,41 @@ tags:
11
  - mcp-server-track
12
  ---
13
 
14
- # BioMedNorm MCP Server
15
-
16
- A MCP server for extracting and normalizing domain-specific entities from biomedical text. We leverage OpenAI LLMs to identify entities and match them to standardized terminology.
17
 
18
  ## Motivation
19
 
20
- Biomedical text normalization addresses a critical challenge in healthcare informatics: approximately 80% of electronic health record (EHR) data exists as unstructured medical text. Such text often contains abbreviations, misspellings, and non-standardized terminology, creating barriers to effective data utilization. This variability hinders leveraging clinical narratives for:
21
 
22
  - **Clinical decision support** at the point of care
23
  - **Patient comprehension** of their own medical records
24
  - **Biomedical research** including cohort identification and pharmacovigilance
25
 
26
- By implementing named entity recognition and normalization to controlled vocabularies like SNOMED-CT, our MCP server enables downstream applications to process biomedical text with greater accuracy, bridging the gap between natural clinical language and structured data requirements of modern healthcare systems.
27
 
28
  ## Installation
29
 
30
- This project uses `uv` from Astral for dependency management. Follow these steps to set up the project:
31
-
32
- ### Clone the repository
33
 
34
  ```bash
35
- git clone https://github.com/yourusername/entity-extraction-mcp
36
- cd entity-extraction-mcp
37
  ```
38
 
39
- ### Set up Python environment
40
 
41
- The project includes a .python-version file that specifies the required Python version. Make sure you have uv installed:
42
 
43
  ```bash
44
- # Install uv if you don't have it already
45
- curl -LsSf https://astral.sh/uv/install.sh | sh
46
  ```
47
 
 
 
 
 
48
  ### Install dependencies
49
 
50
- The project dependencies are defined in `pyproject.toml`. Install them with:
51
 
52
  ```bash
53
  uv pip install -e .
@@ -55,7 +53,7 @@ uv pip install -e .
55
 
56
  ### Set up environment variables
57
 
58
- The project **requires** an OpenAI API key, which should be stored in a .env file.
59
 
60
  ## Running the application
61
 
@@ -68,7 +66,7 @@ uv run app.py
68
  This command ensures that:
69
 
70
  - All project dependencies are correctly installed
71
- - The environment variables from .env are loaded
72
  - The application runs in the proper environment
73
 
74
  After starting the server, you can access:
@@ -100,6 +98,6 @@ Here is a [video link](./MCPServer_test.webm) that shows the MCP server in actio
100
 
101
  Our biomedical text normalization MCP server can be enhanced in several ways:
102
 
103
- - Expanded Entity Coverage: Extend beyond the current entity types (Disease, Tissue, Cell Type) to include medications, procedures, laboratory tests, and genomic entities.
104
  - User Feedback Loop: Implement a mechanism for users to correct normalization errors, creating a dataset for continuous model improvement.
105
  - Multilingual Support: Expand capabilities to handle medical text in languages beyond English.
 
11
  - mcp-server-track
12
  ---
13
 
14
+ A MCP server for extracting and normalizing domain-specific entities from biomedical text. We utilize LLMs (here, from OpenAI) to identify entities and map them to standardized terminology or ontologies.
 
 
15
 
16
  ## Motivation
17
 
18
+ Approximately 80% of electronic health record (EHR) data exists as unstructured medical text. Such text often contains abbreviations, misspellings, and non-standardized terminology, creating barriers to effective data utilization. This variability hinders progress in areas such as:
19
 
20
  - **Clinical decision support** at the point of care
21
  - **Patient comprehension** of their own medical records
22
  - **Biomedical research** including cohort identification and pharmacovigilance
23
 
24
+ By implementing named entity recognition (NER) and normalization to standardized vocabularies (ontologies), our MCP server facilitates the extraction of structured data from diverse biomedical texts. This enhances the accuracy and efficiency of applications in clinical settings and research, bridging the gap between complex natural language and the structured data requirements of modern biomedical systems.
25
 
26
  ## Installation
27
 
28
+ This project uses `uv` from Astral for dependency management. Make sure you have `uv` installed. If not, install it with the following command:
 
 
29
 
30
  ```bash
31
+ curl -LsSf https://astral.sh/uv/install.sh | sh
 
32
  ```
33
 
34
+ Follow these steps to set up the project:
35
 
36
+ ### Clone the repository
37
 
38
  ```bash
39
+ git clone https://huggingface.co/spaces/Agents-MCP-Hackathon/BioMedNorm-MCP-Server
 
40
  ```
41
 
42
+ ### Set up Python environment
43
+
44
+ The project includes a .python-version file to specify the required Python version. Ensure you are using the correct version by setting it up with your Python environment manager (e.g., `pyenv`).
45
+
46
  ### Install dependencies
47
 
48
+ The project dependencies are defined in the `pyproject.toml` file. To install them, run:
49
 
50
  ```bash
51
  uv pip install -e .
 
53
 
54
  ### Set up environment variables
55
 
56
+ The project **requires** an OpenAI API key, which should be stored in a `.env` file.
57
 
58
  ## Running the application
59
 
 
66
  This command ensures that:
67
 
68
  - All project dependencies are correctly installed
69
+ - The environment variables from `.env` are loaded
70
  - The application runs in the proper environment
71
 
72
  After starting the server, you can access:
 
98
 
99
  Our biomedical text normalization MCP server can be enhanced in several ways:
100
 
101
+ - Expanded Entity Coverage: Extend beyond the current entity types (Disease, Tissue, Cell Type) to include other entities such as library processing protocols, cell lines, disease status, to name a few.
102
  - User Feedback Loop: Implement a mechanism for users to correct normalization errors, creating a dataset for continuous model improvement.
103
  - Multilingual Support: Expand capabilities to handle medical text in languages beyond English.
app.py CHANGED
@@ -13,7 +13,7 @@ from openai_utils import ask_openai
13
  NER_PROMPT = """
14
  You are an expert annotator of biomedical text.
15
 
16
- Annotate/Identify/Extract all {entity}s in this text: {text}
17
 
18
  Instructions:
19
  1. If no such entity or entities are found, then **return exactly**: Not Found
@@ -60,7 +60,9 @@ async def extract_entities(paragraph: str, target_entity: str) -> Optional[list[
60
  return extracted_entities
61
 
62
 
63
- async def normalize_entities(raw_terms: list[str]) -> list[Optional[str]]:
 
 
64
  """
65
  Normalize a list of raw terms to the most appropriate standard terms from a list
66
  of candidates.
@@ -71,20 +73,28 @@ async def normalize_entities(raw_terms: list[str]) -> list[Optional[str]]:
71
  raw_terms (list[str]): List of unnormalized terms, typically from extract_entities().
72
 
73
  Returns:
74
- list[Optional[str]]: List of best matching standard terms in the same order as the
75
- input terms. An entry may be None if normalization failed.
 
76
  """
77
 
78
  # Do normalization for each entity
79
- async def process_single_entity(raw_term: str) -> Optional[str]:
80
  # Generate candidates specifically for this entity
81
  # If the oaklib function is not async, wrap it with run_in_executor
82
- candidate_std_terms = await asyncio.to_thread(get_candidates, raw_term)
 
83
 
84
  # Now use these entity-specific candidates for the OpenAI call
85
  prompt = RAG_PROMPT.format(entity=raw_term, top_k_preds=candidate_std_terms)
86
  result = await ask_openai(prompt, usage="rag")
87
- return result
 
 
 
 
 
 
88
 
89
  # Process all entities in parallel
90
  tasks = [process_single_entity(entity) for entity in raw_terms]
@@ -95,7 +105,7 @@ async def normalize_entities(raw_terms: list[str]) -> list[Optional[str]]:
95
 
96
  async def extract_and_normalize(
97
  paragraph: str, target_entity: str
98
- ) -> list[Optional[str]]:
99
  """
100
  Extract entities from a paragraph and normalize them in one operation.
101
 
@@ -104,9 +114,11 @@ async def extract_and_normalize(
104
  target_entity: The type of entity to extract and normalize.
105
 
106
  Returns:
107
- list[Optional[str]]: List of best matching standard terms in the same order as the
108
- input terms. An entry may be None if normalization failed.
 
109
  """
 
110
  extracted_entities = await extract_entities(paragraph, target_entity)
111
  if not extracted_entities or len(extracted_entities) == 0:
112
  return []
@@ -115,35 +127,85 @@ async def extract_and_normalize(
115
  return result
116
 
117
 
118
- # Create a visually appealing Gradio app
119
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
120
- gr.Markdown("# BioMedNorm: Entity Extraction & Normalization")
121
- gr.Markdown(
122
- "Enter text and specify the entity type to extract and normalize entities."
 
 
 
 
123
  )
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  with gr.Row():
126
- with gr.Column(scale=3):
127
  paragraph = gr.Textbox(
128
  label="Text Input",
129
  placeholder="Enter paragraph here...",
130
- lines=5,
131
  info="Enter the biomedical text for entity extraction.",
132
  )
133
- with gr.Column(scale=1):
134
  target_entity = gr.Dropdown(
135
- ["Disease", "Tissue", "Cell Type"],
136
  label="Entity Type",
137
  value="Disease",
 
138
  info="Select the type of entity you want to extract and normalize from the text.",
139
  )
 
 
 
 
 
 
 
 
 
 
 
140
 
141
- normalize_btn = gr.Button("Normalize", variant="primary")
142
-
143
- with gr.Row():
144
  with gr.Column():
145
  output = gr.JSON(label="Normalized Entities")
146
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  # Add a loading indicator
148
  with gr.Row():
149
  status = gr.Markdown("")
 
13
  NER_PROMPT = """
14
  You are an expert annotator of biomedical text.
15
 
16
+ Annotate/Extract all {entity}(s) in this text: {text}
17
 
18
  Instructions:
19
  1. If no such entity or entities are found, then **return exactly**: Not Found
 
60
  return extracted_entities
61
 
62
 
63
+ async def normalize_entities(
64
+ raw_terms: list[str],
65
+ ) -> list[dict[Optional[str], Optional[str]]]:
66
  """
67
  Normalize a list of raw terms to the most appropriate standard terms from a list
68
  of candidates.
 
73
  raw_terms (list[str]): List of unnormalized terms, typically from extract_entities().
74
 
75
  Returns:
76
+ list[dict[Optional[str], Optional[str]]]: A list of dictionaries, where each dictionary contains
77
+ the best matching normalized term (key) and its corresponding URI (value). If normalization fails
78
+ for a term, the dictionary will have a None value for both the key and URI.
79
  """
80
 
81
  # Do normalization for each entity
82
+ async def process_single_entity(raw_term: str) -> dict[Optional[str], Optional[str]]:
83
  # Generate candidates specifically for this entity
84
  # If the oaklib function is not async, wrap it with run_in_executor
85
+ candidates = await asyncio.to_thread(get_candidates, raw_term)
86
+ candidate_std_terms = [candidates for _, candidates in candidates]
87
 
88
  # Now use these entity-specific candidates for the OpenAI call
89
  prompt = RAG_PROMPT.format(entity=raw_term, top_k_preds=candidate_std_terms)
90
  result = await ask_openai(prompt, usage="rag")
91
+ if result is not None:
92
+ # Find the URI for the std term returned by LLM
93
+ result_URI = next((URI for URI, term in candidates if term == result), None)
94
+ else:
95
+ result_URI = None
96
+
97
+ return {result: result_URI}
98
 
99
  # Process all entities in parallel
100
  tasks = [process_single_entity(entity) for entity in raw_terms]
 
105
 
106
  async def extract_and_normalize(
107
  paragraph: str, target_entity: str
108
+ ) -> list[dict[Optional[str], Optional[str]]]:
109
  """
110
  Extract entities from a paragraph and normalize them in one operation.
111
 
 
114
  target_entity: The type of entity to extract and normalize.
115
 
116
  Returns:
117
+ list[dict[Optional[str], Optional[str]]]: A list of dictionaries, where each dictionary contains
118
+ the best matching normalized term (key) and its corresponding URI (value). If normalization fails
119
+ for a term, the dictionary will have a None value for both the key and URI.
120
  """
121
+ target_entity = target_entity.lower()
122
  extracted_entities = await extract_entities(paragraph, target_entity)
123
  if not extracted_entities or len(extracted_entities) == 0:
124
  return []
 
127
  return result
128
 
129
 
130
+ def toggle_custom_box(selected: str):
131
+ show = selected == "Custom"
132
+ return (
133
+ gr.Textbox(visible=show, interactive=show),
134
+ gr.Markdown(
135
+ "**Warning:** This tool is optimized and tested for Disease, Tissue, and Cell Type entities. "
136
+ "While you can input custom entities, results may vary in accuracy and reliability.",
137
+ visible=show,
138
+ ),
139
  )
140
 
141
+
142
+ # Create the Gradio app
143
+ app_theme = gr.themes.Soft(
144
+ primary_hue="teal",
145
+ secondary_hue="green",
146
+ )
147
+
148
+ with gr.Blocks(theme=app_theme) as demo:
149
+ gr.Markdown("""
150
+ # 🧬 BioMedNorm: Entity Extraction & Normalization
151
+
152
+ Welcome to the BioMedNorm MCP Server.
153
+
154
+ This server is designed to be used by LLMs to extract and standardize biological
155
+ entities (like disease, tissue) from biomedical text.
156
+
157
+ Enter the text below, specify the entity type to extract and normalize entities, and voila!
158
+ """)
159
+
160
+ # Two-column layout using Row + Column
161
  with gr.Row():
162
+ with gr.Column():
163
  paragraph = gr.Textbox(
164
  label="Text Input",
165
  placeholder="Enter paragraph here...",
166
+ lines=8,
167
  info="Enter the biomedical text for entity extraction.",
168
  )
 
169
  target_entity = gr.Dropdown(
170
+ ["Disease", "Tissue", "Cell Type", "Custom"],
171
  label="Entity Type",
172
  value="Disease",
173
+ # allow_custom_value=True,
174
  info="Select the type of entity you want to extract and normalize from the text.",
175
  )
176
+ custom_entity = gr.Textbox(
177
+ label="Custom Entity",
178
+ placeholder="Enter custom entity type here",
179
+ visible=False,
180
+ interactive=True,
181
+ info="Enter your custom entity type if 'Custom' is selected.",
182
+ )
183
+ warning = gr.Markdown(
184
+ visible=False # Initially hidden
185
+ )
186
+ normalize_btn = gr.Button("Normalize", variant="primary")
187
 
 
 
 
188
  with gr.Column():
189
  output = gr.JSON(label="Normalized Entities")
190
 
191
+ # Update custom_entity and warning visibility based on dropdown selection
192
+ target_entity.change(
193
+ fn=toggle_custom_box, inputs=target_entity, outputs=[custom_entity, warning]
194
+ )
195
+ # # Event to show the custom entity field only when 'Custom' is selected
196
+ # target_entity.change(
197
+ # lambda x: custom_entity.visible if x == "Custom" else False,
198
+ # target_entity,
199
+ # custom_entity,
200
+ # )
201
+
202
+ # # Add a warning message about custom entities
203
+ # with gr.Row():
204
+ # warning = gr.Markdown(
205
+ # "**Note:** This tool is optimized and tested for Disease, Tissue, and Cell Type entities. "
206
+ # "While you can input custom entities, results may vary in accuracy and reliability."
207
+ # )
208
+
209
  # Add a loading indicator
210
  with gr.Row():
211
  status = gr.Markdown("")
demo_app.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+
4
+ def letter_counts(word: str, letter: str):
5
+ """
6
+ Count the occurrences of a word in a letter.
7
+
8
+ Args:
9
+ word: The word or phrase to analyze.
10
+ letter: The letter to count occurrences of.
11
+
12
+ Returns:
13
+ The number of times the letter appears in the word.
14
+ """
15
+ assert len(letter) == 1, "The letter should only be one character."
16
+ return word.lower().count(letter.lower())
17
+
18
+
19
+ demo = gr.Interface(
20
+ fn=letter_counts,
21
+ inputs=['text', 'text'],
22
+ outputs='number',
23
+ title="Letter Counter",
24
+ description="Count how many times a latter appears in a word."
25
+ )
26
+
27
+ demo.launch(mcp_server=True)
mcp_client.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Use Gradio as a MCP Client to connect to the BioMedNorm MCP server.
3
+
4
+ Follows the MCP course docs: https://huggingface.co/learn/mcp-course/en/unit2/gradio-client
5
+ """
6
+
7
+ import gradio as gr
8
+ import os
9
+
10
+ from smolagents import InferenceClientModel, CodeAgent, MCPClient
11
+
12
+ mcp_client = None
13
+
14
+ try:
15
+ mcp_client = MCPClient(
16
+ {
17
+ "url": "https://agents-mcp-hackathon-biomednorm-mcp-server.hf.space/gradio_api/mcp/sse"
18
+ }
19
+ )
20
+ tools = mcp_client.get_tools()
21
+
22
+ model = InferenceClientModel(provider="nebius", token=os.getenv("HF_TOKEN"))
23
+ agent = CodeAgent(tools=[*tools], model=model)
24
+
25
+ demo = gr.ChatInterface(
26
+ fn=lambda message, history: str(agent.run(message)),
27
+ type="messages",
28
+ title="BioMed Normalization Agent",
29
+ description="I can extract and standardize entities from biomedical text. Try me out!!!",
30
+ )
31
+
32
+ demo.launch()
33
+ finally:
34
+ if mcp_client is not None:
35
+ mcp_client.disconnect()
oaklib_utils.py CHANGED
@@ -2,15 +2,13 @@
2
  Retrieve top k candidate standard terms for normalization using oaklib.
3
  """
4
 
5
- # import argparse
6
-
7
  from oaklib import get_adapter
8
  from oaklib.datamodels.search import SearchConfiguration
9
 
10
  adapter = get_adapter("ols:")
11
 
12
 
13
- def get_candidates(term: str, top_k: int = 10) -> list[str]:
14
  """
15
  Get top k candidates for RAG.
16
  """
@@ -18,38 +16,9 @@ def get_candidates(term: str, top_k: int = 10) -> list[str]:
18
  cfg = SearchConfiguration(limit=top_k)
19
 
20
  results = adapter.basic_search(term, config=cfg)
21
- labels = list(adapter.labels(results)) # list of tuples of ids and labels
22
 
23
- # print(f"## Query: {term} -> {labels}")
24
- candidates = list(label for _, label in labels)
 
25
  return candidates
26
-
27
-
28
- # def main():
29
- # parser = argparse.ArgumentParser(
30
- # description="Fetch top-K candidate passages for a given term (RAG)"
31
- # )
32
- # parser.add_argument(
33
- # "term", type=str, help="The query term or prompt for which to retrieve candidates"
34
- # )
35
- # parser.add_argument(
36
- # "-k",
37
- # "--top_k",
38
- # type=int,
39
- # default=10,
40
- # help="Number of top candidates to return (default: 10)",
41
- # )
42
-
43
- # args = parser.parse_args()
44
-
45
- # # Call your function
46
- # candidates = get_candidates(args.term)
47
-
48
- # print(f"\nTerm: {args.term!r}")
49
- # print(f"Top {args.top_k} candidates:")
50
- # for i, cand in enumerate(candidates, start=1):
51
- # print(f" {i:2d}. {cand}")
52
-
53
-
54
- # if __name__ == "__main__":
55
- # main()
 
2
  Retrieve top k candidate standard terms for normalization using oaklib.
3
  """
4
 
 
 
5
  from oaklib import get_adapter
6
  from oaklib.datamodels.search import SearchConfiguration
7
 
8
  adapter = get_adapter("ols:")
9
 
10
 
11
+ def get_candidates(term: str, top_k: int = 10) -> list[tuple[str, str]]:
12
  """
13
  Get top k candidates for RAG.
14
  """
 
16
  cfg = SearchConfiguration(limit=top_k)
17
 
18
  results = adapter.basic_search(term, config=cfg)
19
+ labels = list(adapter.labels(results)) # list of tuples of CURIE ids and labels
20
 
21
+ # Keep both URI and standard terms for "explainable" output
22
+ # But first convert CURIE IDs to URIs
23
+ candidates = list((adapter.curie_to_uri(curie), term) for (curie, term) in labels)
24
  return candidates
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pyproject.toml CHANGED
@@ -1,11 +1,20 @@
1
  [project]
2
  name = "biomednorm-mcp-server"
3
- version = "0.1.0"
4
- description = "Add your description here"
 
 
 
 
 
 
5
  readme = "README.md"
 
6
  requires-python = ">=3.13"
 
7
  dependencies = [
8
  "oaklib>=0.6.23",
9
  "openai>=1.84.0",
10
  "python-dotenv>=1.1.0",
 
11
  ]
 
1
  [project]
2
  name = "biomednorm-mcp-server"
3
+ version = "0.2.0"
4
+ description = "Extract and normalize entities from biomedical text using LLMs."
5
+ authors = [
6
+ {name = "Rohan Karthikeyan", email = "[email protected]"}
7
+ ]
8
+ maintainers = [
9
+ {name = "Rohan Karthikeyan", email = "[email protected]"}
10
+ ]
11
  readme = "README.md"
12
+
13
  requires-python = ">=3.13"
14
+
15
  dependencies = [
16
  "oaklib>=0.6.23",
17
  "openai>=1.84.0",
18
  "python-dotenv>=1.1.0",
19
+ "smolagents[mcp]>=1.18.0",
20
  ]
uv.lock CHANGED
@@ -99,12 +99,13 @@ wheels = [
99
 
100
  [[package]]
101
  name = "biomednorm-mcp-server"
102
- version = "0.1.0"
103
  source = { virtual = "." }
104
  dependencies = [
105
  { name = "oaklib" },
106
  { name = "openai" },
107
  { name = "python-dotenv" },
 
108
  ]
109
 
110
  [package.metadata]
@@ -112,6 +113,7 @@ requires-dist = [
112
  { name = "oaklib", specifier = ">=0.6.23" },
113
  { name = "openai", specifier = ">=1.84.0" },
114
  { name = "python-dotenv", specifier = ">=1.1.0" },
 
115
  ]
116
 
117
  [[package]]
@@ -310,6 +312,15 @@ wheels = [
310
  { url = "https://files.pythonhosted.org/packages/4e/7f/29c135a5ed6d8b391d2f56501f8ffce5c6d5963aa54c059cbb85ef25c715/fastobo-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:ffa708db4157c40f8ff61edc228f5bfae30f88b8d96d3d3ea4a1f0eb01cba474", size = 2202928, upload-time = "2025-02-14T00:41:01.128Z" },
311
  ]
312
 
 
 
 
 
 
 
 
 
 
313
  [[package]]
314
  name = "fqdn"
315
  version = "1.5.1"
@@ -319,6 +330,15 @@ wheels = [
319
  { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" },
320
  ]
321
 
 
 
 
 
 
 
 
 
 
322
  [[package]]
323
  name = "funowl"
324
  version = "0.2.3"
@@ -387,6 +407,21 @@ wheels = [
387
  { url = "https://files.pythonhosted.org/packages/7b/24/61844afbf38acf419e01ca2639f7bd079584523d34471acbc4152ee991c5/hbreader-0.9.1-py3-none-any.whl", hash = "sha256:9a6e76c9d1afc1b977374a5dc430a1ebb0ea0488205546d4678d6e31cc5f6801", size = 7595, upload-time = "2021-02-25T19:22:31.944Z" },
388
  ]
389
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  [[package]]
391
  name = "httpcore"
392
  version = "1.0.9"
@@ -415,6 +450,34 @@ wheels = [
415
  { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
416
  ]
417
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  [[package]]
419
  name = "idna"
420
  version = "3.10"
@@ -605,6 +668,15 @@ wheels = [
605
  { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" },
606
  ]
607
 
 
 
 
 
 
 
 
 
 
608
  [[package]]
609
  name = "jsonschema"
610
  version = "4.24.0"
@@ -780,6 +852,18 @@ wheels = [
780
  { url = "https://files.pythonhosted.org/packages/fc/14/c115516c62a7d2499781d2d3d7215218c0731b2c940753bf9f9b7b73924d/lxml-5.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:bcb7a1096b4b6b24ce1ac24d4942ad98f983cd3810f9711bcd0293f43a9d8b9f", size = 3814606, upload-time = "2025-04-23T01:47:39.028Z" },
781
  ]
782
 
 
 
 
 
 
 
 
 
 
 
 
 
783
  [[package]]
784
  name = "markupsafe"
785
  version = "3.0.2"
@@ -808,6 +892,50 @@ wheels = [
808
  { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
809
  ]
810
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
811
  [[package]]
812
  name = "more-click"
813
  version = "0.1.2"
@@ -1037,6 +1165,36 @@ wheels = [
1037
  { url = "https://files.pythonhosted.org/packages/d0/31/ba45bf0b2aa7898d81cbbfac0e88c267befb59ad91a19e36e1bc5578ddb1/parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558", size = 20126, upload-time = "2024-06-11T04:41:55.057Z" },
1038
  ]
1039
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1040
  [[package]]
1041
  name = "platformdirs"
1042
  version = "4.3.8"
@@ -1141,6 +1299,20 @@ wheels = [
1141
  { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
1142
  ]
1143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1144
  [[package]]
1145
  name = "pygments"
1146
  version = "2.19.1"
@@ -1280,6 +1452,15 @@ wheels = [
1280
  { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" },
1281
  ]
1282
 
 
 
 
 
 
 
 
 
 
1283
  [[package]]
1284
  name = "pytrie"
1285
  version = "0.4.0"
@@ -1439,6 +1620,19 @@ wheels = [
1439
  { url = "https://files.pythonhosted.org/packages/65/d4/f7407c3d15d5ac779c3dd34fbbc6ea2090f77bd7dd12f207ccf881551208/rfc3987-1.3.8-py2.py3-none-any.whl", hash = "sha256:10702b1e51e5658843460b189b185c0366d2cf4cff716f13111b0ea9fd2dce53", size = 13377, upload-time = "2018-07-29T17:23:45.313Z" },
1440
  ]
1441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1442
  [[package]]
1443
  name = "roman-numerals-py"
1444
  version = "3.1.0"
@@ -1556,6 +1750,29 @@ wheels = [
1556
  { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
1557
  ]
1558
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1559
  [[package]]
1560
  name = "sniffio"
1561
  version = "1.3.1"
@@ -1738,6 +1955,18 @@ wheels = [
1738
  { url = "https://files.pythonhosted.org/packages/e1/90/6ff1829b55cf752994be76bdf5f718db053dcb8d738f9df5b622207a4ab8/SQLAlchemy_Utils-0.38.3-py3-none-any.whl", hash = "sha256:5c13b5d08adfaa85f3d4e8ec09a75136216fad41346980d02974a70a77988bf9", size = 100264, upload-time = "2022-07-12T05:08:31.496Z" },
1739
  ]
1740
 
 
 
 
 
 
 
 
 
 
 
 
 
1741
  [[package]]
1742
  name = "sssom"
1743
  version = "0.4.16"
@@ -1776,6 +2005,18 @@ wheels = [
1776
  { url = "https://files.pythonhosted.org/packages/54/61/dfcccdf334ece31ad67babd095ab9d5750f454411a3f7679155c0b2885d2/sssom_schema-1.0.0-py3-none-any.whl", hash = "sha256:e41031637094eddce7e71d6642e1f7b3f972aae08c0039c1dd27fe5208835d15", size = 32437, upload-time = "2024-08-09T19:46:06.386Z" },
1777
  ]
1778
 
 
 
 
 
 
 
 
 
 
 
 
 
1779
  [[package]]
1780
  name = "tenacity"
1781
  version = "9.1.2"
@@ -1866,6 +2107,19 @@ wheels = [
1866
  { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" },
1867
  ]
1868
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1869
  [[package]]
1870
  name = "validators"
1871
  version = "0.35.0"
 
99
 
100
  [[package]]
101
  name = "biomednorm-mcp-server"
102
+ version = "0.2.0"
103
  source = { virtual = "." }
104
  dependencies = [
105
  { name = "oaklib" },
106
  { name = "openai" },
107
  { name = "python-dotenv" },
108
+ { name = "smolagents", extra = ["mcp"] },
109
  ]
110
 
111
  [package.metadata]
 
113
  { name = "oaklib", specifier = ">=0.6.23" },
114
  { name = "openai", specifier = ">=1.84.0" },
115
  { name = "python-dotenv", specifier = ">=1.1.0" },
116
+ { name = "smolagents", extras = ["mcp"], specifier = ">=1.18.0" },
117
  ]
118
 
119
  [[package]]
 
312
  { url = "https://files.pythonhosted.org/packages/4e/7f/29c135a5ed6d8b391d2f56501f8ffce5c6d5963aa54c059cbb85ef25c715/fastobo-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:ffa708db4157c40f8ff61edc228f5bfae30f88b8d96d3d3ea4a1f0eb01cba474", size = 2202928, upload-time = "2025-02-14T00:41:01.128Z" },
313
  ]
314
 
315
+ [[package]]
316
+ name = "filelock"
317
+ version = "3.18.0"
318
+ source = { registry = "https://pypi.org/simple" }
319
+ sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" }
320
+ wheels = [
321
+ { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" },
322
+ ]
323
+
324
  [[package]]
325
  name = "fqdn"
326
  version = "1.5.1"
 
330
  { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" },
331
  ]
332
 
333
+ [[package]]
334
+ name = "fsspec"
335
+ version = "2025.5.1"
336
+ source = { registry = "https://pypi.org/simple" }
337
+ sdist = { url = "https://files.pythonhosted.org/packages/00/f7/27f15d41f0ed38e8fcc488584b57e902b331da7f7c6dcda53721b15838fc/fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475", size = 303033, upload-time = "2025-05-24T12:03:23.792Z" }
338
+ wheels = [
339
+ { url = "https://files.pythonhosted.org/packages/bb/61/78c7b3851add1481b048b5fdc29067397a1784e2910592bc81bb3f608635/fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462", size = 199052, upload-time = "2025-05-24T12:03:21.66Z" },
340
+ ]
341
+
342
  [[package]]
343
  name = "funowl"
344
  version = "0.2.3"
 
407
  { url = "https://files.pythonhosted.org/packages/7b/24/61844afbf38acf419e01ca2639f7bd079584523d34471acbc4152ee991c5/hbreader-0.9.1-py3-none-any.whl", hash = "sha256:9a6e76c9d1afc1b977374a5dc430a1ebb0ea0488205546d4678d6e31cc5f6801", size = 7595, upload-time = "2021-02-25T19:22:31.944Z" },
408
  ]
409
 
410
+ [[package]]
411
+ name = "hf-xet"
412
+ version = "1.1.3"
413
+ source = { registry = "https://pypi.org/simple" }
414
+ sdist = { url = "https://files.pythonhosted.org/packages/75/dc/dc091aeeb671e71cbec30e84963f9c0202c17337b24b0a800e7d205543e8/hf_xet-1.1.3.tar.gz", hash = "sha256:a5f09b1dd24e6ff6bcedb4b0ddab2d81824098bb002cf8b4ffa780545fa348c3", size = 488127, upload-time = "2025-06-04T00:47:27.456Z" }
415
+ wheels = [
416
+ { url = "https://files.pythonhosted.org/packages/9b/1f/bc01a4c0894973adebbcd4aa338a06815c76333ebb3921d94dcbd40dae6a/hf_xet-1.1.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c3b508b5f583a75641aebf732853deb058953370ce8184f5dabc49f803b0819b", size = 2256929, upload-time = "2025-06-04T00:47:21.206Z" },
417
+ { url = "https://files.pythonhosted.org/packages/78/07/6ef50851b5c6b45b77a6e018fa299c69a2db3b8bbd0d5af594c0238b1ceb/hf_xet-1.1.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b788a61977fbe6b5186e66239e2a329a3f0b7e7ff50dad38984c0c74f44aeca1", size = 2153719, upload-time = "2025-06-04T00:47:19.302Z" },
418
+ { url = "https://files.pythonhosted.org/packages/52/48/e929e6e3db6e4758c2adf0f2ca2c59287f1b76229d8bdc1a4c9cfc05212e/hf_xet-1.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd2da210856444a34aad8ada2fc12f70dabed7cc20f37e90754d1d9b43bc0534", size = 4820519, upload-time = "2025-06-04T00:47:17.244Z" },
419
+ { url = "https://files.pythonhosted.org/packages/28/2e/03f89c5014a5aafaa9b150655f811798a317036646623bdaace25f485ae8/hf_xet-1.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8203f52827e3df65981984936654a5b390566336956f65765a8aa58c362bb841", size = 4964121, upload-time = "2025-06-04T00:47:15.17Z" },
420
+ { url = "https://files.pythonhosted.org/packages/47/8b/5cd399a92b47d98086f55fc72d69bc9ea5e5c6f27a9ed3e0cdd6be4e58a3/hf_xet-1.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:30c575a5306f8e6fda37edb866762140a435037365eba7a17ce7bd0bc0216a8b", size = 5283017, upload-time = "2025-06-04T00:47:23.239Z" },
421
+ { url = "https://files.pythonhosted.org/packages/53/e3/2fcec58d2fcfd25ff07feb876f466cfa11f8dcf9d3b742c07fe9dd51ee0a/hf_xet-1.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7c1a6aa6abed1f696f8099aa9796ca04c9ee778a58728a115607de9cc4638ff1", size = 4970349, upload-time = "2025-06-04T00:47:25.383Z" },
422
+ { url = "https://files.pythonhosted.org/packages/53/bf/10ca917e335861101017ff46044c90e517b574fbb37219347b83be1952f6/hf_xet-1.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:b578ae5ac9c056296bb0df9d018e597c8dc6390c5266f35b5c44696003cde9f3", size = 2310934, upload-time = "2025-06-04T00:47:29.632Z" },
423
+ ]
424
+
425
  [[package]]
426
  name = "httpcore"
427
  version = "1.0.9"
 
450
  { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
451
  ]
452
 
453
+ [[package]]
454
+ name = "httpx-sse"
455
+ version = "0.4.0"
456
+ source = { registry = "https://pypi.org/simple" }
457
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624, upload-time = "2023-12-22T08:01:21.083Z" }
458
+ wheels = [
459
+ { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819, upload-time = "2023-12-22T08:01:19.89Z" },
460
+ ]
461
+
462
+ [[package]]
463
+ name = "huggingface-hub"
464
+ version = "0.32.4"
465
+ source = { registry = "https://pypi.org/simple" }
466
+ dependencies = [
467
+ { name = "filelock" },
468
+ { name = "fsspec" },
469
+ { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
470
+ { name = "packaging" },
471
+ { name = "pyyaml" },
472
+ { name = "requests" },
473
+ { name = "tqdm" },
474
+ { name = "typing-extensions" },
475
+ ]
476
+ sdist = { url = "https://files.pythonhosted.org/packages/60/c8/4f7d270285c46324fd66f62159eb16739aa5696f422dba57678a8c6b78e9/huggingface_hub-0.32.4.tar.gz", hash = "sha256:f61d45cd338736f59fb0e97550b74c24ee771bcc92c05ae0766b9116abe720be", size = 424494, upload-time = "2025-06-03T09:59:46.105Z" }
477
+ wheels = [
478
+ { url = "https://files.pythonhosted.org/packages/67/8b/222140f3cfb6f17b0dd8c4b9a0b36bd4ebefe9fb0098ba35d6960abcda0f/huggingface_hub-0.32.4-py3-none-any.whl", hash = "sha256:37abf8826b38d971f60d3625229221c36e53fe58060286db9baf619cfbf39767", size = 512101, upload-time = "2025-06-03T09:59:44.099Z" },
479
+ ]
480
+
481
  [[package]]
482
  name = "idna"
483
  version = "3.10"
 
668
  { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" },
669
  ]
670
 
671
+ [[package]]
672
+ name = "jsonref"
673
+ version = "1.1.0"
674
+ source = { registry = "https://pypi.org/simple" }
675
+ sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" }
676
+ wheels = [
677
+ { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" },
678
+ ]
679
+
680
  [[package]]
681
  name = "jsonschema"
682
  version = "4.24.0"
 
852
  { url = "https://files.pythonhosted.org/packages/fc/14/c115516c62a7d2499781d2d3d7215218c0731b2c940753bf9f9b7b73924d/lxml-5.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:bcb7a1096b4b6b24ce1ac24d4942ad98f983cd3810f9711bcd0293f43a9d8b9f", size = 3814606, upload-time = "2025-04-23T01:47:39.028Z" },
853
  ]
854
 
855
+ [[package]]
856
+ name = "markdown-it-py"
857
+ version = "3.0.0"
858
+ source = { registry = "https://pypi.org/simple" }
859
+ dependencies = [
860
+ { name = "mdurl" },
861
+ ]
862
+ sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
863
+ wheels = [
864
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
865
+ ]
866
+
867
  [[package]]
868
  name = "markupsafe"
869
  version = "3.0.2"
 
892
  { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
893
  ]
894
 
895
+ [[package]]
896
+ name = "mcp"
897
+ version = "1.9.3"
898
+ source = { registry = "https://pypi.org/simple" }
899
+ dependencies = [
900
+ { name = "anyio" },
901
+ { name = "httpx" },
902
+ { name = "httpx-sse" },
903
+ { name = "pydantic" },
904
+ { name = "pydantic-settings" },
905
+ { name = "python-multipart" },
906
+ { name = "sse-starlette" },
907
+ { name = "starlette" },
908
+ { name = "uvicorn", marker = "sys_platform != 'emscripten'" },
909
+ ]
910
+ sdist = { url = "https://files.pythonhosted.org/packages/f2/df/8fefc0c6c7a5c66914763e3ff3893f9a03435628f6625d5e3b0dc45d73db/mcp-1.9.3.tar.gz", hash = "sha256:587ba38448e81885e5d1b84055cfcc0ca56d35cd0c58f50941cab01109405388", size = 333045, upload-time = "2025-06-05T15:48:25.681Z" }
911
+ wheels = [
912
+ { url = "https://files.pythonhosted.org/packages/79/45/823ad05504bea55cb0feb7470387f151252127ad5c72f8882e8fe6cf5c0e/mcp-1.9.3-py3-none-any.whl", hash = "sha256:69b0136d1ac9927402ed4cf221d4b8ff875e7132b0b06edd446448766f34f9b9", size = 131063, upload-time = "2025-06-05T15:48:24.171Z" },
913
+ ]
914
+
915
+ [[package]]
916
+ name = "mcpadapt"
917
+ version = "0.1.9"
918
+ source = { registry = "https://pypi.org/simple" }
919
+ dependencies = [
920
+ { name = "jsonref" },
921
+ { name = "mcp" },
922
+ { name = "pydantic" },
923
+ { name = "python-dotenv" },
924
+ ]
925
+ sdist = { url = "https://files.pythonhosted.org/packages/9e/68/85c0946d567088d8d55f1c30cb942bcfec2585941a3f45b790e423b994c8/mcpadapt-0.1.9.tar.gz", hash = "sha256:03e601c4c083f3f4eb178e6a6bcd157bcb45e25c140ea0895567bab346b67645", size = 3540887, upload-time = "2025-05-24T19:40:35.823Z" }
926
+ wheels = [
927
+ { url = "https://files.pythonhosted.org/packages/83/78/0310684763e5753a3a8128dab6c87ba1e20dd907b696680592bebebc84b6/mcpadapt-0.1.9-py3-none-any.whl", hash = "sha256:9f2a6ad1155efdf1a43c11e8449ae9258295c4e140c3c6ff672983a8ac8bde33", size = 17469, upload-time = "2025-05-24T19:40:34.055Z" },
928
+ ]
929
+
930
+ [[package]]
931
+ name = "mdurl"
932
+ version = "0.1.2"
933
+ source = { registry = "https://pypi.org/simple" }
934
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
935
+ wheels = [
936
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
937
+ ]
938
+
939
  [[package]]
940
  name = "more-click"
941
  version = "0.1.2"
 
1165
  { url = "https://files.pythonhosted.org/packages/d0/31/ba45bf0b2aa7898d81cbbfac0e88c267befb59ad91a19e36e1bc5578ddb1/parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558", size = 20126, upload-time = "2024-06-11T04:41:55.057Z" },
1166
  ]
1167
 
1168
+ [[package]]
1169
+ name = "pillow"
1170
+ version = "11.2.1"
1171
+ source = { registry = "https://pypi.org/simple" }
1172
+ sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707, upload-time = "2025-04-12T17:50:03.289Z" }
1173
+ wheels = [
1174
+ { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098, upload-time = "2025-04-12T17:48:23.915Z" },
1175
+ { url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166, upload-time = "2025-04-12T17:48:25.738Z" },
1176
+ { url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674, upload-time = "2025-04-12T17:48:27.908Z" },
1177
+ { url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005, upload-time = "2025-04-12T17:48:29.888Z" },
1178
+ { url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707, upload-time = "2025-04-12T17:48:31.874Z" },
1179
+ { url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008, upload-time = "2025-04-12T17:48:34.422Z" },
1180
+ { url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420, upload-time = "2025-04-12T17:48:37.641Z" },
1181
+ { url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655, upload-time = "2025-04-12T17:48:39.652Z" },
1182
+ { url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329, upload-time = "2025-04-12T17:48:41.765Z" },
1183
+ { url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388, upload-time = "2025-04-12T17:48:43.625Z" },
1184
+ { url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950, upload-time = "2025-04-12T17:48:45.475Z" },
1185
+ { url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759, upload-time = "2025-04-12T17:48:47.866Z" },
1186
+ { url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284, upload-time = "2025-04-12T17:48:50.189Z" },
1187
+ { url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826, upload-time = "2025-04-12T17:48:52.346Z" },
1188
+ { url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329, upload-time = "2025-04-12T17:48:54.403Z" },
1189
+ { url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049, upload-time = "2025-04-12T17:48:56.383Z" },
1190
+ { url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408, upload-time = "2025-04-12T17:48:58.782Z" },
1191
+ { url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863, upload-time = "2025-04-12T17:49:00.709Z" },
1192
+ { url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938, upload-time = "2025-04-12T17:49:02.946Z" },
1193
+ { url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774, upload-time = "2025-04-12T17:49:04.889Z" },
1194
+ { url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895, upload-time = "2025-04-12T17:49:06.635Z" },
1195
+ { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234, upload-time = "2025-04-12T17:49:08.399Z" },
1196
+ ]
1197
+
1198
  [[package]]
1199
  name = "platformdirs"
1200
  version = "4.3.8"
 
1299
  { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
1300
  ]
1301
 
1302
+ [[package]]
1303
+ name = "pydantic-settings"
1304
+ version = "2.9.1"
1305
+ source = { registry = "https://pypi.org/simple" }
1306
+ dependencies = [
1307
+ { name = "pydantic" },
1308
+ { name = "python-dotenv" },
1309
+ { name = "typing-inspection" },
1310
+ ]
1311
+ sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234, upload-time = "2025-04-18T16:44:48.265Z" }
1312
+ wheels = [
1313
+ { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356, upload-time = "2025-04-18T16:44:46.617Z" },
1314
+ ]
1315
+
1316
  [[package]]
1317
  name = "pygments"
1318
  version = "2.19.1"
 
1452
  { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" },
1453
  ]
1454
 
1455
+ [[package]]
1456
+ name = "python-multipart"
1457
+ version = "0.0.20"
1458
+ source = { registry = "https://pypi.org/simple" }
1459
+ sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" }
1460
+ wheels = [
1461
+ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" },
1462
+ ]
1463
+
1464
  [[package]]
1465
  name = "pytrie"
1466
  version = "0.4.0"
 
1620
  { url = "https://files.pythonhosted.org/packages/65/d4/f7407c3d15d5ac779c3dd34fbbc6ea2090f77bd7dd12f207ccf881551208/rfc3987-1.3.8-py2.py3-none-any.whl", hash = "sha256:10702b1e51e5658843460b189b185c0366d2cf4cff716f13111b0ea9fd2dce53", size = 13377, upload-time = "2018-07-29T17:23:45.313Z" },
1621
  ]
1622
 
1623
+ [[package]]
1624
+ name = "rich"
1625
+ version = "14.0.0"
1626
+ source = { registry = "https://pypi.org/simple" }
1627
+ dependencies = [
1628
+ { name = "markdown-it-py" },
1629
+ { name = "pygments" },
1630
+ ]
1631
+ sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" }
1632
+ wheels = [
1633
+ { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" },
1634
+ ]
1635
+
1636
  [[package]]
1637
  name = "roman-numerals-py"
1638
  version = "3.1.0"
 
1750
  { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
1751
  ]
1752
 
1753
+ [[package]]
1754
+ name = "smolagents"
1755
+ version = "1.18.0"
1756
+ source = { registry = "https://pypi.org/simple" }
1757
+ dependencies = [
1758
+ { name = "huggingface-hub" },
1759
+ { name = "jinja2" },
1760
+ { name = "pillow" },
1761
+ { name = "python-dotenv" },
1762
+ { name = "requests" },
1763
+ { name = "rich" },
1764
+ ]
1765
+ sdist = { url = "https://files.pythonhosted.org/packages/00/31/f4aa75afd9a98724932fef9b6f1356e9305a22359a0a32eff48e0ee6cb42/smolagents-1.18.0.tar.gz", hash = "sha256:2a44bd9ac85a2c47102bf30c1bcf32e5d399e1f9d66bb9518cc5fcb30879660a", size = 181958, upload-time = "2025-06-10T14:14:34.633Z" }
1766
+ wheels = [
1767
+ { url = "https://files.pythonhosted.org/packages/d3/28/493fcf6fb5f7c4ae21b9541fd588fbc69a22c22d3d9477771554a0779006/smolagents-1.18.0-py3-none-any.whl", hash = "sha256:c8f6d0c37808090213f45ec742be7f818e7f4772516f6e776bf991936ecabaf8", size = 136036, upload-time = "2025-06-10T14:14:32.949Z" },
1768
+ ]
1769
+
1770
+ [package.optional-dependencies]
1771
+ mcp = [
1772
+ { name = "mcp" },
1773
+ { name = "mcpadapt" },
1774
+ ]
1775
+
1776
  [[package]]
1777
  name = "sniffio"
1778
  version = "1.3.1"
 
1955
  { url = "https://files.pythonhosted.org/packages/e1/90/6ff1829b55cf752994be76bdf5f718db053dcb8d738f9df5b622207a4ab8/SQLAlchemy_Utils-0.38.3-py3-none-any.whl", hash = "sha256:5c13b5d08adfaa85f3d4e8ec09a75136216fad41346980d02974a70a77988bf9", size = 100264, upload-time = "2022-07-12T05:08:31.496Z" },
1956
  ]
1957
 
1958
+ [[package]]
1959
+ name = "sse-starlette"
1960
+ version = "2.3.6"
1961
+ source = { registry = "https://pypi.org/simple" }
1962
+ dependencies = [
1963
+ { name = "anyio" },
1964
+ ]
1965
+ sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284, upload-time = "2025-05-30T13:34:12.914Z" }
1966
+ wheels = [
1967
+ { url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606, upload-time = "2025-05-30T13:34:11.703Z" },
1968
+ ]
1969
+
1970
  [[package]]
1971
  name = "sssom"
1972
  version = "0.4.16"
 
2005
  { url = "https://files.pythonhosted.org/packages/54/61/dfcccdf334ece31ad67babd095ab9d5750f454411a3f7679155c0b2885d2/sssom_schema-1.0.0-py3-none-any.whl", hash = "sha256:e41031637094eddce7e71d6642e1f7b3f972aae08c0039c1dd27fe5208835d15", size = 32437, upload-time = "2024-08-09T19:46:06.386Z" },
2006
  ]
2007
 
2008
+ [[package]]
2009
+ name = "starlette"
2010
+ version = "0.47.0"
2011
+ source = { registry = "https://pypi.org/simple" }
2012
+ dependencies = [
2013
+ { name = "anyio" },
2014
+ ]
2015
+ sdist = { url = "https://files.pythonhosted.org/packages/8b/d0/0332bd8a25779a0e2082b0e179805ad39afad642938b371ae0882e7f880d/starlette-0.47.0.tar.gz", hash = "sha256:1f64887e94a447fed5f23309fb6890ef23349b7e478faa7b24a851cd4eb844af", size = 2582856, upload-time = "2025-05-29T15:45:27.628Z" }
2016
+ wheels = [
2017
+ { url = "https://files.pythonhosted.org/packages/e3/81/c60b35fe9674f63b38a8feafc414fca0da378a9dbd5fa1e0b8d23fcc7a9b/starlette-0.47.0-py3-none-any.whl", hash = "sha256:9d052d4933683af40ffd47c7465433570b4949dc937e20ad1d73b34e72f10c37", size = 72796, upload-time = "2025-05-29T15:45:26.305Z" },
2018
+ ]
2019
+
2020
  [[package]]
2021
  name = "tenacity"
2022
  version = "9.1.2"
 
2107
  { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" },
2108
  ]
2109
 
2110
+ [[package]]
2111
+ name = "uvicorn"
2112
+ version = "0.34.3"
2113
+ source = { registry = "https://pypi.org/simple" }
2114
+ dependencies = [
2115
+ { name = "click" },
2116
+ { name = "h11" },
2117
+ ]
2118
+ sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" }
2119
+ wheels = [
2120
+ { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" },
2121
+ ]
2122
+
2123
  [[package]]
2124
  name = "validators"
2125
  version = "0.35.0"