MCP server exposing 75 EODHD financial data API tools via HTTP, SSE, and stdio transports.
Python 3.10+, FastMCP >=2.0, httpx (async HTTP), Ruff, MyPy, pytest, Bandit, Semgrep, pip-audit
pytest tests/auto/ -v --tb=short # run auto
pytest tests/auto/ -v --cov=app --cov-report=term-missing # with coverage
ruff check app/ server.py # lint
ruff format --check app/ server.py # format check
mypy app/ server.py --ignore-missing-imports --explicit-package-bases # type check
bandit -r app/ -ll -ii -x app/resources/ # security scan (AST)
semgrep scan --config p/python --config p/owasp-top-ten --config p/secrets --config p/jwt --error app/ # SAST + taint
python server.py # run (HTTP, port 8000)
python server.py --stdio # run (stdio)server.py— entry point, transport selectionapp/config.py— env vars, API base URLapp/api_client.py— shared async httpx client with retry, rate-limit, auth injectionapp/input_formatter.py— input sanitisation (ticker, exchange) and date coercionapp/response_formatter.py— MCP EmbeddedResource formatting and API error raisingapp/tools/— 75 tool modules, each exportsregister(mcp: FastMCP)app/tools/__init__.py—ALL_TOOLSlist,register_all(mcp), safe dynamic importapp/prompts/— example workflowsapp/resources/— markdown reference docs as MCP resources
- Every tool file must export
register(mcp)— called byregister_all() - All tools are read-only (
readOnlyHint=True) - Sanitise inputs via
sanitize_ticker(),sanitize_exchange()fromapp/input_formatter.py(only reject URL-breaking chars; let API validate) - Raise
ToolErrorfor user-facing errors - Return via
response_formatter(format_json_response,format_text_response,format_binary_response) - Preserve explicit upstream API errors; do not silently pass through
{"error": ...}payloads as successful results - All HTTP calls go through
make_request()inapi_client.py— never call httpx directly - No cross-tool imports (tools must not import from other tool modules)
- No
print()in app code — uselogging - Use
X | NonenotOptional[X]
- pytest, asyncio_mode="auto",
respxfor HTTP mocking - Parametrized tests for URL construction and error paths
- Coverage target: 60% (pyproject.toml
fail_under) - CI uses
EODHD_API_KEY=test_key_for_ci