#
tokens: 5535/50000 10/10 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .cursor
│   └── rules
│       └── akshare-cursor-rule.mdc
├── .DS_Store
├── .gitignore
├── claude_desktop_config.json
├── data
│   └── strong_stocks_20250303.csv
├── Dockerfile
├── install.sh
├── pyproject.toml
├── README.md
├── run_server.py
├── smithery.yaml
├── src
│   ├── .DS_Store
│   └── mcp_server_akshare
│       ├── __init__.py
│       ├── .DS_Store
│       ├── api.py
│       └── server.py
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | venv/
2 | __pycache__/
3 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # AKShare MCP Server
  2 | 
  3 | A Model Context Protocol (MCP) server that provides financial data analysis capabilities using the AKShare library.
  4 | 
  5 | ## Features
  6 | 
  7 | - Access to Chinese and global financial market data through AKShare
  8 | - Integration with Claude Desktop via MCP protocol
  9 | - Support for various financial data queries and analysis
 10 | 
 11 | ## Installation
 12 | 
 13 | ### Using uv (recommended)
 14 | 
 15 | ```bash
 16 | # Clone the repository
 17 | git clone https://github.com/yourusername/akshare_mcp_server.git
 18 | cd akshare_mcp_server
 19 | 
 20 | # Create and activate a virtual environment
 21 | python -m venv venv
 22 | source venv/bin/activate  # On Windows: venv\Scripts\activate
 23 | 
 24 | # Install dependencies with uv
 25 | uv pip install -e .
 26 | ```
 27 | 
 28 | ### Using pip
 29 | 
 30 | ```bash
 31 | # Clone the repository
 32 | git clone https://github.com/yourusername/akshare_mcp_server.git
 33 | cd akshare_mcp_server
 34 | 
 35 | # Create and activate a virtual environment
 36 | python -m venv venv
 37 | source venv/bin/activate  # On Windows: venv\Scripts\activate
 38 | 
 39 | # Install dependencies
 40 | pip install -e .
 41 | ```
 42 | 
 43 | ## Usage
 44 | 
 45 | ### Running the server
 46 | 
 47 | ```bash
 48 | # Activate the virtual environment
 49 | source venv/bin/activate  # On Windows: venv\Scripts\activate
 50 | 
 51 | # Run the server
 52 | python run_server.py
 53 | ```
 54 | 
 55 | ### Integrating with Claude Desktop
 56 | 
 57 | 1. Add the following configuration to your Claude Desktop configuration:
 58 | 
 59 | ```json
 60 | "mcpServers": {
 61 |     "akshare-mcp": {
 62 |         "command": "uv",
 63 |         "args": [
 64 |             "--directory",
 65 |             "/path/to/akshare_mcp_server",
 66 |             "run",
 67 |             "akshare-mcp"
 68 |         ],
 69 |         "env": {
 70 |             "AKSHARE_API_KEY": "<your_api_key_if_needed>"
 71 |         }
 72 |     }
 73 | }
 74 | ```
 75 | 
 76 | 2. Restart Claude Desktop
 77 | 3. Select the AKShare MCP server from the available tools
 78 | 
 79 | ## Available Tools
 80 | 
 81 | The AKShare MCP server provides the following tools:
 82 | 
 83 | - Stock data queries
 84 | - Fund data queries
 85 | - Bond data queries
 86 | - Futures data queries
 87 | - Forex data queries
 88 | - Macroeconomic data queries
 89 | - And more...
 90 | 
 91 | ## Adding a New Tool
 92 | 
 93 | To add a new tool to the MCP server, follow these steps:
 94 | 
 95 | 1. **Add a new API function in `src/mcp_server_akshare/api.py`**:
 96 |    ```python
 97 |    async def fetch_new_data_function(param1: str, param2: str = "default") -> List[Dict[str, Any]]:
 98 |        """
 99 |        Fetch new data type.
100 |        
101 |        Args:
102 |            param1: Description of param1
103 |            param2: Description of param2
104 |        """
105 |        try:
106 |            df = ak.akshare_function_name(param1=param1, param2=param2)
107 |            return dataframe_to_dict(df)
108 |        except Exception as e:
109 |            logger.error(f"Error fetching new data: {e}")
110 |            raise
111 |    ```
112 | 
113 | 2. **Add the new tool to the enum in `src/mcp_server_akshare/server.py`**:
114 |    ```python
115 |    class AKShareTools(str, Enum):
116 |        # Existing tools...
117 |        NEW_TOOL_NAME = "new_tool_name"
118 |    ```
119 | 
120 | 3. **Import the new function in `src/mcp_server_akshare/server.py`**:
121 |    ```python
122 |    from .api import (
123 |        # Existing imports...
124 |        fetch_new_data_function,
125 |    )
126 |    ```
127 | 
128 | 4. **Add the tool definition to the `handle_list_tools()` function**:
129 |    ```python
130 |    types.Tool(
131 |        name=AKShareTools.NEW_TOOL_NAME.value,
132 |        description="Description of the new tool",
133 |        inputSchema={
134 |            "type": "object",
135 |            "properties": {
136 |                "param1": {"type": "string", "description": "Description of param1"},
137 |                "param2": {"type": "string", "description": "Description of param2"},
138 |            },
139 |            "required": ["param1"],  # List required parameters
140 |        },
141 |    ),
142 |    ```
143 | 
144 | 5. **Add the tool handler in the `handle_call_tool()` function**:
145 |    ```python
146 |    case AKShareTools.NEW_TOOL_NAME.value:
147 |        param1 = arguments.get("param1")
148 |        if not param1:
149 |            raise ValueError("Missing required argument: param1")
150 |        
151 |        param2 = arguments.get("param2", "default")
152 |        
153 |        result = await fetch_new_data_function(
154 |            param1=param1,
155 |            param2=param2,
156 |        )
157 |    ```
158 | 
159 | 6. **Test the new tool** by running the server and making a request to the new tool.
160 | 
161 | ## Development
162 | 
163 | ```bash
164 | # Install development dependencies
165 | uv pip install -e ".[dev]"
166 | 
167 | # Run tests
168 | pytest
169 | ```
170 | 
171 | ## Docker
172 | 
173 | You can also run the server using Docker:
174 | 
175 | ```bash
176 | # Build the Docker image
177 | docker build -t akshare-mcp-server .
178 | 
179 | # Run the Docker container
180 | docker run -p 8000:8000 akshare-mcp-server
181 | ```
182 | 
183 | ## License
184 | 
185 | MIT 
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | FROM python:3.10-slim
 2 | 
 3 | WORKDIR /app
 4 | 
 5 | # Install uv
 6 | RUN pip install uv
 7 | 
 8 | # Copy project files
 9 | COPY . .
10 | 
11 | # Install dependencies
12 | RUN uv pip install -e .
13 | 
14 | # Expose port if needed
15 | # EXPOSE 8000
16 | 
17 | # Run the server
18 | CMD ["python", "run_server.py"] 
```

--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | 
 3 | # Create and activate virtual environment
 4 | python3 -m venv venv
 5 | source venv/bin/activate
 6 | 
 7 | # Install uv if not already installed
 8 | pip install uv
 9 | 
10 | # Install the package with uv
11 | uv pip install -e .
12 | 
13 | echo "Installation complete. You can now run the server with 'python run_server.py'" 
```

--------------------------------------------------------------------------------
/claude_desktop_config.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |     "mcpServers": {
 3 |         "akshare-mcp": {
 4 |             "command": "uv",
 5 |             "args": [
 6 |                 "--directory",
 7 |                 "/Users/hlchen/CodeHub/akshare_mcp_server",
 8 |                 "run",
 9 |                 "akshare-mcp"
10 |             ],
11 |             "env": {
12 |                 "AKSHARE_API_KEY": "<insert_api_key_if_needed>"
13 |             }
14 |         }
15 |     }
16 | } 
```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | name: mcp-server-akshare
 2 | version: 0.1.0
 3 | description: MCP server for AKShare financial data
 4 | license: MIT
 5 | authors:
 6 |   - name: Your Name
 7 |     email: [email protected]
 8 | 
 9 | dependencies:
10 |   - akshare>=1.11.0
11 |   - mcp>=0.1.0
12 |   - httpx>=0.24.0
13 |   - python-dotenv>=1.0.0
14 |   - pandas>=2.0.0
15 |   - numpy>=1.24.0
16 | 
17 | dev-dependencies:
18 |   - black>=23.3.0
19 |   - isort>=5.12.0
20 |   - mypy>=1.3.0
21 |   - pytest>=7.3.1
22 |   - pytest-asyncio>=0.21.0
23 | 
24 | scripts:
25 |   akshare-mcp: mcp_server_akshare:main 
```

--------------------------------------------------------------------------------
/run_server.py:
--------------------------------------------------------------------------------

```python
 1 | #!/usr/bin/env python
 2 | """
 3 | Entry point for the AKShare MCP server.
 4 | """
 5 | 
 6 | import asyncio
 7 | import logging
 8 | import sys
 9 | 
10 | from src.mcp_server_akshare import main
11 | 
12 | # Configure logging
13 | logging.basicConfig(
14 |     level=logging.INFO,
15 |     format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
16 | )
17 | logger = logging.getLogger(__name__)
18 | 
19 | if __name__ == "__main__":
20 |     print("Starting AKShare MCP server...")
21 |     try:
22 |         asyncio.run(main())
23 |     except KeyboardInterrupt:
24 |         print("Server stopped by user.")
25 |         sys.exit(0)
26 |     except Exception as e:
27 |         logger.error(f"Error running server: {e}", exc_info=True)
28 |         sys.exit(1) 
```

--------------------------------------------------------------------------------
/src/mcp_server_akshare/__init__.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | MCP server for AKShare financial data.
 3 | """
 4 | 
 5 | import asyncio
 6 | import logging
 7 | from typing import Optional
 8 | 
 9 | from .server import main as server_main
10 | 
11 | # Configure logging
12 | logging.basicConfig(
13 |     level=logging.INFO,
14 |     format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
15 | )
16 | logger = logging.getLogger(__name__)
17 | 
18 | 
19 | async def main() -> None:
20 |     """
21 |     Main entry point for the AKShare MCP server.
22 |     """
23 |     logger.info("Starting AKShare MCP server...")
24 |     try:
25 |         await server_main()
26 |     except Exception as e:
27 |         logger.error(f"Error running AKShare MCP server: {e}", exc_info=True)
28 |         raise
29 | 
30 | 
31 | if __name__ == "__main__":
32 |     asyncio.run(main()) 
```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
 1 | [build-system]
 2 | requires = ["hatchling"]
 3 | build-backend = "hatchling.build"
 4 | 
 5 | [project]
 6 | name = "mcp-server-akshare"
 7 | version = "0.1.0"
 8 | description = "MCP server for AKShare financial data"
 9 | readme = "README.md"
10 | requires-python = ">=3.10"
11 | license = {text = "MIT"}
12 | authors = [
13 |     {name = "Your Name", email = "[email protected]"},
14 | ]
15 | dependencies = [
16 |     "akshare>=1.11.0",
17 |     "mcp>=0.1.0",
18 |     "httpx>=0.24.0",
19 |     "python-dotenv>=1.0.0",
20 |     "pandas>=2.0.0",
21 |     "numpy>=1.24.0",
22 | ]
23 | 
24 | [project.optional-dependencies]
25 | dev = [
26 |     "black>=23.3.0",
27 |     "isort>=5.12.0",
28 |     "mypy>=1.3.0",
29 |     "pytest>=7.3.1",
30 |     "pytest-asyncio>=0.21.0",
31 | ]
32 | 
33 | [project.scripts]
34 | akshare-mcp = "mcp_server_akshare:main"
35 | 
36 | [tool.black]
37 | line-length = 100
38 | target-version = ["py310"]
39 | 
40 | [tool.isort]
41 | profile = "black"
42 | line_length = 100
43 | 
44 | [tool.mypy]
45 | python_version = "3.10"
46 | warn_return_any = true
47 | warn_unused_configs = true
48 | disallow_untyped_defs = true
49 | disallow_incomplete_defs = true 
```

--------------------------------------------------------------------------------
/src/mcp_server_akshare/api.py:
--------------------------------------------------------------------------------

```python
  1 | """
  2 | API functions for interacting with AKShare.
  3 | """
  4 | 
  5 | import logging
  6 | import os
  7 | from typing import Any, Dict, List, Optional, Union
  8 | 
  9 | import akshare as ak
 10 | import pandas as pd
 11 | from dotenv import load_dotenv
 12 | 
 13 | # Load environment variables
 14 | load_dotenv()
 15 | 
 16 | # Configure logging
 17 | logger = logging.getLogger(__name__)
 18 | 
 19 | # Optional API key (if needed for certain endpoints)
 20 | API_KEY = os.getenv("AKSHARE_API_KEY")
 21 | 
 22 | 
 23 | def dataframe_to_dict(df: pd.DataFrame) -> List[Dict[str, Any]]:
 24 |     """
 25 |     Convert a pandas DataFrame to a list of dictionaries.
 26 |     """
 27 |     return df.to_dict(orient="records")
 28 | 
 29 | 
 30 | def dataframe_to_json(df: pd.DataFrame) -> str:
 31 |     """
 32 |     Convert a pandas DataFrame to a JSON string.
 33 |     """
 34 |     return df.to_json(orient="records", date_format="iso")
 35 | 
 36 | 
 37 | async def fetch_stock_zh_a_spot() -> List[Dict[str, Any]]:
 38 |     """
 39 |     Fetch A-share stock data.
 40 |     """
 41 |     try:
 42 |         df = ak.stock_zh_a_spot()
 43 |         return dataframe_to_dict(df)
 44 |     except Exception as e:
 45 |         logger.error(f"Error fetching A-share stock data: {e}")
 46 |         raise
 47 | 
 48 | 
 49 | async def fetch_stock_zh_a_hist(
 50 |     symbol: str, 
 51 |     period: str = "daily", 
 52 |     start_date: str = None, 
 53 |     end_date: str = None,
 54 |     adjust: str = ""
 55 | ) -> List[Dict[str, Any]]:
 56 |     """
 57 |     Fetch A-share stock historical data.
 58 |     
 59 |     Args:
 60 |         symbol: Stock code
 61 |         period: Data frequency, options: daily, weekly, monthly
 62 |         start_date: Start date in format YYYYMMDD
 63 |         end_date: End date in format YYYYMMDD
 64 |         adjust: Price adjustment, options: "", qfq (forward), hfq (backward)
 65 |     """
 66 |     try:
 67 |         df = ak.stock_zh_a_hist(
 68 |             symbol=symbol,
 69 |             period=period,
 70 |             start_date=start_date,
 71 |             end_date=end_date,
 72 |             adjust=adjust
 73 |         )
 74 |         return dataframe_to_dict(df)
 75 |     except Exception as e:
 76 |         logger.error(f"Error fetching stock historical data for {symbol}: {e}")
 77 |         raise
 78 | 
 79 | 
 80 | async def fetch_stock_zh_index_spot() -> List[Dict[str, Any]]:
 81 |     """
 82 |     Fetch Chinese stock market index data.
 83 |     """
 84 |     try:
 85 |         df = ak.stock_zh_index_spot()
 86 |         return dataframe_to_dict(df)
 87 |     except Exception as e:
 88 |         logger.error(f"Error fetching stock index data: {e}")
 89 |         raise
 90 | 
 91 | 
 92 | async def fetch_stock_zh_index_daily(symbol: str) -> List[Dict[str, Any]]:
 93 |     """
 94 |     Fetch Chinese stock market index daily data.
 95 |     
 96 |     Args:
 97 |         symbol: Index code
 98 |     """
 99 |     try:
100 |         df = ak.stock_zh_index_daily(symbol=symbol)
101 |         return dataframe_to_dict(df)
102 |     except Exception as e:
103 |         logger.error(f"Error fetching stock index daily data for {symbol}: {e}")
104 |         raise
105 | 
106 | 
107 | async def fetch_fund_etf_category_sina(category: str = "ETF基金") -> List[Dict[str, Any]]:
108 |     """
109 |     Fetch ETF fund data from Sina.
110 |     
111 |     Args:
112 |         category: Fund category
113 |     """
114 |     try:
115 |         df = ak.fund_etf_category_sina(category=category)
116 |         return dataframe_to_dict(df)
117 |     except Exception as e:
118 |         logger.error(f"Error fetching ETF fund data: {e}")
119 |         raise
120 | 
121 | 
122 | async def fetch_fund_etf_hist_sina(symbol: str) -> List[Dict[str, Any]]:
123 |     """
124 |     Fetch ETF fund historical data from Sina.
125 |     
126 |     Args:
127 |         symbol: ETF fund code
128 |     """
129 |     try:
130 |         df = ak.fund_etf_hist_sina(symbol=symbol)
131 |         return dataframe_to_dict(df)
132 |     except Exception as e:
133 |         logger.error(f"Error fetching ETF fund historical data for {symbol}: {e}")
134 |         raise
135 | 
136 | 
137 | async def fetch_macro_china_gdp() -> List[Dict[str, Any]]:
138 |     """
139 |     Fetch China GDP data.
140 |     """
141 |     try:
142 |         df = ak.macro_china_gdp()
143 |         return dataframe_to_dict(df)
144 |     except Exception as e:
145 |         logger.error(f"Error fetching China GDP data: {e}")
146 |         raise
147 | 
148 | 
149 | async def fetch_macro_china_cpi() -> List[Dict[str, Any]]:
150 |     """
151 |     Fetch China CPI data.
152 |     """
153 |     try:
154 |         df = ak.macro_china_cpi()
155 |         return dataframe_to_dict(df)
156 |     except Exception as e:
157 |         logger.error(f"Error fetching China CPI data: {e}")
158 |         raise
159 | 
160 | 
161 | async def fetch_forex_spot_quote() -> List[Dict[str, Any]]:
162 |     """
163 |     Fetch forex spot quotes.
164 |     """
165 |     try:
166 |         df = ak.forex_spot_quote()
167 |         return dataframe_to_dict(df)
168 |     except Exception as e:
169 |         logger.error(f"Error fetching forex spot quotes: {e}")
170 |         raise
171 | 
172 | 
173 | async def fetch_futures_zh_spot() -> List[Dict[str, Any]]:
174 |     """
175 |     Fetch Chinese futures market spot data.
176 |     """
177 |     try:
178 |         df = ak.futures_zh_spot()
179 |         return dataframe_to_dict(df)
180 |     except Exception as e:
181 |         logger.error(f"Error fetching futures spot data: {e}")
182 |         raise
183 | 
184 | 
185 | async def fetch_bond_zh_hs_cov_spot() -> List[Dict[str, Any]]:
186 |     """
187 |     Fetch Chinese convertible bond data.
188 |     """
189 |     try:
190 |         df = ak.bond_zh_hs_cov_spot()
191 |         return dataframe_to_dict(df)
192 |     except Exception as e:
193 |         logger.error(f"Error fetching convertible bond data: {e}")
194 |         raise
195 | 
196 | 
197 | async def fetch_stock_zt_pool_strong_em(date: str = None) -> List[Dict[str, Any]]:
198 |     """
199 |     Fetch strong stock pool data from East Money.
200 |     
201 |     Args:
202 |         date: Date in format YYYYMMDD
203 |     """
204 |     try:
205 |         logger.info(f"Fetching strong stock pool data for date: {date}")
206 |         df = ak.stock_zt_pool_strong_em(date=date)
207 |         
208 |         logger.info(f"Result type: {type(df)}")
209 |         logger.info(f"Is DataFrame empty: {df.empty}")
210 |         
211 |         if not df.empty:
212 |             logger.info(f"DataFrame shape: {df.shape}")
213 |             logger.info(f"DataFrame columns: {df.columns.tolist()}")
214 |             return dataframe_to_dict(df)
215 |         else:
216 |             logger.warning(f"No data available for date: {date}")
217 |             
218 |             # Try without date parameter as a fallback
219 |             if date:
220 |                 logger.info("Trying again without date parameter as fallback...")
221 |                 df_fallback = ak.stock_zt_pool_strong_em()
222 |                 logger.info(f"Fallback result type: {type(df_fallback)}")
223 |                 logger.info(f"Fallback is DataFrame empty: {df_fallback.empty}")
224 |                 
225 |                 if not df_fallback.empty:
226 |                     logger.info(f"Fallback DataFrame shape: {df_fallback.shape}")
227 |                     logger.info(f"Fallback DataFrame columns: {df_fallback.columns.tolist()}")
228 |                     return dataframe_to_dict(df_fallback)
229 |                 else:
230 |                     logger.warning("Fallback also returned empty DataFrame")
231 |             
232 |             # Return empty list if no data is available
233 |             return []
234 |     except Exception as e:
235 |         logger.error(f"Error fetching strong stock pool data for date {date}: {e}")
236 |         raise 
```