get_filing_sections
Extract specific narrative sections and tables from SEC 10-K or 10-Q filings for financial analysis and research.
Instructions
Parse qualitative sections from SEC 10-K or 10-Q filings and return narrative/tables with metadata.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ticker | Yes | ||
| year | Yes | ||
| quarter | Yes | ||
| sections | No | ||
| format | No | summary | |
| max_words | No | ||
| tables_only | No | ||
| output | No | file |
Implementation Reference
- edgar_mcp/server.py:484-624 (handler)_proxy_get_filing_sections: Core handler implementation that proxies requests to remote EDGAR API, processes sections data, handles tables_only filtering, manages file output mode, and writes markdown files when output='file'.
def _proxy_get_filing_sections(args: dict) -> dict: """Proxy filing sections to remote API, with local file-write for output='file'.""" output_mode = args.get("output", "file") sections_list = args.get("sections") tables_only = args.get("tables_only", False) # Build remote API params params = { "ticker": args["ticker"], "year": args["year"], "quarter": args["quarter"], "format": args.get("format", "summary"), } if sections_list: params["sections"] = ",".join(sections_list) if output_mode == "file": # Fetch full untruncated text for file output params["format"] = "full" params["max_words"] = "none" else: max_words = args.get("max_words", 3000) params["max_words"] = str(max_words) if max_words is not None else "none" result = _call_api("/api/sections", params) if result.get("status") != "success": return result # Normalize section-level table counts for both inline and file modes. for section in result.get("sections", {}).values(): if not isinstance(section, dict): continue tables = section.get("tables", []) or [] nonempty_tables = [t for t in tables if (t or "").strip()] section["table_count"] = len(nonempty_tables) total_tables = sum( s.get("table_count", 0) for s in result.get("sections", {}).values() if isinstance(s, dict) ) if not isinstance(result.get("metadata"), dict): result["metadata"] = {} result["metadata"]["total_table_count"] = total_tables # Strip narrative text if tables_only requested if tables_only: for section in result.get("sections", {}).values(): if not isinstance(section, dict): continue section.pop("text", None) table_words = 0 for table in section.get("tables", []) or []: table_text = (table or "").strip() if table_text: table_words += len(table_text.split()) section["word_count"] = table_words if output_mode != "file": return result # Write sections to local markdown file ticker = _safe_filename_part(str(args["ticker"]).upper(), "ticker") year = int(args["year"]) quarter = int(args["quarter"]) filing_type = result.get("filing_type", "") sections_data = result.get("sections", {}) FILE_OUTPUT_DIR.mkdir(parents=True, exist_ok=True) # Build filename if sections_list: safe_keys = [_safe_filename_part(key, "section") for key in sorted(sections_list)] keys_part = "_".join(safe_keys) filename = f"{ticker}_{quarter}Q{year % 100:02d}_{keys_part}.md" else: filename = f"{ticker}_{quarter}Q{year % 100:02d}_sections.md" file_path = (FILE_OUTPUT_DIR / filename).resolve() root_dir = FILE_OUTPUT_DIR.resolve() if not file_path.is_relative_to(root_dir): return {"status": "error", "message": "Invalid output path"} if _deadline_expired(args): return {"status": "error", "message": "Request timed out before file output could be written"} # Build markdown total_words = sum(s.get("word_count", 0) for s in sections_data.values()) section_keys = ", ".join(sections_data.keys()) if sections_data else "none" lines = [ f"# {ticker} {filing_type} - Q{quarter} FY{year}: Filing Sections", f"> Sections: {section_keys} | Total words: {total_words:,} | Total tables: {total_tables:,}", "---", ] for section in sections_data.values(): header = section.get("header", "Unknown Section") lines.append(f"## SECTION: {header}") lines.append(f"**Word count:** {section.get('word_count', 0):,}") lines.append(f"**Table count:** {section.get('table_count', 0):,}") if not tables_only: text = section.get("text", "").strip() if text: lines.append(text) tables = section.get("tables", []) if tables: lines.append("### TABLES") for table in tables: table_text = (table or "").strip() if table_text: lines.append(table_text) lines.append("---") if lines and lines[-1] == "---": lines.pop() file_path.write_text("\n".join(lines).strip() + "\n", encoding="utf-8") # Return summary (no full text inline) + file_path summary_sections = { key: { "header": s.get("header"), "word_count": s.get("word_count", 0), "table_count": s.get("table_count", 0), } for key, s in sections_data.items() } return { "status": "success", "ticker": ticker, "year": year, "quarter": quarter, "filing_type": filing_type, "output": "file", "file_path": str(file_path.resolve()), "hint": "Use Read tool with file_path. Grep '^## SECTION:' for anchors.", "sections": summary_sections, "sections_found": list(sections_data.keys()), "metadata": { "total_word_count": total_words, "total_words": total_words, "total_table_count": total_tables, "section_count": len(sections_data), }, } - edgar_mcp/server.py:828-854 (registration)get_filing_sections: Public MCP tool definition decorated with @mcp.tool() that exposes the filing sections API. Defines input parameters (ticker, year, quarter, sections, format, max_words, tables_only, output) and delegates to _run_tool_guarded.
@mcp.tool() async def get_filing_sections( ticker: str, year: int, quarter: int, sections: list[str] | None = None, format: Literal["summary", "full"] = "summary", max_words: int | None = 3000, tables_only: bool = False, output: Literal["inline", "file"] = "file", ) -> dict: """ Parse qualitative sections from SEC 10-K or 10-Q filings and return narrative/tables with metadata. """ args = { "ticker": ticker, "year": year, "quarter": quarter, "format": format, "max_words": max_words, "tables_only": tables_only, "output": output, } if sections: args["sections"] = sections return await _run_tool_guarded("get_filing_sections", args) - edgar_mcp/server.py:647-663 (registration)Tool registration in _TOOL_DISPATCH and _TOOL_TIMEOUT dictionaries mapping 'get_filing_sections' to its handler function (_proxy_get_filing_sections) and timeout value (60 seconds).
_TOOL_DISPATCH = { "get_filings": _proxy_get_filings, "get_financials": _proxy_get_financials, "get_metric": _proxy_get_metric, "list_metrics": _proxy_list_metrics, "search_metrics": _proxy_search_metrics, "get_filing_sections": _proxy_get_filing_sections, } _TOOL_TIMEOUT = { "get_filings": 30, "get_financials": 60, "get_metric": 30, "list_metrics": 45, "search_metrics": 45, "get_filing_sections": 60, }