JC321 commited on
Commit
b890771
·
verified ·
1 Parent(s): b5415fc

Upload mcp_server.py

Browse files
Files changed (1) hide show
  1. mcp_server.py +111 -3
mcp_server.py CHANGED
@@ -3,11 +3,12 @@ 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 fastapi.responses import HTMLResponse
9
  from fastapi.staticfiles import StaticFiles
10
- from pydantic import BaseModel, Field
 
11
  from typing import Optional, List, Dict, Any
12
  from edgar_client import EdgarDataClient
13
  from financial_analyzer import FinancialAnalyzer
@@ -40,6 +41,51 @@ financial_analyzer = FinancialAnalyzer(
40
  )
41
 
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  # Request/Response Models
44
  class UserAgentRequest(BaseModel):
45
  user_agent: str = Field(
@@ -54,6 +100,16 @@ class CompanySearchRequest(BaseModel):
54
 
55
  class CompanyInfoRequest(BaseModel):
56
  cik: str = Field(..., description="Company CIK code")
 
 
 
 
 
 
 
 
 
 
57
 
58
 
59
  class CompanyFilingsRequest(BaseModel):
@@ -62,10 +118,30 @@ class CompanyFilingsRequest(BaseModel):
62
  None,
63
  description="List of form types (e.g., ['10-K', '10-Q']), None for all types"
64
  )
 
 
 
 
 
 
 
 
 
 
65
 
66
 
67
  class CompanyFactsRequest(BaseModel):
68
  cik: str = Field(..., description="Company CIK code")
 
 
 
 
 
 
 
 
 
 
69
 
70
 
71
  class FinancialDataRequest(BaseModel):
@@ -74,6 +150,18 @@ class FinancialDataRequest(BaseModel):
74
  ...,
75
  description="Period in format 'YYYY' or 'YYYYQX' (e.g., '2025' or '2025Q3')"
76
  )
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
 
79
  class AdvancedSearchRequest(BaseModel):
@@ -83,10 +171,30 @@ class AdvancedSearchRequest(BaseModel):
83
  class ExtractMetricsRequest(BaseModel):
84
  cik: str = Field(..., description="Company CIK code")
85
  years: int = Field(default=3, description="Number of years to extract", ge=1, le=10)
 
 
 
 
 
 
 
 
 
 
86
 
87
 
88
  class LatestDataRequest(BaseModel):
89
  cik: str = Field(..., description="Company CIK code")
 
 
 
 
 
 
 
 
 
 
90
 
91
 
92
  class CompanySearchResponse(BaseModel):
 
3
  Based on FastAPI framework
4
  """
5
 
6
+ from fastapi import FastAPI, HTTPException, Request
7
  from fastapi.middleware.cors import CORSMiddleware
8
+ from fastapi.responses import HTMLResponse, JSONResponse
9
  from fastapi.staticfiles import StaticFiles
10
+ from fastapi.exceptions import RequestValidationError
11
+ from pydantic import BaseModel, Field, ValidationError
12
  from typing import Optional, List, Dict, Any
13
  from edgar_client import EdgarDataClient
14
  from financial_analyzer import FinancialAnalyzer
 
41
  )
42
 
43
 
44
+ # Custom error handler for validation errors (422)
45
+ @app.exception_handler(RequestValidationError)
46
+ async def validation_exception_handler(request: Request, exc: RequestValidationError):
47
+ """
48
+ Custom handler for 422 Unprocessable Entity errors
49
+ Provides user-friendly error messages
50
+ """
51
+ errors = exc.errors()
52
+
53
+ # Build friendly error message
54
+ error_messages = []
55
+ for error in errors:
56
+ field = " -> ".join(str(loc) for loc in error['loc'])
57
+ msg = error['msg']
58
+ error_type = error['type']
59
+
60
+ if error_type == 'missing':
61
+ error_messages.append(f"Missing required field: {field}")
62
+ elif 'type_error' in error_type:
63
+ error_messages.append(f"Invalid type for field '{field}': {msg}")
64
+ else:
65
+ error_messages.append(f"Validation error in '{field}': {msg}")
66
+
67
+ return JSONResponse(
68
+ status_code=422,
69
+ content={
70
+ "error": "Request validation failed",
71
+ "details": error_messages,
72
+ "help": {
73
+ "note": "Please ensure you send data in the request BODY (not URL parameters)",
74
+ "example": {
75
+ "cik": "1577552",
76
+ "period": "2024" # if applicable
77
+ },
78
+ "common_issues": [
79
+ "Don't put parameters in the URL query string",
80
+ "Ensure CIK has no extra spaces",
81
+ "Send Content-Type: application/json header",
82
+ "Use POST method with JSON body"
83
+ ]
84
+ }
85
+ }
86
+ )
87
+
88
+
89
  # Request/Response Models
90
  class UserAgentRequest(BaseModel):
91
  user_agent: str = Field(
 
100
 
101
  class CompanyInfoRequest(BaseModel):
102
  cik: str = Field(..., description="Company CIK code")
103
+
104
+ class Config:
105
+ # Enable validation for strings
106
+ str_strip_whitespace = True
107
+
108
+ def __init__(self, **data):
109
+ # Strip whitespace and clean CIK
110
+ if 'cik' in data and data['cik']:
111
+ data['cik'] = str(data['cik']).strip()
112
+ super().__init__(**data)
113
 
114
 
115
  class CompanyFilingsRequest(BaseModel):
 
118
  None,
119
  description="List of form types (e.g., ['10-K', '10-Q']), None for all types"
120
  )
121
+
122
+ class Config:
123
+ # Enable validation for strings
124
+ str_strip_whitespace = True
125
+
126
+ def __init__(self, **data):
127
+ # Strip whitespace and clean CIK
128
+ if 'cik' in data and data['cik']:
129
+ data['cik'] = str(data['cik']).strip()
130
+ super().__init__(**data)
131
 
132
 
133
  class CompanyFactsRequest(BaseModel):
134
  cik: str = Field(..., description="Company CIK code")
135
+
136
+ class Config:
137
+ # Enable validation for strings
138
+ str_strip_whitespace = True
139
+
140
+ def __init__(self, **data):
141
+ # Strip whitespace and clean CIK
142
+ if 'cik' in data and data['cik']:
143
+ data['cik'] = str(data['cik']).strip()
144
+ super().__init__(**data)
145
 
146
 
147
  class FinancialDataRequest(BaseModel):
 
150
  ...,
151
  description="Period in format 'YYYY' or 'YYYYQX' (e.g., '2025' or '2025Q3')"
152
  )
153
+
154
+ class Config:
155
+ # Enable validation for strings
156
+ str_strip_whitespace = True
157
+
158
+ def __init__(self, **data):
159
+ # Strip whitespace and clean inputs
160
+ if 'cik' in data and data['cik']:
161
+ data['cik'] = str(data['cik']).strip()
162
+ if 'period' in data and data['period']:
163
+ data['period'] = str(data['period']).strip()
164
+ super().__init__(**data)
165
 
166
 
167
  class AdvancedSearchRequest(BaseModel):
 
171
  class ExtractMetricsRequest(BaseModel):
172
  cik: str = Field(..., description="Company CIK code")
173
  years: int = Field(default=3, description="Number of years to extract", ge=1, le=10)
174
+
175
+ class Config:
176
+ # Enable validation for strings
177
+ str_strip_whitespace = True
178
+
179
+ def __init__(self, **data):
180
+ # Strip whitespace and clean CIK
181
+ if 'cik' in data and data['cik']:
182
+ data['cik'] = str(data['cik']).strip()
183
+ super().__init__(**data)
184
 
185
 
186
  class LatestDataRequest(BaseModel):
187
  cik: str = Field(..., description="Company CIK code")
188
+
189
+ class Config:
190
+ # Enable validation for strings
191
+ str_strip_whitespace = True
192
+
193
+ def __init__(self, **data):
194
+ # Strip whitespace and clean CIK
195
+ if 'cik' in data and data['cik']:
196
+ data['cik'] = str(data['cik']).strip()
197
+ super().__init__(**data)
198
 
199
 
200
  class CompanySearchResponse(BaseModel):