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

```
├── .env.example
├── .gitignore
├── .python-version
├── LICENSE
├── pyproject.toml
├── README.md
├── server.py
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------

```
1 | 3.11
2 | 
```

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
1 | # Get your Financial Datasets API key from https://financialdatasets.ai/
2 | FINANCIAL_DATASETS_API_KEY=your-financial-datasets-api-key
```

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

```
  1 | # Byte-compiled / optimized / DLL files
  2 | __pycache__/
  3 | *.py[cod]
  4 | *$py.class
  5 | 
  6 | # C extensions
  7 | *.so
  8 | 
  9 | # Distribution / packaging
 10 | .Python
 11 | build/
 12 | develop-eggs/
 13 | dist/
 14 | downloads/
 15 | eggs/
 16 | .eggs/
 17 | lib/
 18 | lib64/
 19 | parts/
 20 | sdist/
 21 | var/
 22 | wheels/
 23 | share/python-wheels/
 24 | *.egg-info/
 25 | .installed.cfg
 26 | *.egg
 27 | MANIFEST
 28 | 
 29 | # PyInstaller
 30 | #  Usually these files are written by a python script from a template
 31 | #  before PyInstaller builds the exe, so as to inject date/other infos into it.
 32 | *.manifest
 33 | *.spec
 34 | 
 35 | # Installer logs
 36 | pip-log.txt
 37 | pip-delete-this-directory.txt
 38 | 
 39 | # Unit test / coverage reports
 40 | htmlcov/
 41 | .tox/
 42 | .nox/
 43 | .coverage
 44 | .coverage.*
 45 | .cache
 46 | nosetests.xml
 47 | coverage.xml
 48 | *.cover
 49 | *.py,cover
 50 | .hypothesis/
 51 | .pytest_cache/
 52 | cover/
 53 | 
 54 | # Translations
 55 | *.mo
 56 | *.pot
 57 | 
 58 | # Django stuff:
 59 | *.log
 60 | local_settings.py
 61 | db.sqlite3
 62 | db.sqlite3-journal
 63 | 
 64 | # Flask stuff:
 65 | instance/
 66 | .webassets-cache
 67 | 
 68 | # Scrapy stuff:
 69 | .scrapy
 70 | 
 71 | # Sphinx documentation
 72 | docs/_build/
 73 | 
 74 | # PyBuilder
 75 | .pybuilder/
 76 | target/
 77 | 
 78 | # Jupyter Notebook
 79 | .ipynb_checkpoints
 80 | 
 81 | # IPython
 82 | profile_default/
 83 | ipython_config.py
 84 | 
 85 | # pyenv
 86 | #   For a library or package, you might want to ignore these files since the code is
 87 | #   intended to run in multiple environments; otherwise, check them in:
 88 | # .python-version
 89 | 
 90 | # pipenv
 91 | #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 92 | #   However, in case of collaboration, if having platform-specific dependencies or dependencies
 93 | #   having no cross-platform support, pipenv may install dependencies that don't work, or not
 94 | #   install all needed dependencies.
 95 | #Pipfile.lock
 96 | 
 97 | # UV
 98 | #   Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
 99 | #   This is especially recommended for binary packages to ensure reproducibility, and is more
100 | #   commonly ignored for libraries.
101 | #uv.lock
102 | 
103 | # poetry
104 | #   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105 | #   This is especially recommended for binary packages to ensure reproducibility, and is more
106 | #   commonly ignored for libraries.
107 | #   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108 | #poetry.lock
109 | 
110 | # pdm
111 | #   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112 | #pdm.lock
113 | #   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114 | #   in version control.
115 | #   https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116 | .pdm.toml
117 | .pdm-python
118 | .pdm-build/
119 | 
120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121 | __pypackages__/
122 | 
123 | # Celery stuff
124 | celerybeat-schedule
125 | celerybeat.pid
126 | 
127 | # SageMath parsed files
128 | *.sage.py
129 | 
130 | # Environments
131 | .env
132 | .venv
133 | env/
134 | venv/
135 | ENV/
136 | env.bak/
137 | venv.bak/
138 | 
139 | # Spyder project settings
140 | .spyderproject
141 | .spyproject
142 | 
143 | # Rope project settings
144 | .ropeproject
145 | 
146 | # mkdocs documentation
147 | /site
148 | 
149 | # mypy
150 | .mypy_cache/
151 | .dmypy.json
152 | dmypy.json
153 | 
154 | # Pyre type checker
155 | .pyre/
156 | 
157 | # pytype static type analyzer
158 | .pytype/
159 | 
160 | # Cython debug symbols
161 | cython_debug/
162 | 
163 | # PyCharm
164 | #  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165 | #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166 | #  and can be added to the global gitignore or merged into this file.  For a more nuclear
167 | #  option (not recommended) you can uncomment the following to ignore the entire idea folder.
168 | #.idea/
169 | 
170 | # PyPI configuration file
171 | .pypirc
172 | 
173 | .DS_Store
174 | 
175 | # Ignore vscode
176 | .vscode/
```

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

```markdown
  1 | # Financial Datasets MCP Server
  2 | 
  3 | ## Introduction
  4 | 
  5 | This is a Model Context Protocol (MCP) server that provides access to stock market data from [Financial Datasets](https://www.financialdatasets.ai/). 
  6 | 
  7 | It allows Claude and other AI assistants to retrieve income statements, balance sheets, cash flow statements, stock prices, and market news directly through the MCP interface.
  8 | 
  9 | ## Available Tools
 10 | 
 11 | This MCP server provides the following tools:
 12 | - **get_income_statements**: Get income statements for a company.
 13 | - **get_balance_sheets**: Get balance sheets for a company.
 14 | - **get_cash_flow_statements**: Get cash flow statements for a company.
 15 | - **get_current_stock_price**: Get the current / latest price of a company.
 16 | - **get_historical_stock_prices**: Gets historical stock prices for a company.
 17 | - **get_company_news**: Get news for a company.
 18 | - **get_available_crypto_tickers**: Gets all available crypto tickers.
 19 | - **get_crypto_prices**: Gets historical prices for a crypto currency.
 20 | - **get_historical_crypto_prices**: Gets historical prices for a crypto currency.
 21 | - **get_current_crypto_price**: Get the current / latest price of a crypto currency.
 22 | 
 23 | ## Setup
 24 | 
 25 | ### Prerequisites
 26 | 
 27 | - Python 3.10 or higher
 28 | - [uv](https://github.com/astral-sh/uv) package manager
 29 | 
 30 | ### Installation
 31 | 
 32 | 1. Clone this repository:
 33 |    ```bash
 34 |    git clone https://github.com/financial-datasets/mcp-server
 35 |    cd mcp-server
 36 |    ```
 37 | 
 38 | 2. If you don't have uv installed, install it:
 39 |    ```bash
 40 |    # macOS/Linux
 41 |    curl -LsSf https://astral.sh/uv/install.sh | sh
 42 |    
 43 |    # Windows
 44 |    curl -LsSf https://astral.sh/uv/install.ps1 | powershell
 45 |    ```
 46 | 
 47 | 3. Install dependencies:
 48 |    ```bash
 49 |    # Create virtual env and activate it
 50 |    uv venv
 51 |    source .venv/bin/activate  # On Windows: .venv\Scripts\activate
 52 |    
 53 |    # Install dependencies
 54 |    uv add "mcp[cli]" httpx  # On Windows: uv add mcp[cli] httpx
 55 | 
 56 |    ```
 57 | 
 58 | 4. Set up environment variables:
 59 |    ```bash
 60 |    # Create .env file for your API keys
 61 |    cp .env.example .env
 62 | 
 63 |    # Set API key in .env
 64 |    FINANCIAL_DATASETS_API_KEY=your-financial-datasets-api-key
 65 |    ```
 66 | 
 67 | 5. Run the server:
 68 |    ```bash
 69 |    uv run server.py
 70 |    ```
 71 | 
 72 | ## Connecting to Claude Desktop
 73 | 
 74 | 1. Install [Claude Desktop](https://claude.ai/desktop) if you haven't already
 75 | 
 76 | 2. Create or edit the Claude Desktop configuration file:
 77 |    ```bash
 78 |    # macOS
 79 |    mkdir -p ~/Library/Application\ Support/Claude/
 80 |    nano ~/Library/Application\ Support/Claude/claude_desktop_config.json
 81 |    ```
 82 | 
 83 | 3. Add the following configuration:
 84 |    ```json
 85 |    {
 86 |      "mcpServers": {
 87 |        "financial-datasets": {
 88 |          "command": "/path/to/uv",
 89 |          "args": [
 90 |            "--directory",
 91 |            "/absolute/path/to/financial-datasets-mcp",
 92 |            "run",
 93 |            "server.py"
 94 |          ]
 95 |        }
 96 |      }
 97 |    }
 98 |    ```
 99 |    
100 |    Replace `/path/to/uv` with the result of `which uv` and `/absolute/path/to/financial-datasets-mcp` with the absolute path to this project.
101 | 
102 | 4. Restart Claude Desktop
103 | 
104 | 5. You should now see the financial tools available in Claude Desktop's tools menu (hammer icon)
105 | 
106 | 6. Try asking Claude questions like:
107 |    - "What are Apple's recent income statements?"
108 |    - "Show me the current price of Tesla stock"
109 |    - "Get historical prices for MSFT from 2024-01-01 to 2024-12-31"
110 | 
```

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

```toml
 1 | [project]
 2 | name = "mcp-server"
 3 | version = "0.1.0"
 4 | description = "Add your description here"
 5 | readme = "README.md"
 6 | requires-python = ">=3.11"
 7 | dependencies = [
 8 |     "httpx>=0.28.1",
 9 |     "mcp[cli]>=1.3.0",
10 |     "python-dotenv>=1.0.0",
11 | ]
12 | 
```

--------------------------------------------------------------------------------
/server.py:
--------------------------------------------------------------------------------

```python
  1 | import json
  2 | import os
  3 | import httpx
  4 | import logging
  5 | import sys
  6 | from mcp.server.fastmcp import FastMCP
  7 | from dotenv import load_dotenv
  8 | 
  9 | # Configure logging to write to stderr
 10 | logging.basicConfig(
 11 |     level=logging.INFO,
 12 |     format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
 13 |     stream=sys.stderr,
 14 | )
 15 | logger = logging.getLogger("financial-datasets-mcp")
 16 | 
 17 | # Initialize FastMCP server
 18 | mcp = FastMCP("financial-datasets")
 19 | 
 20 | # Constants
 21 | FINANCIAL_DATASETS_API_BASE = "https://api.financialdatasets.ai"
 22 | 
 23 | 
 24 | # Helper function to make API requests
 25 | async def make_request(url: str) -> dict[str, any] | None:
 26 |     """Make a request to the Financial Datasets API with proper error handling."""
 27 |     # Load environment variables from .env file
 28 |     load_dotenv()
 29 |     
 30 |     headers = {}
 31 |     if api_key := os.environ.get("FINANCIAL_DATASETS_API_KEY"):
 32 |         headers["X-API-KEY"] = api_key
 33 | 
 34 |     async with httpx.AsyncClient() as client:
 35 |         try:
 36 |             response = await client.get(url, headers=headers, timeout=30.0)
 37 |             response.raise_for_status()
 38 |             return response.json()
 39 |         except Exception as e:
 40 |             return {"Error": str(e)}
 41 | 
 42 | 
 43 | @mcp.tool()
 44 | async def get_income_statements(
 45 |     ticker: str,
 46 |     period: str = "annual",
 47 |     limit: int = 4,
 48 | ) -> str:
 49 |     """Get income statements for a company.
 50 | 
 51 |     Args:
 52 |         ticker: Ticker symbol of the company (e.g. AAPL, GOOGL)
 53 |         period: Period of the income statement (e.g. annual, quarterly, ttm)
 54 |         limit: Number of income statements to return (default: 4)
 55 |     """
 56 |     # Fetch data from the API
 57 |     url = f"{FINANCIAL_DATASETS_API_BASE}/financials/income-statements/?ticker={ticker}&period={period}&limit={limit}"
 58 |     data = await make_request(url)
 59 | 
 60 |     # Check if data is found
 61 |     if not data:
 62 |         return "Unable to fetch income statements or no income statements found."
 63 | 
 64 |     # Extract the income statements
 65 |     income_statements = data.get("income_statements", [])
 66 | 
 67 |     # Check if income statements are found
 68 |     if not income_statements:
 69 |         return "Unable to fetch income statements or no income statements found."
 70 | 
 71 |     # Stringify the income statements
 72 |     return json.dumps(income_statements, indent=2)
 73 | 
 74 | 
 75 | @mcp.tool()
 76 | async def get_balance_sheets(
 77 |     ticker: str,
 78 |     period: str = "annual",
 79 |     limit: int = 4,
 80 | ) -> str:
 81 |     """Get balance sheets for a company.
 82 | 
 83 |     Args:
 84 |         ticker: Ticker symbol of the company (e.g. AAPL, GOOGL)
 85 |         period: Period of the balance sheet (e.g. annual, quarterly, ttm)
 86 |         limit: Number of balance sheets to return (default: 4)
 87 |     """
 88 |     # Fetch data from the API
 89 |     url = f"{FINANCIAL_DATASETS_API_BASE}/financials/balance-sheets/?ticker={ticker}&period={period}&limit={limit}"
 90 |     data = await make_request(url)
 91 | 
 92 |     # Check if data is found
 93 |     if not data:
 94 |         return "Unable to fetch balance sheets or no balance sheets found."
 95 | 
 96 |     # Extract the balance sheets
 97 |     balance_sheets = data.get("balance_sheets", [])
 98 | 
 99 |     # Check if balance sheets are found
100 |     if not balance_sheets:
101 |         return "Unable to fetch balance sheets or no balance sheets found."
102 | 
103 |     # Stringify the balance sheets
104 |     return json.dumps(balance_sheets, indent=2)
105 | 
106 | 
107 | @mcp.tool()
108 | async def get_cash_flow_statements(
109 |     ticker: str,
110 |     period: str = "annual",
111 |     limit: int = 4,
112 | ) -> str:
113 |     """Get cash flow statements for a company.
114 | 
115 |     Args:
116 |         ticker: Ticker symbol of the company (e.g. AAPL, GOOGL)
117 |         period: Period of the cash flow statement (e.g. annual, quarterly, ttm)
118 |         limit: Number of cash flow statements to return (default: 4)
119 |     """
120 |     # Fetch data from the API
121 |     url = f"{FINANCIAL_DATASETS_API_BASE}/financials/cash-flow-statements/?ticker={ticker}&period={period}&limit={limit}"
122 |     data = await make_request(url)
123 | 
124 |     # Check if data is found
125 |     if not data:
126 |         return "Unable to fetch cash flow statements or no cash flow statements found."
127 | 
128 |     # Extract the cash flow statements
129 |     cash_flow_statements = data.get("cash_flow_statements", [])
130 | 
131 |     # Check if cash flow statements are found
132 |     if not cash_flow_statements:
133 |         return "Unable to fetch cash flow statements or no cash flow statements found."
134 | 
135 |     # Stringify the cash flow statements
136 |     return json.dumps(cash_flow_statements, indent=2)
137 | 
138 | 
139 | @mcp.tool()
140 | async def get_current_stock_price(ticker: str) -> str:
141 |     """Get the current / latest price of a company.
142 | 
143 |     Args:
144 |         ticker: Ticker symbol of the company (e.g. AAPL, GOOGL)
145 |     """
146 |     # Fetch data from the API
147 |     url = f"{FINANCIAL_DATASETS_API_BASE}/prices/snapshot/?ticker={ticker}"
148 |     data = await make_request(url)
149 | 
150 |     # Check if data is found
151 |     if not data:
152 |         return "Unable to fetch current price or no current price found."
153 | 
154 |     # Extract the current price
155 |     snapshot = data.get("snapshot", {})
156 | 
157 |     # Check if current price is found
158 |     if not snapshot:
159 |         return "Unable to fetch current price or no current price found."
160 | 
161 |     # Stringify the current price
162 |     return json.dumps(snapshot, indent=2)
163 | 
164 | 
165 | @mcp.tool()
166 | async def get_historical_stock_prices(
167 |     ticker: str,
168 |     start_date: str,
169 |     end_date: str,
170 |     interval: str = "day",
171 |     interval_multiplier: int = 1,
172 | ) -> str:
173 |     """Gets historical stock prices for a company.
174 | 
175 |     Args:
176 |         ticker: Ticker symbol of the company (e.g. AAPL, GOOGL)
177 |         start_date: Start date of the price data (e.g. 2020-01-01)
178 |         end_date: End date of the price data (e.g. 2020-12-31)
179 |         interval: Interval of the price data (e.g. minute, hour, day, week, month)
180 |         interval_multiplier: Multiplier of the interval (e.g. 1, 2, 3)
181 |     """
182 |     # Fetch data from the API
183 |     url = f"{FINANCIAL_DATASETS_API_BASE}/prices/?ticker={ticker}&interval={interval}&interval_multiplier={interval_multiplier}&start_date={start_date}&end_date={end_date}"
184 |     data = await make_request(url)
185 | 
186 |     # Check if data is found
187 |     if not data:
188 |         return "Unable to fetch prices or no prices found."
189 | 
190 |     # Extract the prices
191 |     prices = data.get("prices", [])
192 | 
193 |     # Check if prices are found
194 |     if not prices:
195 |         return "Unable to fetch prices or no prices found."
196 | 
197 |     # Stringify the prices
198 |     return json.dumps(prices, indent=2)
199 | 
200 | 
201 | @mcp.tool()
202 | async def get_company_news(ticker: str) -> str:
203 |     """Get news for a company.
204 | 
205 |     Args:
206 |         ticker: Ticker symbol of the company (e.g. AAPL, GOOGL)
207 |     """
208 |     # Fetch data from the API
209 |     url = f"{FINANCIAL_DATASETS_API_BASE}/news/?ticker={ticker}"
210 |     data = await make_request(url)
211 | 
212 |     # Check if data is found
213 |     if not data:
214 |         return "Unable to fetch news or no news found."
215 | 
216 |     # Extract the news
217 |     news = data.get("news", [])
218 | 
219 |     # Check if news are found
220 |     if not news:
221 |         return "Unable to fetch news or no news found."
222 |     return json.dumps(news, indent=2)
223 | 
224 | 
225 | @mcp.tool()
226 | async def get_available_crypto_tickers() -> str:
227 |     """
228 |     Gets all available crypto tickers.
229 |     """
230 |     # Fetch data from the API
231 |     url = f"{FINANCIAL_DATASETS_API_BASE}/crypto/prices/tickers"
232 |     data = await make_request(url)
233 | 
234 |     # Check if data is found
235 |     if not data:
236 |         return "Unable to fetch available crypto tickers or no available crypto tickers found."
237 | 
238 |     # Extract the available crypto tickers
239 |     tickers = data.get("tickers", [])
240 | 
241 |     # Stringify the available crypto tickers
242 |     return json.dumps(tickers, indent=2)
243 | 
244 | 
245 | @mcp.tool()
246 | async def get_crypto_prices(
247 |     ticker: str,
248 |     start_date: str,
249 |     end_date: str,
250 |     interval: str = "day",
251 |     interval_multiplier: int = 1,
252 | ) -> str:
253 |     """
254 |     Gets historical prices for a crypto currency.
255 |     """
256 |     # Fetch data from the API
257 |     url = f"{FINANCIAL_DATASETS_API_BASE}/crypto/prices/?ticker={ticker}&interval={interval}&interval_multiplier={interval_multiplier}&start_date={start_date}&end_date={end_date}"
258 |     data = await make_request(url)
259 | 
260 |     # Check if data is found
261 |     if not data:
262 |         return "Unable to fetch prices or no prices found."
263 | 
264 |     # Extract the prices
265 |     prices = data.get("prices", [])
266 | 
267 |     # Check if prices are found
268 |     if not prices:
269 |         return "Unable to fetch prices or no prices found."
270 | 
271 |     # Stringify the prices
272 |     return json.dumps(prices, indent=2)
273 | 
274 | 
275 | @mcp.tool()
276 | async def get_historical_crypto_prices(
277 |     ticker: str,
278 |     start_date: str,
279 |     end_date: str,
280 |     interval: str = "day",
281 |     interval_multiplier: int = 1,
282 | ) -> str:
283 |     """Gets historical prices for a crypto currency.
284 | 
285 |     Args:
286 |         ticker: Ticker symbol of the crypto currency (e.g. BTC-USD). The list of available crypto tickers can be retrieved via the get_available_crypto_tickers tool.
287 |         start_date: Start date of the price data (e.g. 2020-01-01)
288 |         end_date: End date of the price data (e.g. 2020-12-31)
289 |         interval: Interval of the price data (e.g. minute, hour, day, week, month)
290 |         interval_multiplier: Multiplier of the interval (e.g. 1, 2, 3)
291 |     """
292 |     # Fetch data from the API
293 |     url = f"{FINANCIAL_DATASETS_API_BASE}/crypto/prices/?ticker={ticker}&interval={interval}&interval_multiplier={interval_multiplier}&start_date={start_date}&end_date={end_date}"
294 |     data = await make_request(url)
295 | 
296 |     # Check if data is found
297 |     if not data:
298 |         return "Unable to fetch prices or no prices found."
299 | 
300 |     # Extract the prices
301 |     prices = data.get("prices", [])
302 | 
303 |     # Check if prices are found
304 |     if not prices:
305 |         return "Unable to fetch prices or no prices found."
306 | 
307 |     # Stringify the prices
308 |     return json.dumps(prices, indent=2)
309 | 
310 | 
311 | @mcp.tool()
312 | async def get_current_crypto_price(ticker: str) -> str:
313 |     """Get the current / latest price of a crypto currency.
314 | 
315 |     Args:
316 |         ticker: Ticker symbol of the crypto currency (e.g. BTC-USD). The list of available crypto tickers can be retrieved via the get_available_crypto_tickers tool.
317 |     """
318 |     # Fetch data from the API
319 |     url = f"{FINANCIAL_DATASETS_API_BASE}/crypto/prices/snapshot/?ticker={ticker}"
320 |     data = await make_request(url)
321 | 
322 |     # Check if data is found
323 |     if not data:
324 |         return "Unable to fetch current price or no current price found."
325 | 
326 |     # Extract the current price
327 |     snapshot = data.get("snapshot", {})
328 | 
329 |     # Check if current price is found
330 |     if not snapshot:
331 |         return "Unable to fetch current price or no current price found."
332 | 
333 |     # Stringify the current price
334 |     return json.dumps(snapshot, indent=2)
335 | 
336 | 
337 | @mcp.tool()
338 | async def get_sec_filings(
339 |     ticker: str,
340 |     limit: int = 10,
341 |     filing_type: str | None = None,
342 | ) -> str:
343 |     """Get all SEC filings for a company.
344 | 
345 |     Args:
346 |         ticker: Ticker symbol of the company (e.g. AAPL, GOOGL)
347 |         limit: Number of SEC filings to return (default: 10)
348 |         filing_type: Type of SEC filing (e.g. 10-K, 10-Q, 8-K)
349 |     """
350 |     # Fetch data from the API
351 |     url = f"{FINANCIAL_DATASETS_API_BASE}/filings/?ticker={ticker}&limit={limit}"
352 |     if filing_type:
353 |         url += f"&filing_type={filing_type}"
354 |  
355 |     # Call the API
356 |     data = await make_request(url)
357 | 
358 |     # Extract the SEC filings
359 |     filings = data.get("filings", [])
360 | 
361 |     # Check if SEC filings are found
362 |     if not filings:
363 |         return f"Unable to fetch SEC filings or no SEC filings found."
364 | 
365 |     # Stringify the SEC filings
366 |     return json.dumps(filings, indent=2)
367 | 
368 | if __name__ == "__main__":
369 |     # Log server startup
370 |     logger.info("Starting Financial Datasets MCP Server...")
371 | 
372 |     # Initialize and run the server
373 |     mcp.run(transport="stdio")
374 | 
375 |     # This line won't be reached during normal operation
376 |     logger.info("Server stopped")
377 | 
```