Skip to main content
Glama
henrysouchien

edgar-mcp

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
NameRequiredDescriptionDefault
tickerYes
yearYes
quarterYes
sectionsNo
formatNosummary
max_wordsNo
tables_onlyNo
outputNofile

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • _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),
            },
        }
  • 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)
  • 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,
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions parsing and returning narrative/tables with metadata, but lacks details on permissions, rate limits, data sources, or error handling. For a tool with 8 parameters and no annotation coverage, this is a significant gap in transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured sentence that efficiently conveys the core functionality. It is front-loaded with the main action and output, with no wasted words. Every part of the sentence earns its place by specifying the source (SEC filings), action (parse), and result (narrative/tables with metadata).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (8 parameters, no annotations, but with an output schema), the description is moderately complete. The output schema likely covers return values, reducing the need for output details in the description. However, for a parsing tool with many configuration options, more guidance on parameter usage and behavioral context would improve completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for undocumented parameters. It only vaguely references 'sections' and 'format' without explaining what specific sections are available (e.g., 'Management Discussion'), what 'summary' vs 'full' entails, or the purpose of parameters like 'tables_only' and 'output'. The description adds minimal value beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: parsing qualitative sections from SEC 10-K or 10-Q filings and returning narrative/tables with metadata. It specifies the verb ('parse'), resource ('qualitative sections from SEC 10-K or 10-Q filings'), and output type. However, it doesn't explicitly differentiate from sibling tools like 'get_filings' or 'get_financials', which likely retrieve different aspects of filings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention sibling tools like 'get_filings' (which might retrieve entire filings) or 'get_financials' (which might focus on quantitative data), leaving the agent without context for tool selection. Usage is implied only by the purpose statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/henrysouchien/edgar-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server