Commit c2fcc072 authored by 何家明's avatar 何家明

搭建mcp服务

parents
Pipeline #1213 canceled with stages
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
log
from pydantic import BaseModel
class QueryParam(BaseModel):
"""查询参数实体"""
message: str
"""用户输入的消息内容"""
customer_token: str
"""客户token"""
import uvicorn
from fastapi import FastAPI
from QueryParam import QueryParam
from mcp_client import user_query
app = FastAPI(name=["BME MCP服务"])
@app.post(path="/mcp/query", description="调用mcp工具查询")
async def query(query_param: QueryParam):
message = await user_query(query_param)
return {
"message": message
}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
active: deepseek_v3_bme
model:
# deepseek-v3官方模型
deepseek_v3_official:
api_key: sk-1fc82f2f3b424668bddc84c89b7131d8
base_url: https://api.deepseek.com
model_name: deepseek-chat
# deepseek-r1官方模型
deepseek_r1_official:
api_key: sk-1fc82f2f3b424668bddc84c89b7131d8
base_url: https://api.deepseek.com
model_name: deepseek-reasoner
# deepseek-r1公司模型
deepseek_r1_bme:
api_key: Tc7sY47hiU5d1LNGbJjGBfqfY13IE3khIc0uBvpJ11U
base_url: http://10.10.10.12:30070/v1
model_name: deepseek-r1
# deepseek-v3公司模型
deepseek_v3_bme:
api_key: Tc7sY47hiU5d1LNGbJjGBfqfY13IE3khIc0uBvpJ11U
base_url: http://10.10.10.12:30100/v1
model_name: deepseek-v3
server:
# 运行命令
command: python
args:
- mcp_server.py
tool_calls_deep: 20 # tool_call调用深度
log:
base_path: log
remote:
base_url:
bme-screen-service: https://vis.bmetech.com/vis
customer:
01ce2837d453c02f9b0e1828d0134e8e: bme # 超级管理员,可以查看所有客户资源
ef616aad53d3eddfb53ca71980421440: 59 # 连云港华乐合金集团有限公司
\ No newline at end of file
from contextlib import AsyncExitStack
from datetime import datetime
from typing import Optional
from loguru import logger
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from openai import OpenAI
from openai.types.chat import ChatCompletionToolParam, ChatCompletionAssistantMessageParam, \
ChatCompletionMessageToolCallParam, ChatCompletionToolMessageParam
from openai.types.chat.chat_completion_message_tool_call_param import Function
from openai.types.shared_params import FunctionDefinition
import json
import yaml
from pydantic import AnyUrl
from QueryParam import QueryParam
with open("config.yaml", "r", encoding="utf-8") as yml_file:
config = yaml.safe_load(yml_file)
if config["log"]["base_path"]:
logger.add(config["log"]["base_path"] + "/mcp_client/log_{time:%Y-%m-%d}.log", rotation="1 day", encoding="utf-8",
level="INFO")
class McpClient:
def __init__(self):
self.customer_resource: [] = None # 客户资源
self.default_system_prompt = None # mcp_server提供的默认提示词
self.available_tools: [] = None # mcp_server提供的tool
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
active_model = config["model"][config["active"]]
self.client = OpenAI(
api_key=active_model["api_key"],
base_url=active_model["base_url"],
)
self.model_name = active_model["model_name"]
self.connected = False
async def connect_to_server(self):
"""
连接mcp_server服务
"""
server_params = StdioServerParameters(
command=config["server"]["command"],
args=config["server"]["args"],
)
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
stdio, write = stdio_transport
self.session = await self.exit_stack.enter_async_context(ClientSession(stdio, write))
await self.session.initialize()
self.connected = True
async def read_mcp(self):
"""
读取mcp服务提供的数据
"""
if self.available_tools is None:
await self.get_server_tools()
if self.default_system_prompt is None:
await self.get_server_prompts()
if self.customer_resource is None:
await self.get_server_resources()
async def get_server_tools(self):
"""
获取server提供的tool
"""
mcp_server_tools = await self.session.list_tools()
self.available_tools = [ChatCompletionToolParam(
type="function",
function=FunctionDefinition(
name=tool.name,
description=tool.description,
parameters=tool.inputSchema
),
) for tool in mcp_server_tools.tools]
logger.info(f"--> available_tools: {self.available_tools}")
async def get_server_prompts(self):
"""
获取server提供的prompt
"""
mcp_server_default_system_prompt = await self.session.get_prompt(name="default_system_prompt")
self.default_system_prompt = mcp_server_default_system_prompt.messages[0].content.text
logger.info(f"--> default_system_prompt: {self.default_system_prompt}")
async def get_server_resources(self):
"""
获取server提供的resource
"""
# 这里查出所有客户信息,使用时直接取即可,不要通过id去获取客户资源,这样每次都要重新查询
mcp_server_customer_resource = await self.session.read_resource(AnyUrl("api://customers"))
self.customer_resource = json.loads(mcp_server_customer_resource.contents[0].text)
logger.info(f"--> customer_resource: {self.customer_resource}")
def deal_customer_permission(self, customer_token: str):
customer_id = config["customer"].get(customer_token, None)
if not customer_id:
logger.info(f"Access restricted, customer token[{customer_token}] is not configured!")
return "访问受限,客户信息未配置!"
if not self.customer_resource:
logger.info("No customer resources found!")
return "访问受限,客户信息未配置!"
if customer_id == "bme":
customer_resource = "客户资源如下(如果用户查询没有指定客户,请提示并要求用户传入客户信息):\n"
customer_resource += "\n".join(
f"客户id:{data.get('customerId')},客户名:{data.get('customerName')},客户全称:{data.get('customerFullname')}"
for data in self.customer_resource)
return customer_resource
else:
customer = list(filter(lambda c: c["customerId"] == customer_id, self.customer_resource))
if not customer:
return "访问受限,客户信息未配置!"
return f"""请使用下面的客户信息:
客户id:{customer[0]['customerId']}
客户名:{customer[0]['customerName']}
客户全称:{customer[0]['customerFullname']}"""
async def process_query(self, param: QueryParam):
"""
处理查询逻辑
:param param: 请求参数
:return: 经过mcp加工后的ai回答
"""
logger.info(f"--> user origin query: {param}")
messages = [
{"role": "system", "content": self.default_system_prompt},
{"role": "system", "content": self.deal_customer_permission(param.customer_token)},
{"role": "system", "content": f"如果要使用到当前时间,请使用{datetime.now()}"},
{"role": "user", "content": param.message}
]
logger.info(f"--> messages: {messages}")
logger.info(f"--> model: {self.model_name}")
# 调用ai
ai_response = self.client.chat.completions.create(
model=self.model_name,
messages=messages,
tools=self.available_tools
)
logger.info(f"--> ai response: {ai_response}")
final_text = []
chat_completion_message = ai_response.choices[0].message
final_text.append(chat_completion_message.content) if chat_completion_message.content else None
# 防止死循环
loop = 0
# 只要还存在工具调用,就循环下去
while chat_completion_message.tool_calls and loop < config["tool_calls_deep"]:
loop = loop + 1
logger.info(f"----> Available tools: {chat_completion_message.tool_calls}")
# 可能ai一次选取了多个工具,这里循环处理
for tool_call in chat_completion_message.tool_calls:
tool_name = tool_call.function.name
tool_args = json.loads(tool_call.function.arguments)
logger.info(f"------> start to call tool...")
logger.info(f"------> tool_name: {tool_name}, tool_args: {tool_args}")
result = await self.session.call_tool(tool_name, tool_args)
logger.info(f"------> call result: {result}")
messages.append(ChatCompletionAssistantMessageParam(
role="assistant",
tool_calls=[ChatCompletionMessageToolCallParam(
id=tool_call.id,
type="function",
function=Function(
name=tool_name,
arguments=json.dumps(tool_args)
)
)]
))
messages.append(ChatCompletionToolMessageParam(
role="tool",
tool_call_id=tool_call.id,
content=str(result.content)
))
ai_response = self.client.chat.completions.create(
model=self.model_name,
messages=messages,
tools=self.available_tools
)
logger.info(f"----> ai response: {ai_response}")
chat_completion_message = ai_response.choices[0].message
final_text.append(chat_completion_message.content) if chat_completion_message.content else None
return "\n".join(final_text)
async def handle_query(self, param: QueryParam):
try:
return await self.process_query(param)
except Exception as e:
logger.exception(e)
async def cleanup(self):
"""Clean up resources."""
await self.exit_stack.aclose()
client = McpClient()
async def user_query(param: QueryParam):
try:
if not client.connected:
await client.connect_to_server()
await client.read_mcp()
result = await client.handle_query(param)
logger.info(f"Final return: {result}")
return result
except Exception as e:
logger.exception(e)
import requests
import yaml
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("BME-MCP")
with open("config.yaml", "r", encoding="utf-8") as yml_file:
config = yaml.safe_load(yml_file)
base_url = config["remote"]["base_url"]
bme_screen_service = base_url["bme-screen-service"]
def deal_request_exception(response):
"""
处理response的异常信息
:param response: 响应体
:return: json数据
"""
response.raise_for_status()
response_json = response.json()
if isinstance(response_json, dict) and "success" in response_json and not response_json["success"]:
raise Exception(response_json.get("msg"))
return response_json
def get_size_limit(size: int, max_size=20, min_size=0) -> int:
"""
处理数据量边界
:param size: 数据量
:param max_size: 最大边界
:param min_size: 最小边界
:return:
"""
size = max_size if size > max_size else size
size = min_size if size < min_size else size
return size
@mcp.tool()
def get_air_pm_10_month_focus(customer_id: int) -> []:
"""
根据客户id获取本月重点关注的空气质量微站(PM10标准)
:param customer_id: 客户id
:return: 返回结构中的字段名解释:
rank:排行,
deviceNo:设备编号,
deviceName:设备名称
"""
response = requests.get(bme_screen_service + "/monitor/getMonitorRankingTitle", {
"customerId": customer_id,
"type": "1", # 随便传,该参数当前无用
"order": "1", # 随便传,该参数当前无用
})
response_data = deal_request_exception(response).get("data", {}).get("title", [])
return [{
"rank": data.get("rank"),
"deviceNo": data.get("deviceNo"),
"deviceName": data.get("deviceName")
} for data in response_data]
@mcp.tool()
def get_air_pm_rank(customer_id: int, order: int=1, statistic_type: int=1) -> []:
"""
根据客户id获取空气质量微站排行(包含PM2.5和PM10标准)
:param customer_id: 客户id
:param order: 排序规则:按统计区间的类型进行排序,升序排序传1,降序排序传2,默认为1
:param statistic_type: 统计区间:按小时统计传1,按日统计传2,要按月统计传3,默认为1
:return: 返回结构中的字段名解释:
PM10:PM10的数据集,
PM2.5:PM2.5的数据集,
avgValue:平均值 单位为μg/m³,
deviceNo:设备编号,
deviceName:设备名称,
"""
response = requests.get(bme_screen_service + "/monitor/getMonitorRanking", {
"customerId": customer_id,
"type": statistic_type,
"order": order,
})
response_data = deal_request_exception(response).get("data", {})
result = {
"PM10": [{
"avgValue": pm10.get("avg_value"),
"deviceNo": pm10.get("deviceNo"),
"deviceName": pm10.get("deviceName")
} for pm10 in response_data.get("pm10lists")],
"PM2.5": [{
"avgValue": pm2_5.get("avg_value"),
"deviceNo": pm2_5.get("deviceNo"),
"deviceName": pm2_5.get("deviceName")
} for pm2_5 in response_data.get("pm25lists")],
}
return result
@mcp.tool()
def get_tsp_rank(customer_id: int, order: int=1, statistic_type: int=1, size: int=10) -> []:
"""
根据客户id获取TSP监测排行
:param customer_id: 客户id
:param order: 排序规则:按统计区间的类型进行排序,升序排序传1,降序排序传2,默认为1
:param statistic_type: 统计区间:按小时统计传1,按日统计传2,要按月统计传3,默认为1
:param size: 要取多少条数据,比如按小时升序取前两条,则传2,默认为10
:return: 返回结构中的字段名解释:
total: 区域总数
data: 详细数据
data->area: 区域名称
data->devicecount: 关联设备数
data->realtimePollutionIndex: 实时污染指数
data->hourRank: 小时排名
data->dayRank: 日排名
data->monthRank: 月排名
"""
response = requests.get(bme_screen_service + "/monitor/getMonitorTSPRanking", {
"customerId": customer_id,
"order": order,
"pageNo": 1,
"pageSize": get_size_limit(size),
"type": statistic_type,
})
response_data = deal_request_exception(response).get("data", {})
result = {
"total": response_data.get("areaCount", 0),
"data": [{
"area": data.get("area"),
"devicecount": data.get("devicecount"),
"realtimePollutionIndex": f"{data.get('tdc')} {data.get('tspUnit')}",
"hourRank": data.get("hourRank"),
"dayRank": data.get("dayRank"),
"monthRank": data.get("monthRank"),
} for data in response_data.get("page", {}).get("records")],
}
return result
@mcp.tool()
def get_governance_process_statistics(customer_id: int) -> {}:
"""
根据客户id获取今天治理过程记录统计数据
:param customer_id: 客户id
:return: 返回结构中的字段名解释:
timeCount: 今日雾炮平均开启时长(小时)
allCount: 今日污染发生次数(次)
openCount: 今日雾炮开启次数(次)
"""
response = requests.get(bme_screen_service + "/eq/selectEquCount", {"customerId": customer_id})
response_data = deal_request_exception(response).get("data", {})
result = {
"timeCount": response_data.get("timeCount", 0),
"allCount": response_data.get("allCount", 0),
"openCount": response_data.get("openCount", 0),
}
return result
@mcp.tool()
def get_governance_process_records(customer_id: int, instruct_type: str, device_name: str, start_time: str,
end_time: str, size: int) -> []:
"""
根据客户id查询治理过程全记录列表
:param customer_id: 客户id
:param instruct_type: 触发机制:智能联动为0,手动开启为1,全部为空
:param device_name: 设备名称
:param start_time: 开始时间,格式为yyyy-MM-dd HH:mm:ss,如:2025-04-27 17:30:00,默认为七天前的时间
:param end_time: 结束时间,格式为yyyy-MM-dd HH:mm:ss,如:2025-04-27 17:30:00,默认为当前时间
:param size: 查询数量,默认10
:return: 返回结构中的字段名解释:
total:总数(由于该接口是分页查询,所以这里返回总数)
data:数据集
data->createTime: 时间
data->deviceName: 设备
data->position: 位置
data->instructType: 触发机制,0为智能联动,1为手动开启
data->describe: 治理过程描述
"""
response = requests.get(bme_screen_service + "/eq/selectEquPage", {
"customerId": customer_id,
"InstructType": instruct_type,
"deviceName": device_name,
"StatcDate": start_time,
"StopDate": end_time,
"pageNo": 1,
"pageSize": get_size_limit(size),
})
response_data = deal_request_exception(response).get("data", {})
result = {
"total": response_data.get("total", 0),
"data": [{
"createTime": data.get("createTime"),
"deviceName": data.get("deviceName"),
"position": data.get("position"),
"instructType": data.get("instructType"),
"describe": data.get("describe"),
} for data in response_data.get("records")],
}
return result
@mcp.resource("api://customers")
def get_all_available_customer() -> []:
"""
获取所有的客户信息
:return: 客户信息数据
"""
response = requests.get(bme_screen_service + "/reportgen/customer")
response_data = deal_request_exception(response).get("data", [])
return [{
"customerId": data.get("customerId"),
"customerName": data.get("customerName"),
"customerFullname": data.get("customerFullname")
} for data in response_data]
@mcp.prompt(name="default_system_prompt")
def get_default_system_prompt() -> str:
"""
默认的系统提示词
:return: 默认的系统提示词
"""
return (
"你可以结合一系列的工具(tool)来回答用户的问题。\n"
"以下是你应该始终遵循的规则:\n"
"1.始终传入类型正确的参数,如果从输入中没有解析到参数,则取工具对应参数描述的默认值,如果也没有默认值,则结束流程并告知用户信息不全。\n"
"2.只在需要时调用工具,如果你不需要额外信息,不要调用搜索代理,尽量自己解决任务。\n"
"3.如果不需要调用工具,直接回答问题即可。\n"
"4.永远不要用完全相同的参数重新进行之前的工具调用。\n"
"5.如果要使用到客户信息,当用名字查找时,优先匹配客户全称,其次匹配客户名,再考虑使用部分匹配,最后考虑读音相近的名字,都没找到则结束流程并告知用户客户不存在。\n"
)
if __name__ == '__main__':
mcp.run(transport="stdio")
[project]
name = "bme-mcp"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"mcp[cli]>=1.6.0",
]
version = 1
revision = 2
requires-python = ">=3.13"
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload_time = "2024-05-20T21:33:25.928Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload_time = "2024-05-20T21:33:24.1Z" },
]
[[package]]
name = "anyio"
version = "4.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload_time = "2025-03-17T00:02:54.77Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload_time = "2025-03-17T00:02:52.713Z" },
]
[[package]]
name = "bme-mcp"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "mcp", extra = ["cli"] },
]
[package.metadata]
requires-dist = [{ name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }]
[[package]]
name = "certifi"
version = "2025.1.31"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload_time = "2025-01-31T02:16:47.166Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload_time = "2025-01-31T02:16:45.015Z" },
]
[[package]]
name = "click"
version = "8.1.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload_time = "2024-12-21T18:38:44.339Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload_time = "2024-12-21T18:38:41.666Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload_time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload_time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "h11"
version = "0.14.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload_time = "2022-09-25T15:40:01.519Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload_time = "2022-09-25T15:39:59.68Z" },
]
[[package]]
name = "httpcore"
version = "1.0.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/45/ad3e1b4d448f22c0cff4f5692f5ed0666658578e358b8d58a19846048059/httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad", size = 85385, upload_time = "2025-04-11T14:42:46.661Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/8d/f052b1e336bb2c1fc7ed1aaed898aa570c0b61a09707b108979d9fc6e308/httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be", size = 78732, upload_time = "2025-04-11T14:42:44.896Z" },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload_time = "2024-12-06T15:37:23.222Z" }
wheels = [
{ 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" },
]
[[package]]
name = "httpx-sse"
version = "0.4.0"
source = { registry = "https://pypi.org/simple" }
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" }
wheels = [
{ 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" },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload_time = "2024-09-15T18:07:39.745Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload_time = "2024-09-15T18:07:37.964Z" },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
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" }
wheels = [
{ 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" },
]
[[package]]
name = "mcp"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "httpx" },
{ name = "httpx-sse" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
{ name = "sse-starlette" },
{ name = "starlette" },
{ name = "uvicorn" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723", size = 200031, upload_time = "2025-03-27T16:46:32.336Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077, upload_time = "2025-03-27T16:46:29.919Z" },
]
[package.optional-dependencies]
cli = [
{ name = "python-dotenv" },
{ name = "typer" },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
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" }
wheels = [
{ 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" },
]
[[package]]
name = "pydantic"
version = "2.11.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513, upload_time = "2025-04-08T13:27:06.399Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591, upload_time = "2025-04-08T13:27:03.789Z" },
]
[[package]]
name = "pydantic-core"
version = "2.33.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395, upload_time = "2025-04-02T09:49:41.8Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551, upload_time = "2025-04-02T09:47:51.648Z" },
{ url = "https://files.pythonhosted.org/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785, upload_time = "2025-04-02T09:47:53.149Z" },
{ url = "https://files.pythonhosted.org/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758, upload_time = "2025-04-02T09:47:55.006Z" },
{ url = "https://files.pythonhosted.org/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109, upload_time = "2025-04-02T09:47:56.532Z" },
{ url = "https://files.pythonhosted.org/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159, upload_time = "2025-04-02T09:47:58.088Z" },
{ url = "https://files.pythonhosted.org/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222, upload_time = "2025-04-02T09:47:59.591Z" },
{ url = "https://files.pythonhosted.org/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980, upload_time = "2025-04-02T09:48:01.397Z" },
{ url = "https://files.pythonhosted.org/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840, upload_time = "2025-04-02T09:48:03.056Z" },
{ url = "https://files.pythonhosted.org/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518, upload_time = "2025-04-02T09:48:04.662Z" },
{ url = "https://files.pythonhosted.org/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025, upload_time = "2025-04-02T09:48:06.226Z" },
{ url = "https://files.pythonhosted.org/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991, upload_time = "2025-04-02T09:48:08.114Z" },
{ url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262, upload_time = "2025-04-02T09:48:09.708Z" },
{ url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626, upload_time = "2025-04-02T09:48:11.288Z" },
{ url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590, upload_time = "2025-04-02T09:48:12.861Z" },
{ url = "https://files.pythonhosted.org/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963, upload_time = "2025-04-02T09:48:14.553Z" },
{ url = "https://files.pythonhosted.org/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896, upload_time = "2025-04-02T09:48:16.222Z" },
{ url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810, upload_time = "2025-04-02T09:48:17.97Z" },
]
[[package]]
name = "pydantic-settings"
version = "2.9.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
{ name = "python-dotenv" },
{ name = "typing-inspection" },
]
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" }
wheels = [
{ 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" },
]
[[package]]
name = "pygments"
version = "2.19.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload_time = "2025-01-06T17:26:30.443Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload_time = "2025-01-06T17:26:25.553Z" },
]
[[package]]
name = "python-dotenv"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload_time = "2025-03-25T10:14:56.835Z" }
wheels = [
{ 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" },
]
[[package]]
name = "rich"
version = "14.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
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" }
wheels = [
{ 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" },
]
[[package]]
name = "shellingham"
version = "1.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload_time = "2023-10-24T04:13:40.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload_time = "2023-10-24T04:13:38.866Z" },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload_time = "2024-02-25T23:20:04.057Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload_time = "2024-02-25T23:20:01.196Z" },
]
[[package]]
name = "sse-starlette"
version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "starlette" },
]
sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376, upload_time = "2024-12-25T09:09:30.616Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120, upload_time = "2024-12-25T09:09:26.761Z" },
]
[[package]]
name = "starlette"
version = "0.46.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846, upload_time = "2025-04-13T13:56:17.942Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload_time = "2025-04-13T13:56:16.21Z" },
]
[[package]]
name = "typer"
version = "0.15.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "rich" },
{ name = "shellingham" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8b/6f/3991f0f1c7fcb2df31aef28e0594d8d54b05393a0e4e34c65e475c2a5d41/typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5", size = 100711, upload_time = "2025-02-27T19:17:34.807Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/fc/5b29fea8cee020515ca82cc68e3b8e1e34bb19a3535ad854cac9257b414c/typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc", size = 45061, upload_time = "2025-02-27T19:17:32.111Z" },
]
[[package]]
name = "typing-extensions"
version = "4.13.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload_time = "2025-04-10T14:19:05.416Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload_time = "2025-04-10T14:19:03.967Z" },
]
[[package]]
name = "typing-inspection"
version = "0.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload_time = "2025-02-25T17:27:59.638Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload_time = "2025-02-25T17:27:57.754Z" },
]
[[package]]
name = "uvicorn"
version = "0.34.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload_time = "2025-04-19T06:02:50.101Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload_time = "2025-04-19T06:02:48.42Z" },
]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment