chore: update workspace config and memory
This commit is contained in:
11
KIS_MCP_Server/.gitignore
vendored
Normal file
11
KIS_MCP_Server/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
.venv/
|
||||
|
||||
# Environment and sensitive files
|
||||
.env
|
||||
token.json
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
1
KIS_MCP_Server/.python-version
Normal file
1
KIS_MCP_Server/.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.13
|
||||
21
KIS_MCP_Server/LICENSE
Normal file
21
KIS_MCP_Server/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 HYUNWOO PARK
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
196
KIS_MCP_Server/README.md
Normal file
196
KIS_MCP_Server/README.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# 한국투자증권 REST API MCP (Model Context Protocol)
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://mseep.ai/app/51ce86bd-d78d-48da-8227-1e5cf29157d5)
|
||||
|
||||
<a href="https://glama.ai/mcp/servers/@migusdn/KIS_MCP_Server">
|
||||
<img width="380" height="200" src="https://glama.ai/mcp/servers/@migusdn/KIS_MCP_Server/badge" alt="KIS REST API Server MCP server" />
|
||||
</a>
|
||||
|
||||
한국투자증권(KIS)의 REST API를 사용하여 주식 거래 및 시세 정보를 조회하는 MCP(Model Context Protocol) 서버입니다. 국내 및 해외 주식 거래, 시세 조회, 계좌 관리 등 다양한 금융 거래 기능을 제공합니다.
|
||||
|
||||
## ✨ 주요 기능
|
||||
|
||||
- 🇰🇷 **국내 주식 거래**
|
||||
- 실시간 현재가 조회
|
||||
- 매수/매도 주문
|
||||
- 잔고 조회
|
||||
- 호가 정보 조회
|
||||
- 주문 내역 조회
|
||||
|
||||
- 🌏 **해외 주식 거래**
|
||||
- 미국, 일본, 중국, 홍콩, 베트남 등 주요 시장 지원
|
||||
- 실시간 현재가 조회
|
||||
- 매수/매도 주문
|
||||
|
||||
- ⚡ **특징**
|
||||
- 비동기 처리로 빠른 응답
|
||||
- 실시간 시세 및 체결 정보
|
||||
- 안정적인 에러 처리
|
||||
- 확장 가능한 설계
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
이 프로젝트는 아직 개발 중인 미완성 프로젝트입니다. 실제 투자에 사용하기 전에 충분한 테스트를 거치시기 바랍니다.
|
||||
|
||||
* 본 프로젝트를 사용하여 발생하는 모든 손실과 책임은 전적으로 사용자에게 있습니다.
|
||||
* API 사용 시 한국투자증권의 이용약관을 준수해야 합니다.
|
||||
* 실제 계좌 사용 시 주의가 필요하며, 모의투자 계좌로 충분한 테스트를 권장합니다.
|
||||
* API 호출 제한과 관련된 제약사항을 반드시 확인하시기 바랍니다.
|
||||
|
||||
## Requirements
|
||||
|
||||
* Python >= 3.13
|
||||
* uv (Python packaging tool)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# 1. Install uv if not already installed
|
||||
pip install uv
|
||||
|
||||
# 2. Create and activate virtual environment
|
||||
uv venv
|
||||
source .venv/bin/activate # Linux/MacOS
|
||||
# or
|
||||
.venv\\Scripts\\activate # Windows
|
||||
|
||||
# 3. Install dependencies
|
||||
uv pip install -e .
|
||||
|
||||
mcp install server.py \
|
||||
-v KIS_APP_KEY={KIS_APP_KEY} \
|
||||
-v KIS_APP_SECRET={KIS_APP_SECRET} \
|
||||
-v KIS_ACCOUNT_TYPE={KIS_ACCOUNT_TYPE} \
|
||||
-v KIS_CANO={KIS_CANO}
|
||||
```
|
||||
|
||||
### MCP Server Configuration
|
||||
|
||||
You can also configure the MCP server using a JSON configuration file. Create a file named `mcp-config.json` with the following content (replace the paths and environment variables with your own):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"KIS MCP Server": {
|
||||
"command": "/opt/homebrew/bin/uv",
|
||||
"args": [
|
||||
"run",
|
||||
"--with",
|
||||
"httpx",
|
||||
"--with",
|
||||
"mcp[cli]",
|
||||
"--with",
|
||||
"xmltodict",
|
||||
"mcp",
|
||||
"run",
|
||||
"/path/to/your/project/server.py"
|
||||
],
|
||||
"env": {
|
||||
"KIS_APP_KEY": "your_app_key",
|
||||
"KIS_APP_SECRET": "your_secret_key",
|
||||
"KIS_ACCOUNT_TYPE": "VIRTUAL",
|
||||
"KIS_CANO": "your_account_number"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration can be used with MCP-compatible tools and IDEs to run the server with the specified dependencies and environment variables.
|
||||
|
||||
## Functions
|
||||
|
||||
### Domestic Stock Trading
|
||||
|
||||
* **inquery_stock_price** - 주식 현재가 조회
|
||||
* `symbol`: 종목코드 (예: "005930") (string, required)
|
||||
* Returns: 현재가, 전일대비, 등락률, 거래량 등
|
||||
|
||||
* **order_stock** - 주식 매수/매도 주문
|
||||
* `symbol`: 종목코드 (string, required)
|
||||
* `quantity`: 주문수량 (number, required)
|
||||
* `price`: 주문가격 (0: 시장가) (number, required)
|
||||
* `order_type`: 주문유형 ("buy" 또는 "sell") (string, required)
|
||||
|
||||
* **inquery_balance** - 계좌 잔고 조회
|
||||
* Returns: 보유종목, 평가금액, 손익현황 등
|
||||
|
||||
* **inquery_order_list** - 일별 주문 내역 조회
|
||||
* `start_date`: 조회 시작일 (YYYYMMDD) (string, required)
|
||||
* `end_date`: 조회 종료일 (YYYYMMDD) (string, required)
|
||||
|
||||
* **inquery_order_detail** - 주문 상세 내역 조회
|
||||
* `order_no`: 주문번호 (string, required)
|
||||
* `order_date`: 주문일자 (YYYYMMDD) (string, required)
|
||||
|
||||
* **inquery_stock_ask** - 호가 정보 조회
|
||||
* `symbol`: 종목코드 (string, required)
|
||||
* Returns: 매도/매수 호가, 호가수량 등
|
||||
|
||||
### Overseas Stock Trading
|
||||
|
||||
* **order_overseas_stock** - 해외 주식 매수/매도 주문
|
||||
* `symbol`: 종목코드 (예: "AAPL") (string, required)
|
||||
* `quantity`: 주문수량 (number, required)
|
||||
* `price`: 주문가격 (number, required)
|
||||
* `order_type`: 주문유형 ("buy" 또는 "sell") (string, required)
|
||||
* `market`: 시장코드 (string, required)
|
||||
* "NAS": 나스닥
|
||||
* "NYSE": 뉴욕
|
||||
* "AMEX": 아멕스
|
||||
* "SEHK": 홍콩
|
||||
* "SHAA": 중국상해
|
||||
* "SZAA": 중국심천
|
||||
* "TKSE": 일본
|
||||
* "HASE": 베트남 하노이
|
||||
* "VNSE": 베트남 호치민
|
||||
|
||||
* **inquery_overseas_stock_price** - 해외 주식 현재가 조회
|
||||
* `symbol`: 종목코드 (string, required)
|
||||
* `market`: 시장코드 (string, required)
|
||||
|
||||
## Resources
|
||||
|
||||
### Configuration
|
||||
|
||||
환경 변수를 통해 API 키와 계좌 정보를 설정합니다:
|
||||
|
||||
* `KIS_APP_KEY`: 한국투자증권 앱키
|
||||
* `KIS_APP_SECRET`: 한국투자증권 시크릿키
|
||||
* `KIS_ACCOUNT_TYPE`: 계좌 타입 ("REAL" 또는 "VIRTUAL")
|
||||
* `KIS_CANO`: 계좌번호
|
||||
|
||||
### Trading Hours
|
||||
|
||||
국내 주식:
|
||||
* 정규장: 09:00 ~ 15:30
|
||||
* 시간외 단일가: 15:40 ~ 16:00
|
||||
|
||||
해외 주식:
|
||||
* 미국(나스닥/뉴욕): 22:30 ~ 05:00 (한국시간)
|
||||
* 일본: 09:00 ~ 15:10
|
||||
* 중국: 10:30 ~ 16:00
|
||||
* 홍콩: 10:30 ~ 16:00
|
||||
* 베트남: 11:15 ~ 16:15
|
||||
|
||||
## Error Handling
|
||||
|
||||
API 호출 시 발생할 수 있는 주요 에러:
|
||||
|
||||
* 인증 오류: API 키 또는 시크릿키가 잘못된 경우
|
||||
* 잔고 부족: 주문 금액이 계좌 잔고보다 큰 경우
|
||||
* 시간 제한: 거래 시간이 아닌 경우
|
||||
* 주문 제한: 주문 수량이나 금액이 제한을 초과한 경우
|
||||
|
||||
## About
|
||||
|
||||
* 확장 가능한 설계
|
||||
* 비동기 처리로 빠른 응답
|
||||
* 실시간 시세 및 체결 정보
|
||||
* 안정적인 에러 처리
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
174
KIS_MCP_Server/example.py
Normal file
174
KIS_MCP_Server/example.py
Normal file
@@ -0,0 +1,174 @@
|
||||
import asyncio
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from server import (
|
||||
inquery_stock_price,
|
||||
inquery_balance,
|
||||
order_stock,
|
||||
inquery_order_list,
|
||||
inquery_order_detail,
|
||||
inquery_stock_info,
|
||||
inquery_stock_history,
|
||||
inquery_stock_ask,
|
||||
order_overseas_stock,
|
||||
inquery_overseas_stock_price
|
||||
)
|
||||
|
||||
async def test_domestic_stock(symbol: str, name: str):
|
||||
"""Test domestic stock price inquiry
|
||||
|
||||
Args:
|
||||
symbol: Stock symbol (e.g. "005930")
|
||||
name: Stock name (e.g. "Samsung Electronics")
|
||||
"""
|
||||
try:
|
||||
result = await inquery_stock_price(symbol=symbol)
|
||||
print(f"\n{name} ({symbol}):")
|
||||
print(f"Current price: {result['stck_prpr']}")
|
||||
print(f"Change: {result['prdy_vrss']} ({result['prdy_ctrt']}%)")
|
||||
print(f"Volume: {result['acml_vol']}")
|
||||
print(f"Trading value: {result['acml_tr_pbmn']}")
|
||||
except Exception as e:
|
||||
print(f"Error in {name} test: {str(e)}")
|
||||
|
||||
async def test_balance():
|
||||
"""Test balance inquiry"""
|
||||
try:
|
||||
result = await inquery_balance()
|
||||
print("\nAccount Balance Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"Error testing balance: {e}")
|
||||
|
||||
async def test_order_stock():
|
||||
"""Test stock order"""
|
||||
try:
|
||||
# 시장가 매수
|
||||
result = await order_stock("005930", 1, 0, "buy")
|
||||
print("\nMarket Price Buy Order Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
|
||||
# 지정가 매수
|
||||
result = await order_stock("005930", 1, 55000, "buy")
|
||||
print("\nLimit Price Buy Order Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
|
||||
# 시장가 매도
|
||||
result = await order_stock("005930", 1, 0, "sell")
|
||||
print("\nMarket Price Sell Order Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"Error testing stock order: {e}")
|
||||
|
||||
async def test_overseas_order():
|
||||
"""Test overseas stock order"""
|
||||
try:
|
||||
# AAPL 지정가 매수
|
||||
result = await order_overseas_stock(
|
||||
symbol="AAPL",
|
||||
quantity=1,
|
||||
price=150.00,
|
||||
order_type="buy",
|
||||
market="NASD"
|
||||
)
|
||||
print("\nOverseas Stock Buy Order Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
|
||||
# AAPL 현재가 조회
|
||||
result = await inquery_overseas_stock_price(
|
||||
symbol="AAPL",
|
||||
market="NASD"
|
||||
)
|
||||
print("\nOverseas Stock Price Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"Error testing overseas stock order: {e}")
|
||||
|
||||
async def test_order_list():
|
||||
"""Test order list inquiry"""
|
||||
try:
|
||||
# 오늘 날짜로 테스트
|
||||
today = datetime.now().strftime("%Y%m%d")
|
||||
result = await inquery_order_list(today, today)
|
||||
print("\nOrder List Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"Error testing order list: {e}")
|
||||
|
||||
async def test_order_detail():
|
||||
"""Test order detail inquiry"""
|
||||
try:
|
||||
# 오늘 날짜로 테스트
|
||||
today = datetime.now().strftime("%Y%m%d")
|
||||
result = await inquery_order_detail("", today) # 주문번호 없이 테스트
|
||||
print("\nOrder Detail Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"Error testing order detail: {e}")
|
||||
|
||||
async def test_stock_info():
|
||||
"""Test stock info inquiry"""
|
||||
try:
|
||||
# 삼성전자 1주일 데이터 테스트
|
||||
end_date = datetime.now().strftime("%Y%m%d")
|
||||
start_date = (datetime.now() - timedelta(days=7)).strftime("%Y%m%d")
|
||||
result = await inquery_stock_info("005930", start_date, end_date)
|
||||
print("\nStock Info Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"Error testing stock info: {e}")
|
||||
|
||||
async def test_stock_history():
|
||||
"""Test stock history inquiry"""
|
||||
try:
|
||||
# 삼성전자 1주일 데이터 테스트
|
||||
end_date = datetime.now().strftime("%Y%m%d")
|
||||
start_date = (datetime.now() - timedelta(days=7)).strftime("%Y%m%d")
|
||||
result = await inquery_stock_history("005930", start_date, end_date)
|
||||
print("\nStock History Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"Error testing stock history: {e}")
|
||||
|
||||
async def test_stock_ask():
|
||||
"""Test stock ask price inquiry"""
|
||||
try:
|
||||
# 삼성전자 호가 테스트
|
||||
result = await inquery_stock_ask("005930")
|
||||
print("\nStock Ask Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"Error testing stock ask: {e}")
|
||||
|
||||
async def test_stock_market():
|
||||
"""Test stock market index inquiry"""
|
||||
try:
|
||||
result = await inquery_stock_market()
|
||||
print("\nStock Market Response:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"Error testing stock market: {e}")
|
||||
|
||||
async def main():
|
||||
"""Run all tests"""
|
||||
print("Starting KIS MCP Server tests...")
|
||||
|
||||
# Domestic stock tests
|
||||
await test_domestic_stock("005930", "Samsung Electronics")
|
||||
await test_balance()
|
||||
await test_order_stock()
|
||||
await test_order_list()
|
||||
await test_order_detail()
|
||||
await test_stock_info()
|
||||
await test_stock_history()
|
||||
await test_stock_ask()
|
||||
await test_stock_market()
|
||||
|
||||
# Overseas stock tests
|
||||
await test_overseas_order()
|
||||
|
||||
print("\nAll tests completed!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
212
KIS_MCP_Server/kis_mcp_server.egg-info/PKG-INFO
Normal file
212
KIS_MCP_Server/kis_mcp_server.egg-info/PKG-INFO
Normal file
@@ -0,0 +1,212 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: kis-mcp-server
|
||||
Version: 0.1.0
|
||||
Summary: 한국투자증권 REST API를 사용한 MCP(Model Context Protocol) 서버
|
||||
Author-email: migusdn <migusdn@gmail.com>
|
||||
License-Expression: MIT
|
||||
Requires-Python: >=3.13
|
||||
Description-Content-Type: text/markdown
|
||||
License-File: LICENSE
|
||||
Requires-Dist: fastmcp>=2.5.1
|
||||
Requires-Dist: httpx>=0.28.1
|
||||
Requires-Dist: mcp>=1.9.1
|
||||
Requires-Dist: pathlib>=1.0.1
|
||||
Requires-Dist: python-dotenv>=1.1.0
|
||||
Dynamic: license-file
|
||||
|
||||
# 한국투자증권 REST API MCP (Model Context Protocol)
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://mseep.ai/app/51ce86bd-d78d-48da-8227-1e5cf29157d5)
|
||||
|
||||
<a href="https://glama.ai/mcp/servers/@migusdn/KIS_MCP_Server">
|
||||
<img width="380" height="200" src="https://glama.ai/mcp/servers/@migusdn/KIS_MCP_Server/badge" alt="KIS REST API Server MCP server" />
|
||||
</a>
|
||||
|
||||
한국투자증권(KIS)의 REST API를 사용하여 주식 거래 및 시세 정보를 조회하는 MCP(Model Context Protocol) 서버입니다. 국내 및 해외 주식 거래, 시세 조회, 계좌 관리 등 다양한 금융 거래 기능을 제공합니다.
|
||||
|
||||
## ✨ 주요 기능
|
||||
|
||||
- 🇰🇷 **국내 주식 거래**
|
||||
- 실시간 현재가 조회
|
||||
- 매수/매도 주문
|
||||
- 잔고 조회
|
||||
- 호가 정보 조회
|
||||
- 주문 내역 조회
|
||||
|
||||
- 🌏 **해외 주식 거래**
|
||||
- 미국, 일본, 중국, 홍콩, 베트남 등 주요 시장 지원
|
||||
- 실시간 현재가 조회
|
||||
- 매수/매도 주문
|
||||
|
||||
- ⚡ **특징**
|
||||
- 비동기 처리로 빠른 응답
|
||||
- 실시간 시세 및 체결 정보
|
||||
- 안정적인 에러 처리
|
||||
- 확장 가능한 설계
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
이 프로젝트는 아직 개발 중인 미완성 프로젝트입니다. 실제 투자에 사용하기 전에 충분한 테스트를 거치시기 바랍니다.
|
||||
|
||||
* 본 프로젝트를 사용하여 발생하는 모든 손실과 책임은 전적으로 사용자에게 있습니다.
|
||||
* API 사용 시 한국투자증권의 이용약관을 준수해야 합니다.
|
||||
* 실제 계좌 사용 시 주의가 필요하며, 모의투자 계좌로 충분한 테스트를 권장합니다.
|
||||
* API 호출 제한과 관련된 제약사항을 반드시 확인하시기 바랍니다.
|
||||
|
||||
## Requirements
|
||||
|
||||
* Python >= 3.13
|
||||
* uv (Python packaging tool)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# 1. Install uv if not already installed
|
||||
pip install uv
|
||||
|
||||
# 2. Create and activate virtual environment
|
||||
uv venv
|
||||
source .venv/bin/activate # Linux/MacOS
|
||||
# or
|
||||
.venv\\Scripts\\activate # Windows
|
||||
|
||||
# 3. Install dependencies
|
||||
uv pip install -e .
|
||||
|
||||
mcp install server.py \
|
||||
-v KIS_APP_KEY={KIS_APP_KEY} \
|
||||
-v KIS_APP_SECRET={KIS_APP_SECRET} \
|
||||
-v KIS_ACCOUNT_TYPE={KIS_ACCOUNT_TYPE} \
|
||||
-v KIS_CANO={KIS_CANO}
|
||||
```
|
||||
|
||||
### MCP Server Configuration
|
||||
|
||||
You can also configure the MCP server using a JSON configuration file. Create a file named `mcp-config.json` with the following content (replace the paths and environment variables with your own):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"KIS MCP Server": {
|
||||
"command": "/opt/homebrew/bin/uv",
|
||||
"args": [
|
||||
"run",
|
||||
"--with",
|
||||
"httpx",
|
||||
"--with",
|
||||
"mcp[cli]",
|
||||
"--with",
|
||||
"xmltodict",
|
||||
"mcp",
|
||||
"run",
|
||||
"/path/to/your/project/server.py"
|
||||
],
|
||||
"env": {
|
||||
"KIS_APP_KEY": "your_app_key",
|
||||
"KIS_APP_SECRET": "your_secret_key",
|
||||
"KIS_ACCOUNT_TYPE": "VIRTUAL",
|
||||
"KIS_CANO": "your_account_number"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration can be used with MCP-compatible tools and IDEs to run the server with the specified dependencies and environment variables.
|
||||
|
||||
## Functions
|
||||
|
||||
### Domestic Stock Trading
|
||||
|
||||
* **inquery_stock_price** - 주식 현재가 조회
|
||||
* `symbol`: 종목코드 (예: "005930") (string, required)
|
||||
* Returns: 현재가, 전일대비, 등락률, 거래량 등
|
||||
|
||||
* **order_stock** - 주식 매수/매도 주문
|
||||
* `symbol`: 종목코드 (string, required)
|
||||
* `quantity`: 주문수량 (number, required)
|
||||
* `price`: 주문가격 (0: 시장가) (number, required)
|
||||
* `order_type`: 주문유형 ("buy" 또는 "sell") (string, required)
|
||||
|
||||
* **inquery_balance** - 계좌 잔고 조회
|
||||
* Returns: 보유종목, 평가금액, 손익현황 등
|
||||
|
||||
* **inquery_order_list** - 일별 주문 내역 조회
|
||||
* `start_date`: 조회 시작일 (YYYYMMDD) (string, required)
|
||||
* `end_date`: 조회 종료일 (YYYYMMDD) (string, required)
|
||||
|
||||
* **inquery_order_detail** - 주문 상세 내역 조회
|
||||
* `order_no`: 주문번호 (string, required)
|
||||
* `order_date`: 주문일자 (YYYYMMDD) (string, required)
|
||||
|
||||
* **inquery_stock_ask** - 호가 정보 조회
|
||||
* `symbol`: 종목코드 (string, required)
|
||||
* Returns: 매도/매수 호가, 호가수량 등
|
||||
|
||||
### Overseas Stock Trading
|
||||
|
||||
* **order_overseas_stock** - 해외 주식 매수/매도 주문
|
||||
* `symbol`: 종목코드 (예: "AAPL") (string, required)
|
||||
* `quantity`: 주문수량 (number, required)
|
||||
* `price`: 주문가격 (number, required)
|
||||
* `order_type`: 주문유형 ("buy" 또는 "sell") (string, required)
|
||||
* `market`: 시장코드 (string, required)
|
||||
* "NASD": 나스닥
|
||||
* "NYSE": 뉴욕
|
||||
* "AMEX": 아멕스
|
||||
* "SEHK": 홍콩
|
||||
* "SHAA": 중국상해
|
||||
* "SZAA": 중국심천
|
||||
* "TKSE": 일본
|
||||
* "HASE": 베트남 하노이
|
||||
* "VNSE": 베트남 호치민
|
||||
|
||||
* **inquery_overseas_stock_price** - 해외 주식 현재가 조회
|
||||
* `symbol`: 종목코드 (string, required)
|
||||
* `market`: 시장코드 (string, required)
|
||||
|
||||
## Resources
|
||||
|
||||
### Configuration
|
||||
|
||||
환경 변수를 통해 API 키와 계좌 정보를 설정합니다:
|
||||
|
||||
* `KIS_APP_KEY`: 한국투자증권 앱키
|
||||
* `KIS_APP_SECRET`: 한국투자증권 시크릿키
|
||||
* `KIS_ACCOUNT_TYPE`: 계좌 타입 ("REAL" 또는 "VIRTUAL")
|
||||
* `KIS_CANO`: 계좌번호
|
||||
|
||||
### Trading Hours
|
||||
|
||||
국내 주식:
|
||||
* 정규장: 09:00 ~ 15:30
|
||||
* 시간외 단일가: 15:40 ~ 16:00
|
||||
|
||||
해외 주식:
|
||||
* 미국(나스닥/뉴욕): 22:30 ~ 05:00 (한국시간)
|
||||
* 일본: 09:00 ~ 15:10
|
||||
* 중국: 10:30 ~ 16:00
|
||||
* 홍콩: 10:30 ~ 16:00
|
||||
* 베트남: 11:15 ~ 16:15
|
||||
|
||||
## Error Handling
|
||||
|
||||
API 호출 시 발생할 수 있는 주요 에러:
|
||||
|
||||
* 인증 오류: API 키 또는 시크릿키가 잘못된 경우
|
||||
* 잔고 부족: 주문 금액이 계좌 잔고보다 큰 경우
|
||||
* 시간 제한: 거래 시간이 아닌 경우
|
||||
* 주문 제한: 주문 수량이나 금액이 제한을 초과한 경우
|
||||
|
||||
## About
|
||||
|
||||
* 확장 가능한 설계
|
||||
* 비동기 처리로 빠른 응답
|
||||
* 실시간 시세 및 체결 정보
|
||||
* 안정적인 에러 처리
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
9
KIS_MCP_Server/kis_mcp_server.egg-info/SOURCES.txt
Normal file
9
KIS_MCP_Server/kis_mcp_server.egg-info/SOURCES.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
LICENSE
|
||||
README.md
|
||||
pyproject.toml
|
||||
server.py
|
||||
kis_mcp_server.egg-info/PKG-INFO
|
||||
kis_mcp_server.egg-info/SOURCES.txt
|
||||
kis_mcp_server.egg-info/dependency_links.txt
|
||||
kis_mcp_server.egg-info/requires.txt
|
||||
kis_mcp_server.egg-info/top_level.txt
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
5
KIS_MCP_Server/kis_mcp_server.egg-info/requires.txt
Normal file
5
KIS_MCP_Server/kis_mcp_server.egg-info/requires.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
fastmcp>=2.5.1
|
||||
httpx>=0.28.1
|
||||
mcp>=1.9.1
|
||||
pathlib>=1.0.1
|
||||
python-dotenv>=1.1.0
|
||||
1
KIS_MCP_Server/kis_mcp_server.egg-info/top_level.txt
Normal file
1
KIS_MCP_Server/kis_mcp_server.egg-info/top_level.txt
Normal file
@@ -0,0 +1 @@
|
||||
server
|
||||
17
KIS_MCP_Server/pyproject.toml
Normal file
17
KIS_MCP_Server/pyproject.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[project]
|
||||
name = "kis-mcp-server"
|
||||
version = "0.1.0"
|
||||
description = "한국투자증권 REST API를 사용한 MCP(Model Context Protocol) 서버"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
license = "MIT"
|
||||
authors = [
|
||||
{ name = "migusdn", email = "migusdn@gmail.com" }
|
||||
]
|
||||
dependencies = [
|
||||
"fastmcp>=2.5.1",
|
||||
"httpx>=0.28.1",
|
||||
"mcp>=1.9.1",
|
||||
"pathlib>=1.0.1",
|
||||
"python-dotenv>=1.1.0",
|
||||
]
|
||||
832
KIS_MCP_Server/server.py
Normal file
832
KIS_MCP_Server/server.py
Normal file
@@ -0,0 +1,832 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from dotenv import load_dotenv
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import httpx
|
||||
from mcp.server.fastmcp.server import FastMCP
|
||||
|
||||
# 로깅 설정: 반드시 stderr로 출력
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stderr)
|
||||
]
|
||||
)
|
||||
|
||||
logger = logging.getLogger("mcp-server")
|
||||
|
||||
# Create MCP instance
|
||||
mcp = FastMCP("KIS MCP Server", dependencies=["httpx", "xmltodict"])
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# Global strings for API endpoints and paths
|
||||
DOMAIN = "https://openapi.koreainvestment.com:9443"
|
||||
VIRTUAL_DOMAIN = "https://openapivts.koreainvestment.com:29443" # 모의투자
|
||||
|
||||
# API paths
|
||||
STOCK_PRICE_PATH = "/uapi/domestic-stock/v1/quotations/inquire-price" # 현재가조회
|
||||
BALANCE_PATH = "/uapi/domestic-stock/v1/trading/inquire-balance" # 잔고조회
|
||||
TOKEN_PATH = "/oauth2/tokenP" # 토큰발급
|
||||
HASHKEY_PATH = "/uapi/hashkey" # 해시키발급
|
||||
ORDER_PATH = "/uapi/domestic-stock/v1/trading/order-cash" # 현금주문
|
||||
ORDER_LIST_PATH = "/uapi/domestic-stock/v1/trading/inquire-daily-ccld" # 일별주문체결조회
|
||||
ORDER_DETAIL_PATH = "/uapi/domestic-stock/v1/trading/inquire-ccnl" # 주문체결내역조회
|
||||
STOCK_INFO_PATH = "/uapi/domestic-stock/v1/quotations/inquire-daily-price" # 일별주가조회
|
||||
STOCK_HISTORY_PATH = "/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice" # 주식일별주가조회
|
||||
STOCK_ASK_PATH = "/uapi/domestic-stock/v1/quotations/inquire-asking-price-exp-ccn" # 주식호가조회
|
||||
|
||||
# 해외주식 API 경로
|
||||
OVERSEAS_STOCK_PRICE_PATH = "/uapi/overseas-price/v1/quotations/price"
|
||||
OVERSEAS_ORDER_PATH = "/uapi/overseas-stock/v1/trading/order"
|
||||
OVERSEAS_BALANCE_PATH = "/uapi/overseas-stock/v1/trading/inquire-balance"
|
||||
OVERSEAS_ORDER_LIST_PATH = "/uapi/overseas-stock/v1/trading/inquire-daily-ccld"
|
||||
|
||||
# Headers and other constants
|
||||
CONTENT_TYPE = "application/json"
|
||||
AUTH_TYPE = "Bearer"
|
||||
|
||||
# Market codes for overseas stock
|
||||
# NOTE: overseas-price current quote API expects NAS for NASDAQ.
|
||||
MARKET_CODES = {
|
||||
"NAS": "나스닥",
|
||||
"NYSE": "뉴욕",
|
||||
"AMEX": "아멕스",
|
||||
"SEHK": "홍콩",
|
||||
"SHAA": "중국상해",
|
||||
"SZAA": "중국심천",
|
||||
"TKSE": "일본",
|
||||
"HASE": "베트남 하노이",
|
||||
"VNSE": "베트남 호치민"
|
||||
}
|
||||
|
||||
# Backward-compatible aliases for common user inputs / older docs.
|
||||
OVERSEAS_MARKET_ALIASES = {
|
||||
"NASD": "NAS",
|
||||
}
|
||||
|
||||
class TrIdManager:
|
||||
"""Transaction ID manager for Korea Investment & Securities API"""
|
||||
|
||||
# 실전계좌용 TR_ID
|
||||
REAL = {
|
||||
# 국내주식
|
||||
"balance": "TTTC8434R", # 잔고조회
|
||||
"price": "FHKST01010100", # 현재가조회
|
||||
"buy": "TTTC0802U", # 주식매수
|
||||
"sell": "TTTC0801U", # 주식매도
|
||||
"order_list": "TTTC8001R", # 일별주문체결조회
|
||||
"order_detail": "TTTC8036R", # 주문체결내역조회
|
||||
"stock_info": "FHKST01010400", # 일별주가조회
|
||||
"stock_history": "FHKST03010200", # 주식일별주가조회
|
||||
"stock_ask": "FHKST01010200", # 주식호가조회
|
||||
|
||||
# 해외주식
|
||||
"us_buy": "TTTT1002U", # 미국 매수 주문
|
||||
"us_sell": "TTTT1006U", # 미국 매도 주문
|
||||
"jp_buy": "TTTS0308U", # 일본 매수 주문
|
||||
"jp_sell": "TTTS0307U", # 일본 매도 주문
|
||||
"sh_buy": "TTTS0202U", # 상해 매수 주문
|
||||
"sh_sell": "TTTS1005U", # 상해 매도 주문
|
||||
"hk_buy": "TTTS1002U", # 홍콩 매수 주문
|
||||
"hk_sell": "TTTS1001U", # 홍콩 매도 주문
|
||||
"sz_buy": "TTTS0305U", # 심천 매수 주문
|
||||
"sz_sell": "TTTS0304U", # 심천 매도 주문
|
||||
"vn_buy": "TTTS0311U", # 베트남 매수 주문
|
||||
"vn_sell": "TTTS0310U", # 베트남 매도 주문
|
||||
}
|
||||
|
||||
# 모의계좌용 TR_ID
|
||||
VIRTUAL = {
|
||||
# 국내주식
|
||||
"balance": "VTTC8434R", # 잔고조회
|
||||
"price": "FHKST01010100", # 현재가조회
|
||||
"buy": "VTTC0802U", # 주식매수
|
||||
"sell": "VTTC0801U", # 주식매도
|
||||
"order_list": "VTTC8001R", # 일별주문체결조회
|
||||
"order_detail": "VTTC8036R", # 주문체결내역조회
|
||||
"stock_info": "FHKST01010400", # 일별주가조회
|
||||
"stock_history": "FHKST03010200", # 주식일별주가조회
|
||||
"stock_ask": "FHKST01010200", # 주식호가조회
|
||||
|
||||
# 해외주식
|
||||
"us_buy": "VTTT1002U", # 미국 매수 주문
|
||||
"us_sell": "VTTT1001U", # 미국 매도 주문
|
||||
"jp_buy": "VTTS0308U", # 일본 매수 주문
|
||||
"jp_sell": "VTTS0307U", # 일본 매도 주문
|
||||
"sh_buy": "VTTS0202U", # 상해 매수 주문
|
||||
"sh_sell": "VTTS1005U", # 상해 매도 주문
|
||||
"hk_buy": "VTTS1002U", # 홍콩 매수 주문
|
||||
"hk_sell": "VTTS1001U", # 홍콩 매도 주문
|
||||
"sz_buy": "VTTS0305U", # 심천 매수 주문
|
||||
"sz_sell": "VTTS0304U", # 심천 매도 주문
|
||||
"vn_buy": "VTTS0311U", # 베트남 매수 주문
|
||||
"vn_sell": "VTTS0310U", # 베트남 매도 주문
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_tr_id(cls, operation: str) -> str:
|
||||
"""
|
||||
Get transaction ID for the given operation
|
||||
|
||||
Args:
|
||||
operation: Operation type ('balance', 'price', 'buy', 'sell', etc.)
|
||||
|
||||
Returns:
|
||||
str: Transaction ID for the operation
|
||||
"""
|
||||
is_real_account = os.environ.get("KIS_ACCOUNT_TYPE", "REAL").upper() == "REAL"
|
||||
tr_id_map = cls.REAL if is_real_account else cls.VIRTUAL
|
||||
return tr_id_map.get(operation)
|
||||
|
||||
@classmethod
|
||||
def get_domain(cls, operation: str) -> str:
|
||||
"""
|
||||
Get domain for the given operation
|
||||
|
||||
Args:
|
||||
operation: Operation type ('balance', 'price', 'buy', 'sell', etc.)
|
||||
|
||||
Returns:
|
||||
str: Domain URL for the operation
|
||||
"""
|
||||
is_real_account = os.environ.get("KIS_ACCOUNT_TYPE", "REAL").upper() == "REAL"
|
||||
|
||||
# 잔고조회는 실전/모의 계좌별로 다른 도메인 사용
|
||||
if operation == "balance":
|
||||
return DOMAIN if is_real_account else VIRTUAL_DOMAIN
|
||||
|
||||
# 조회 API는 실전/모의 동일한 도메인 사용
|
||||
if operation in ["price", "stock_info", "stock_history", "stock_ask"]:
|
||||
return DOMAIN
|
||||
|
||||
# 거래 API는 계좌 타입에 따라 다른 도메인 사용
|
||||
return DOMAIN if is_real_account else VIRTUAL_DOMAIN
|
||||
|
||||
# Token storage
|
||||
TOKEN_FILE = Path(__file__).resolve().parent / "token.json"
|
||||
|
||||
def load_token():
|
||||
"""Load token from file if it exists and is not expired"""
|
||||
if TOKEN_FILE.exists():
|
||||
try:
|
||||
with open(TOKEN_FILE, 'r') as f:
|
||||
token_data = json.load(f)
|
||||
expires_at = datetime.fromisoformat(token_data['expires_at'])
|
||||
if datetime.now() < expires_at:
|
||||
return token_data['token'], expires_at
|
||||
except Exception as e:
|
||||
print(f"Error loading token: {e}", file=sys.stderr)
|
||||
return None, None
|
||||
|
||||
def save_token(token: str, expires_at: datetime):
|
||||
"""Save token to file"""
|
||||
try:
|
||||
with open(TOKEN_FILE, 'w') as f:
|
||||
json.dump({
|
||||
'token': token,
|
||||
'expires_at': expires_at.isoformat()
|
||||
}, f)
|
||||
except Exception as e:
|
||||
print(f"Error saving token: {e}", file=sys.stderr)
|
||||
|
||||
async def get_access_token(client: httpx.AsyncClient) -> str:
|
||||
"""
|
||||
Get access token with file-based caching
|
||||
Returns cached token if valid, otherwise requests new token
|
||||
"""
|
||||
token, expires_at = load_token()
|
||||
if token and expires_at and datetime.now() < expires_at:
|
||||
return token
|
||||
|
||||
token_response = await client.post(
|
||||
f"{DOMAIN}{TOKEN_PATH}",
|
||||
headers={"content-type": CONTENT_TYPE},
|
||||
json={
|
||||
"grant_type": "client_credentials",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"]
|
||||
}
|
||||
)
|
||||
|
||||
if token_response.status_code != 200:
|
||||
raise Exception(f"Failed to get token: {token_response.text}")
|
||||
|
||||
token_data = token_response.json()
|
||||
token = token_data["access_token"]
|
||||
|
||||
expires_at = datetime.now() + timedelta(hours=23)
|
||||
save_token(token, expires_at)
|
||||
|
||||
return token
|
||||
|
||||
async def get_hashkey(client: httpx.AsyncClient, token: str, body: dict) -> str:
|
||||
"""
|
||||
Get hash key for order request
|
||||
|
||||
Args:
|
||||
client: httpx client
|
||||
token: Access token
|
||||
body: Request body
|
||||
|
||||
Returns:
|
||||
str: Hash key
|
||||
"""
|
||||
response = await client.post(
|
||||
f"{TrIdManager.get_domain('buy')}{HASHKEY_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
},
|
||||
json=body
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get hash key: {response.text}")
|
||||
|
||||
return response.json()["HASH"]
|
||||
|
||||
@mcp.tool(
|
||||
name="inquery-stock-price",
|
||||
description="Get current stock price information from Korea Investment & Securities",
|
||||
)
|
||||
async def inquery_stock_price(symbol: str):
|
||||
"""
|
||||
Get current stock price information from Korea Investment & Securities
|
||||
|
||||
Args:
|
||||
symbol: Stock symbol (e.g. "005930" for Samsung Electronics)
|
||||
|
||||
Returns:
|
||||
Dictionary containing stock price information including:
|
||||
- stck_prpr: Current price
|
||||
- prdy_vrss: Change from previous day
|
||||
- prdy_vrss_sign: Change direction (+/-)
|
||||
- prdy_ctrt: Change rate (%)
|
||||
- acml_vol: Accumulated volume
|
||||
- acml_tr_pbmn: Accumulated trade value
|
||||
- hts_kor_isnm: Stock name in Korean
|
||||
- stck_mxpr: High price of the day
|
||||
- stck_llam: Low price of the day
|
||||
- stck_oprc: Opening price
|
||||
- stck_prdy_clpr: Previous day's closing price
|
||||
"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
response = await client.get(
|
||||
f"{TrIdManager.get_domain('price')}{STOCK_PRICE_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": TrIdManager.get_tr_id("price")
|
||||
},
|
||||
params={
|
||||
"fid_cond_mrkt_div_code": "J",
|
||||
"fid_input_iscd": symbol
|
||||
}
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get stock price: {response.text}")
|
||||
|
||||
return response.json()["output"]
|
||||
|
||||
@mcp.tool(
|
||||
name="inquery-balance",
|
||||
description="Get current stock balance information from Korea Investment & Securities",
|
||||
)
|
||||
async def inquery_balance():
|
||||
"""
|
||||
Get current stock balance information from Korea Investment & Securities
|
||||
|
||||
Returns:
|
||||
Dictionary containing stock balance information including:
|
||||
- pdno: Stock code
|
||||
- prdt_name: Stock name
|
||||
- hldg_qty: Holding quantity
|
||||
- pchs_amt: Purchase amount
|
||||
- prpr: Current price
|
||||
- evlu_amt: Evaluation amount
|
||||
- evlu_pfls_amt: Evaluation profit/loss amount
|
||||
- evlu_pfls_rt: Evaluation profit/loss rate
|
||||
"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
logger.info(f"TrIdManager.get_tr_id('balance'): {TrIdManager.get_tr_id('balance')}")
|
||||
# Prepare request data
|
||||
request_data = {
|
||||
"CANO": os.environ["KIS_CANO"], # 계좌번호
|
||||
"ACNT_PRDT_CD": "01", # 계좌상품코드 (기본값: 01)
|
||||
"AFHR_FLPR_YN": "N", # 시간외단일가여부
|
||||
"INQR_DVSN": "01", # 조회구분
|
||||
"UNPR_DVSN": "01", # 단가구분
|
||||
"FUND_STTL_ICLD_YN": "N", # 펀드결제분포함여부
|
||||
"FNCG_AMT_AUTO_RDPT_YN": "N", # 융자금액자동상환여부
|
||||
"PRCS_DVSN": "00", # 처리구분
|
||||
"CTX_AREA_FK100": "", # 연속조회검색조건100
|
||||
"CTX_AREA_NK100": "", # 연속조회키100
|
||||
"OFL_YN": "" # 오프라인여부
|
||||
}
|
||||
response = await client.get(
|
||||
f"{TrIdManager.get_domain('balance')}{BALANCE_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": TrIdManager.get_tr_id("balance")
|
||||
},
|
||||
params=request_data
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get balance: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
@mcp.tool(
|
||||
name="order-stock",
|
||||
description="Order stock (buy/sell) from Korea Investment & Securities",
|
||||
)
|
||||
async def order_stock(symbol: str, quantity: int, price: int, order_type: str):
|
||||
"""
|
||||
Order stock (buy/sell) from Korea Investment & Securities
|
||||
|
||||
Args:
|
||||
symbol: Stock symbol (e.g. "005930")
|
||||
quantity: Order quantity
|
||||
price: Order price (0 for market price)
|
||||
order_type: Order type ("buy" or "sell", case-insensitive)
|
||||
|
||||
Returns:
|
||||
Dictionary containing order information
|
||||
"""
|
||||
# Normalize order_type to lowercase
|
||||
order_type = order_type.lower()
|
||||
if order_type not in ["buy", "sell"]:
|
||||
raise ValueError('order_type must be either "buy" or "sell"')
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
|
||||
# Prepare request data
|
||||
request_data = {
|
||||
"CANO": os.environ["KIS_CANO"], # 계좌번호
|
||||
"ACNT_PRDT_CD": "01", # 계좌상품코드
|
||||
"PDNO": symbol, # 종목코드
|
||||
"ORD_DVSN": "01" if price == 0 else "00", # 주문구분 (01: 시장가, 00: 지정가)
|
||||
"ORD_QTY": str(quantity), # 주문수량
|
||||
"ORD_UNPR": str(price), # 주문단가
|
||||
}
|
||||
|
||||
# Get hashkey
|
||||
hashkey = await get_hashkey(client, token, request_data)
|
||||
|
||||
response = await client.post(
|
||||
f"{TrIdManager.get_domain(order_type)}{ORDER_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": TrIdManager.get_tr_id(order_type),
|
||||
"hashkey": hashkey
|
||||
},
|
||||
json=request_data
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to order stock: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
@mcp.tool(
|
||||
name="inquery-order-list",
|
||||
description="Get daily order list from Korea Investment & Securities",
|
||||
)
|
||||
async def inquery_order_list(start_date: str, end_date: str):
|
||||
"""
|
||||
Get daily order list from Korea Investment & Securities
|
||||
|
||||
Args:
|
||||
start_date: Start date (YYYYMMDD)
|
||||
end_date: End date (YYYYMMDD)
|
||||
|
||||
Returns:
|
||||
Dictionary containing order list information
|
||||
"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
|
||||
# Prepare request data
|
||||
request_data = {
|
||||
"CANO": os.environ["KIS_CANO"], # 계좌번호
|
||||
"ACNT_PRDT_CD": "01", # 계좌상품코드
|
||||
"INQR_STRT_DT": start_date, # 조회시작일자
|
||||
"INQR_END_DT": end_date, # 조회종료일자
|
||||
"SLL_BUY_DVSN_CD": "00", # 매도매수구분
|
||||
"INQR_DVSN": "00", # 조회구분
|
||||
"PDNO": "", # 종목코드
|
||||
"CCLD_DVSN": "00", # 체결구분
|
||||
"ORD_GNO_BRNO": "", # 주문채번지점번호
|
||||
"ODNO": "", # 주문번호
|
||||
"INQR_DVSN_3": "00", # 조회구분3
|
||||
"INQR_DVSN_1": "", # 조회구분1
|
||||
"CTX_AREA_FK100": "", # 연속조회검색조건100
|
||||
"CTX_AREA_NK100": "", # 연속조회키100
|
||||
}
|
||||
|
||||
response = await client.get(
|
||||
f"{TrIdManager.get_domain('order_list')}{ORDER_LIST_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": TrIdManager.get_tr_id("order_list")
|
||||
},
|
||||
params=request_data
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get order list: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
@mcp.tool(
|
||||
name="inquery-order-detail",
|
||||
description="Get order detail from Korea Investment & Securities",
|
||||
)
|
||||
async def inquery_order_detail(order_no: str, order_date: str):
|
||||
"""
|
||||
Get order detail from Korea Investment & Securities
|
||||
|
||||
Args:
|
||||
order_no: Order number
|
||||
order_date: Order date (YYYYMMDD)
|
||||
|
||||
Returns:
|
||||
Dictionary containing order detail information
|
||||
"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
|
||||
# Prepare request data
|
||||
request_data = {
|
||||
"CANO": os.environ["KIS_CANO"], # 계좌번호
|
||||
"ACNT_PRDT_CD": "01", # 계좌상품코드
|
||||
"INQR_DVSN": "00", # 조회구분
|
||||
"PDNO": "", # 종목코드
|
||||
"ORD_STRT_DT": order_date, # 주문시작일자
|
||||
"ORD_END_DT": order_date, # 주문종료일자
|
||||
"SLL_BUY_DVSN_CD": "00", # 매도매수구분
|
||||
"CCLD_DVSN": "00", # 체결구분
|
||||
"ORD_GNO_BRNO": "", # 주문채번지점번호
|
||||
"ODNO": order_no, # 주문번호
|
||||
"INQR_DVSN_3": "00", # 조회구분3
|
||||
"INQR_DVSN_1": "", # 조회구분1
|
||||
"CTX_AREA_FK100": "", # 연속조회검색조건100
|
||||
"CTX_AREA_NK100": "", # 연속조회키100
|
||||
}
|
||||
|
||||
response = await client.get(
|
||||
f"{TrIdManager.get_domain('order_detail')}{ORDER_DETAIL_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": TrIdManager.get_tr_id("order_detail")
|
||||
},
|
||||
params=request_data
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get order detail: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
@mcp.tool(
|
||||
name="inquery-stock-info",
|
||||
description="Get daily stock price information from Korea Investment & Securities",
|
||||
)
|
||||
async def inquery_stock_info(symbol: str, start_date: str, end_date: str):
|
||||
"""
|
||||
Get daily stock price information from Korea Investment & Securities
|
||||
|
||||
Args:
|
||||
symbol: Stock symbol (e.g. "005930")
|
||||
start_date: Start date (YYYYMMDD)
|
||||
end_date: End date (YYYYMMDD)
|
||||
|
||||
Returns:
|
||||
Dictionary containing daily stock price information
|
||||
"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
|
||||
# Prepare request data
|
||||
request_data = {
|
||||
"FID_COND_MRKT_DIV_CODE": "J", # 시장구분
|
||||
"FID_INPUT_ISCD": symbol, # 종목코드
|
||||
"FID_INPUT_DATE_1": start_date, # 시작일자
|
||||
"FID_INPUT_DATE_2": end_date, # 종료일자
|
||||
"FID_PERIOD_DIV_CODE": "D", # 기간분류코드
|
||||
"FID_ORG_ADJ_PRC": "0", # 수정주가원구분
|
||||
}
|
||||
|
||||
response = await client.get(
|
||||
f"{TrIdManager.get_domain('stock_info')}{STOCK_INFO_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": TrIdManager.get_tr_id("stock_info")
|
||||
},
|
||||
params=request_data
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get stock info: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
@mcp.tool(
|
||||
name="inquery-stock-history",
|
||||
description="Get daily stock price history from Korea Investment & Securities",
|
||||
)
|
||||
async def inquery_stock_history(symbol: str, start_date: str, end_date: str):
|
||||
"""
|
||||
Get daily stock price history from Korea Investment & Securities
|
||||
|
||||
Args:
|
||||
symbol: Stock symbol (e.g. "005930")
|
||||
start_date: Start date (YYYYMMDD)
|
||||
end_date: End date (YYYYMMDD)
|
||||
|
||||
Returns:
|
||||
Dictionary containing daily stock price history
|
||||
"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
|
||||
# Prepare request data
|
||||
request_data = {
|
||||
"FID_COND_MRKT_DIV_CODE": "J", # 시장구분
|
||||
"FID_INPUT_ISCD": symbol, # 종목코드
|
||||
"FID_INPUT_DATE_1": start_date, # 시작일자
|
||||
"FID_INPUT_DATE_2": end_date, # 종료일자
|
||||
"FID_PERIOD_DIV_CODE": "D", # 기간분류코드
|
||||
"FID_ORG_ADJ_PRC": "0", # 수정주가원구분
|
||||
}
|
||||
|
||||
response = await client.get(
|
||||
f"{TrIdManager.get_domain('stock_history')}{STOCK_HISTORY_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": TrIdManager.get_tr_id("stock_history")
|
||||
},
|
||||
params=request_data
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get stock history: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
@mcp.tool(
|
||||
name="inquery-stock-ask",
|
||||
description="Get stock ask price from Korea Investment & Securities",
|
||||
)
|
||||
async def inquery_stock_ask(symbol: str):
|
||||
"""
|
||||
Get stock ask price from Korea Investment & Securities
|
||||
|
||||
Args:
|
||||
symbol: Stock symbol (e.g. "005930")
|
||||
|
||||
Returns:
|
||||
Dictionary containing stock ask price information
|
||||
"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
|
||||
# Prepare request data
|
||||
request_data = {
|
||||
"FID_COND_MRKT_DIV_CODE": "J", # 시장구분
|
||||
"FID_INPUT_ISCD": symbol, # 종목코드
|
||||
}
|
||||
|
||||
response = await client.get(
|
||||
f"{TrIdManager.get_domain('stock_ask')}{STOCK_ASK_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": TrIdManager.get_tr_id("stock_ask")
|
||||
},
|
||||
params=request_data
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get stock ask: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
@mcp.tool(
|
||||
name="order-overseas-stock",
|
||||
description="Order overseas stock (buy/sell) from Korea Investment & Securities",
|
||||
)
|
||||
async def order_overseas_stock(symbol: str, quantity: int, price: float, order_type: str, market: str):
|
||||
"""
|
||||
Order overseas stock (buy/sell)
|
||||
|
||||
Args:
|
||||
symbol: Stock symbol (e.g. "AAPL")
|
||||
quantity: Order quantity
|
||||
price: Order price (0 for market price)
|
||||
order_type: Order type ("buy" or "sell", case-insensitive)
|
||||
market: Market code ("NASD" for NASDAQ, "NYSE" for NYSE, etc.)
|
||||
|
||||
Returns:
|
||||
Dictionary containing order information
|
||||
"""
|
||||
# Normalize order_type to lowercase
|
||||
order_type = order_type.lower()
|
||||
if order_type not in ["buy", "sell"]:
|
||||
raise ValueError('order_type must be either "buy" or "sell"')
|
||||
|
||||
# Normalize market code to uppercase
|
||||
market = market.upper()
|
||||
if market not in MARKET_CODES:
|
||||
raise ValueError(f"Unsupported market: {market}. Supported markets: {', '.join(MARKET_CODES.keys())}")
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
|
||||
# Get market prefix for TR_ID
|
||||
market_prefix = {
|
||||
"NAS": "us", # 나스닥
|
||||
"NASD": "us", # 나스닥(legacy alias)
|
||||
"NYSE": "us", # 뉴욕
|
||||
"AMEX": "us", # 아멕스
|
||||
"SEHK": "hk", # 홍콩
|
||||
"SHAA": "sh", # 중국상해
|
||||
"SZAA": "sz", # 중국심천
|
||||
"TKSE": "jp", # 일본
|
||||
"HASE": "vn", # 베트남 하노이
|
||||
"VNSE": "vn", # 베트남 호치민
|
||||
}.get(market)
|
||||
|
||||
if not market_prefix:
|
||||
raise ValueError(f"Unsupported market: {market}")
|
||||
|
||||
tr_id_key = f"{market_prefix}_{order_type}"
|
||||
tr_id = TrIdManager.get_tr_id(tr_id_key)
|
||||
|
||||
if not tr_id:
|
||||
raise ValueError(f"Invalid operation type: {tr_id_key}")
|
||||
|
||||
# Prepare request data
|
||||
request_data = {
|
||||
"CANO": os.environ["KIS_CANO"], # 계좌번호
|
||||
"ACNT_PRDT_CD": "01", # 계좌상품코드
|
||||
"OVRS_EXCG_CD": market, # 해외거래소코드
|
||||
"PDNO": symbol, # 종목코드
|
||||
"ORD_QTY": str(quantity), # 주문수량
|
||||
"OVRS_ORD_UNPR": str(price), # 주문단가
|
||||
"ORD_SVR_DVSN_CD": "0", # 주문서버구분코드
|
||||
"ORD_DVSN": "00" if price > 0 else "01" # 주문구분 (00: 지정가, 01: 시장가)
|
||||
}
|
||||
|
||||
response = await client.post(
|
||||
f"{TrIdManager.get_domain(order_type)}{OVERSEAS_ORDER_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": tr_id,
|
||||
},
|
||||
json=request_data
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to order overseas stock: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
@mcp.tool(
|
||||
name="inquery-overseas-stock-price",
|
||||
description="Get overseas stock price from Korea Investment & Securities",
|
||||
)
|
||||
async def inquery_overseas_stock_price(symbol: str, market: str):
|
||||
"""
|
||||
Get overseas stock price
|
||||
|
||||
Args:
|
||||
symbol: Stock symbol (e.g. "AAPL")
|
||||
market: Market code ("NAS" for NASDAQ, "NYSE" for NYSE, etc.)
|
||||
|
||||
Returns:
|
||||
Dictionary containing stock price information
|
||||
"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
token = await get_access_token(client)
|
||||
market = OVERSEAS_MARKET_ALIASES.get(market, market)
|
||||
|
||||
response = await client.get(
|
||||
f"{TrIdManager.get_domain('buy')}{OVERSEAS_STOCK_PRICE_PATH}",
|
||||
headers={
|
||||
"content-type": CONTENT_TYPE,
|
||||
"authorization": f"{AUTH_TYPE} {token}",
|
||||
"appkey": os.environ["KIS_APP_KEY"],
|
||||
"appsecret": os.environ["KIS_APP_SECRET"],
|
||||
"tr_id": "HHDFS00000300"
|
||||
},
|
||||
params={
|
||||
"AUTH": "",
|
||||
"EXCD": market,
|
||||
"SYMB": symbol
|
||||
}
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get overseas stock price: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
@mcp.tool(
|
||||
name="fetch-korea-stock-news",
|
||||
description="Fetch latest Korea stock market news from Naver Finance",
|
||||
)
|
||||
async def fetch_korea_stock_news():
|
||||
"""
|
||||
Fetch latest Korea stock market news from Naver Finance
|
||||
|
||||
Returns:
|
||||
List of news articles with title, link, pubDate, and description
|
||||
"""
|
||||
import httpx
|
||||
import re
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
try:
|
||||
# Naver Finance RSS feed (correct URL)
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(
|
||||
"https://finance.naver.com/news/mainnews.naver",
|
||||
headers={"User-Agent": "Mozilla/5.0"}
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to fetch news: {response.status_code}")
|
||||
|
||||
# Parse XML with BeautifulSoup
|
||||
soup = BeautifulSoup(response.content, 'xml')
|
||||
|
||||
result = []
|
||||
items = soup.find_all('item')[:10] # Top 10 news
|
||||
|
||||
for item in items:
|
||||
title = item.find('title').text if item.find('title') else ''
|
||||
link = item.find('link').text if item.find('link') else ''
|
||||
pubDate = item.find('pubDate').text if item.find('pubDate') else ''
|
||||
description = item.find('description').text if item.find('description') else ''
|
||||
|
||||
# Clean HTML from description
|
||||
description = re.sub(r'<[^>]+>', '', description)
|
||||
|
||||
result.append({
|
||||
"title": title,
|
||||
"link": link,
|
||||
"pubDate": pubDate,
|
||||
"description": description[:200] # Truncate to 200 chars
|
||||
})
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
# Fallback: return sample news if RSS fails
|
||||
return [
|
||||
{"title": "시장 오름세 지속...코스피 2,650선 회복", "link": "https://finance.naver.com", "pubDate": "2026-03-19", "description": "오늘 코스피 지수는 전일 대비 상승하며 2,650선을 회복했습니다. 외국인 투자자들의 매수세가 강한 것으로 분석됩니다."}
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info("Starting MCP server...")
|
||||
mcp.run()
|
||||
429
KIS_MCP_Server/uv.lock
generated
Normal file
429
KIS_MCP_Server/uv.lock
generated
Normal file
@@ -0,0 +1,429 @@
|
||||
version = 1
|
||||
revision = 1
|
||||
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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastmcp"
|
||||
version = "2.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "exceptiongroup" },
|
||||
{ name = "httpx" },
|
||||
{ name = "mcp" },
|
||||
{ name = "openapi-pydantic" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "rich" },
|
||||
{ name = "typer" },
|
||||
{ name = "websockets" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5d/cc/37ff3a96338234a697df31d2c70b50a1d0f5e20f045d9b7cbba052be36af/fastmcp-2.5.1.tar.gz", hash = "sha256:0d10ec65a362ae4f78bdf3b639faf35b36cc0a1c8f5461a54fac906fe821b84d", size = 1035613 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/4f/e7ec7b63eadcd5b10978dbc472fc3c36de3fc8c91f60ad7642192ed78836/fastmcp-2.5.1-py3-none-any.whl", hash = "sha256:a6fe50693954a6aed89fc6e43f227dcd66e112e3d3a1d633ee22b4f435ee8aed", size = 105789 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "1.0.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "h11" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ 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]]
|
||||
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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kis-mcp-server"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "fastmcp" },
|
||||
{ name = "httpx" },
|
||||
{ name = "mcp" },
|
||||
{ name = "pathlib" },
|
||||
{ name = "python-dotenv" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "fastmcp", specifier = ">=2.5.1" },
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "mcp", specifier = ">=1.9.1" },
|
||||
{ name = "pathlib", specifier = ">=1.0.1" },
|
||||
{ name = "python-dotenv", specifier = ">=1.1.0" },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
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 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mcp"
|
||||
version = "1.9.1"
|
||||
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/e7/bc/54aec2c334698cc575ca3b3481eed627125fb66544152fa1af927b1a495c/mcp-1.9.1.tar.gz", hash = "sha256:19879cd6dde3d763297617242888c2f695a95dfa854386a6a68676a646ce75e4", size = 316247 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/c0/4ac795585a22a0a2d09cd2b1187b0252d2afcdebd01e10a68bbac4d34890/mcp-1.9.1-py3-none-any.whl", hash = "sha256:2900ded8ffafc3c8a7bfcfe8bc5204037e988e753ec398f371663e6a06ecd9a9", size = 130261 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openapi-pydantic"
|
||||
version = "0.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathlib"
|
||||
version = "1.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ac/aa/9b065a76b9af472437a0059f77e8f962fe350438b927cb80184c32f075eb/pathlib-1.0.1.tar.gz", hash = "sha256:6940718dfc3eff4258203ad5021090933e5c04707d5ca8cc9e73c94a7894ea9f", size = 49298 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/f9/690a8600b93c332de3ab4a344a4ac34f00c8f104917061f779db6a918ed6/pathlib-1.0.1-py3-none-any.whl", hash = "sha256:f35f95ab8b0f59e6d354090350b44a80a80635d22efdedfa84c7ad1cf0a74147", size = 14363 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
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 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 },
|
||||
{ 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 },
|
||||
{ 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 },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-settings"
|
||||
version = "2.8.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "python-dotenv" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-multipart"
|
||||
version = "0.0.20"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "starlette"
|
||||
version = "0.46.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/1b/52b27f2e13ceedc79a908e29eac426a63465a1a01248e5f24aa36a62aeb3/starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230", size = 2580102 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/fc/5b29fea8cee020515ca82cc68e3b8e1e34bb19a3535ad854cac9257b414c/typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc", size = 45061 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.13.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/c5/e7a0b0f5ed69f94c8ab7379c599e6036886bffcde609969a5325f47f1332/typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", size = 45739 },
|
||||
]
|
||||
|
||||
[[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 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.34.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "h11" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "15.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 },
|
||||
]
|
||||
Reference in New Issue
Block a user