Skip to main content
Glama

Stock MCP Server

by huweihua123
DEVELOPMENT.mdโ€ข10.3 kB
# ๐Ÿ”ง Stock MCP ๅผ€ๅ‘ๆ–‡ๆกฃ > ๆžถๆž„่ฎพ่ฎกใ€ไบŒๆฌกๅผ€ๅ‘ๅ’Œๆ‰ฉๅฑ•ๆŒ‡ๅ— --- ## ๐Ÿ“ ้กน็›ฎๆžถๆž„ ### ๆ•ดไฝ“ๆžถๆž„ ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Client Layer โ”‚ โ”‚ (Claude Desktop / API Client / Web Dashboard) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ–ผ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ MCP Server โ”‚ โ”‚ FastAPI โ”‚ โ”‚ (Port 9999) โ”‚ โ”‚ (Port 9998) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Service Layer โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Market Service โ”‚ โ”‚ โ”‚ โ”‚ Quote Service โ”‚ โ”‚ โ”‚ โ”‚ News Service โ”‚ โ”‚ โ”‚ โ”‚ Fundamentals Service โ”‚ โ”‚ โ”‚ โ”‚ Tavily Service โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Data Source Layer โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ AKShare โ”‚ โ”‚ โ”‚ โ”‚ Tushare โ”‚ โ”‚ โ”‚ โ”‚ yFinance โ”‚ โ”‚ โ”‚ โ”‚ Finnhub โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Redis Cache Layer โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### ็›ฎๅฝ•็ป“ๆž„ ``` src/ โ”œโ”€โ”€ server/ โ”‚ โ”œโ”€โ”€ app.py # FastAPI ๅบ”็”จๅ…ฅๅฃ โ”‚ โ”œโ”€โ”€ mcp_server.py # MCP ๆœๅŠกๅ™จ โ”‚ โ”œโ”€โ”€ routes/ # API ่ทฏ็”ฑ โ”‚ โ”‚ โ”œโ”€โ”€ api_routes.py # RESTful ๆŽฅๅฃ โ”‚ โ”‚ โ””โ”€โ”€ sse_routes.py # SSE ๆŽฅๅฃ โ”‚ โ”œโ”€โ”€ services/ # ไธšๅŠก้€ป่พ‘ๅฑ‚ โ”‚ โ”‚ โ”œโ”€โ”€ market_service.py # ๅธ‚ๅœบๆ•ฐๆฎ โ”‚ โ”‚ โ”œโ”€โ”€ quote_service.py # ่กŒๆƒ…ๆœๅŠก โ”‚ โ”‚ โ”œโ”€โ”€ news_service.py # ๆ–ฐ้—ปๆœๅŠก โ”‚ โ”‚ โ””โ”€โ”€ ... โ”‚ โ””โ”€โ”€ utils/ # ๅทฅๅ…ท็ฑป โ”‚ โ”œโ”€โ”€ redis_cache.py # ็ผ“ๅญ˜็ฎก็† โ”‚ โ”œโ”€โ”€ symbol_processor.py # ่‚ก็ฅจไปฃ็ ๅค„็† โ”‚ โ””โ”€โ”€ ... โ””โ”€โ”€ config/ โ””โ”€โ”€ settings.py # ้…็ฝฎ็ฎก็† ``` --- ## ๐Ÿ› ๏ธ ๆœฌๅœฐๅผ€ๅ‘ ### ๅผ€ๅ‘็Žฏๅขƒ่ฎพ็ฝฎ ```bash # 1. ๅˆ›ๅปบ่™šๆ‹Ÿ็Žฏๅขƒ python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate # 2. ๅฎ‰่ฃ…ไพ่ต– pip install -r requirements.txt pip install -r requirements-dev.txt # ๅผ€ๅ‘ไพ่ต– # 3. ้…็ฝฎ็Žฏๅขƒๅ˜้‡ cp .env.example .env vim .env # 4. ๅฏๅŠจ Redis๏ผˆDocker๏ผ‰ docker run -d -p 6379:6379 redis:alpine # 5. ๅฏๅŠจๅผ€ๅ‘ๆœๅŠกๅ™จ python main.py --http-port 9998 --mcp-port 9999 ``` ### ็ƒญ้‡่ฝฝๅผ€ๅ‘ ```bash # ไฝฟ็”จ uvicorn ็ƒญ้‡่ฝฝ uvicorn src.server.app:app --reload --port 9998 ``` --- ## ๐Ÿ”Œ ๆทปๅŠ ๆ–ฐๆ•ฐๆฎๆบ ### 1. ๅˆ›ๅปบๆœๅŠก็ฑป ```python # src/server/services/new_data_source_service.py from typing import Dict, Any, Optional from ..utils.redis_cache import cache_result class NewDataSourceService: """ๆ–ฐๆ•ฐๆฎๆบๆœๅŠก""" def __init__(self): self.api_key = os.getenv("NEW_API_KEY") @cache_result(ttl=3600) async def get_data(self, symbol: str) -> Dict[str, Any]: """่Žทๅ–ๆ•ฐๆฎ๏ผˆๅธฆ็ผ“ๅญ˜๏ผ‰""" # ๅฎž็Žฐๆ•ฐๆฎ่Žทๅ–้€ป่พ‘ return { "symbol": symbol, "data": "..." } ``` ### 2. ๆณจๅ†Œๅˆฐ่ทฏ็”ฑ ```python # src/server/routes/api_routes.py from ..services.new_data_source_service import NewDataSourceService new_service = NewDataSourceService() @router.get("/api/new-endpoint") async def get_new_data(symbol: str): """ๆ–ฐๆŽฅๅฃ""" try: data = await new_service.get_data(symbol) return {"code": 0, "data": data} except Exception as e: return {"code": 1, "error": str(e)} ``` ### 3. ๆทปๅŠ  MCP ๅทฅๅ…ท ```python # src/server/mcp_server.py @server.call_tool() async def call_tool(name: str, arguments: Any) -> Sequence[TextContent | ImageContent | EmbeddedResource]: if name == "new_tool": symbol = arguments.get("symbol") data = await new_service.get_data(symbol) return [TextContent(type="text", text=json.dumps(data))] ``` --- ## ๐Ÿงช ๆต‹่ฏ• ### ๅ•ๅ…ƒๆต‹่ฏ• ```bash # ่ฟ่กŒๆ‰€ๆœ‰ๆต‹่ฏ• pytest # ่ฟ่กŒ็‰นๅฎšๆต‹่ฏ• pytest tests/test_news_service.py # ็”Ÿๆˆ่ฆ†็›–็އๆŠฅๅ‘Š pytest --cov=src --cov-report=html ``` ### ๆต‹่ฏ•็คบไพ‹ ```python # tests/test_quote_service.py import pytest from src.server.services.quote_service import QuoteService @pytest.mark.asyncio async def test_get_quote(): service = QuoteService() result = await service.get_quote("600519") assert result is not None assert result["symbol"] == "600519" assert "price" in result ``` ### API ๆต‹่ฏ• ```bash # ไฝฟ็”จ httpie http GET localhost:9998/api/quote symbol==600519 # ไฝฟ็”จ curl curl "http://localhost:9998/api/quote?symbol=600519" ``` --- ## ๐Ÿ“Š ๆ€ง่ƒฝไผ˜ๅŒ– ### ็ผ“ๅญ˜็ญ–็•ฅ ```python # ไธๅŒๆ•ฐๆฎ็ฑปๅž‹ไฝฟ็”จไธๅŒ็š„็ผ“ๅญ˜ๆ—ถ้—ด CACHE_TTL_CONFIG = { "realtime_quote": 60, # ๅฎžๆ—ถ่กŒๆƒ… 1ๅˆ†้’Ÿ "daily_data": 3600, # ๆ—ฅ็บฟๆ•ฐๆฎ 1ๅฐๆ—ถ "financial": 86400, # ่ดขๅŠกๆ•ฐๆฎ 1ๅคฉ "company_info": 604800, # ๅ…ฌๅธไฟกๆฏ 1ๅ‘จ } @cache_result(ttl=CACHE_TTL_CONFIG["realtime_quote"]) async def get_realtime_quote(symbol: str): # ... ``` ### ๅนถๅ‘ๅค„็† ```python import asyncio async def batch_get_quotes(symbols: list): """ๆ‰น้‡่Žทๅ–่กŒๆƒ…""" tasks = [get_quote(symbol) for symbol in symbols] results = await asyncio.gather(*tasks, return_exceptions=True) return results ``` ### ่ฟžๆŽฅๆฑ  ```python # ไฝฟ็”จ aiohttp ่ฟžๆŽฅๆฑ  import aiohttp session = aiohttp.ClientSession( connector=aiohttp.TCPConnector(limit=100), timeout=aiohttp.ClientTimeout(total=30) ) ``` --- ## ๐Ÿ”’ ๅฎ‰ๅ…จๆœ€ไฝณๅฎž่ทต ### ็Žฏๅขƒๅ˜้‡ ```python # โŒ ไธ่ฆ็กฌ็ผ–็  API_KEY = "sk-abc123..." # โœ… ไฝฟ็”จ็Žฏๅขƒๅ˜้‡ import os API_KEY = os.getenv("API_KEY") ``` ### API ้™ๆต ```python from fastapi import HTTPException from slowapi import Limiter from slowapi.util import get_remote_address limiter = Limiter(key_func=get_remote_address) @app.get("/api/quote") @limiter.limit("100/minute") async def get_quote(request: Request, symbol: str): # ... ``` ### ่พ“ๅ…ฅ้ชŒ่ฏ ```python from pydantic import BaseModel, validator class QuoteRequest(BaseModel): symbol: str market: str = "CN" @validator("symbol") def validate_symbol(cls, v): if not v or len(v) > 10: raise ValueError("Invalid symbol") return v.upper() ``` --- ## ๐Ÿ“ ไปฃ็ ้ฃŽๆ ผ ### ๆ ผๅผๅŒ– ```bash # ไฝฟ็”จ black ๆ ผๅผๅŒ–ไปฃ็  black src/ # ไฝฟ็”จ isort ๆŽ’ๅบๅฏผๅ…ฅ isort src/ # ไฝฟ็”จ flake8 ๆฃ€ๆŸฅ flake8 src/ ``` ### ็ฑปๅž‹ๆ็คบ ```python from typing import Dict, List, Optional def get_quote(symbol: str, market: str = "CN") -> Dict[str, Any]: """่Žทๅ–่‚ก็ฅจ่กŒๆƒ… Args: symbol: ่‚ก็ฅจไปฃ็  market: ๅธ‚ๅœบไปฃ็  Returns: ๅŒ…ๅซ่กŒๆƒ…ๆ•ฐๆฎ็š„ๅญ—ๅ…ธ """ # ... ``` --- ## ๐Ÿš€ ้ƒจ็ฝฒ ### Docker ๆž„ๅปบ ```bash # ๆž„ๅปบ้•œๅƒ docker build -t stock-mcp:latest . # ๅคšๆžถๆž„ๆž„ๅปบ docker buildx build --platform linux/amd64,linux/arm64 -t stock-mcp:latest . ``` ### ๅฅๅบทๆฃ€ๆŸฅ ```python @app.get("/health") async def health_check(): """ๅฅๅบทๆฃ€ๆŸฅๆŽฅๅฃ""" redis_ok = await check_redis() return { "status": "healthy" if redis_ok else "degraded", "redis": redis_ok, "timestamp": datetime.now().isoformat() } ``` --- ## ๐Ÿ› ่ฐƒ่ฏ•ๆŠ€ๅทง ### ๆ—ฅๅฟ—้…็ฝฎ ```python import logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('logs/app.log'), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) logger.debug("Debug message") ``` ### ๆ€ง่ƒฝๅˆ†ๆž ```python import time from functools import wraps def timing_decorator(func): @wraps(func) async def wrapper(*args, **kwargs): start = time.time() result = await func(*args, **kwargs) duration = time.time() - start logger.info(f"{func.__name__} took {duration:.2f}s") return result return wrapper @timing_decorator async def slow_function(): # ... ``` --- ## ๐Ÿ“š ๅ‚่€ƒ่ต„ๆบ - [FastAPI ๆ–‡ๆกฃ](https://fastapi.tiangolo.com/) - [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) - [Redis Python ๅฎขๆˆท็ซฏ](https://redis-py.readthedocs.io/) - [AKShare ๆ–‡ๆกฃ](https://akshare.akfamily.xyz/) --- <div align="center"> **๐Ÿ’ก ๆœ‰็–‘้—ฎ๏ผŸๆŸฅ็œ‹ [ๅฎŒๆ•ดๆŒ‡ๅ—](GUIDE.md) ๆˆ–ๆไบค [Issue](https://github.com/your-repo/issues)** </div>

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/huweihua123/stock-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server