JC321 commited on
Commit
98e3256
·
verified ·
1 Parent(s): 67eb185

Upload 8 files

Browse files
Files changed (8) hide show
  1. API_USAGE.md +500 -0
  2. DEPLOYMENT.md +121 -0
  3. Dockerfile +21 -0
  4. README.md +84 -12
  5. edgar_client.py +342 -0
  6. mcp_client_examples.json +490 -0
  7. mcp_server.py +312 -0
  8. 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: EasyReportDateMCP
3
- emoji: 🔥
4
- colorFrom: red
5
- colorTo: green
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- short_description: EasyReportDateMCP
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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