Skip to main content
Glama
momotime7584

SEC EDGAR Filings MCP Server

by momotime7584
README.md
# SEC EDGAR Filings MCP Server

A Model Context Protocol (MCP) server that enables AI assistants like Claude to interact with SEC EDGAR filings. Download, convert, and parse SEC financial documents seamlessly.

## 🎯 Features

- **πŸ“„ PDF to Markdown**: Parse PDF filings into Markdown using LlamaCloud or Docling
- **πŸ”„ HTML to PDF**: Convert SEC EDGAR HTML/iXBRL files to PDF
- **πŸ“₯ Download SEC Filings**: Automatically download filings from SEC EDGAR
- **⚑ Rate Limiting**: Respects SEC's 10 requests/second limit
- **🐳 Docker Support**: Easy deployment with Docker

## πŸ“‹ Supported Filing Types

- **8-K**: Current Report
- **10-Q**: Quarterly Report
- **10-K**: Annual Report
- **DEF 14A**: Proxy Statement (bonus)

## πŸ”§ Prerequisites

- Python 3.8+
- LlamaCloud API key (for PDF parsing) - Get from https://cloud.llamaindex.ai/
- Claude Desktop (for testing)

## πŸš€ Quick Start (Docker - Recommended)

**⚑ Fastest way to get started (< 2 minutes):**

### 1. Prerequisites

- **Docker Desktop** installed and running
  - **Windows/macOS**: [Download Docker Desktop](https://www.docker.com/products/docker-desktop)
  - **Linux**: [Install Docker Engine](https://docs.docker.com/engine/install/)
- [LlamaCloud API key](https://cloud.llamaindex.ai/)

**⚠️ Important: Start Docker Desktop first!**

Verify Docker is running:
```bash
docker --version
docker ps
```

If you see "Cannot connect to the Docker daemon", start Docker Desktop and wait until it's fully running.

### 2. Clone & Configure

```bash
git clone https://github.com/momotime7584/sec-edgar-filings-mcp.git
cd sec-edgar-filings-mcp 

# Create .env file
cp .env.example .env
# Edit .env with your LLAMA_CLOUD_API_KEY
```

**Required Configuration:**

```env
# LlamaCloud API Key (get from https://cloud.llamaindex.ai/)
LLAMA_CLOUD_API_KEY=your_actual_api_key_here

# SEC API User-Agent (CRITICAL - use browser format!)
# ⚠️ IMPORTANT: Must use browser User-Agent to avoid 403 Forbidden errors
SEC_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36

# Your email (optional but recommended)
SEC_FROM_EMAIL=your.email@example.com
```

**🚨 Critical: SEC_USER_AGENT Format**

The SEC API **requires** a browser-like User-Agent string. Using a simple format like `"YourName your.email@example.com"` will result in **403 Forbidden errors**.

**Why?** While SEC's documentation suggests simple identification, their servers actually filter requests and prefer browser User-Agents to prevent automated scraping abuse.

**βœ… Use the browser User-Agent shown above** (already in `.env.example`)


### 3. Start Server

```bash
# Download pre-built image (~1-2 seconds) and start
docker-compose pull
docker-compose up -d
```

**βœ… Done!** The MCP server is now running in a container with all dependencies.

### 4. Configure Claude Desktop

See [Claude Desktop Configuration](#claude-desktop-configuration) section below (use Docker option).

---

## πŸ”§ Alternative Installation (Python)

If you prefer not to use Docker, you can install directly with Python:

### 1. Clone Repository

```bash
git clone <your-repository-url>
cd "SEC EDGAR filings MCP"
```

### 2. Create Virtual Environment

```bash
# Windows
python -m venv venv
venv\Scripts\activate

# macOS/Linux
python3 -m venv venv
source venv/bin/activate
```

### 3. Install Dependencies

```bash
pip install -r requirements.txt
```

### 4. Install Playwright Browsers

```bash
playwright install chromium
```

### 5. Configure Environment

**Create `.env` file from template:**

```bash
# Copy example file
cp .env.example .env

# Edit with your credentials
```

## πŸ“– Usage

### Claude Desktop Configuration

**Configuration File Location:**
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`



#### **Option 1: Using Docker (Recommended)**

Start the Docker container first:

```bash
docker-compose up -d
```

Then add this configuration:

```json
{
  "mcpServers": {
    "sec-edgar": {
      "command": "docker",
      "args": [
        "exec", "-i",
        "sec-edgar-mcp-server",
        "python", "/app/server.py"
      ],
      "toolTimeout": 500000
    }
  }
}
```

**βœ… Benefits:**
- No Python installation needed
- All dependencies included
- Reproducible environment

**⚠️ Important:**
- Container must be running before starting Claude Desktop
- Run `docker-compose up -d` first
- Use `docker ps` to verify container is running

---

#### **Option 2: Local Python Installation**

```json
{
  "mcpServers": {
    "sec-edgar": {
      "command": "C:\\Users\\YourName\\path\\to\\venv\\Scripts\\python.exe",
      "args": [
        "C:\\Users\\YourName\\path\\to\\server.py"
      ],
      "env": {
        "LLAMA_CLOUD_API_KEY": "your_actual_api_key",
        "SEC_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
      },
      "toolTimeout": 500000
    }
  }
}
```

**Windows Path Examples:**
- Using venv: `C:\\Users\\YourName\\projects\\sec-edgar\\venv\\Scripts\\python.exe`
- System Python: `C:\\Python311\\python.exe`
- βœ… Use double backslashes (`\\`) or forward slashes (`/`)

**macOS/Linux:**

```json
{
  "mcpServers": {
    "sec-edgar": {
      "command": "/Users/yourname/projects/sec-edgar/venv/bin/python",
      "args": ["/Users/yourname/projects/sec-edgar/server.py"],
      "env": {
        "LLAMA_CLOUD_API_KEY": "your_actual_api_key",
        "SEC_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
      },
      "toolTimeout": 500000
    }
  }
}
```

---

## πŸ’¬ Example Prompts for Claude Desktop

### Multiple Tools

**Single Prompt Challenge #1: Apple 2021 8-K Analysis**
```
2021 μ• ν”Œ 8-k κ³΅μ‹œ λ‹€ μˆ™μ§€ν•΄μ€˜
```

**Expected Workflow:**
1. Downloads Apple's (CIK: 0000320193) 8-K filing for 2021
2. Converts HTML to PDF
3. Parses PDF to markdown
4. Reads and summarizes the entire filing

**Result:**
```
βœ… Successfully completed all steps automatically

Downloaded: html/apple_2021_8k/apple_20211109.htm
Converted: pdf/apple_2021_8k.pdf
Parsed: markdown/apple_2021_8k.md

Summary:
2021λ…„ 11μ›” 9일자 μ• ν”Œ 8-K κ³΅μ‹œ - μ£Όμš” κΈ°μ—… 변동사항 보고

μ£Όμš” λ‚΄μš©:
1. μƒˆ 이사 μž„λͺ… (Item 5.02)
   - Alex Gorsky (Johnson & Johnson μ „ CEO) μž„λͺ…
   - μ§€λͺ… 및 기업지배ꡬ쑰 μœ„μ›νšŒ μ†Œμ†
   - μ—°κ°„ ν˜„κΈˆ 보수: $100,000 (뢄기별 μ§€κΈ‰)
   - 초기 주식 λΆ€μ—¬: 486 RSU

2. μ •κ΄€ κ°œμ • (Item 5.03)
   - μ΄μ‚¬νšŒ 정원 8λͺ… β†’ 9λͺ… ν™•λŒ€

3. ν—¬μŠ€μΌ€μ–΄ μ „λ¬Έμ„± κ°•ν™”
   - Gorsky의 J&J CEO κ²½ν—˜μ„ ν†΅ν•œ ν—¬μŠ€μΌ€μ–΄ λΆ„μ•Ό μ „λ¬Έμ„± 확보
```

---

**Single Prompt Challenge #2: Apple 2021 DEF 14A Analysis**
```
2021 μ• ν”Œ 14def λ‹€ μˆ™μ§€ν•΄μ€˜
```

**Expected Workflow:**
1. Downloads Apple's (CIK: 0000320193) DEF 14A filing for 2021
2. Converts HTML to PDF
3. Parses PDF to markdown (creates multiple chunks due to large size)
4. Auto-recovery: Uses `list_markdown_files` to locate chunks
5. Reads all chunks sequentially with `read_markdown_chunk`
6. Comprehends the entire proxy statement

**Result:**
```
βœ… Successfully completed with automatic recovery workflow

Downloaded: html/apple_2021_def14a/apple_20210223.htm
Converted: pdf/apple_2021_def14a.pdf
Parsed: 5 markdown chunks (total ~213KB)
  - apple_2021_def14a_chunk_1_of_5.md (50KB)
  - apple_2021_def14a_chunk_2_of_5.md (50KB)
  - apple_2021_def14a_chunk_3_of_5.md (50KB)
  - apple_2021_def14a_chunk_4_of_5.md (50KB)
  - apple_2021_def14a_chunk_5_of_5.md (12KB)

⚠️ Known Issue (UNFIXED):
   Problem: read_as_markdown returned "No result received from client-side tool execution"
   
   Server Status: βœ… NORMAL
   - Server successfully creates chunk files
   - Server logs confirm normal operation
   - Server returns chunk file list
   
   Claude Status: ❌ ERROR
   - Tool execution appears successful in Claude UI
   - But no response content received
   - Error: "No result received from client-side tool execution"
   
   Root Cause: MCP protocol communication issue
   - Server β†’ Claude transmission fails
   - NOT a server logic problem
   - Possibly related to response format or MCP protocol limitation
   
   Status: UNFIXED (architectural/protocol-level issue)

βœ… Workaround Applied:
   Used alternative tool chain to bypass the issue:
   
   Step 1: read_as_markdown (Expected to fail, but creates chunk files)
   └─ Server creates 5 chunk files
   └─ Claude shows "No result" error
   
   Step 2: list_markdown_files (Discovery)
   └─ Lists all markdown files in markdown/ directory
   └─ Confirms 5 chunk files were created
   
   Step 3: read_markdown_chunk Γ— 5 (Individual retrieval)
   └─ Reads apple_2021_def14a_chunk_1_of_5.md
   └─ Reads apple_2021_def14a_chunk_2_of_5.md
   └─ Reads apple_2021_def14a_chunk_3_of_5.md
   └─ Reads apple_2021_def14a_chunk_4_of_5.md
   └─ Reads apple_2021_def14a_chunk_5_of_5.md
   
   Result: βœ… All content successfully retrieved

Summary:
2021λ…„ 2μ›” 23일 μ• ν”Œ μ—°λ‘€ 주주총회 κ³΅μ‹œ (81νŽ˜μ΄μ§€)

μ£Όμš” λ‚΄μš©:
1. μ—°λ‘€ 주주총회
   - μΌμ‹œ: 2021λ…„ 2μ›” 23일 μ˜€μ „ 9μ‹œ (PST)
   - 가상 회의 (COVID-19 λŒ€μ‘)
   - 8λͺ… 이사 μ„ μΆœ μ•ˆκ±΄

2. μ΄μ‚¬νšŒ ꡬ성 (λ‹€μ–‘μ„± κ°•ν™”)
   - 이사 8λͺ… 후보: Bell, Cook, Gore, Jung, Levinson, Lozano, Sugar, Wagner
   - 50% μ—¬μ„± 리더십, 50% μ†Œμˆ˜μ§‘λ‹¨ μΆœμ‹ 
   - Monica Lozano μ‹ κ·œ 이사 (2021.1μ›” μž„λͺ…)

3. μž„μ› 보상 ν”„λ‘œκ·Έλž¨
   - CEO Tim Cook: 2020λ…„ 총 보상 $14.8M
     * κΈ°λ³ΈκΈ‰: $3M
     * ν˜„κΈˆ μΈμ„Όν‹°λΈŒ: $10.7M (λͺ©ν‘œ λŒ€λΉ„ 179% 달성)
     * 기타: $1M (λ³΄μ•ˆ, 항곡기 μ‚¬μš©)
   - NEO 평균 보상: ~$26M
     * κΈ°λ³ΈκΈ‰: $1M
     * ν˜„κΈˆ μΈμ„Όν‹°λΈŒ: $3.6M
     * 주식 보상: $21.7M (μ‹œκ°„/μ„±κ³Ό 기반 RSU)
   
4. 2020λ…„ 재무 μ„±κ³Ό
   - 맀좜: $274.5B (μ „λ…„ λŒ€λΉ„ +6%)
   - μ£Όλ‹Ήμˆœμ΄μ΅: $3.28 (+10%)
   - μ˜μ—…μ΄μ΅: $66.3B (+4%)
   - μ£Όμ£Όν™˜μ›: $90B (λ°°λ‹Ή+μžμ‚¬μ£Ό)
   - 총주주수읡λ₯ (TSR): 1λ…„ 107%, 3λ…„ 203%, 5λ…„ 324%

5. 2021λ…„ 변경사항
   - ESG μˆ˜μ • μš”μ†Œ μΆ”κ°€: Apple Values 기반 평가λ₯Ό μ—°κ°„ μΈμ„Όν‹°λΈŒμ— 반영
   - CEO μž₯κΈ° 주식 보상: Tim Cookμ—κ²Œ 2011λ…„ 이후 첫 μ‹ κ·œ RSU λΆ€μ—¬
     (10λ…„κ°„ μ‹œμ΄ $1.5T 증가, TSR 867% μ„±κ³Ό 인정)

6. κΈ°μ—… κ°€μΉ˜ μ‹€μ²œ (2020λ…„)
   - COVID-19 λŒ€μ‘: 마슀크 3천만개, μ•ˆλ©΄λ³΄ν˜Έκ΅¬ 1천만개 κΈ°λΆ€
   - νƒ„μ†Œμ€‘λ¦½ λͺ©ν‘œ: 2030λ…„κΉŒμ§€ 곡급망 포함 100% 달성
   - 인쒅평등: $100M 투자 (ꡐ윑, κ²½μ œν‰λ“±, ν˜•μ‚¬μ‚¬λ²• 개혁)
   - μ£Όνƒμœ„κΈ° ν•΄κ²°: μΊ˜λ¦¬ν¬λ‹ˆμ•„ 주택 ν”„λ‘œμ νŠΈ $400M 투자

7. μ£Όμ£Ό μ œμ•ˆ (μ•ˆκ±΄ 4, 5)
   - μ•ˆκ±΄ 4: λŒ€λ¦¬μ ‘κ·ΌκΆŒ κ°œμ • (이사 μ΅œμ†Œ 2λͺ… λ˜λŠ” 20%) - μ΄μ‚¬νšŒ λ°˜λŒ€
   - μ•ˆκ±΄ 5: μž„μ› 보상에 κΈ‰μ—¬ λΉ„μœ¨ κ³ λ € - μ΄μ‚¬νšŒ λ°˜λŒ€
   - μ΄μ‚¬νšŒ ꢌ고: μ•ˆκ±΄ 1,2,3 μ°¬μ„± / μ•ˆκ±΄ 4,5 λ°˜λŒ€

8. 감사 정보
   - 독립 감사인: Ernst & Young LLP (2009λ…„λΆ€ν„°)
   - 2020λ…„ 감사 λΉ„μš©: $22.2M
     * 감사: $17.6M, 세무: $3.4M, 기타: $1.2M

**Key Insights:**
- 81νŽ˜μ΄μ§€ λΆ„λŸ‰μ˜ μƒμ„Έν•œ 기업지배ꡬ쑰 및 보상 μ •μ±… κ³΅μ‹œ
- κ°•λ ₯ν•œ 재무 성과와 μ£Όμ£Όκ°€μΉ˜ 창좜 (맀좜/이읡 μ„±μž₯, 높은 TSR)
- λ‹€μ–‘μ„±κ³Ό ESGλ₯Ό κ°•ν™”ν•˜λŠ” μ΄μ‚¬νšŒ 및 κ²½μ˜μ§„ ꡬ성
- 투λͺ…ν•œ μž„μ› 보상 체계 (성과연동, μž₯κΈ° μΈμ„Όν‹°λΈŒ 쀑심)
- 2021λ…„λΆ€ν„° ESG μš”μ†Œλ₯Ό 보상에 직접 λ°˜μ˜ν•˜λŠ” 선진적 μ ‘κ·Ό
```

---

## πŸ”€ Markdown Chunking System

**Why Chunking?**

MCP client-side (Claude) has response size limitations. Large SEC filings (10-K reports often exceed 200,000+ characters) cannot be transmitted from the server to Claude in a single tool response, even though the server successfully processes and returns them.

**How it works:**
1. When `read_as_markdown` parses a large PDF (>50,000 chars)
2. Content is automatically split into 50,000-character chunks
3. Each chunk saved as: `{filename}_chunk_{N}_of_{total}.md`
4. Tool returns chunk file list instead of full text
5. Claude can then call `read_markdown_chunk` to read specific chunks
6. Use `list_markdown_files` to see all available chunks

**Benefits:**
- Handles documents of any size
- Complies with MCP response limits
- Allows selective reading of document sections
- Preserves full content without truncation

---

## πŸ› οΈ MCP Tools

### 1. `read_as_markdown`

Parse PDF files to Markdown format.

**Parameters:**
- `input_file_path` (string): PDF file path relative to `pdf/` directory

**Example:**
```python
Input: "Amazon.com Inc. - Form 10-K. For the Fiscal Year Ended 2024-12-31.pdf"
Output: Markdown text content (or chunk file list for large documents)
```

**Supported Parsers:**
- **LlamaCloud LlamaExtract** (primary): High-quality parsing with API

**πŸ“¦ Automatic Chunking:**
- Documents >50,000 characters are automatically split into chunks
- Each chunk saved as separate `.md` file in `markdown/` directory
- Returns list of chunk files instead of full text
- Use `read_markdown_chunk` tool to read individual chunks

**πŸ”§ How it works (Code Logic):**
1. **Path Resolution**: Converts relative path to absolute path in `pdf/` directory
2. **File Validation**: Checks if PDF file exists
3. **API Key Validation**: Verifies LLAMA_CLOUD_API_KEY is configured
4. **LlamaCloud Parsing**: 
   - Initializes `LlamaParse` client with API key
   - Calls `load_data()` to parse PDF to markdown
   - Runs synchronously in thread pool to avoid blocking
5. **Chunking Logic**:
   - Checks if markdown length > 50,000 characters
   - Splits into 50KB chunks: `chunks = [text[i:i+50000] for i in range(0, len(text), 50000)]`
   - Saves each chunk: `{filename}_chunk_{N}_of_{total}.md`
6. **Return Strategy**:
   - Small files (≀50K): Returns markdown text directly
   - Large files (>50K): Returns formatted message with chunk file list
   - ⚠️ Claude may show "No result" for large files (MCP client limitation)

**Code Reference:**
```python
# Main workflow
markdown_text = await _parse_pdf_with_llamacloud(file_path)
chunk_files = await _save_markdown_chunks(markdown_text, base_filename)

# Chunking implementation
chunk_size = 50000
total_chunks = (total_length + chunk_size - 1) // chunk_size

for i in range(total_chunks):
    start_idx = i * chunk_size
    end_idx = min(start_idx + chunk_size, total_length)
    chunk_text = markdown_text[start_idx:end_idx]
```

---

### 2. `html_to_pdf`

Convert HTML/iXBRL files to PDF format.

**Parameters:**
- `input_file_path` (string): HTML file path relative to `html/` directory
- `output_file_path` (string): PDF output path relative to `pdf/` directory

**Example:**
```python
Input: "html/Form 10-K/amzn-20241231.htm"
Output: "pdf/amazon_10k_2024.pdf"
```

**πŸ”§ How it works (Code Logic):**
1. **Path Resolution**: Converts HTML input path and PDF output path to absolute paths
2. **File Validation**: Checks HTML file exists, creates output directory if needed
3. **Playwright Browser Launch**: Launches Chromium browser in headless mode
4. **HTML Loading**: Loads local HTML file using `file://` protocol
   - `wait_until="networkidle"`: Waits for all network requests to complete (CSS/images)
   - 60-second timeout for large SEC documents
5. **PDF Generation**: Calls `page.pdf()` with optimized settings
   - Format: Letter (US standard paper size)
   - Background: Enabled (preserves SEC document styling)
   - Margins: 0.5 inches on all sides (improved readability)
6. **File Stabilization**: Waits for file write completion
   - Checks file size stabilization (10 checks over 2 seconds)
   - Prevents empty file errors

**Code Reference:**
```python
await page.goto(file_url, wait_until="networkidle", timeout=60000)
await page.pdf(
    path=str(output_path),
    format="Letter",
    print_background=True,
    margin={"top": "0.5in", "right": "0.5in", 
            "bottom": "0.5in", "left": "0.5in"}
)
```

---

### 3. `download_sec_filing`

Download SEC filings from EDGAR.

**Parameters:**
- `cik` (string): Company CIK number (e.g., "0001018724")
- `year` (integer): Filing year (2021-2025)
- `filing_type` (string): "8-K" | "10-Q" | "10-K" | "DEF 14A"
- `output_dir_path` (string): Output directory relative to `html/`

**Example:**
```python
CIK: "0001018724"
Year: 2024
Filing Type: "10-K"
Output: "amzn_2024_10k"
Result: "html/amzn_2024_10k/amzn-20241231.htm"
```

**πŸ”§ How it works (Code Logic):**
1. **Input Validation**: 
   - Validates year range (2021-2025)
   - Normalizes CIK: Removes leading zeros, pads to 10 digits
2. **SEC API Request**: 
   - URL: `https://data.sec.gov/submissions/CIK{cik_padded}.json`
   - Uses curl_cffi with Chrome impersonation (prevents 403 errors)
   - Applies rate limiting: 100ms delay between requests (SEC 10req/s limit)
3. **Filing Search**:
   - Matches filings by `reportDate` (falls back to `filingDate` if unavailable)
   - Filters by `form_types` for specified filing_type
   - Selects most recent filing (sorts by reportDate descending)
4. **Primary Document Naming**:
   - Extracts ticker from `output_dir_path` (e.g., `amzn_2024_8k` β†’ `amzn`)
   - Formats date: `YYYY-MM-DD` β†’ `YYYYMMDD`
   - Final filename: `{ticker}_{date}.{ext}` (e.g., `amzn_20241231.htm`)
5. **Full Archive Download**:
   - Fetches `index.json` for complete file list
   - Downloads all files with rate limiting
   - Renames primary document automatically
6. **Return**: Returns relative path of downloaded primary document

**Code Reference:**
```python
# Rate limiting
await sec_rate_limiter.wait()

# CIK normalization
cik_normalized = str(cik).lstrip("0")
cik_padded = cik_normalized.zfill(10)

# Filing search
matching_filings.sort(key=lambda x: x["reportDate"] or x["filingDate"], reverse=True)
target_filing = matching_filings[0]
```

---

### 4. `list_markdown_files`

List all available markdown files in the `markdown/` directory.

**Parameters:**
- None

**Example:**
```python
Output: List of markdown files with size and modification date
```

**πŸ”§ How it works (Code Logic):**
1. **Directory Scan**: Scans all `.md` files in `markdown/` directory
2. **File Metadata Collection**:
   - Collects filename, file size (bytes), modification time
   - Uses `Path.glob("*.md")` for pattern matching
3. **Sorting**: Sorts by modification time descending (newest first)
4. **Format Output**:
   - Bold filename
   - Size: KB + bytes display
   - Modification time: `YYYY-MM-DD HH:MM:SS` format
5. **Usage Hint**: Adds instruction to use `read_markdown_chunk` tool

**Code Reference:**
```python
for file_path in MARKDOWN_DIR.glob("*.md"):
    stat = file_path.stat()
    files.append({
        'name': file_path.name,
        'size': stat.st_size,
        'modified': stat.st_mtime
    })

files.sort(key=lambda x: x['modified'], reverse=True)
```

---

### 5. `read_markdown_chunk`

Read a specific markdown chunk file (for large documents).

**Parameters:**
- `file_path` (string): Markdown file path relative to base directory (e.g., "markdown/amazon_10k_2024_chunk_1_of_5.md")

**Example:**
```python
Input: "markdown/amazon_10k_2024_chunk_1_of_5.md"
Output: Markdown content of that specific chunk
```

**πŸ”§ How it works (Code Logic):**
1. **Path Resolution**: 
   - Handles `markdown/` prefix
   - Converts to absolute path
2. **File Validation**: Checks if markdown file exists
3. **Async File Read**: Uses `aiofiles` for non-blocking read (UTF-8 encoding)
4. **Response Formatting**:
   - Adds file path and size header
   - Adds separator line (`---`)
   - Appends markdown content
5. **Return**: Returns formatted markdown text with metadata

**Code Reference:**
```python
async with aiofiles.open(full_path, "r", encoding="utf-8") as f:
    content = await f.read()

result = f"πŸ“„ **File:** `{file_path}`\n"
result += f"πŸ“Š **Size:** {file_size:,} characters\n\n"
result += "---\n\n"
result += content
```

---




## πŸ“ Project Structure

```
SEC EDGAR filings MCP/
β”œβ”€β”€ server.py              # Main MCP server
β”œβ”€β”€ requirements.txt       # Python dependencies
β”œβ”€β”€ .env.example          # Environment template
β”œβ”€β”€ .env                  # Your config (not in git)
β”œβ”€β”€ .gitignore           # Git ignore rules
β”œβ”€β”€ README.md            # This file
β”œβ”€β”€ Dockerfile           # Docker config
β”œβ”€β”€ docker-compose.yml   # Docker Compose config
β”œβ”€β”€ pdf/                 # PDF files directory
β”‚   └── (downloaded/test PDFs)
└── html/                # HTML/iXBRL files
    └── (downloaded filings)
```

## 🏒 Common Company CIKs

| Company | CIK |
|---------|-----|
| Amazon | 0001018724 |
| Apple | 0000320193 |
| Microsoft | 0000789019 |
| Alphabet (Google) | 0001652044 |
| Meta (Facebook) | 0001326801 |
| Tesla | 0001318605 |
| NVIDIA | 0001045810 |

Find more: https://www.sec.gov/edgar/searchedgar/companysearch.html

## πŸ” Testing

### With MCP Inspector

```bash
npm install -g @modelcontextprotocol/inspector
npx @modelcontextprotocol/inspector python server.py
```


### Manual Testing

```bash
# Test imports
python -c "import fastmcp; print('FastMCP OK')"
python -c "from playwright.async_api import async_playwright; print('Playwright OK')"

# Test server startup
python server.py
```

## 🐳 Docker Setup Details

### Quick Start (Pre-built Image - Recommended)

**⚑ Fastest deployment (~2 seconds):**

```bash
# 1. Pull pre-built image from Docker Hub
docker-compose pull

# 2. Start container
docker-compose up -d

# 3. Verify
docker ps
```

**Image:** `momotime7584/sec-edgar-mcp:latest`
- Size: ~856 MB
- Pull time: 1-2 seconds
- Includes: Python 3.11, LlamaCloud, Playwright, all dependencies

### Useful Commands

```bash
# View logs (for debugging)
docker-compose logs -f

# Stop container (after testing)
docker-compose down

# Check environment variables
docker exec sec-edgar-mcp-server env | grep -E "(LLAMA|SEC)"
```

---

## ⚠️ Troubleshooting

### "PDF file not found"
- Verify file is in `pdf/` directory
- Check file name (case-sensitive on Linux/macOS)
- Use exact file name from assignment

### "HTML file not found"
- Extract HTML files to `html/` directory
- Maintain subdirectory structure if needed
- Check path separators (`/` vs `\`)

### "LlamaCloud API Error"
- Verify API key in `.env`
- Check API credits/quota
- Server falls back to Docling automatically

### "Rate limit exceeded"
- Server handles SEC rate limiting automatically
- Wait 1-2 seconds if error persists
- Check User-Agent is properly configured

### "Playwright browser not installed"
```bash
playwright install chromium
```

### Claude Desktop not showing tools
- Restart Claude Desktop completely
- Verify JSON configuration syntax
- Check server path is correct
- Look for errors in Claude Desktop logs

**Important**: This project is for educational purposes. Respect SEC's usage policies and rate limits when accessing EDGAR data.