This commit is contained in:
2025-06-09 00:18:57 +08:00
parent e3dbe8a7d3
commit 8226a78482
10 changed files with 196 additions and 92 deletions
+2 -4
View File
@@ -2,10 +2,8 @@ import base64
import hmac import hmac
import json import json
import time import time
from typing import Optional
from core.character.adventure import get_adv_info
from core.basic.character import Character from core.basic.character import Character
from fastapi import Header, Request from fastapi import Header
from api.core.exception import ResponseException from api.core.exception import ResponseException
@@ -67,7 +65,7 @@ def deleteToken(token, redis, env):
return token return token
def uid(access_uid: Optional[str] = Header(None)): def uid(access_uid: str | None = Header(None)):
try: try:
return int(access_uid) return int(access_uid)
except Exception: except Exception:
+1 -1
View File
@@ -1,7 +1,7 @@
import gzip import gzip
from fastapi import Request, APIRouter, FastAPI from fastapi import Request, APIRouter, FastAPI
from fastapi.responses import Response from fastapi.responses import Response
from typing import Callable from collections.abc import Callable
from fastapi.middleware.gzip import GZipMiddleware from fastapi.middleware.gzip import GZipMiddleware
from fastapi.routing import APIRoute from fastapi.routing import APIRoute
+1 -1
View File
@@ -10,7 +10,7 @@ def get_redis_info(redis, key, fun,without_gzip=False):
info = json.loads(redis.get(key)) info = json.loads(redis.get(key))
else: else:
info = json.loads(gzip_unzip(redis.get(key))) info = json.loads(gzip_unzip(redis.get(key)))
except Exception as ex: except Exception:
info = None info = None
if info is None: if info is None:
info = fun() info = fun()
+7 -7
View File
@@ -1,20 +1,20 @@
import base64 import base64
import json import json
from typing import Generic, TypeVar from typing import Generic, TypeVar
from pydantic import Field
from pydantic.generics import GenericModel from pydantic.generics import GenericModel
import gzip # 导入 gzip 模块 import gzip # 导入 gzip 模块
from io import BytesIO # 从 io 模块导入 BytesIO 类 from io import BytesIO # 从 io 模块导入 BytesIO 类
T = TypeVar('T') T = TypeVar('T')
class Return(GenericModel, Generic[T]): class Return(GenericModel, Generic[T]):
code: int = 200 code: int = Field(..., description='响应状态码')
message: str = '' """响应状态码"""
data: T | dict | str = None message: str = Field(..., description='响应消息')
"""响应消息"""
data: T = Field(..., description='响应数据')
"""响应数据"""
# 定义一个函数,将字符串压缩为 gzip 格式,并进行 base64 编码后返回结果 # 定义一个函数,将字符串压缩为 gzip 格式,并进行 base64 编码后返回结果
def gzip_str(to_gzip: str) -> str: def gzip_str(to_gzip: str) -> str:
+2 -2
View File
@@ -1,5 +1,5 @@
import time import time
from typing import Annotated, Optional from typing import Annotated
from redis import Redis from redis import Redis
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from fastapi import Depends, Header from fastapi import Depends, Header
@@ -16,7 +16,7 @@ SessionDep = Annotated[Session, Depends(get_db)]
RedisDep = Annotated[Redis, Depends(get_redis)] RedisDep = Annotated[Redis, Depends(get_redis)]
def alterToken(redis:RedisDep,alter_token: Optional[str] = Header(None)): def alterToken(redis:RedisDep,alter_token: str | None = Header(None)):
if alter_token is not None: if alter_token is not None:
try: try:
decoded_bytes = base64.b64decode(alter_token) decoded_bytes = base64.b64decode(alter_token)
+28 -36
View File
@@ -2,20 +2,19 @@ from fastapi.params import Param
from api.core.Gzip import register_gzip_request from api.core.Gzip import register_gzip_request
import json import json
from fastapi import APIRouter, Path from fastapi import APIRouter, Path
from api.core.Response import response,Return from api.core.Response import response
from api.core.Redis import get_redis_info from api.core.Redis import get_redis_info
from api.dp import RedisDep from api.dp import RedisDep
import re import re
from typing import TYPE_CHECKING, Annotated from typing import Annotated
from api.types.open import SkillInfo,SkillDetail from api.types.open import SkillDetailResponse, SkillsListResponse
if TYPE_CHECKING:
from api.core.Response import Return
router = APIRouter() router = APIRouter()
register_gzip_request(router) register_gzip_request(router)
def replace_placeholders(template): def replace_placeholders(template):
# 匹配 <int>、<float>、<float1>、<float2> 等 # 匹配 <int>、<float>、<float1>、<float2> 等
pattern = re.compile(r'<(int|float\d*)>') pattern = re.compile(r'<(int|float\d*)>')
@@ -29,64 +28,57 @@ def replace_placeholders(template):
return pattern.sub(repl, template) return pattern.sub(repl, template)
@router.get('/skills/') @router.get('/skills/', operation_id='skillsList')
async def get_skills_list(redis: RedisDep,skillName: str) -> Return[list[SkillInfo]]: async def get_skills_list(redis: RedisDep, skillName: Annotated[str, Param(..., description='技能名称,支持模糊查询')])->SkillsListResponse:
""" """
根据技能名称,获取对应的技能列表信息 根据技能名称,获取对应的技能列表信息
""" """
key = 'openapi:skills' key = 'openapi:skills'
def get_skills(): def get_skills():
# This function should retrieve the skills list from the data source # This function should retrieve the skills list from the data source
# For now, we return a placeholder list # For now, we return a placeholder list
with open('./openapi/skills.json', encoding='utf-8') as f: with open('./openapi/skills.json', encoding='utf-8') as f:
skills_data = json.load(f) skills_data = json.load(f)
return skills_data return skills_data
skills_list = get_redis_info(redis, key, get_skills) skills_list = get_redis_info(redis, key, get_skills)
data = list( data = list(filter(lambda x: re.sub(r'\s+', '', skillName) in re.sub(r'\s+', '', x['skillName']), skills_list)) if skillName else skills_list
filter(
lambda x: re.sub(r'\s+', '', skillName) == re.sub(r'\s+', '', x["skillName"]),
skills_list
)
) if skillName else skills_list
return response(data=data) return response(data=data)
@router.get('/{job}/{jobGrow}/{skillId}/') @router.get('/{jobId}/{jobGrowId}/{skillId}/', operation_id='skillDetail')
async def get_cn_skll_info(redis: RedisDep, async def get_cn_skill_info(redis: RedisDep, jobId: Annotated[str, Path(..., description='职业')], jobGrowId: Annotated[str, Path(..., description='转职')], skillId: Annotated[str, Path(..., description='技能')], level: Annotated[int, Param(..., description='技能等级')]) -> SkillDetailResponse:
job: Annotated[str,Path(..., title='职业')],
jobGrow: Annotated[str,Path(..., title='转职')],
skillId: Annotated[str,Path(..., title='技能')],
level: Annotated[int,Param(..., title="技能等级")]
) -> Return[SkillDetail]:
""" """
获取技能详细信息 获取技能详细信息
""" """
skill_info = {} skill_info = {}
key = f'openapi:{job}:{jobGrow}:{skillId}' key = f'openapi:{jobId}:{jobGrowId}:{skillId}'
try: try:
def get_skill_info(): def get_skill_info():
# This function should retrieve the skill info based on job and jobGrow # This function should retrieve the skill info based on job and jobGrow
# For now, we return a placeholder dictionary # For now, we return a placeholder dictionary
with open(f'./openapi/{job}/{jobGrow}/cn/skillDetail/{skillId}.json', encoding='utf-8') as f: with open(f'./openapi/{jobId}/{jobGrowId}/cn/skillDetail/{skillId}.json', encoding='utf-8') as f:
skill_data = json.load(f) skill_data = json.load(f)
if "levelInfo" in skill_data and "optionDesc" in skill_data["levelInfo"] and skill_data["levelInfo"]["optionDesc"] is not None: if 'levelInfo' in skill_data and 'optionDesc' in skill_data['levelInfo'] and skill_data['levelInfo']['optionDesc'] is not None:
skill_data["levelInfo"]["optionDesc"] = replace_placeholders(skill_data["levelInfo"]["optionDesc"]) skill_data['levelInfo']['optionDesc'] = replace_placeholders(skill_data['levelInfo']['optionDesc'])
return skill_data return skill_data
skill_info = get_redis_info(redis, key, get_skill_info) skill_info = get_redis_info(redis, key, get_skill_info)
if level is not None: if level is not None:
level = max(0,min(level,skill_info.get("maxLevel",0))) level = max(0, min(level, skill_info.get('maxLevel', 0)))
if "levelInfo" in skill_info and "rows" in skill_info["levelInfo"]: if 'levelInfo' in skill_info and 'rows' in skill_info['levelInfo']:
detail = list(filter(lambda x: x["level"] == level, skill_info["levelInfo"]["rows"])) detail = list(filter(lambda x: x['level'] == level, skill_info['levelInfo']['rows']))
if detail and len(detail) > 0: if detail and len(detail) > 0:
skill_info["levelInfo"].update(detail[0]) skill_info['levelInfo'].update(detail[0])
skill_info["levelInfo"]["detail"] = skill_info["levelInfo"].get("optionDesc", "") skill_info['levelInfo']['detail'] = skill_info['levelInfo'].get('optionDesc', '')
for key in skill_info["levelInfo"]["optionValue"].keys(): for key in skill_info['levelInfo']['optionValue'].keys():
skill_info["levelInfo"]["detail"] = skill_info["levelInfo"]["detail"].replace(f"{{{key}}}",str(skill_info["levelInfo"]["optionValue"][key])) skill_info['levelInfo']['detail'] = skill_info['levelInfo']['detail'].replace(f'{{{key}}}', str(skill_info['levelInfo']['optionValue'][key]))
del skill_info["levelInfo"]["rows"] del skill_info['levelInfo']['rows']
skill_info["attribute"] = {} skill_info['attribute'] = {}
skill_info["attribute"].update(skill_info["levelInfo"]) skill_info['attribute'].update(skill_info['levelInfo'])
del skill_info["levelInfo"] del skill_info['levelInfo']
except FileNotFoundError: except FileNotFoundError:
return response(code=404, message=f"技能信息未找到: {job} {jobGrow} {skillId}", data=None) return response(code=404, message=f'技能信息未找到: {jobId} {jobGrowId} {skillId}', data=None)
return response(data=skill_info) return response(data=skill_info)
+46 -35
View File
@@ -1,16 +1,22 @@
from enum import Enum from enum import Enum
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from typing import Generic, TypeVar
from pydantic.generics import GenericModel
from api.core.Response import Return as Response
class SkillInfo(BaseModel): class SkillInfo(BaseModel):
skillId: str = Field(..., description='技能ID') skillId: str = Field(..., description='技能ID')
"""技能ID""" """技能ID"""
skillName: str = Field(..., description='技能名称') skillName: str = Field(..., description='技能名称')
"""技能名称""" """技能名称"""
job: str = Field(..., description='职业') jobId: str = Field(..., description='职业Id')
"""职业""" """职业Id"""
jobGrow: str = Field(..., description='转职') jobName: str = Field(..., description='职业名称')
"""转职""" """职业名称"""
jobGrowId: str = Field(..., description='转职Id')
"""转职Id"""
jobGrowName: str = Field(..., description='转职名称')
"""转职名称"""
class SkillType(str, Enum): class SkillType(str, Enum):
@@ -33,36 +39,38 @@ class IConsumeItem(BaseModel):
"""物品ID""" """物品ID"""
itemName: str = Field(..., description='物品名称') itemName: str = Field(..., description='物品名称')
"""物品名称""" """物品名称"""
value: str = Field(..., description='物品类型') value: int = Field(..., description='物品类型')
"""物品类型""" """物品类型"""
class IPreSkill(BaseModel): class IPreSkill(BaseModel):
level: str = Field(..., description='前置技能等级') level: int = Field(..., description='前置技能等级')
"""前置技能等级""" """前置技能等级"""
name: str = Field(..., description='前置技能名称') name: str = Field(..., description='前置技能名称')
"""前置技能名称""" """前置技能名称"""
skillId: int = Field(..., description='前置技能ID') skillId: str = Field(..., description='前置技能ID')
"""前置技能ID""" """前置技能ID"""
class IAttribute(BaseModel): class IAttribute(BaseModel):
castingTime: float | None = Field(..., description='施放时间') castingTime: float | None = Field(None, description='施放时间')
"""施放时间""" """施放时间"""
coolTime: float | None = Field(..., description='冷却时间') coolTime: float | None = Field(None, description='冷却时间')
"""冷却时间""" """冷却时间"""
consumeMp: int | None = Field(..., description='魔法值') consumeMp: int | None = Field(None, description='魔法值')
"""魔法值""" """魔法值"""
detail: str | None = Field(..., description='技能描述') detail: str | None = Field(None, description='技能描述')
"""技能描述""" """技能描述"""
level: int | None = Field(..., description='技能等级') level: int | None = Field(None, description='技能等级')
"""技能等级""" """技能等级"""
optionDesc: str | None = Field(..., description='技能选项描述') optionDesc: str | None = Field(None, description='技能选项描述')
"""技能选项描述""" """技能选项描述"""
optionValue: dict[str, float] | None = Field(..., description='技能选项值') optionValue: dict[str, float | str] | None = Field(None, description='技能选项值')
class ILevelInfo(BaseModel): class ILevelInfo(BaseModel):
optionDesc: str | None = Field(..., description='技能选项描述') optionDesc: str | None = Field(None, description='技能选项描述')
rows: list[IAttribute] | None = Field(..., description='技能等级信息') rows: list[IAttribute] | None = Field(None, description='技能等级信息')
class IEvolution(BaseModel): class IEvolution(BaseModel):
@@ -70,23 +78,23 @@ class IEvolution(BaseModel):
"""进化类型""" """进化类型"""
name: str = Field(..., description='进化名称') name: str = Field(..., description='进化名称')
"""进化名称""" """进化名称"""
desc: str | None = Field(..., description='进化简介') desc: str = Field(..., description='进化简介')
"""进化简介""" """进化简介"""
descDetail: str | None = Field(..., description='进化详细描述') descDetail: str = Field(..., description='进化详细描述')
"""进化详细描述""" """进化详细描述"""
class IKeyValues(BaseModel): class IKeyValues(BaseModel):
name: str = Field(..., description='名称') name: str = Field(..., description='名称')
"""名称""" """名称"""
value: str | None = Field(..., description='数值') value: str = Field(..., description='数值')
"""数值""" """数值"""
class IEnhancement(BaseModel): class IEnhancement(BaseModel):
type: str = Field(..., description='强化类型') type: int = Field(None, description='强化类型')
"""强化类型""" """强化类型"""
status: list[IKeyValues] | None = Field(..., description='强化状态') status: list[IKeyValues] = Field(None, description='强化状态')
class SkillDetail(BaseModel): class SkillDetail(BaseModel):
@@ -94,31 +102,34 @@ class SkillDetail(BaseModel):
"""技能名称""" """技能名称"""
type: SkillType = Field(..., description='技能类型') type: SkillType = Field(..., description='技能类型')
"""技能类型""" """技能类型"""
desc: str | None = Field(..., description='技能描述') desc: str | None = Field(None, description='技能描述')
"""技能描述""" """技能描述"""
descDetail: str | None = Field(..., description='技能详细描述') descDetail: str | None = Field(None, description='技能详细描述')
"""技能详细描述""" """技能详细描述"""
consumeItem: IConsumeItem | None = Field(..., description='消耗物品') consumeItem: IConsumeItem | None = Field(None, description='消耗物品')
"""消耗物品""" """消耗物品"""
descSpecial: str | None = Field(..., description='特殊功能') descSpecial: list[str] | None = Field(None, description='特殊功能')
"""特殊功能""" """特殊功能"""
maxLevel: int = Field(..., description='等级上限') maxLevel: int = Field(..., description='等级上限')
"""等级上限""" """等级上限"""
requiredLevel: int = Field(1, description='学习等级') requiredLevel: int = Field(..., description='学习等级')
"""学习等级""" """学习等级"""
requiredLevelRange: str | None = Field(..., description='学习间隔') requiredLevelRange: int = Field(..., description='学习间隔')
"""学习间隔""" """学习间隔"""
preRequiredSkill: list[IPreSkill] | None = Field(..., description='前置技能') preRequiredSkill: list[IPreSkill] | None = Field(None, description='前置技能')
"""前置技能""" """前置技能"""
jobId: str | None = Field(..., description='职业ID') jobId: str = Field(..., description='职业ID')
"""职业ID""" """职业ID"""
jobName: str | None = Field(..., description='职业名称') jobName: str = Field(..., description='职业名称')
"""职业名称""" """职业名称"""
levelInfo: list[ILevelInfo] | None = Field(..., description='技能等级信息') levelInfo: list[ILevelInfo] | None = Field(None, description='技能等级信息')
"""技能等级信息""" """技能等级信息"""
attribute: IAttribute | None = Field(..., description='技能属性') attribute: IAttribute | None = Field(None, description='技能属性')
"""技能属性""" """技能属性"""
evolution: list[IEvolution] | None = Field(..., description='技能进化信息') evolution: list[IEvolution] | None = Field(None, description='技能进化信息')
"""技能进化信息""" """技能进化信息"""
enhancement: list[IEnhancement] | None = Field(..., description='技能强化信息') enhancement: list[IEnhancement] | None = Field(None, description='技能强化信息')
"""技能强化信息""" """技能强化信息"""
SkillDetailResponse = Response[SkillDetail]
SkillsListResponse = Response[list[SkillInfo]]
+5 -2
View File
@@ -3,15 +3,18 @@ from api.main import api_router
from fastapi import FastAPI from fastapi import FastAPI
from multiprocessing import freeze_support from multiprocessing import freeze_support
from api.core import Exceptions,Middleware,Gzip from api.core import Exceptions,Middleware,Gzip
from fastapi_mcp import FastApiMCP
import uvicorn import uvicorn
application = FastAPI(docs_url='/doc', redoc_url=None)
application = FastAPI(docs_url=None, redoc_url=None)
application.add_exception_handler(Exception, Exceptions.all_exception_handler) application.add_exception_handler(Exception, Exceptions.all_exception_handler)
Middleware.register_cors(application) Middleware.register_cors(application)
Gzip.register_gzip_response(application) Gzip.register_gzip_response(application)
application.include_router(api_router) application.include_router(api_router)
mcp = FastApiMCP(application,name="dcalc-mcp",description="Dcalc MCP Service",include_operations=["skillsList", "skillDetail"])
mcp.mount()
if __name__ == '__main__': if __name__ == '__main__':
freeze_support() freeze_support()
+1
View File
@@ -5,6 +5,7 @@ description = "A backend project for dcalc, a calculator application."
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [ dependencies = [
"fastapi-mcp>=0.3.4",
"fastapi[all]>=0.115.11", "fastapi[all]>=0.115.11",
"redis>=5.2.1", "redis>=5.2.1",
"requests>=2.32.3", "requests>=2.32.3",
+103 -4
View File
@@ -2,10 +2,6 @@ version = 1
revision = 1 revision = 1
requires-python = ">=3.10" requires-python = ">=3.10"
[[tool.uv.index]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
default = true
[[package]] [[package]]
name = "annotated-types" name = "annotated-types"
version = "0.7.0" version = "0.7.0"
@@ -136,6 +132,7 @@ version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "fastapi", extra = ["all"] }, { name = "fastapi", extra = ["all"] },
{ name = "fastapi-mcp" },
{ name = "redis" }, { name = "redis" },
{ name = "requests" }, { name = "requests" },
{ name = "sqlalchemy" }, { name = "sqlalchemy" },
@@ -144,6 +141,7 @@ dependencies = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "fastapi", extras = ["all"], specifier = ">=0.115.11" }, { name = "fastapi", extras = ["all"], specifier = ">=0.115.11" },
{ name = "fastapi-mcp", specifier = ">=0.3.4" },
{ name = "redis", specifier = ">=5.2.1" }, { name = "redis", specifier = ">=5.2.1" },
{ name = "requests", specifier = ">=2.32.3" }, { name = "requests", specifier = ">=2.32.3" },
{ name = "sqlalchemy", specifier = ">=2.0.38" }, { name = "sqlalchemy", specifier = ">=2.0.38" },
@@ -229,6 +227,27 @@ standard = [
{ name = "uvicorn", extra = ["standard"] }, { name = "uvicorn", extra = ["standard"] },
] ]
[[package]]
name = "fastapi-mcp"
version = "0.3.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "fastapi" },
{ name = "httpx" },
{ name = "mcp" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
{ name = "requests" },
{ name = "rich" },
{ name = "tomli" },
{ name = "typer" },
{ name = "uvicorn" },
]
sdist = { url = "https://files.pythonhosted.org/packages/73/93/f0cc88f6ec51ce9c92570d097c3e94a6fe5c98cf9f372504456fc29cb33c/fastapi_mcp-0.3.4.tar.gz", hash = "sha256:2a83dde5b249cfa6276f0ee90958387bb8c9c430f1a6213f559ead757dfe18b2", size = 164950 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b8/4e/63e2ae715135406b0d3c74c301743f470888740bd4ed0ebd170a8f005ca3/fastapi_mcp-0.3.4-py3-none-any.whl", hash = "sha256:b2bcddd0514201c6434b970263f8f4e9e8e1f494ea93fdc0de251e19337e19cf", size = 22928 },
]
[[package]] [[package]]
name = "greenlet" name = "greenlet"
version = "3.1.1" version = "3.1.1"
@@ -353,6 +372,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
] ]
[[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 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.10" version = "3.10"
@@ -453,6 +481,26 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
] ]
[[package]]
name = "mcp"
version = "1.9.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "httpx" },
{ name = "httpx-sse" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
{ name = "python-multipart" },
{ name = "sse-starlette" },
{ name = "starlette" },
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f2/df/8fefc0c6c7a5c66914763e3ff3893f9a03435628f6625d5e3b0dc45d73db/mcp-1.9.3.tar.gz", hash = "sha256:587ba38448e81885e5d1b84055cfcc0ca56d35cd0c58f50941cab01109405388", size = 333045 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/79/45/823ad05504bea55cb0feb7470387f151252127ad5c72f8882e8fe6cf5c0e/mcp-1.9.3-py3-none-any.whl", hash = "sha256:69b0136d1ac9927402ed4cf221d4b8ff875e7132b0b06edd446448766f34f9b9", size = 131063 },
]
[[package]] [[package]]
name = "mdurl" name = "mdurl"
version = "0.1.2" version = "0.1.2"
@@ -826,6 +874,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/aa/e4/592120713a314621c692211eba034d09becaf6bc8848fabc1dc2a54d8c16/SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753", size = 1896347 }, { url = "https://files.pythonhosted.org/packages/aa/e4/592120713a314621c692211eba034d09becaf6bc8848fabc1dc2a54d8c16/SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753", size = 1896347 },
] ]
[[package]]
name = "sse-starlette"
version = "2.3.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606 },
]
[[package]] [[package]]
name = "starlette" name = "starlette"
version = "0.46.1" version = "0.46.1"
@@ -838,6 +898,45 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 }, { url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 },
] ]
[[package]]
name = "tomli"
version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 },
{ url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 },
{ url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 },
{ url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 },
{ url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 },
{ url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 },
{ url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 },
{ url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 },
{ url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 },
{ url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 },
{ url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 },
{ url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 },
{ url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 },
{ url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 },
{ url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 },
{ url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 },
{ url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 },
{ url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 },
{ url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 },
{ url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 },
{ url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 },
{ url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 },
{ url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 },
{ url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 },
{ url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 },
{ url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 },
{ url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 },
{ url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 },
{ url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 },
{ url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 },
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 },
]
[[package]] [[package]]
name = "typer" name = "typer"
version = "0.15.2" version = "0.15.2"