Spaces:
Runtime error
Runtime error
Upload 8 files
Browse files- API_USAGE.md +500 -0
- DEPLOYMENT.md +121 -0
- Dockerfile +21 -0
- README.md +84 -12
- edgar_client.py +342 -0
- mcp_client_examples.json +490 -0
- mcp_server.py +312 -0
- requirements.txt +5 -0
API_USAGE.md
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# API Usage Guide
|
| 2 |
+
|
| 3 |
+
Complete guide for using the SEC Financial Report MCP Server API.
|
| 4 |
+
|
| 5 |
+
## Base URL
|
| 6 |
+
|
| 7 |
+
After deployment: `https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space`
|
| 8 |
+
|
| 9 |
+
## Interactive Documentation
|
| 10 |
+
|
| 11 |
+
- **Swagger UI**: `{BASE_URL}/docs`
|
| 12 |
+
- **ReDoc**: `{BASE_URL}/redoc`
|
| 13 |
+
|
| 14 |
+
## Quick Start
|
| 15 |
+
|
| 16 |
+
### Python Example
|
| 17 |
+
|
| 18 |
+
```python
|
| 19 |
+
import requests
|
| 20 |
+
|
| 21 |
+
BASE_URL = "https://YOUR_SPACE_URL.hf.space"
|
| 22 |
+
|
| 23 |
+
# 1. Search company
|
| 24 |
+
response = requests.post(
|
| 25 |
+
f"{BASE_URL}/api/search_company",
|
| 26 |
+
json={"company_name": "NVIDIA"}
|
| 27 |
+
)
|
| 28 |
+
company = response.json()
|
| 29 |
+
print(f"Found: {company['name']} (CIK: {company['cik']})")
|
| 30 |
+
|
| 31 |
+
# 2. Get financial data
|
| 32 |
+
response = requests.post(
|
| 33 |
+
f"{BASE_URL}/api/get_financial_data",
|
| 34 |
+
json={"cik": company['cik'], "period": "2024"}
|
| 35 |
+
)
|
| 36 |
+
data = response.json()
|
| 37 |
+
print(f"Revenue: ${data['total_revenue']:,.0f}")
|
| 38 |
+
print(f"Net Income: ${data['net_income']:,.0f}")
|
| 39 |
+
print(f"EPS: ${data['earnings_per_share']:.2f}")
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
## Base URL
|
| 43 |
+
|
| 44 |
+
```
|
| 45 |
+
https://your-space-name.hf.space
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
## API Endpoints
|
| 49 |
+
|
| 50 |
+
### 1. Search Company by Name
|
| 51 |
+
|
| 52 |
+
**Endpoint:** `POST /api/search_company`
|
| 53 |
+
|
| 54 |
+
**Request:**
|
| 55 |
+
```json
|
| 56 |
+
{
|
| 57 |
+
"company_name": "NVIDIA"
|
| 58 |
+
}
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
**Response:**
|
| 62 |
+
```json
|
| 63 |
+
{
|
| 64 |
+
"cik": "0001045810",
|
| 65 |
+
"name": "NVIDIA CORP",
|
| 66 |
+
"ticker": "NVDA",
|
| 67 |
+
"error": null
|
| 68 |
+
}
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
**cURL Example:**
|
| 72 |
+
```bash
|
| 73 |
+
curl -X POST "https://your-space-name.hf.space/api/search_company" \
|
| 74 |
+
-H "Content-Type: application/json" \
|
| 75 |
+
-d '{"company_name": "NVIDIA"}'
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
**Python Example:**
|
| 79 |
+
```python
|
| 80 |
+
import requests
|
| 81 |
+
|
| 82 |
+
url = "https://your-space-name.hf.space/api/search_company"
|
| 83 |
+
payload = {"company_name": "NVIDIA"}
|
| 84 |
+
response = requests.post(url, json=payload)
|
| 85 |
+
print(response.json())
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
---
|
| 89 |
+
|
| 90 |
+
### 2. Get Company Information
|
| 91 |
+
|
| 92 |
+
**Endpoint:** `POST /api/get_company_info`
|
| 93 |
+
|
| 94 |
+
**Request:**
|
| 95 |
+
```json
|
| 96 |
+
{
|
| 97 |
+
"cik": "0001045810"
|
| 98 |
+
}
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
**Response:**
|
| 102 |
+
```json
|
| 103 |
+
{
|
| 104 |
+
"cik": "0001045810",
|
| 105 |
+
"name": "NVIDIA CORP",
|
| 106 |
+
"tickers": ["NVDA"],
|
| 107 |
+
"sic": "3674",
|
| 108 |
+
"sic_description": "Semiconductors & Related Devices",
|
| 109 |
+
"error": null
|
| 110 |
+
}
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
**cURL Example:**
|
| 114 |
+
```bash
|
| 115 |
+
curl -X POST "https://your-space-name.hf.space/api/get_company_info" \
|
| 116 |
+
-H "Content-Type: application/json" \
|
| 117 |
+
-d '{"cik": "0001045810"}'
|
| 118 |
+
```
|
| 119 |
+
|
| 120 |
+
**Python Example:**
|
| 121 |
+
```python
|
| 122 |
+
import requests
|
| 123 |
+
|
| 124 |
+
url = "https://your-space-name.hf.space/api/get_company_info"
|
| 125 |
+
payload = {"cik": "0001045810"}
|
| 126 |
+
response = requests.post(url, json=payload)
|
| 127 |
+
print(response.json())
|
| 128 |
+
```
|
| 129 |
+
|
| 130 |
+
---
|
| 131 |
+
|
| 132 |
+
### 3. Get Company Filings
|
| 133 |
+
|
| 134 |
+
**Endpoint:** `POST /api/get_company_filings`
|
| 135 |
+
|
| 136 |
+
**Request (All form types):**
|
| 137 |
+
```json
|
| 138 |
+
{
|
| 139 |
+
"cik": "0001045810",
|
| 140 |
+
"form_types": null
|
| 141 |
+
}
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
**Request (Specific form types):**
|
| 145 |
+
```json
|
| 146 |
+
{
|
| 147 |
+
"cik": "0001045810",
|
| 148 |
+
"form_types": ["10-K", "10-Q"]
|
| 149 |
+
}
|
| 150 |
+
```
|
| 151 |
+
|
| 152 |
+
**Response:**
|
| 153 |
+
```json
|
| 154 |
+
{
|
| 155 |
+
"filings": [
|
| 156 |
+
{
|
| 157 |
+
"form_type": "10-K",
|
| 158 |
+
"filing_date": "2024-02-21",
|
| 159 |
+
"accession_number": "0001045810-24-000029",
|
| 160 |
+
"primary_document": "nvda-20240128.htm"
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"form_type": "10-Q",
|
| 164 |
+
"filing_date": "2024-05-22",
|
| 165 |
+
"accession_number": "0001045810-24-000067",
|
| 166 |
+
"primary_document": "nvda-20240428.htm"
|
| 167 |
+
}
|
| 168 |
+
],
|
| 169 |
+
"error": null
|
| 170 |
+
}
|
| 171 |
+
```
|
| 172 |
+
|
| 173 |
+
**cURL Example:**
|
| 174 |
+
```bash
|
| 175 |
+
curl -X POST "https://your-space-name.hf.space/api/get_company_filings" \
|
| 176 |
+
-H "Content-Type: application/json" \
|
| 177 |
+
-d '{"cik": "0001045810", "form_types": ["10-K", "10-Q"]}'
|
| 178 |
+
```
|
| 179 |
+
|
| 180 |
+
**Python Example:**
|
| 181 |
+
```python
|
| 182 |
+
import requests
|
| 183 |
+
|
| 184 |
+
url = "https://your-space-name.hf.space/api/get_company_filings"
|
| 185 |
+
payload = {
|
| 186 |
+
"cik": "0001045810",
|
| 187 |
+
"form_types": ["10-K", "10-Q"]
|
| 188 |
+
}
|
| 189 |
+
response = requests.post(url, json=payload)
|
| 190 |
+
print(response.json())
|
| 191 |
+
```
|
| 192 |
+
|
| 193 |
+
---
|
| 194 |
+
|
| 195 |
+
### 4. Get Company Facts
|
| 196 |
+
|
| 197 |
+
**Endpoint:** `POST /api/get_company_facts`
|
| 198 |
+
|
| 199 |
+
**Request:**
|
| 200 |
+
```json
|
| 201 |
+
{
|
| 202 |
+
"cik": "0001045810"
|
| 203 |
+
}
|
| 204 |
+
```
|
| 205 |
+
|
| 206 |
+
**Response:**
|
| 207 |
+
```json
|
| 208 |
+
{
|
| 209 |
+
"facts": {
|
| 210 |
+
"dei": {
|
| 211 |
+
"EntityCommonStockSharesOutstanding": {...},
|
| 212 |
+
"EntityPublicFloat": {...}
|
| 213 |
+
},
|
| 214 |
+
"us-gaap": {
|
| 215 |
+
"Revenues": {...},
|
| 216 |
+
"NetIncomeLoss": {...},
|
| 217 |
+
"EarningsPerShareBasic": {...}
|
| 218 |
+
}
|
| 219 |
+
},
|
| 220 |
+
"error": null
|
| 221 |
+
}
|
| 222 |
+
```
|
| 223 |
+
|
| 224 |
+
**cURL Example:**
|
| 225 |
+
```bash
|
| 226 |
+
curl -X POST "https://your-space-name.hf.space/api/get_company_facts" \
|
| 227 |
+
-H "Content-Type: application/json" \
|
| 228 |
+
-d '{"cik": "0001045810"}'
|
| 229 |
+
```
|
| 230 |
+
|
| 231 |
+
**Python Example:**
|
| 232 |
+
```python
|
| 233 |
+
import requests
|
| 234 |
+
|
| 235 |
+
url = "https://your-space-name.hf.space/api/get_company_facts"
|
| 236 |
+
payload = {"cik": "0001045810"}
|
| 237 |
+
response = requests.post(url, json=payload)
|
| 238 |
+
print(response.json())
|
| 239 |
+
```
|
| 240 |
+
|
| 241 |
+
---
|
| 242 |
+
|
| 243 |
+
### 5. Get Financial Data for Period
|
| 244 |
+
|
| 245 |
+
**Endpoint:** `POST /api/get_financial_data`
|
| 246 |
+
|
| 247 |
+
**Request (Annual Data):**
|
| 248 |
+
```json
|
| 249 |
+
{
|
| 250 |
+
"cik": "0001045810",
|
| 251 |
+
"period": "2024"
|
| 252 |
+
}
|
| 253 |
+
```
|
| 254 |
+
|
| 255 |
+
**Request (Quarterly Data):**
|
| 256 |
+
```json
|
| 257 |
+
{
|
| 258 |
+
"cik": "0001045810",
|
| 259 |
+
"period": "2024Q3"
|
| 260 |
+
}
|
| 261 |
+
```
|
| 262 |
+
|
| 263 |
+
**Response:**
|
| 264 |
+
```json
|
| 265 |
+
{
|
| 266 |
+
"period": "2024",
|
| 267 |
+
"total_revenue": 60922000000,
|
| 268 |
+
"net_income": 29760000000,
|
| 269 |
+
"earnings_per_share": 12.04,
|
| 270 |
+
"operating_expenses": 11822000000,
|
| 271 |
+
"operating_cash_flow": 28091000000,
|
| 272 |
+
"source_url": "https://www.sec.gov/Archives/edgar/data/1045810/000104581024000029",
|
| 273 |
+
"source_form": "10-K",
|
| 274 |
+
"data_source": "us-gaap",
|
| 275 |
+
"additional_details": {
|
| 276 |
+
"total_revenue_details": {
|
| 277 |
+
"tag": "Revenues",
|
| 278 |
+
"form": "10-K",
|
| 279 |
+
"fy": 2024,
|
| 280 |
+
"fp": "FY",
|
| 281 |
+
"val": 60922000000,
|
| 282 |
+
"start": "2023-01-30",
|
| 283 |
+
"end": "2024-01-28",
|
| 284 |
+
"accn": "0001045810-24-000029",
|
| 285 |
+
"filed": "2024-02-21",
|
| 286 |
+
"frame": "CY2023",
|
| 287 |
+
"data_source": "us-gaap"
|
| 288 |
+
}
|
| 289 |
+
},
|
| 290 |
+
"error": null
|
| 291 |
+
}
|
| 292 |
+
```
|
| 293 |
+
|
| 294 |
+
**cURL Example:**
|
| 295 |
+
```bash
|
| 296 |
+
curl -X POST "https://your-space-name.hf.space/api/get_financial_data" \
|
| 297 |
+
-H "Content-Type: application/json" \
|
| 298 |
+
-d '{"cik": "0001045810", "period": "2024"}'
|
| 299 |
+
```
|
| 300 |
+
|
| 301 |
+
**Python Example:**
|
| 302 |
+
```python
|
| 303 |
+
import requests
|
| 304 |
+
|
| 305 |
+
url = "https://your-space-name.hf.space/api/get_financial_data"
|
| 306 |
+
payload = {
|
| 307 |
+
"cik": "0001045810",
|
| 308 |
+
"period": "2024"
|
| 309 |
+
}
|
| 310 |
+
response = requests.post(url, json=payload)
|
| 311 |
+
data = response.json()
|
| 312 |
+
print(f"Total Revenue: ${data['total_revenue']:,.0f}")
|
| 313 |
+
print(f"Net Income: ${data['net_income']:,.0f}")
|
| 314 |
+
print(f"EPS: ${data['earnings_per_share']:.2f}")
|
| 315 |
+
```
|
| 316 |
+
|
| 317 |
+
---
|
| 318 |
+
|
| 319 |
+
## Complete Python Client Example
|
| 320 |
+
|
| 321 |
+
```python
|
| 322 |
+
import requests
|
| 323 |
+
import json
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
class SECMCPClient:
|
| 327 |
+
"""Client for SEC Financial Report MCP Server"""
|
| 328 |
+
|
| 329 |
+
def __init__(self, base_url):
|
| 330 |
+
self.base_url = base_url.rstrip('/')
|
| 331 |
+
|
| 332 |
+
def search_company(self, company_name):
|
| 333 |
+
"""Search company by name"""
|
| 334 |
+
url = f"{self.base_url}/api/search_company"
|
| 335 |
+
response = requests.post(url, json={"company_name": company_name})
|
| 336 |
+
return response.json()
|
| 337 |
+
|
| 338 |
+
def get_company_info(self, cik):
|
| 339 |
+
"""Get company information"""
|
| 340 |
+
url = f"{self.base_url}/api/get_company_info"
|
| 341 |
+
response = requests.post(url, json={"cik": cik})
|
| 342 |
+
return response.json()
|
| 343 |
+
|
| 344 |
+
def get_company_filings(self, cik, form_types=None):
|
| 345 |
+
"""Get company filings"""
|
| 346 |
+
url = f"{self.base_url}/api/get_company_filings"
|
| 347 |
+
payload = {"cik": cik, "form_types": form_types}
|
| 348 |
+
response = requests.post(url, json=payload)
|
| 349 |
+
return response.json()
|
| 350 |
+
|
| 351 |
+
def get_company_facts(self, cik):
|
| 352 |
+
"""Get company facts"""
|
| 353 |
+
url = f"{self.base_url}/api/get_company_facts"
|
| 354 |
+
response = requests.post(url, json={"cik": cik})
|
| 355 |
+
return response.json()
|
| 356 |
+
|
| 357 |
+
def get_financial_data(self, cik, period):
|
| 358 |
+
"""Get financial data for period"""
|
| 359 |
+
url = f"{self.base_url}/api/get_financial_data"
|
| 360 |
+
payload = {"cik": cik, "period": period}
|
| 361 |
+
response = requests.post(url, json=payload)
|
| 362 |
+
return response.json()
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
# Usage example
|
| 366 |
+
if __name__ == "__main__":
|
| 367 |
+
# Initialize client
|
| 368 |
+
client = SECMCPClient("https://your-space-name.hf.space")
|
| 369 |
+
|
| 370 |
+
# Search for a company
|
| 371 |
+
company = client.search_company("NVIDIA")
|
| 372 |
+
print(f"Found: {company['name']} (CIK: {company['cik']})")
|
| 373 |
+
|
| 374 |
+
# Get company information
|
| 375 |
+
info = client.get_company_info(company['cik'])
|
| 376 |
+
print(f"Industry: {info['sic_description']}")
|
| 377 |
+
|
| 378 |
+
# Get recent filings
|
| 379 |
+
filings = client.get_company_filings(company['cik'], ["10-K"])
|
| 380 |
+
print(f"Recent 10-K filings: {len(filings['filings'])}")
|
| 381 |
+
|
| 382 |
+
# Get financial data for 2024
|
| 383 |
+
financial_data = client.get_financial_data(company['cik'], "2024")
|
| 384 |
+
print(f"FY2024 Revenue: ${financial_data['total_revenue']:,.0f}")
|
| 385 |
+
print(f"FY2024 Net Income: ${financial_data['net_income']:,.0f}")
|
| 386 |
+
print(f"FY2024 EPS: ${financial_data['earnings_per_share']:.2f}")
|
| 387 |
+
```
|
| 388 |
+
|
| 389 |
+
---
|
| 390 |
+
|
| 391 |
+
## Complete JavaScript/Node.js Client Example
|
| 392 |
+
|
| 393 |
+
```javascript
|
| 394 |
+
const axios = require('axios');
|
| 395 |
+
|
| 396 |
+
class SECMCPClient {
|
| 397 |
+
constructor(baseUrl) {
|
| 398 |
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
async searchCompany(companyName) {
|
| 402 |
+
const response = await axios.post(
|
| 403 |
+
`${this.baseUrl}/api/search_company`,
|
| 404 |
+
{ company_name: companyName }
|
| 405 |
+
);
|
| 406 |
+
return response.data;
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
async getCompanyInfo(cik) {
|
| 410 |
+
const response = await axios.post(
|
| 411 |
+
`${this.baseUrl}/api/get_company_info`,
|
| 412 |
+
{ cik: cik }
|
| 413 |
+
);
|
| 414 |
+
return response.data;
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
async getCompanyFilings(cik, formTypes = null) {
|
| 418 |
+
const response = await axios.post(
|
| 419 |
+
`${this.baseUrl}/api/get_company_filings`,
|
| 420 |
+
{ cik: cik, form_types: formTypes }
|
| 421 |
+
);
|
| 422 |
+
return response.data;
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
async getCompanyFacts(cik) {
|
| 426 |
+
const response = await axios.post(
|
| 427 |
+
`${this.baseUrl}/api/get_company_facts`,
|
| 428 |
+
{ cik: cik }
|
| 429 |
+
);
|
| 430 |
+
return response.data;
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
async getFinancialData(cik, period) {
|
| 434 |
+
const response = await axios.post(
|
| 435 |
+
`${this.baseUrl}/api/get_financial_data`,
|
| 436 |
+
{ cik: cik, period: period }
|
| 437 |
+
);
|
| 438 |
+
return response.data;
|
| 439 |
+
}
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
// Usage example
|
| 443 |
+
(async () => {
|
| 444 |
+
const client = new SECMCPClient('https://your-space-name.hf.space');
|
| 445 |
+
|
| 446 |
+
// Search for a company
|
| 447 |
+
const company = await client.searchCompany('NVIDIA');
|
| 448 |
+
console.log(`Found: ${company.name} (CIK: ${company.cik})`);
|
| 449 |
+
|
| 450 |
+
// Get financial data
|
| 451 |
+
const financialData = await client.getFinancialData(company.cik, '2024');
|
| 452 |
+
console.log(`FY2024 Revenue: $${financialData.total_revenue.toLocaleString()}`);
|
| 453 |
+
console.log(`FY2024 Net Income: $${financialData.net_income.toLocaleString()}`);
|
| 454 |
+
console.log(`FY2024 EPS: $${financialData.earnings_per_share.toFixed(2)}`);
|
| 455 |
+
})();
|
| 456 |
+
```
|
| 457 |
+
|
| 458 |
+
---
|
| 459 |
+
|
| 460 |
+
## Error Handling
|
| 461 |
+
|
| 462 |
+
All endpoints return standard HTTP status codes:
|
| 463 |
+
|
| 464 |
+
- **200 OK**: Successful request
|
| 465 |
+
- **422 Unprocessable Entity**: Invalid request parameters
|
| 466 |
+
- **500 Internal Server Error**: Server error
|
| 467 |
+
|
| 468 |
+
Error response format:
|
| 469 |
+
```json
|
| 470 |
+
{
|
| 471 |
+
"detail": "Error message description"
|
| 472 |
+
}
|
| 473 |
+
```
|
| 474 |
+
|
| 475 |
+
Or for API-specific errors:
|
| 476 |
+
```json
|
| 477 |
+
{
|
| 478 |
+
"error": "No company found matching 'INVALID_NAME'"
|
| 479 |
+
}
|
| 480 |
+
```
|
| 481 |
+
|
| 482 |
+
---
|
| 483 |
+
|
| 484 |
+
## Notes
|
| 485 |
+
|
| 486 |
+
- All monetary values are in USD (original values, not converted to millions)
|
| 487 |
+
- The `earnings_per_share` value is per share
|
| 488 |
+
- Data source: US SEC EDGAR system
|
| 489 |
+
- Supports US-GAAP and IFRS standards
|
| 490 |
+
- User-Agent: Juntao Peng (jtyxabc@gmail.com)
|
| 491 |
+
- Rate limiting follows SEC EDGAR API guidelines (10 requests per second)
|
| 492 |
+
|
| 493 |
+
---
|
| 494 |
+
|
| 495 |
+
## Interactive API Documentation
|
| 496 |
+
|
| 497 |
+
Once deployed, you can access interactive API documentation at:
|
| 498 |
+
|
| 499 |
+
- Swagger UI: `https://your-space-name.hf.space/docs`
|
| 500 |
+
- ReDoc: `https://your-space-name.hf.space/redoc`
|
DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hugging Face Space Deployment Guide
|
| 2 |
+
|
| 3 |
+
## Quick Deployment Steps
|
| 4 |
+
|
| 5 |
+
### 1. Create a New Space
|
| 6 |
+
|
| 7 |
+
1. Visit https://huggingface.co/spaces
|
| 8 |
+
2. Click **"Create new Space"**
|
| 9 |
+
3. Configure:
|
| 10 |
+
- **Name**: Choose a unique name (e.g., `sec-financial-mcp`)
|
| 11 |
+
- **License**: MIT
|
| 12 |
+
- **SDK**: **Docker** ⚠️ Important!
|
| 13 |
+
- **Visibility**: Public or Private
|
| 14 |
+
|
| 15 |
+
### 2. Clone and Upload
|
| 16 |
+
|
| 17 |
+
```bash
|
| 18 |
+
# Clone your Space
|
| 19 |
+
git clone https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
|
| 20 |
+
cd YOUR_SPACE_NAME
|
| 21 |
+
|
| 22 |
+
# Copy all project files
|
| 23 |
+
cp /path/to/EasyReportDateMCP/* .
|
| 24 |
+
|
| 25 |
+
# Commit and push
|
| 26 |
+
git add .
|
| 27 |
+
git commit -m "Initial deployment: SEC Financial MCP Server"
|
| 28 |
+
git push
|
| 29 |
+
```
|
| 30 |
+
|
| 31 |
+
### 3. Required Files
|
| 32 |
+
|
| 33 |
+
Make sure these files are in your Space repository:
|
| 34 |
+
|
| 35 |
+
```
|
| 36 |
+
✓ Dockerfile # Docker configuration
|
| 37 |
+
✓ mcp_server.py # FastAPI application
|
| 38 |
+
✓ edgar_client.py # EDGAR client
|
| 39 |
+
✓ requirements.txt # Dependencies
|
| 40 |
+
✓ README.md # Space description
|
| 41 |
+
✓ .gitignore # Git ignore rules
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
### 4. Wait for Build
|
| 45 |
+
|
| 46 |
+
Hugging Face will automatically build your Space (takes 3-5 minutes).
|
| 47 |
+
|
| 48 |
+
Monitor the build in the **"Logs"** tab.
|
| 49 |
+
|
| 50 |
+
### 5. Access Your API
|
| 51 |
+
|
| 52 |
+
Once deployed, your API will be available at:
|
| 53 |
+
|
| 54 |
+
```
|
| 55 |
+
https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
**Interactive Documentation**:
|
| 59 |
+
- Swagger UI: `https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space/docs`
|
| 60 |
+
- ReDoc: `https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space/redoc`
|
| 61 |
+
|
| 62 |
+
## Testing Your Deployment
|
| 63 |
+
|
| 64 |
+
### Health Check
|
| 65 |
+
|
| 66 |
+
```bash
|
| 67 |
+
curl https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space/health
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
### Search Company
|
| 71 |
+
|
| 72 |
+
```bash
|
| 73 |
+
curl -X POST "https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space/api/search_company" \
|
| 74 |
+
-H "Content-Type: application/json" \
|
| 75 |
+
-d '{"company_name": "NVIDIA"}'
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
### Python Test
|
| 79 |
+
|
| 80 |
+
```python
|
| 81 |
+
import requests
|
| 82 |
+
|
| 83 |
+
BASE_URL = "https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space"
|
| 84 |
+
|
| 85 |
+
# Search company
|
| 86 |
+
response = requests.post(
|
| 87 |
+
f"{BASE_URL}/api/search_company",
|
| 88 |
+
json={"company_name": "NVIDIA"}
|
| 89 |
+
)
|
| 90 |
+
print(response.json())
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
## Troubleshooting
|
| 94 |
+
|
| 95 |
+
### Build Fails
|
| 96 |
+
- Check the **Logs** tab for errors
|
| 97 |
+
- Verify all files are present
|
| 98 |
+
- Ensure `Dockerfile` is correct
|
| 99 |
+
|
| 100 |
+
### Server Not Responding
|
| 101 |
+
- Check if build completed successfully
|
| 102 |
+
- Verify port 7860 is exposed in Dockerfile
|
| 103 |
+
- Check application logs in Logs tab
|
| 104 |
+
|
| 105 |
+
### API Errors
|
| 106 |
+
- Test with Swagger UI at `/docs`
|
| 107 |
+
- Check request JSON format
|
| 108 |
+
- Review error messages in response
|
| 109 |
+
|
| 110 |
+
## Notes
|
| 111 |
+
|
| 112 |
+
- Free Spaces sleep after 48 hours of inactivity
|
| 113 |
+
- Port 7860 is standard for Hugging Face Spaces
|
| 114 |
+
- Docker SDK is required for this deployment
|
| 115 |
+
- All API endpoints use POST method except `/health`
|
| 116 |
+
|
| 117 |
+
## Support
|
| 118 |
+
|
| 119 |
+
- Documentation: See `API_USAGE.md`
|
| 120 |
+
- Interactive Testing: Visit `/docs` after deployment
|
| 121 |
+
- Hugging Face Docs: https://huggingface.co/docs/hub/spaces
|
Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Copy requirements and install dependencies
|
| 6 |
+
COPY requirements.txt .
|
| 7 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 8 |
+
|
| 9 |
+
# Copy application files
|
| 10 |
+
COPY edgar_client.py .
|
| 11 |
+
COPY financial_analyzer.py .
|
| 12 |
+
COPY mcp_server.py .
|
| 13 |
+
|
| 14 |
+
# Expose port
|
| 15 |
+
EXPOSE 7860
|
| 16 |
+
|
| 17 |
+
# Set environment variable
|
| 18 |
+
ENV PYTHONUNBUFFERED=1
|
| 19 |
+
|
| 20 |
+
# Run the MCP server
|
| 21 |
+
CMD ["uvicorn", "mcp_server:app", "--host", "0.0.0.0", "--port", "7860"]
|
README.md
CHANGED
|
@@ -1,12 +1,84 @@
|
|
| 1 |
-
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo: green
|
| 6 |
-
sdk: docker
|
| 7 |
-
pinned: false
|
| 8 |
-
license: mit
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: SEC Financial Report MCP Server
|
| 3 |
+
emoji: 📊
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# SEC Financial Report MCP Server
|
| 12 |
+
|
| 13 |
+
A FastAPI-based MCP (Model Context Protocol) Server for querying SEC EDGAR financial data.
|
| 14 |
+
|
| 15 |
+
## Features
|
| 16 |
+
|
| 17 |
+
- **Company Search**: Search companies by name and get CIK information
|
| 18 |
+
- **Company Information**: Retrieve detailed company information from SEC EDGAR
|
| 19 |
+
- **Filings Retrieval**: Get all historical filings (10-K, 10-Q, 20-F)
|
| 20 |
+
- **Financial Facts**: Access complete financial facts data
|
| 21 |
+
- **Financial Metrics**: Extract key financial metrics for specific periods (annual/quarterly)
|
| 22 |
+
- Total Revenue
|
| 23 |
+
- Net Income
|
| 24 |
+
- Earnings Per Share (EPS)
|
| 25 |
+
- Operating Expenses
|
| 26 |
+
- Operating Cash Flow
|
| 27 |
+
|
| 28 |
+
## Supported Report Types
|
| 29 |
+
|
| 30 |
+
- **10-K**: Annual reports (US companies)
|
| 31 |
+
- **10-Q**: Quarterly reports (US companies)
|
| 32 |
+
- **20-F**: Annual reports (Foreign private issuers)
|
| 33 |
+
|
| 34 |
+
## Data Standards
|
| 35 |
+
|
| 36 |
+
- US-GAAP (US Generally Accepted Accounting Principles)
|
| 37 |
+
- IFRS (International Financial Reporting Standards)
|
| 38 |
+
|
| 39 |
+
## API Documentation
|
| 40 |
+
|
| 41 |
+
Once deployed, visit `/docs` for interactive Swagger UI documentation or `/redoc` for ReDoc documentation.
|
| 42 |
+
|
| 43 |
+
## API Endpoints
|
| 44 |
+
|
| 45 |
+
- `POST /api/search_company` - Search company by name
|
| 46 |
+
- `POST /api/get_company_info` - Get company information
|
| 47 |
+
- `POST /api/get_company_filings` - Get company filings
|
| 48 |
+
- `POST /api/get_company_facts` - Get company financial facts
|
| 49 |
+
- `POST /api/get_financial_data` - Get financial data for period
|
| 50 |
+
- `GET /health` - Health check
|
| 51 |
+
|
| 52 |
+
## Usage Example
|
| 53 |
+
|
| 54 |
+
```python
|
| 55 |
+
import requests
|
| 56 |
+
|
| 57 |
+
BASE_URL = "https://YOUR_SPACE_URL.hf.space"
|
| 58 |
+
|
| 59 |
+
# Search company
|
| 60 |
+
response = requests.post(
|
| 61 |
+
f"{BASE_URL}/api/search_company",
|
| 62 |
+
json={"company_name": "NVIDIA"}
|
| 63 |
+
)
|
| 64 |
+
print(response.json())
|
| 65 |
+
# Output: {'cik': '0001045810', 'name': 'NVIDIA CORP', 'ticker': 'NVDA'}
|
| 66 |
+
|
| 67 |
+
# Get financial data
|
| 68 |
+
response = requests.post(
|
| 69 |
+
f"{BASE_URL}/api/get_financial_data",
|
| 70 |
+
json={"cik": "0001045810", "period": "2024"}
|
| 71 |
+
)
|
| 72 |
+
data = response.json()
|
| 73 |
+
print(f"Revenue: ${data['total_revenue']:,.0f}")
|
| 74 |
+
print(f"Net Income: ${data['net_income']:,.0f}")
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
## Data Source
|
| 78 |
+
|
| 79 |
+
All data is retrieved from the US Securities and Exchange Commission (SEC) EDGAR system.
|
| 80 |
+
|
| 81 |
+
## User Agent
|
| 82 |
+
|
| 83 |
+
This service uses the following User-Agent (required by SEC API):
|
| 84 |
+
- Juntao Peng Financial Report Metrics App (jtyxabc@gmail.com)
|
edgar_client.py
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""EDGAR API Client Module"""
|
| 2 |
+
|
| 3 |
+
import requests
|
| 4 |
+
try:
|
| 5 |
+
from sec_edgar_api.EdgarClient import EdgarClient
|
| 6 |
+
except ImportError:
|
| 7 |
+
EdgarClient = None
|
| 8 |
+
import json
|
| 9 |
+
import time
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class EdgarDataClient:
|
| 13 |
+
def __init__(self, user_agent="Juntao Peng Financial Report Metrics App (jtyxabc@gmail.com)"):
|
| 14 |
+
"""Initialize EDGAR client"""
|
| 15 |
+
self.user_agent = user_agent
|
| 16 |
+
if EdgarClient:
|
| 17 |
+
self.edgar = EdgarClient(user_agent=user_agent)
|
| 18 |
+
else:
|
| 19 |
+
self.edgar = None
|
| 20 |
+
|
| 21 |
+
def search_company_by_name(self, company_name):
|
| 22 |
+
"""Search company CIK by company name"""
|
| 23 |
+
try:
|
| 24 |
+
# Use SEC company ticker database
|
| 25 |
+
url = "https://www.sec.gov/files/company_tickers.json"
|
| 26 |
+
headers = {"User-Agent": self.user_agent}
|
| 27 |
+
|
| 28 |
+
response = requests.get(url, headers=headers)
|
| 29 |
+
response.raise_for_status()
|
| 30 |
+
|
| 31 |
+
companies = response.json()
|
| 32 |
+
|
| 33 |
+
# Search for matching company names
|
| 34 |
+
matches = []
|
| 35 |
+
exact_matches = []
|
| 36 |
+
for _, company in companies.items():
|
| 37 |
+
company_title = company["title"].lower()
|
| 38 |
+
search_name = company_name.lower()
|
| 39 |
+
|
| 40 |
+
# Exact match
|
| 41 |
+
if search_name == company_title:
|
| 42 |
+
exact_matches.append({
|
| 43 |
+
"cik": str(company["cik_str"]).zfill(10),
|
| 44 |
+
"name": company["title"],
|
| 45 |
+
"ticker": company["ticker"]
|
| 46 |
+
})
|
| 47 |
+
# Partial match
|
| 48 |
+
elif search_name in company_title or \
|
| 49 |
+
search_name in company["ticker"].lower():
|
| 50 |
+
matches.append({
|
| 51 |
+
"cik": str(company["cik_str"]).zfill(10),
|
| 52 |
+
"name": company["title"],
|
| 53 |
+
"ticker": company["ticker"]
|
| 54 |
+
})
|
| 55 |
+
|
| 56 |
+
# Return exact match first, then partial match
|
| 57 |
+
if exact_matches:
|
| 58 |
+
return exact_matches[0]
|
| 59 |
+
elif matches:
|
| 60 |
+
return matches[0]
|
| 61 |
+
else:
|
| 62 |
+
return None
|
| 63 |
+
|
| 64 |
+
except Exception as e:
|
| 65 |
+
print(f"搜索公司时出错: {e}")
|
| 66 |
+
return None
|
| 67 |
+
|
| 68 |
+
def get_company_info(self, cik):
|
| 69 |
+
"""
|
| 70 |
+
获取公司基本信息
|
| 71 |
+
|
| 72 |
+
Args:
|
| 73 |
+
cik (str): 公司CIK码
|
| 74 |
+
|
| 75 |
+
Returns:
|
| 76 |
+
dict: 包含公司信息的字典
|
| 77 |
+
"""
|
| 78 |
+
if not self.edgar:
|
| 79 |
+
print("sec_edgar_api库未安装")
|
| 80 |
+
return None
|
| 81 |
+
|
| 82 |
+
try:
|
| 83 |
+
# 获取公司提交信息
|
| 84 |
+
submissions = self.edgar.get_submissions(cik=cik)
|
| 85 |
+
|
| 86 |
+
return {
|
| 87 |
+
"cik": cik,
|
| 88 |
+
"name": submissions.get("name", ""),
|
| 89 |
+
"tickers": submissions.get("tickers", []),
|
| 90 |
+
"sic": submissions.get("sic", ""),
|
| 91 |
+
"sic_description": submissions.get("sicDescription", "")
|
| 92 |
+
}
|
| 93 |
+
except Exception as e:
|
| 94 |
+
print(f"获取公司信息时出错: {e}")
|
| 95 |
+
return None
|
| 96 |
+
|
| 97 |
+
def get_company_filings(self, cik, form_types=None):
|
| 98 |
+
"""
|
| 99 |
+
获取公司所有财报文件列表
|
| 100 |
+
|
| 101 |
+
Args:
|
| 102 |
+
cik (str): 公司CIK码
|
| 103 |
+
form_types (list): 财报类型列表,如['10-K', '10-Q'],默认为None表示获取所有类型
|
| 104 |
+
|
| 105 |
+
Returns:
|
| 106 |
+
list: 财报文件列表
|
| 107 |
+
"""
|
| 108 |
+
if not self.edgar:
|
| 109 |
+
print("sec_edgar_api库未安装")
|
| 110 |
+
return []
|
| 111 |
+
|
| 112 |
+
try:
|
| 113 |
+
# 获取公司提交信息
|
| 114 |
+
submissions = self.edgar.get_submissions(cik=cik)
|
| 115 |
+
|
| 116 |
+
# 提取财报信息
|
| 117 |
+
filings = []
|
| 118 |
+
recent = submissions.get("filings", {}).get("recent", {})
|
| 119 |
+
|
| 120 |
+
# 获取各个字段的数据
|
| 121 |
+
form_types_list = recent.get("form", [])
|
| 122 |
+
filing_dates = recent.get("filingDate", [])
|
| 123 |
+
accession_numbers = recent.get("accessionNumber", [])
|
| 124 |
+
primary_documents = recent.get("primaryDocument", [])
|
| 125 |
+
|
| 126 |
+
# 遍历所有财报
|
| 127 |
+
for i in range(len(form_types_list)):
|
| 128 |
+
form_type = form_types_list[i]
|
| 129 |
+
|
| 130 |
+
# 如果指定了财报类型,则只返回匹配的类型
|
| 131 |
+
if form_types and form_type not in form_types:
|
| 132 |
+
continue
|
| 133 |
+
|
| 134 |
+
filing_date = filing_dates[i] if i < len(filing_dates) else ""
|
| 135 |
+
accession_number = accession_numbers[i] if i < len(accession_numbers) else ""
|
| 136 |
+
primary_document = primary_documents[i] if i < len(primary_documents) else ""
|
| 137 |
+
|
| 138 |
+
filing = {
|
| 139 |
+
"form_type": form_type,
|
| 140 |
+
"filing_date": filing_date,
|
| 141 |
+
"accession_number": accession_number,
|
| 142 |
+
"primary_document": primary_document
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
filings.append(filing)
|
| 146 |
+
|
| 147 |
+
return filings
|
| 148 |
+
except Exception as e:
|
| 149 |
+
print(f"获取公司财报列表时出错: {e}")
|
| 150 |
+
return []
|
| 151 |
+
|
| 152 |
+
def get_company_facts(self, cik):
|
| 153 |
+
"""
|
| 154 |
+
获取公司所有财务事实数据
|
| 155 |
+
|
| 156 |
+
Args:
|
| 157 |
+
cik (str): 公司CIK码
|
| 158 |
+
|
| 159 |
+
Returns:
|
| 160 |
+
dict: 公司财务事实数据
|
| 161 |
+
"""
|
| 162 |
+
if not self.edgar:
|
| 163 |
+
print("sec_edgar_api库未安装")
|
| 164 |
+
return {}
|
| 165 |
+
|
| 166 |
+
try:
|
| 167 |
+
facts = self.edgar.get_company_facts(cik=cik)
|
| 168 |
+
return facts
|
| 169 |
+
except Exception as e:
|
| 170 |
+
print(f"获取公司财务事实时出错: {e}")
|
| 171 |
+
return {}
|
| 172 |
+
|
| 173 |
+
def get_financial_data_for_period(self, cik, period):
|
| 174 |
+
"""
|
| 175 |
+
获取指定期间的财务数据(支持年度和季度)
|
| 176 |
+
|
| 177 |
+
Args:
|
| 178 |
+
cik (str): 公司CIK码
|
| 179 |
+
period (str): 期间,格式为'YYYY'或'YYYYQX'(如'2025'或'2025Q3')
|
| 180 |
+
|
| 181 |
+
Returns:
|
| 182 |
+
dict: 财务数据字典
|
| 183 |
+
"""
|
| 184 |
+
if not self.edgar:
|
| 185 |
+
print("sec_edgar_api库未安装")
|
| 186 |
+
return {}
|
| 187 |
+
|
| 188 |
+
try:
|
| 189 |
+
# 获取公司财务事实
|
| 190 |
+
facts = self.get_company_facts(cik)
|
| 191 |
+
|
| 192 |
+
if not facts:
|
| 193 |
+
return {}
|
| 194 |
+
|
| 195 |
+
# 提取us-gaap和ifrs-full部分的财务数据(20-F可能使用IFRS)
|
| 196 |
+
us_gaap = facts.get("facts", {}).get("us-gaap", {})
|
| 197 |
+
ifrs_full = facts.get("facts", {}).get("ifrs-full", {})
|
| 198 |
+
|
| 199 |
+
# 定义要获取的财务指标及其XBRL标签
|
| 200 |
+
# 包含多个可能的标签以提高匹配率(包括US-GAAP和IFRS标签)
|
| 201 |
+
financial_metrics = {
|
| 202 |
+
"total_revenue": ["Revenues", "RevenueFromContractWithCustomerExcludingAssessedTax", "RevenueFromContractWithCustomerIncludingAssessedTax", "SalesRevenueNet", "RevenueFromContractWithCustomer", "Revenue"],
|
| 203 |
+
"net_income": ["NetIncomeLoss", "ProfitLoss", "NetIncome", "ProfitLossAttributableToOwnersOfParent"],
|
| 204 |
+
"earnings_per_share": ["EarningsPerShareBasic", "EarningsPerShare", "BasicEarningsPerShare", "BasicEarningsLossPerShare"],
|
| 205 |
+
"operating_expenses": ["OperatingExpenses", "OperatingCostsAndExpenses", "OperatingExpensesExcludingDepreciationAndAmortization", "CostsAndExpenses", "GeneralAndAdministrativeExpense", "CostOfRevenue", "ResearchAndDevelopmentExpense", "SellingAndMarketingExpense"],
|
| 206 |
+
"operating_cash_flow": ["NetCashProvidedByUsedInOperatingActivities", "NetCashProvidedUsedInOperatingActivities", "NetCashFlowsFromUsedInOperatingActivities", "CashFlowsFromUsedInOperatingActivities"],
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
# 存储结果
|
| 210 |
+
result = {"period": period}
|
| 211 |
+
|
| 212 |
+
# 确定要查找的表格类型
|
| 213 |
+
if 'Q' in period:
|
| 214 |
+
# 季度数据,主要查找10-Q(20-F通常没有季度报告)
|
| 215 |
+
target_forms = ["10-Q"]
|
| 216 |
+
target_forms_annual = ["10-K", "20-F"] # 用于回退查找
|
| 217 |
+
year = int(period.split('Q')[0])
|
| 218 |
+
quarter = period.split('Q')[1]
|
| 219 |
+
else:
|
| 220 |
+
# 年度数据,查找10-K和20-F年度表格
|
| 221 |
+
target_forms = ["10-K", "20-F"]
|
| 222 |
+
target_forms_annual = target_forms
|
| 223 |
+
year = int(period)
|
| 224 |
+
quarter = None
|
| 225 |
+
|
| 226 |
+
# 遍历每个财务指标
|
| 227 |
+
for metric_key, metric_tags in financial_metrics.items():
|
| 228 |
+
# 支持多个可能的标签
|
| 229 |
+
for metric_tag in metric_tags:
|
| 230 |
+
# 同时查找US-GAAP和IFRS标签
|
| 231 |
+
metric_data = None
|
| 232 |
+
data_source = None
|
| 233 |
+
|
| 234 |
+
if metric_tag in us_gaap:
|
| 235 |
+
metric_data = us_gaap[metric_tag]
|
| 236 |
+
data_source = "us-gaap"
|
| 237 |
+
elif metric_tag in ifrs_full:
|
| 238 |
+
metric_data = ifrs_full[metric_tag]
|
| 239 |
+
data_source = "ifrs-full"
|
| 240 |
+
|
| 241 |
+
if metric_data:
|
| 242 |
+
units = metric_data.get("units", {})
|
| 243 |
+
|
| 244 |
+
# 查找美元单位的数据(支持USD和USD/shares)
|
| 245 |
+
usd_data = None
|
| 246 |
+
if "USD" in units:
|
| 247 |
+
usd_data = units["USD"]
|
| 248 |
+
elif "USD/shares" in units and metric_key == "earnings_per_share":
|
| 249 |
+
# EPS使用USD/shares单位
|
| 250 |
+
usd_data = units["USD/shares"]
|
| 251 |
+
|
| 252 |
+
if usd_data:
|
| 253 |
+
# 首先尝试精确匹配,然后尝试宽松匹配
|
| 254 |
+
matched_entry = None
|
| 255 |
+
|
| 256 |
+
# 查找指定期间的数据
|
| 257 |
+
for entry in usd_data:
|
| 258 |
+
form = entry.get("form", "")
|
| 259 |
+
fy = entry.get("fy", 0)
|
| 260 |
+
fp = entry.get("fp", "")
|
| 261 |
+
end_date = entry.get("end", "")
|
| 262 |
+
|
| 263 |
+
if not end_date or len(end_date) < 4:
|
| 264 |
+
continue
|
| 265 |
+
|
| 266 |
+
entry_year = int(end_date[:4])
|
| 267 |
+
|
| 268 |
+
# 检查表格类型是否匹配
|
| 269 |
+
if form in target_forms:
|
| 270 |
+
if quarter:
|
| 271 |
+
# 季度数据匹配
|
| 272 |
+
if entry_year == year and fp == f"Q{quarter}":
|
| 273 |
+
# 如果已有匹配,比较end date,选择最新的
|
| 274 |
+
if matched_entry:
|
| 275 |
+
if entry.get("end", "") > matched_entry.get("end", ""):
|
| 276 |
+
matched_entry = entry
|
| 277 |
+
else:
|
| 278 |
+
matched_entry = entry
|
| 279 |
+
else:
|
| 280 |
+
# 年度数据匹配 - 优先匹配FY字段
|
| 281 |
+
if fy == year and (fp == "FY" or fp == "" or not fp):
|
| 282 |
+
# 如果已有匹配,比较end date,选择最新的(最近的财年结束日期)
|
| 283 |
+
if matched_entry:
|
| 284 |
+
if entry.get("end", "") > matched_entry.get("end", ""):
|
| 285 |
+
matched_entry = entry
|
| 286 |
+
else:
|
| 287 |
+
matched_entry = entry
|
| 288 |
+
# 备选:匹配end日期的年份(仅当没有FY匹配时)
|
| 289 |
+
elif not matched_entry and entry_year == year and (fp == "FY" or fp == "" or not fp):
|
| 290 |
+
matched_entry = entry
|
| 291 |
+
# 20-F特殊处理:有些20-F没有FY标记,通过frame字段匹配
|
| 292 |
+
elif not matched_entry and form == "20-F" and "frame" in entry:
|
| 293 |
+
frame = entry.get("frame", "")
|
| 294 |
+
if f"CY{year}" in frame or str(year) in end_date:
|
| 295 |
+
matched_entry = entry
|
| 296 |
+
|
| 297 |
+
# 如果季度数据没找到,尝试从年度报告中查找(回退策略)
|
| 298 |
+
if not matched_entry and quarter and target_forms_annual:
|
| 299 |
+
for entry in usd_data:
|
| 300 |
+
form = entry.get("form", "")
|
| 301 |
+
end_date = entry.get("end", "")
|
| 302 |
+
fp = entry.get("fp", "")
|
| 303 |
+
|
| 304 |
+
if form in target_forms_annual and end_date:
|
| 305 |
+
# 检查结束日期是否在该季度范围内
|
| 306 |
+
if str(year) in end_date and f"Q{quarter}" in fp:
|
| 307 |
+
matched_entry = entry
|
| 308 |
+
break
|
| 309 |
+
|
| 310 |
+
# 应用匹配的数据
|
| 311 |
+
if matched_entry:
|
| 312 |
+
result[metric_key] = matched_entry.get("val", 0)
|
| 313 |
+
# 添加数据来源信息
|
| 314 |
+
accn = matched_entry.get('accn', '').replace('-', '')
|
| 315 |
+
result["source_url"] = f"https://www.sec.gov/Archives/edgar/data/{cik}/{accn}"
|
| 316 |
+
result["source_form"] = matched_entry.get("form", "")
|
| 317 |
+
result["data_source"] = data_source
|
| 318 |
+
# 添加详细信息
|
| 319 |
+
result[f"{metric_key}_details"] = {
|
| 320 |
+
"tag": metric_tag,
|
| 321 |
+
"form": matched_entry.get("form", ""),
|
| 322 |
+
"fy": matched_entry.get("fy", 0),
|
| 323 |
+
"fp": matched_entry.get("fp", ""),
|
| 324 |
+
"val": matched_entry.get("val", 0),
|
| 325 |
+
"start": matched_entry.get("start", ""),
|
| 326 |
+
"end": matched_entry.get("end", ""),
|
| 327 |
+
"accn": matched_entry.get("accn", ""),
|
| 328 |
+
"filed": matched_entry.get("filed", ""),
|
| 329 |
+
"frame": matched_entry.get("frame", ""),
|
| 330 |
+
"data_source": data_source
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
# 如果找到了数据,就跳出标签循环
|
| 334 |
+
if metric_key in result:
|
| 335 |
+
break
|
| 336 |
+
|
| 337 |
+
return result
|
| 338 |
+
except Exception as e:
|
| 339 |
+
print(f"获取{period}期间财务数据时出错: {e}")
|
| 340 |
+
return {}
|
| 341 |
+
|
| 342 |
+
|
mcp_client_examples.json
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"mcp_server_info": {
|
| 3 |
+
"name": "SEC Financial Report MCP Server",
|
| 4 |
+
"description": "FastAPI-based MCP Server for querying SEC EDGAR financial data",
|
| 5 |
+
"base_url_local": "http://localhost:7860",
|
| 6 |
+
"base_url_deployed": "https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space",
|
| 7 |
+
"documentation": {
|
| 8 |
+
"swagger_ui": "/docs",
|
| 9 |
+
"redoc": "/redoc"
|
| 10 |
+
}
|
| 11 |
+
},
|
| 12 |
+
|
| 13 |
+
"api_endpoints": {
|
| 14 |
+
|
| 15 |
+
"1_search_company": {
|
| 16 |
+
"name": "Search Company by Name",
|
| 17 |
+
"endpoint": "/api/search_company",
|
| 18 |
+
"method": "POST",
|
| 19 |
+
"description": "Search for a company by name and retrieve its CIK number",
|
| 20 |
+
|
| 21 |
+
"request_example": {
|
| 22 |
+
"company_name": "NVIDIA"
|
| 23 |
+
},
|
| 24 |
+
|
| 25 |
+
"response_success": {
|
| 26 |
+
"cik": "0001045810",
|
| 27 |
+
"name": "NVIDIA CORP",
|
| 28 |
+
"ticker": "NVDA",
|
| 29 |
+
"error": null
|
| 30 |
+
},
|
| 31 |
+
|
| 32 |
+
"response_not_found": {
|
| 33 |
+
"cik": null,
|
| 34 |
+
"name": null,
|
| 35 |
+
"ticker": null,
|
| 36 |
+
"error": "No company found matching 'INVALID_COMPANY'"
|
| 37 |
+
},
|
| 38 |
+
|
| 39 |
+
"curl_example": "curl -X POST 'https://YOUR_SPACE.hf.space/api/search_company' -H 'Content-Type: application/json' -d '{\"company_name\": \"NVIDIA\"}'",
|
| 40 |
+
|
| 41 |
+
"python_example": "import requests\nresponse = requests.post('https://YOUR_SPACE.hf.space/api/search_company', json={'company_name': 'NVIDIA'})\nprint(response.json())",
|
| 42 |
+
|
| 43 |
+
"javascript_example": "fetch('https://YOUR_SPACE.hf.space/api/search_company', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: JSON.stringify({company_name: 'NVIDIA'})\n}).then(r => r.json()).then(console.log)",
|
| 44 |
+
|
| 45 |
+
"notes": [
|
| 46 |
+
"Returns exact match first, then partial matches",
|
| 47 |
+
"CIK is zero-padded to 10 digits",
|
| 48 |
+
"Search is case-insensitive"
|
| 49 |
+
]
|
| 50 |
+
},
|
| 51 |
+
|
| 52 |
+
"2_get_company_info": {
|
| 53 |
+
"name": "Get Company Information",
|
| 54 |
+
"endpoint": "/api/get_company_info",
|
| 55 |
+
"method": "POST",
|
| 56 |
+
"description": "Retrieve detailed company information using CIK",
|
| 57 |
+
|
| 58 |
+
"request_example": {
|
| 59 |
+
"cik": "0001045810"
|
| 60 |
+
},
|
| 61 |
+
|
| 62 |
+
"response_success": {
|
| 63 |
+
"cik": "0001045810",
|
| 64 |
+
"name": "NVIDIA CORP",
|
| 65 |
+
"tickers": ["NVDA"],
|
| 66 |
+
"sic": "3674",
|
| 67 |
+
"sic_description": "Semiconductors & Related Devices",
|
| 68 |
+
"error": null
|
| 69 |
+
},
|
| 70 |
+
|
| 71 |
+
"response_not_found": {
|
| 72 |
+
"cik": null,
|
| 73 |
+
"name": null,
|
| 74 |
+
"tickers": null,
|
| 75 |
+
"sic": null,
|
| 76 |
+
"sic_description": null,
|
| 77 |
+
"error": "No company information found for CIK: 9999999999"
|
| 78 |
+
},
|
| 79 |
+
|
| 80 |
+
"curl_example": "curl -X POST 'https://YOUR_SPACE.hf.space/api/get_company_info' -H 'Content-Type: application/json' -d '{\"cik\": \"0001045810\"}'",
|
| 81 |
+
|
| 82 |
+
"python_example": "import requests\nresponse = requests.post('https://YOUR_SPACE.hf.space/api/get_company_info', json={'cik': '0001045810'})\ninfo = response.json()\nprint(f\"Company: {info['name']}\")\nprint(f\"Industry: {info['sic_description']}\")",
|
| 83 |
+
|
| 84 |
+
"javascript_example": "fetch('https://YOUR_SPACE.hf.space/api/get_company_info', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: JSON.stringify({cik: '0001045810'})\n}).then(r => r.json()).then(data => console.log(data.name))",
|
| 85 |
+
|
| 86 |
+
"notes": [
|
| 87 |
+
"CIK must be zero-padded to 10 digits",
|
| 88 |
+
"SIC code represents industry classification",
|
| 89 |
+
"Multiple tickers may exist for one company"
|
| 90 |
+
]
|
| 91 |
+
},
|
| 92 |
+
|
| 93 |
+
"3_get_company_filings": {
|
| 94 |
+
"name": "Get Company Filings",
|
| 95 |
+
"endpoint": "/api/get_company_filings",
|
| 96 |
+
"method": "POST",
|
| 97 |
+
"description": "Retrieve company SEC filings with optional form type filter",
|
| 98 |
+
|
| 99 |
+
"request_all_forms": {
|
| 100 |
+
"cik": "0001045810",
|
| 101 |
+
"form_types": null
|
| 102 |
+
},
|
| 103 |
+
|
| 104 |
+
"request_specific_forms": {
|
| 105 |
+
"cik": "0001045810",
|
| 106 |
+
"form_types": ["10-K", "10-Q"]
|
| 107 |
+
},
|
| 108 |
+
|
| 109 |
+
"response_success": {
|
| 110 |
+
"filings": [
|
| 111 |
+
{
|
| 112 |
+
"form_type": "10-K",
|
| 113 |
+
"filing_date": "2024-02-21",
|
| 114 |
+
"accession_number": "0001045810-24-000029",
|
| 115 |
+
"primary_document": "nvda-20240128.htm"
|
| 116 |
+
},
|
| 117 |
+
{
|
| 118 |
+
"form_type": "10-Q",
|
| 119 |
+
"filing_date": "2024-11-20",
|
| 120 |
+
"accession_number": "0001045810-24-000147",
|
| 121 |
+
"primary_document": "nvda-20241027.htm"
|
| 122 |
+
}
|
| 123 |
+
],
|
| 124 |
+
"error": null
|
| 125 |
+
},
|
| 126 |
+
|
| 127 |
+
"response_not_found": {
|
| 128 |
+
"filings": [],
|
| 129 |
+
"error": "No filings found for CIK: 0001045810"
|
| 130 |
+
},
|
| 131 |
+
|
| 132 |
+
"curl_example": "curl -X POST 'https://YOUR_SPACE.hf.space/api/get_company_filings' -H 'Content-Type: application/json' -d '{\"cik\": \"0001045810\", \"form_types\": [\"10-K\", \"10-Q\"]}'",
|
| 133 |
+
|
| 134 |
+
"python_example": "import requests\nresponse = requests.post(\n 'https://YOUR_SPACE.hf.space/api/get_company_filings',\n json={'cik': '0001045810', 'form_types': ['10-K', '10-Q']}\n)\nfilings = response.json()['filings']\nfor filing in filings[:5]:\n print(f\"{filing['form_type']}: {filing['filing_date']}\")",
|
| 135 |
+
|
| 136 |
+
"javascript_example": "fetch('https://YOUR_SPACE.hf.space/api/get_company_filings', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: JSON.stringify({cik: '0001045810', form_types: ['10-K']})\n}).then(r => r.json()).then(data => data.filings.forEach(f => console.log(f.form_type)))",
|
| 137 |
+
|
| 138 |
+
"form_types_reference": {
|
| 139 |
+
"10-K": "Annual report (US companies)",
|
| 140 |
+
"10-Q": "Quarterly report (US companies)",
|
| 141 |
+
"20-F": "Annual report (Foreign private issuers)",
|
| 142 |
+
"8-K": "Current report (major events)",
|
| 143 |
+
"S-1": "Registration statement"
|
| 144 |
+
},
|
| 145 |
+
|
| 146 |
+
"notes": [
|
| 147 |
+
"Set form_types to null to get all filing types",
|
| 148 |
+
"Filings are returned in reverse chronological order",
|
| 149 |
+
"Most companies file multiple forms per year"
|
| 150 |
+
]
|
| 151 |
+
},
|
| 152 |
+
|
| 153 |
+
"4_get_company_facts": {
|
| 154 |
+
"name": "Get Company Financial Facts",
|
| 155 |
+
"endpoint": "/api/get_company_facts",
|
| 156 |
+
"method": "POST",
|
| 157 |
+
"description": "Retrieve complete XBRL financial facts data for a company",
|
| 158 |
+
|
| 159 |
+
"request_example": {
|
| 160 |
+
"cik": "0001045810"
|
| 161 |
+
},
|
| 162 |
+
|
| 163 |
+
"response_structure": {
|
| 164 |
+
"facts": {
|
| 165 |
+
"us-gaap": {
|
| 166 |
+
"Revenues": {
|
| 167 |
+
"label": "Revenues",
|
| 168 |
+
"description": "Amount of revenue recognized...",
|
| 169 |
+
"units": {
|
| 170 |
+
"USD": [
|
| 171 |
+
{
|
| 172 |
+
"end": "2024-01-28",
|
| 173 |
+
"val": 60922000000,
|
| 174 |
+
"accn": "0001045810-24-000029",
|
| 175 |
+
"fy": 2024,
|
| 176 |
+
"fp": "FY",
|
| 177 |
+
"form": "10-K",
|
| 178 |
+
"filed": "2024-02-21",
|
| 179 |
+
"frame": "CY2023"
|
| 180 |
+
}
|
| 181 |
+
]
|
| 182 |
+
}
|
| 183 |
+
},
|
| 184 |
+
"NetIncomeLoss": "...",
|
| 185 |
+
"EarningsPerShareBasic": "..."
|
| 186 |
+
},
|
| 187 |
+
"ifrs-full": {
|
| 188 |
+
"Revenue": "..."
|
| 189 |
+
},
|
| 190 |
+
"dei": {
|
| 191 |
+
"EntityCommonStockSharesOutstanding": "..."
|
| 192 |
+
}
|
| 193 |
+
},
|
| 194 |
+
"error": null
|
| 195 |
+
},
|
| 196 |
+
|
| 197 |
+
"curl_example": "curl -X POST 'https://YOUR_SPACE.hf.space/api/get_company_facts' -H 'Content-Type: application/json' -d '{\"cik\": \"0001045810\"}'",
|
| 198 |
+
|
| 199 |
+
"python_example": "import requests\nresponse = requests.post(\n 'https://YOUR_SPACE.hf.space/api/get_company_facts',\n json={'cik': '0001045810'}\n)\nfacts = response.json()['facts']\nprint(f\"Available standards: {list(facts.keys())}\")\nif 'us-gaap' in facts:\n print(f\"US-GAAP metrics count: {len(facts['us-gaap'])}\")",
|
| 200 |
+
|
| 201 |
+
"javascript_example": "fetch('https://YOUR_SPACE.hf.space/api/get_company_facts', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: JSON.stringify({cik: '0001045810'})\n}).then(r => r.json()).then(data => console.log(Object.keys(data.facts)))",
|
| 202 |
+
|
| 203 |
+
"notes": [
|
| 204 |
+
"Response can be very large (several MB)",
|
| 205 |
+
"Contains historical data for all reported periods",
|
| 206 |
+
"Supports both US-GAAP and IFRS standards",
|
| 207 |
+
"Best for advanced analysis requiring raw XBRL data"
|
| 208 |
+
]
|
| 209 |
+
},
|
| 210 |
+
|
| 211 |
+
"5_get_financial_data_annual": {
|
| 212 |
+
"name": "Get Annual Financial Data",
|
| 213 |
+
"endpoint": "/api/get_financial_data",
|
| 214 |
+
"method": "POST",
|
| 215 |
+
"description": "Extract key financial metrics for a specific fiscal year",
|
| 216 |
+
|
| 217 |
+
"request_example": {
|
| 218 |
+
"cik": "0001045810",
|
| 219 |
+
"period": "2024"
|
| 220 |
+
},
|
| 221 |
+
|
| 222 |
+
"response_success": {
|
| 223 |
+
"period": "2024",
|
| 224 |
+
"total_revenue": 60922000000,
|
| 225 |
+
"net_income": 29760000000,
|
| 226 |
+
"earnings_per_share": 12.04,
|
| 227 |
+
"operating_expenses": 11822000000,
|
| 228 |
+
"operating_cash_flow": 28091000000,
|
| 229 |
+
"source_url": "https://www.sec.gov/Archives/edgar/data/1045810/000104581024000029",
|
| 230 |
+
"source_form": "10-K",
|
| 231 |
+
"data_source": "us-gaap",
|
| 232 |
+
"additional_details": {
|
| 233 |
+
"total_revenue_details": {
|
| 234 |
+
"tag": "Revenues",
|
| 235 |
+
"form": "10-K",
|
| 236 |
+
"fy": 2024,
|
| 237 |
+
"fp": "FY",
|
| 238 |
+
"val": 60922000000,
|
| 239 |
+
"start": "2023-01-30",
|
| 240 |
+
"end": "2024-01-28",
|
| 241 |
+
"accn": "0001045810-24-000029",
|
| 242 |
+
"filed": "2024-02-21",
|
| 243 |
+
"frame": "CY2023",
|
| 244 |
+
"data_source": "us-gaap"
|
| 245 |
+
}
|
| 246 |
+
},
|
| 247 |
+
"error": null
|
| 248 |
+
},
|
| 249 |
+
|
| 250 |
+
"curl_example": "curl -X POST 'https://YOUR_SPACE.hf.space/api/get_financial_data' -H 'Content-Type: application/json' -d '{\"cik\": \"0001045810\", \"period\": \"2024\"}'",
|
| 251 |
+
|
| 252 |
+
"python_example": "import requests\nresponse = requests.post(\n 'https://YOUR_SPACE.hf.space/api/get_financial_data',\n json={'cik': '0001045810', 'period': '2024'}\n)\ndata = response.json()\nprint(f\"Period: FY{data['period']}\")\nprint(f\"Revenue: ${data['total_revenue']:,.0f}\")\nprint(f\"Net Income: ${data['net_income']:,.0f}\")\nprint(f\"EPS: ${data['earnings_per_share']:.2f}\")\nprint(f\"Source: {data['source_form']}\")",
|
| 253 |
+
|
| 254 |
+
"javascript_example": "fetch('https://YOUR_SPACE.hf.space/api/get_financial_data', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: JSON.stringify({cik: '0001045810', period: '2024'})\n}).then(r => r.json()).then(data => {\n console.log(`Revenue: $${(data.total_revenue / 1e9).toFixed(2)}B`);\n console.log(`Net Income: $${(data.net_income / 1e9).toFixed(2)}B`);\n})",
|
| 255 |
+
|
| 256 |
+
"notes": [
|
| 257 |
+
"Period format: 'YYYY' for annual data",
|
| 258 |
+
"All monetary values in USD (original values)",
|
| 259 |
+
"EPS is per share value",
|
| 260 |
+
"Data sourced from 10-K (US) or 20-F (foreign) forms"
|
| 261 |
+
]
|
| 262 |
+
},
|
| 263 |
+
|
| 264 |
+
"6_get_financial_data_quarterly": {
|
| 265 |
+
"name": "Get Quarterly Financial Data",
|
| 266 |
+
"endpoint": "/api/get_financial_data",
|
| 267 |
+
"method": "POST",
|
| 268 |
+
"description": "Extract key financial metrics for a specific quarter",
|
| 269 |
+
|
| 270 |
+
"request_example": {
|
| 271 |
+
"cik": "0001045810",
|
| 272 |
+
"period": "2024Q3"
|
| 273 |
+
},
|
| 274 |
+
|
| 275 |
+
"response_success": {
|
| 276 |
+
"period": "2024Q3",
|
| 277 |
+
"total_revenue": 30040000000,
|
| 278 |
+
"net_income": 19309000000,
|
| 279 |
+
"earnings_per_share": 7.81,
|
| 280 |
+
"operating_expenses": 3932000000,
|
| 281 |
+
"operating_cash_flow": 14502000000,
|
| 282 |
+
"source_url": "https://www.sec.gov/Archives/edgar/data/1045810/000104581024000147",
|
| 283 |
+
"source_form": "10-Q",
|
| 284 |
+
"data_source": "us-gaap",
|
| 285 |
+
"additional_details": null,
|
| 286 |
+
"error": null
|
| 287 |
+
},
|
| 288 |
+
|
| 289 |
+
"curl_example": "curl -X POST 'https://YOUR_SPACE.hf.space/api/get_financial_data' -H 'Content-Type: application/json' -d '{\"cik\": \"0001045810\", \"period\": \"2024Q3\"}'",
|
| 290 |
+
|
| 291 |
+
"python_example": "import requests\n\n# Get multiple quarters for comparison\nquarters = ['2024Q1', '2024Q2', '2024Q3']\nfor quarter in quarters:\n response = requests.post(\n 'https://YOUR_SPACE.hf.space/api/get_financial_data',\n json={'cik': '0001045810', 'period': quarter}\n )\n data = response.json()\n if not data.get('error'):\n print(f\"{quarter}: Revenue ${data['total_revenue']/1e9:.2f}B\")",
|
| 292 |
+
|
| 293 |
+
"javascript_example": "// Compare Q3 vs Q2\nconst periods = ['2024Q3', '2024Q2'];\nPromise.all(periods.map(period =>\n fetch('https://YOUR_SPACE.hf.space/api/get_financial_data', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: JSON.stringify({cik: '0001045810', period})\n }).then(r => r.json())\n)).then(results => results.forEach(d => \n console.log(`${d.period}: $${(d.total_revenue/1e9).toFixed(2)}B`)\n))",
|
| 294 |
+
|
| 295 |
+
"quarter_reference": {
|
| 296 |
+
"Q1": "First quarter (typically Jan-Mar or similar)",
|
| 297 |
+
"Q2": "Second quarter (typically Apr-Jun or similar)",
|
| 298 |
+
"Q3": "Third quarter (typically Jul-Sep or similar)",
|
| 299 |
+
"Q4": "Fourth quarter (typically Oct-Dec or similar)"
|
| 300 |
+
},
|
| 301 |
+
|
| 302 |
+
"notes": [
|
| 303 |
+
"Period format: 'YYYYQX' where X is 1, 2, 3, or 4",
|
| 304 |
+
"Data sourced from 10-Q forms",
|
| 305 |
+
"Foreign companies (20-F filers) typically don't file quarterly reports",
|
| 306 |
+
"Quarters may not align with calendar year for all companies"
|
| 307 |
+
]
|
| 308 |
+
},
|
| 309 |
+
|
| 310 |
+
"7_health_check": {
|
| 311 |
+
"name": "Health Check",
|
| 312 |
+
"endpoint": "/health",
|
| 313 |
+
"method": "GET",
|
| 314 |
+
"description": "Check if the MCP server is running and healthy",
|
| 315 |
+
|
| 316 |
+
"request": "No request body required (GET request)",
|
| 317 |
+
|
| 318 |
+
"response": {
|
| 319 |
+
"status": "healthy",
|
| 320 |
+
"service": "SEC Financial Report MCP Server"
|
| 321 |
+
},
|
| 322 |
+
|
| 323 |
+
"curl_example": "curl https://YOUR_SPACE.hf.space/health",
|
| 324 |
+
|
| 325 |
+
"python_example": "import requests\nresponse = requests.get('https://YOUR_SPACE.hf.space/health')\nif response.json()['status'] == 'healthy':\n print('✓ Server is running')",
|
| 326 |
+
|
| 327 |
+
"javascript_example": "fetch('https://YOUR_SPACE.hf.space/health')\n .then(r => r.json())\n .then(data => console.log(`Status: ${data.status}`))",
|
| 328 |
+
|
| 329 |
+
"notes": [
|
| 330 |
+
"No authentication required",
|
| 331 |
+
"Use for monitoring and health checks",
|
| 332 |
+
"Always returns 200 if server is running"
|
| 333 |
+
]
|
| 334 |
+
}
|
| 335 |
+
},
|
| 336 |
+
|
| 337 |
+
"complete_workflow_examples": {
|
| 338 |
+
|
| 339 |
+
"example_1_basic_company_analysis": {
|
| 340 |
+
"name": "Basic Company Financial Analysis",
|
| 341 |
+
"description": "Search a company and get its latest annual financial data",
|
| 342 |
+
"steps": [
|
| 343 |
+
{
|
| 344 |
+
"step": 1,
|
| 345 |
+
"action": "Search for company",
|
| 346 |
+
"endpoint": "/api/search_company",
|
| 347 |
+
"request": {
|
| 348 |
+
"company_name": "Apple"
|
| 349 |
+
},
|
| 350 |
+
"extract": "cik"
|
| 351 |
+
},
|
| 352 |
+
{
|
| 353 |
+
"step": 2,
|
| 354 |
+
"action": "Get company information",
|
| 355 |
+
"endpoint": "/api/get_company_info",
|
| 356 |
+
"request": {
|
| 357 |
+
"cik": "{{cik_from_step_1}}"
|
| 358 |
+
},
|
| 359 |
+
"extract": "name, tickers, sic_description"
|
| 360 |
+
},
|
| 361 |
+
{
|
| 362 |
+
"step": 3,
|
| 363 |
+
"action": "Get latest annual financial data",
|
| 364 |
+
"endpoint": "/api/get_financial_data",
|
| 365 |
+
"request": {
|
| 366 |
+
"cik": "{{cik_from_step_1}}",
|
| 367 |
+
"period": "2024"
|
| 368 |
+
},
|
| 369 |
+
"extract": "total_revenue, net_income, earnings_per_share"
|
| 370 |
+
}
|
| 371 |
+
],
|
| 372 |
+
"python_code": "import requests\n\nBASE_URL = 'https://YOUR_SPACE.hf.space'\n\n# Step 1: Search company\nresponse = requests.post(f'{BASE_URL}/api/search_company', \n json={'company_name': 'Apple'})\ncompany = response.json()\ncik = company['cik']\nprint(f\"Found: {company['name']} (CIK: {cik})\")\n\n# Step 2: Get company info\nresponse = requests.post(f'{BASE_URL}/api/get_company_info',\n json={'cik': cik})\ninfo = response.json()\nprint(f\"Industry: {info['sic_description']}\")\n\n# Step 3: Get financial data\nresponse = requests.post(f'{BASE_URL}/api/get_financial_data',\n json={'cik': cik, 'period': '2024'})\ndata = response.json()\nprint(f\"FY2024 Revenue: ${data['total_revenue']:,.0f}\")\nprint(f\"FY2024 Net Income: ${data['net_income']:,.0f}\")\nprint(f\"FY2024 EPS: ${data['earnings_per_share']:.2f}\")"
|
| 373 |
+
},
|
| 374 |
+
|
| 375 |
+
"example_2_quarterly_trend_analysis": {
|
| 376 |
+
"name": "Quarterly Revenue Trend Analysis",
|
| 377 |
+
"description": "Analyze revenue trends across multiple quarters",
|
| 378 |
+
"steps": [
|
| 379 |
+
{
|
| 380 |
+
"step": 1,
|
| 381 |
+
"action": "Get Q1 2024 data",
|
| 382 |
+
"endpoint": "/api/get_financial_data",
|
| 383 |
+
"request": {
|
| 384 |
+
"cik": "0001045810",
|
| 385 |
+
"period": "2024Q1"
|
| 386 |
+
}
|
| 387 |
+
},
|
| 388 |
+
{
|
| 389 |
+
"step": 2,
|
| 390 |
+
"action": "Get Q2 2024 data",
|
| 391 |
+
"endpoint": "/api/get_financial_data",
|
| 392 |
+
"request": {
|
| 393 |
+
"cik": "0001045810",
|
| 394 |
+
"period": "2024Q2"
|
| 395 |
+
}
|
| 396 |
+
},
|
| 397 |
+
{
|
| 398 |
+
"step": 3,
|
| 399 |
+
"action": "Get Q3 2024 data",
|
| 400 |
+
"endpoint": "/api/get_financial_data",
|
| 401 |
+
"request": {
|
| 402 |
+
"cik": "0001045810",
|
| 403 |
+
"period": "2024Q3"
|
| 404 |
+
}
|
| 405 |
+
}
|
| 406 |
+
],
|
| 407 |
+
"python_code": "import requests\nimport pandas as pd\n\nBASE_URL = 'https://YOUR_SPACE.hf.space'\nCIK = '0001045810' # NVIDIA\n\n# Get quarterly data\nquarters = ['2024Q1', '2024Q2', '2024Q3']\ndata_list = []\n\nfor quarter in quarters:\n response = requests.post(\n f'{BASE_URL}/api/get_financial_data',\n json={'cik': CIK, 'period': quarter}\n )\n data = response.json()\n if not data.get('error'):\n data_list.append({\n 'Quarter': quarter,\n 'Revenue': data['total_revenue'] / 1e9, # Convert to billions\n 'Net Income': data['net_income'] / 1e9,\n 'EPS': data['earnings_per_share']\n })\n\n# Create DataFrame and analyze\ndf = pd.DataFrame(data_list)\nprint(df)\nprint(f\"\\nRevenue Growth Q2->Q3: {((df.iloc[2]['Revenue'] / df.iloc[1]['Revenue']) - 1) * 100:.1f}%\")"
|
| 408 |
+
},
|
| 409 |
+
|
| 410 |
+
"example_3_multi_company_comparison": {
|
| 411 |
+
"name": "Compare Multiple Companies",
|
| 412 |
+
"description": "Compare financial metrics across different companies",
|
| 413 |
+
"companies": ["NVIDIA", "AMD", "Intel"],
|
| 414 |
+
"python_code": "import requests\n\nBASE_URL = 'https://YOUR_SPACE.hf.space'\ncompanies = ['NVIDIA', 'AMD', 'Intel']\nperiod = '2024'\n\nresults = []\nfor company_name in companies:\n # Search company\n response = requests.post(\n f'{BASE_URL}/api/search_company',\n json={'company_name': company_name}\n )\n company = response.json()\n \n if not company.get('error'):\n # Get financial data\n response = requests.post(\n f'{BASE_URL}/api/get_financial_data',\n json={'cik': company['cik'], 'period': period}\n )\n data = response.json()\n \n if not data.get('error'):\n results.append({\n 'Company': company['name'],\n 'Revenue (B)': data['total_revenue'] / 1e9,\n 'Net Income (B)': data['net_income'] / 1e9,\n 'EPS': data['earnings_per_share']\n })\n\n# Print comparison\nfor result in results:\n print(f\"{result['Company']}:\")\n print(f\" Revenue: ${result['Revenue (B)']:.2f}B\")\n print(f\" Net Income: ${result['Net Income (B)']:.2f}B\")\n print(f\" EPS: ${result['EPS']:.2f}\\n\")"
|
| 415 |
+
},
|
| 416 |
+
|
| 417 |
+
"example_4_filing_history_analysis": {
|
| 418 |
+
"name": "Analyze Filing History",
|
| 419 |
+
"description": "Get and analyze a company's filing history",
|
| 420 |
+
"python_code": "import requests\nfrom datetime import datetime\n\nBASE_URL = 'https://YOUR_SPACE.hf.space'\nCIK = '0001045810'\n\n# Get all 10-K filings\nresponse = requests.post(\n f'{BASE_URL}/api/get_company_filings',\n json={'cik': CIK, 'form_types': ['10-K']}\n)\nfilings = response.json()['filings']\n\nprint(f\"Found {len(filings)} annual reports (10-K)\\n\")\n\n# Analyze filing dates\nfor filing in filings[:5]: # Last 5 years\n filing_date = datetime.strptime(filing['filing_date'], '%Y-%m-%d')\n print(f\"Form: {filing['form_type']}\")\n print(f\"Filed: {filing_date.strftime('%B %d, %Y')}\")\n print(f\"Document: {filing['primary_document']}\")\n print(f\"Accession: {filing['accession_number']}\")\n print()"
|
| 421 |
+
}
|
| 422 |
+
},
|
| 423 |
+
|
| 424 |
+
"error_handling_examples": {
|
| 425 |
+
|
| 426 |
+
"handling_company_not_found": {
|
| 427 |
+
"scenario": "Company name not found in database",
|
| 428 |
+
"python_code": "import requests\n\nBASE_URL = 'https://YOUR_SPACE.hf.space'\n\nresponse = requests.post(\n f'{BASE_URL}/api/search_company',\n json={'company_name': 'INVALID_COMPANY_XYZ'}\n)\nresult = response.json()\n\nif result.get('error'):\n print(f\"Error: {result['error']}\")\n # Handle error: ask user to try again, suggest alternatives, etc.\nelse:\n print(f\"Found: {result['name']}\")"
|
| 429 |
+
},
|
| 430 |
+
|
| 431 |
+
"handling_no_financial_data": {
|
| 432 |
+
"scenario": "No financial data available for requested period",
|
| 433 |
+
"python_code": "import requests\n\nBASE_URL = 'https://YOUR_SPACE.hf.space'\n\nresponse = requests.post(\n f'{BASE_URL}/api/get_financial_data',\n json={'cik': '0001045810', 'period': '1990'}\n)\ndata = response.json()\n\nif data.get('error'):\n print(f\"No data available: {data['error']}\")\n # Try different period or notify user\nelif not data.get('total_revenue'):\n print(\"Warning: Incomplete data for this period\")\n # Handle partial data\nelse:\n print(f\"Revenue: ${data['total_revenue']:,.0f}\")"
|
| 434 |
+
},
|
| 435 |
+
|
| 436 |
+
"handling_network_errors": {
|
| 437 |
+
"scenario": "Handle network errors and timeouts",
|
| 438 |
+
"python_code": "import requests\nfrom requests.exceptions import RequestException, Timeout\n\nBASE_URL = 'https://YOUR_SPACE.hf.space'\n\ntry:\n response = requests.post(\n f'{BASE_URL}/api/search_company',\n json={'company_name': 'NVIDIA'},\n timeout=10 # 10 seconds timeout\n )\n response.raise_for_status() # Raise error for 4xx/5xx status\n data = response.json()\n print(f\"Success: {data['name']}\")\n \nexcept Timeout:\n print(\"Request timed out. Server may be slow or unavailable.\")\nexcept RequestException as e:\n print(f\"Network error occurred: {e}\")\nexcept Exception as e:\n print(f\"Unexpected error: {e}\")"
|
| 439 |
+
},
|
| 440 |
+
|
| 441 |
+
"handling_validation_errors": {
|
| 442 |
+
"scenario": "Handle request validation errors (422 status)",
|
| 443 |
+
"python_code": "import requests\n\nBASE_URL = 'https://YOUR_SPACE.hf.space'\n\n# Invalid request (missing required field)\nresponse = requests.post(\n f'{BASE_URL}/api/search_company',\n json={'wrong_field': 'value'}\n)\n\nif response.status_code == 422:\n errors = response.json()['detail']\n print(\"Validation errors:\")\n for error in errors:\n field = error['loc'][-1]\n message = error['msg']\n print(f\" - {field}: {message}\")\nelse:\n data = response.json()\n print(data)"
|
| 444 |
+
}
|
| 445 |
+
},
|
| 446 |
+
|
| 447 |
+
"best_practices": {
|
| 448 |
+
"rate_limiting": {
|
| 449 |
+
"description": "SEC EDGAR API has rate limits (10 requests per second)",
|
| 450 |
+
"recommendation": "Add delays between requests when fetching multiple companies",
|
| 451 |
+
"python_example": "import requests\nimport time\n\nBASE_URL = 'https://YOUR_SPACE.hf.space'\ncompanies = ['NVIDIA', 'AMD', 'Intel', 'TSMC']\n\nfor company in companies:\n response = requests.post(\n f'{BASE_URL}/api/search_company',\n json={'company_name': company}\n )\n print(response.json())\n time.sleep(0.2) # 200ms delay = max 5 requests/second"
|
| 452 |
+
},
|
| 453 |
+
|
| 454 |
+
"caching_results": {
|
| 455 |
+
"description": "Cache results to avoid repeated API calls for same data",
|
| 456 |
+
"python_example": "import requests\nimport json\nfrom pathlib import Path\n\ncache_file = Path('financial_data_cache.json')\n\ndef get_financial_data(cik, period):\n # Try to load from cache\n cache = {}\n if cache_file.exists():\n cache = json.loads(cache_file.read_text())\n \n cache_key = f\"{cik}_{period}\"\n if cache_key in cache:\n print(\"Using cached data\")\n return cache[cache_key]\n \n # Fetch from API\n response = requests.post(\n 'https://YOUR_SPACE.hf.space/api/get_financial_data',\n json={'cik': cik, 'period': period}\n )\n data = response.json()\n \n # Save to cache\n cache[cache_key] = data\n cache_file.write_text(json.dumps(cache, indent=2))\n \n return data"
|
| 457 |
+
},
|
| 458 |
+
|
| 459 |
+
"error_handling": {
|
| 460 |
+
"description": "Always check for errors in API responses",
|
| 461 |
+
"python_example": "def safe_api_call(endpoint, payload):\n try:\n response = requests.post(endpoint, json=payload, timeout=10)\n response.raise_for_status()\n data = response.json()\n \n if data.get('error'):\n print(f\"API Error: {data['error']}\")\n return None\n \n return data\n except Exception as e:\n print(f\"Request failed: {e}\")\n return None\n\n# Usage\nresult = safe_api_call(\n 'https://YOUR_SPACE.hf.space/api/search_company',\n {'company_name': 'NVIDIA'}\n)\nif result:\n print(f\"Found: {result['name']}\")"
|
| 462 |
+
},
|
| 463 |
+
|
| 464 |
+
"data_validation": {
|
| 465 |
+
"description": "Validate data before using it in calculations",
|
| 466 |
+
"python_example": "def calculate_profit_margin(financial_data):\n revenue = financial_data.get('total_revenue')\n net_income = financial_data.get('net_income')\n \n # Validate data exists and is valid\n if not revenue or not net_income:\n return None\n if revenue <= 0:\n return None\n \n # Calculate margin\n margin = (net_income / revenue) * 100\n return round(margin, 2)\n\n# Usage\ndata = get_financial_data('0001045810', '2024')\nmargin = calculate_profit_margin(data)\nif margin:\n print(f\"Profit Margin: {margin}%\")\nelse:\n print(\"Unable to calculate profit margin\")"
|
| 467 |
+
}
|
| 468 |
+
},
|
| 469 |
+
|
| 470 |
+
"notes": {
|
| 471 |
+
"data_format": "All monetary values are in USD (original amounts, not scaled)",
|
| 472 |
+
"eps_format": "Earnings per share is per-share value",
|
| 473 |
+
"period_formats": {
|
| 474 |
+
"annual": "YYYY (e.g., '2024')",
|
| 475 |
+
"quarterly": "YYYYQX (e.g., '2024Q3')"
|
| 476 |
+
},
|
| 477 |
+
"supported_forms": {
|
| 478 |
+
"10-K": "Annual report for US companies",
|
| 479 |
+
"10-Q": "Quarterly report for US companies",
|
| 480 |
+
"20-F": "Annual report for foreign private issuers"
|
| 481 |
+
},
|
| 482 |
+
"accounting_standards": ["US-GAAP", "IFRS"],
|
| 483 |
+
"user_agent": "Juntao Peng Financial Report Metrics App (jtyxabc@gmail.com)",
|
| 484 |
+
"rate_limiting": "Follow SEC EDGAR guidelines (10 requests per second)",
|
| 485 |
+
"documentation_urls": {
|
| 486 |
+
"swagger_ui": "/docs",
|
| 487 |
+
"redoc": "/redoc"
|
| 488 |
+
}
|
| 489 |
+
}
|
| 490 |
+
}
|
mcp_server.py
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
MCP Server for SEC Financial Report Data Query
|
| 3 |
+
Based on FastAPI framework
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from fastapi import FastAPI, HTTPException
|
| 7 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 8 |
+
from pydantic import BaseModel, Field
|
| 9 |
+
from typing import Optional, List, Dict, Any
|
| 10 |
+
from edgar_client import EdgarDataClient
|
| 11 |
+
import uvicorn
|
| 12 |
+
|
| 13 |
+
# Initialize FastAPI app
|
| 14 |
+
app = FastAPI(
|
| 15 |
+
title="SEC Financial Report MCP Server",
|
| 16 |
+
description="MCP Server for querying SEC EDGAR financial data",
|
| 17 |
+
version="1.0.0"
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
# Configure CORS
|
| 21 |
+
app.add_middleware(
|
| 22 |
+
CORSMiddleware,
|
| 23 |
+
allow_origins=["*"],
|
| 24 |
+
allow_credentials=True,
|
| 25 |
+
allow_methods=["*"],
|
| 26 |
+
allow_headers=["*"],
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
# Initialize EDGAR client with user information
|
| 30 |
+
edgar_client = EdgarDataClient(
|
| 31 |
+
user_agent="Juntao Peng Financial Report Metrics App (jtyxabc@gmail.com)"
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
# Request/Response Models
|
| 36 |
+
class UserAgentRequest(BaseModel):
|
| 37 |
+
user_agent: str = Field(
|
| 38 |
+
default="Financial Report Metrics App (your-email@example.com)",
|
| 39 |
+
description="User agent string for identifying request source"
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class CompanySearchRequest(BaseModel):
|
| 44 |
+
company_name: str = Field(..., description="Company name to search")
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class CompanyInfoRequest(BaseModel):
|
| 48 |
+
cik: str = Field(..., description="Company CIK code")
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class CompanyFilingsRequest(BaseModel):
|
| 52 |
+
cik: str = Field(..., description="Company CIK code")
|
| 53 |
+
form_types: Optional[List[str]] = Field(
|
| 54 |
+
None,
|
| 55 |
+
description="List of form types (e.g., ['10-K', '10-Q']), None for all types"
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class CompanyFactsRequest(BaseModel):
|
| 60 |
+
cik: str = Field(..., description="Company CIK code")
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class FinancialDataRequest(BaseModel):
|
| 64 |
+
cik: str = Field(..., description="Company CIK code")
|
| 65 |
+
period: str = Field(
|
| 66 |
+
...,
|
| 67 |
+
description="Period in format 'YYYY' or 'YYYYQX' (e.g., '2025' or '2025Q3')"
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class CompanySearchResponse(BaseModel):
|
| 72 |
+
cik: Optional[str] = None
|
| 73 |
+
name: Optional[str] = None
|
| 74 |
+
ticker: Optional[str] = None
|
| 75 |
+
error: Optional[str] = None
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class CompanyInfoResponse(BaseModel):
|
| 79 |
+
cik: Optional[str] = None
|
| 80 |
+
name: Optional[str] = None
|
| 81 |
+
tickers: Optional[List[str]] = None
|
| 82 |
+
sic: Optional[str] = None
|
| 83 |
+
sic_description: Optional[str] = None
|
| 84 |
+
error: Optional[str] = None
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
class FilingItem(BaseModel):
|
| 88 |
+
form_type: str
|
| 89 |
+
filing_date: str
|
| 90 |
+
accession_number: str
|
| 91 |
+
primary_document: str
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
class CompanyFilingsResponse(BaseModel):
|
| 95 |
+
filings: List[FilingItem] = []
|
| 96 |
+
error: Optional[str] = None
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class CompanyFactsResponse(BaseModel):
|
| 100 |
+
facts: Dict[str, Any] = {}
|
| 101 |
+
error: Optional[str] = None
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
class FinancialDataResponse(BaseModel):
|
| 105 |
+
period: Optional[str] = None
|
| 106 |
+
total_revenue: Optional[float] = None
|
| 107 |
+
net_income: Optional[float] = None
|
| 108 |
+
earnings_per_share: Optional[float] = None
|
| 109 |
+
operating_expenses: Optional[float] = None
|
| 110 |
+
operating_cash_flow: Optional[float] = None
|
| 111 |
+
source_url: Optional[str] = None
|
| 112 |
+
source_form: Optional[str] = None
|
| 113 |
+
data_source: Optional[str] = None
|
| 114 |
+
additional_details: Optional[Dict[str, Any]] = None
|
| 115 |
+
error: Optional[str] = None
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
# API Endpoints
|
| 119 |
+
@app.get("/")
|
| 120 |
+
async def root():
|
| 121 |
+
"""Root endpoint with API information"""
|
| 122 |
+
return {
|
| 123 |
+
"service": "SEC Financial Report MCP Server",
|
| 124 |
+
"version": "1.0.0",
|
| 125 |
+
"description": "MCP Server for querying SEC EDGAR financial data",
|
| 126 |
+
"endpoints": {
|
| 127 |
+
"health": "/health",
|
| 128 |
+
"search_company": "/api/search_company",
|
| 129 |
+
"get_company_info": "/api/get_company_info",
|
| 130 |
+
"get_company_filings": "/api/get_company_filings",
|
| 131 |
+
"get_company_facts": "/api/get_company_facts",
|
| 132 |
+
"get_financial_data": "/api/get_financial_data"
|
| 133 |
+
},
|
| 134 |
+
"user_agent": "Juntao Peng (jtyxabc@gmail.com)"
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
@app.get("/health")
|
| 139 |
+
async def health_check():
|
| 140 |
+
"""Health check endpoint"""
|
| 141 |
+
return {"status": "healthy", "service": "SEC Financial Report MCP Server"}
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
@app.post("/api/search_company", response_model=CompanySearchResponse)
|
| 145 |
+
async def search_company_by_name(request: CompanySearchRequest):
|
| 146 |
+
"""
|
| 147 |
+
Search company CIK by company name
|
| 148 |
+
|
| 149 |
+
Args:
|
| 150 |
+
company_name (str): Company name
|
| 151 |
+
|
| 152 |
+
Returns:
|
| 153 |
+
dict: Dictionary containing company information
|
| 154 |
+
"""
|
| 155 |
+
try:
|
| 156 |
+
result = edgar_client.search_company_by_name(request.company_name)
|
| 157 |
+
|
| 158 |
+
if result is None:
|
| 159 |
+
return CompanySearchResponse(
|
| 160 |
+
error=f"No company found matching '{request.company_name}'"
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
return CompanySearchResponse(
|
| 164 |
+
cik=result.get("cik"),
|
| 165 |
+
name=result.get("name"),
|
| 166 |
+
ticker=result.get("ticker")
|
| 167 |
+
)
|
| 168 |
+
except Exception as e:
|
| 169 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
@app.post("/api/get_company_info", response_model=CompanyInfoResponse)
|
| 173 |
+
async def get_company_info(request: CompanyInfoRequest):
|
| 174 |
+
"""
|
| 175 |
+
Get basic company information
|
| 176 |
+
|
| 177 |
+
Args:
|
| 178 |
+
cik (str): Company CIK code
|
| 179 |
+
|
| 180 |
+
Returns:
|
| 181 |
+
dict: Dictionary containing company information
|
| 182 |
+
"""
|
| 183 |
+
try:
|
| 184 |
+
result = edgar_client.get_company_info(request.cik)
|
| 185 |
+
|
| 186 |
+
if result is None:
|
| 187 |
+
return CompanyInfoResponse(
|
| 188 |
+
error=f"No company information found for CIK: {request.cik}"
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
return CompanyInfoResponse(
|
| 192 |
+
cik=result.get("cik"),
|
| 193 |
+
name=result.get("name"),
|
| 194 |
+
tickers=result.get("tickers"),
|
| 195 |
+
sic=result.get("sic"),
|
| 196 |
+
sic_description=result.get("sic_description")
|
| 197 |
+
)
|
| 198 |
+
except Exception as e:
|
| 199 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
@app.post("/api/get_company_filings", response_model=CompanyFilingsResponse)
|
| 203 |
+
async def get_company_filings(request: CompanyFilingsRequest):
|
| 204 |
+
"""
|
| 205 |
+
Get all company filing documents
|
| 206 |
+
|
| 207 |
+
Args:
|
| 208 |
+
cik (str): Company CIK code
|
| 209 |
+
form_types (list): List of form types (e.g., ['10-K', '10-Q']), None for all types
|
| 210 |
+
|
| 211 |
+
Returns:
|
| 212 |
+
list: List of filing documents
|
| 213 |
+
"""
|
| 214 |
+
try:
|
| 215 |
+
result = edgar_client.get_company_filings(request.cik, request.form_types)
|
| 216 |
+
|
| 217 |
+
if not result:
|
| 218 |
+
return CompanyFilingsResponse(
|
| 219 |
+
filings=[],
|
| 220 |
+
error=f"No filings found for CIK: {request.cik}"
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
filings = [
|
| 224 |
+
FilingItem(
|
| 225 |
+
form_type=filing.get("form_type", ""),
|
| 226 |
+
filing_date=filing.get("filing_date", ""),
|
| 227 |
+
accession_number=filing.get("accession_number", ""),
|
| 228 |
+
primary_document=filing.get("primary_document", "")
|
| 229 |
+
)
|
| 230 |
+
for filing in result
|
| 231 |
+
]
|
| 232 |
+
|
| 233 |
+
return CompanyFilingsResponse(filings=filings)
|
| 234 |
+
except Exception as e:
|
| 235 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
@app.post("/api/get_company_facts", response_model=CompanyFactsResponse)
|
| 239 |
+
async def get_company_facts(request: CompanyFactsRequest):
|
| 240 |
+
"""
|
| 241 |
+
Get all company financial facts data
|
| 242 |
+
|
| 243 |
+
Args:
|
| 244 |
+
cik (str): Company CIK code
|
| 245 |
+
|
| 246 |
+
Returns:
|
| 247 |
+
dict: Company financial facts data
|
| 248 |
+
"""
|
| 249 |
+
try:
|
| 250 |
+
result = edgar_client.get_company_facts(request.cik)
|
| 251 |
+
|
| 252 |
+
if not result:
|
| 253 |
+
return CompanyFactsResponse(
|
| 254 |
+
facts={},
|
| 255 |
+
error=f"No financial facts found for CIK: {request.cik}"
|
| 256 |
+
)
|
| 257 |
+
|
| 258 |
+
return CompanyFactsResponse(facts=result)
|
| 259 |
+
except Exception as e:
|
| 260 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
@app.post("/api/get_financial_data", response_model=FinancialDataResponse)
|
| 264 |
+
async def get_financial_data_for_period(request: FinancialDataRequest):
|
| 265 |
+
"""
|
| 266 |
+
Get financial data for a specific period (supports annual and quarterly)
|
| 267 |
+
|
| 268 |
+
Args:
|
| 269 |
+
cik (str): Company CIK code
|
| 270 |
+
period (str): Period in format 'YYYY' or 'YYYYQX' (e.g., '2025' or '2025Q3')
|
| 271 |
+
|
| 272 |
+
Returns:
|
| 273 |
+
dict: Financial data dictionary
|
| 274 |
+
"""
|
| 275 |
+
try:
|
| 276 |
+
result = edgar_client.get_financial_data_for_period(request.cik, request.period)
|
| 277 |
+
|
| 278 |
+
if not result or "period" not in result:
|
| 279 |
+
return FinancialDataResponse(
|
| 280 |
+
error=f"No financial data found for CIK: {request.cik}, Period: {request.period}"
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
# Collect additional details
|
| 284 |
+
additional_details = {}
|
| 285 |
+
for key, value in result.items():
|
| 286 |
+
if key.endswith("_details"):
|
| 287 |
+
additional_details[key] = value
|
| 288 |
+
|
| 289 |
+
return FinancialDataResponse(
|
| 290 |
+
period=result.get("period"),
|
| 291 |
+
total_revenue=result.get("total_revenue"),
|
| 292 |
+
net_income=result.get("net_income"),
|
| 293 |
+
earnings_per_share=result.get("earnings_per_share"),
|
| 294 |
+
operating_expenses=result.get("operating_expenses"),
|
| 295 |
+
operating_cash_flow=result.get("operating_cash_flow"),
|
| 296 |
+
source_url=result.get("source_url"),
|
| 297 |
+
source_form=result.get("source_form"),
|
| 298 |
+
data_source=result.get("data_source"),
|
| 299 |
+
additional_details=additional_details if additional_details else None
|
| 300 |
+
)
|
| 301 |
+
except Exception as e:
|
| 302 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
if __name__ == "__main__":
|
| 306 |
+
# Run the server
|
| 307 |
+
uvicorn.run(
|
| 308 |
+
"mcp_server:app",
|
| 309 |
+
host="0.0.0.0",
|
| 310 |
+
port=7860,
|
| 311 |
+
reload=True
|
| 312 |
+
)
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi==0.109.0
|
| 2 |
+
uvicorn[standard]==0.27.0
|
| 3 |
+
pydantic==2.5.3
|
| 4 |
+
sec-edgar-api==1.1.0
|
| 5 |
+
requests==2.31.0
|