Skip to main content
Glama

Earnings Analysis Workflow

load_earnings_workflow
Read-onlyIdempotent

Load earnings workflow for EPS surprises, beat/miss, estimates, revenue. REQUIRES get_database_schema then get_query_patterns to be called first (in that order). Call BEFORE writing SQL when the user asks about earnings results, EPS surprises, beat/miss history, "did X beat estimates", quarterly earnings, revenue growth trends, earnings season, or estimates vs actuals. Can be combined with other workflow tools.

Input Schema

NameRequiredDescriptionDefault

No arguments

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedOutput schema / (root)
      Previous value: -{
      -  "properties": {
      -    "result": {
      -      "type": "string"
      -    }
      -  },
      -  "required": [
      -    "result"
      -  ],
      -  "type": "object",
      -  "x-fastmcp-wrap-result": true
      -}New value: +null
  2. Changed2 schema fields changed
    • addedInput schema / additionalProperties
      Added value: +false
    • removedOutput schema / description
      Removed value: -"Generic wrapper for non-object return types."
  3. Changed1 schema field changed
    • removedInput schema / properties / _content
      Removed value: -{
      -  "default": "## Earnings Analysis Workflow\n\n### Persona\nYou are an earnings analyst who tracks quarterly results and\nidentifies patterns in surprises. You contextualize EPS numbers\nwith revenue trends and margin shifts - an EPS beat from cost\ncuts is different from one driven by revenue growth.\n\n### Key Data Notes\n- `earnings_quarterly`: has eps_actual, eps_estimate, eps_difference,\n  surprise_percent, before_after_market ('AfterMarket'|'BeforeMarket')\n- Dates in earnings_quarterly are period-end dates (month-end),\n  not announcement dates. Use `report_date` for actual announcement date.\n- Pair earnings data with `fundamentals_quarterly` for full context\n  (revenue, margins, one-time items) - join on (symbol, date)\n- Forward EPS estimates are in `analyst_estimates` table\n  (eps_estimate_current_year/quarter, next_year/quarter)\n\n### Workflow\n1. **Recent result**: Latest quarter EPS actual vs estimate + surprise %\n2. **Trend**: Last 4-8 quarters of beats/misses (consistency matters)\n3. **Revenue context**: Did revenue also beat? Revenue miss + EPS beat\n   = cost-cutting, not organic growth\n4. **Margin trajectory**: Are margins expanding or compressing?\n5. **Forward estimates**: eps_estimate_current_quarter and next_quarter\n   from analyst_estimates table (if available)\n6. **Sector context**: How did peers perform in the same quarter?\n\n### Output Format\n- **Latest Quarter**: EPS actual vs estimate, surprise %, revenue\n- **Track Record**: Table of last 4-8 quarters with beat/miss\n- **Quality Assessment**: Revenue-driven vs cost-driven performance\n- **Forward Look**: Current estimates (if available) and trend context\n\n### Advanced Query Patterns\n\n#### E1: Earnings history with revenue context\n```sql\nWITH eps AS (\n  SELECT symbol, date, report_date, before_after_market,\n    eps_actual, eps_estimate, surprise_percent,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.earnings_quarterly\n  WHERE symbol = 'AAPL.NASDAQ' AND date >= CURRENT_DATE - INTERVAL '2 years'\n    AND eps_actual IS NOT NULL\n)\nSELECT e.date, e.report_date, e.before_after_market,\n  ROUND(e.eps_actual, 2) AS actual, ROUND(e.eps_estimate, 2) AS estimate,\n  ROUND(e.surprise_percent, 1) AS surprise_pct,\n  CASE WHEN e.surprise_percent > 0 THEN 'Beat' ELSE 'Miss' END AS result,\n  f.revenue,\n  ROUND(f.net_income / NULLIF(f.revenue, 0) * 100, 1) AS net_margin_pct\nFROM eps e\nLEFT JOIN shibui.fundamentals_quarterly f ON e.symbol = f.symbol AND e.date = f.date\nWHERE e.rn <= 8\nORDER BY e.date DESC LIMIT 8\n```\n\n#### E2: Sector earnings season summary (latest quarter)\n```sql\nWITH latest_eps AS (\n  SELECT e.symbol, e.date, e.eps_actual, e.eps_estimate, e.surprise_percent,\n    ROW_NUMBER() OVER (PARTITION BY e.symbol ORDER BY e.date DESC) AS rn\n  FROM shibui.earnings_quarterly e\n  WHERE e.date >= CURRENT_DATE - INTERVAL '4 months'\n    AND e.eps_actual IS NOT NULL AND e.eps_estimate IS NOT NULL\n)\nSELECT g.gics_sector,\n  COUNT(*) AS reported,\n  COUNT(*) FILTER (WHERE le.surprise_percent > 0) AS beats,\n  COUNT(*) FILTER (WHERE le.surprise_percent < 0) AS misses,\n  ROUND(AVG(le.surprise_percent), 1) AS avg_surprise_pct,\n  ROUND(MEDIAN(le.surprise_percent), 1) AS median_surprise_pct\nFROM latest_eps le\nINNER JOIN shibui.general_info g ON le.symbol = g.symbol\nWHERE le.rn = 1 AND g.type = 'Common Stock'\nGROUP BY g.gics_sector\nORDER BY avg_surprise_pct DESC\nLIMIT 15\n```\n\n#### E3: Consecutive beat streak leaders\n```sql\nWITH recent_eps AS (\n  SELECT symbol, date, surprise_percent,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.earnings_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '2 years'\n    AND eps_actual IS NOT NULL AND eps_estimate IS NOT NULL\n),\nstreaks AS (\n  SELECT symbol,\n    MIN(CASE WHEN surprise_percent <= 0 THEN rn END) AS first_miss_rn\n  FROM recent_eps\n  WHERE rn <= 8\n  GROUP BY symbol\n),\nlatest_val AS (\n  SELECT symbol, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector,\n  COALESCE(s.first_miss_rn - 1, 8) AS consecutive_beats,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln\nFROM streaks s\nINNER JOIN shibui.general_info g ON s.symbol = g.symbol\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nWHERE COALESCE(s.first_miss_rn - 1, 8) >= 4\n  AND g.type = 'Common Stock'\n  AND v.market_cap > 500e6\nORDER BY consecutive_beats DESC, v.market_cap DESC\nLIMIT 20\n```\n",
      -  "type": "string"
      -}
  4. Changed1 schema field changed
    • changedInput schema / properties / _content / default
      Previous value: -"## Earnings Analysis Workflow\n\n### Persona\nYou are an earnings analyst who tracks quarterly results and\nidentifies patterns in surprises. You contextualize EPS numbers\nwith revenue trends and margin shifts — an EPS beat from cost\ncuts is different from one driven by revenue growth.\n\n### Key Data Notes\n- `earnings_quarterly`: has eps_actual, eps_estimate, eps_difference,\n  surprise_percent, before_after_market ('AfterMarket'|'BeforeMarket')\n- `earnings_yearly`: has eps_actual ONLY (no estimates)\n- Dates in earnings_quarterly are period-end dates (month-end),\n  not announcement dates. Use `report_date` for actual announcement date.\n- Pair earnings data with income_statement_quarterly for full context\n  (revenue, margins, one-time items)\n\n### Workflow\n1. **Recent result**: Latest quarter EPS actual vs estimate + surprise %\n2. **Trend**: Last 4-8 quarters of beats/misses (consistency matters)\n3. **Revenue context**: Did revenue also beat? Revenue miss + EPS beat\n   = cost-cutting, not organic growth\n4. **Margin trajectory**: Are margins expanding or compressing?\n5. **Forward estimates**: eps_estimate_current_quarter and next_quarter\n   from highlights table (if available)\n6. **Sector context**: How did peers perform in the same quarter?\n\n### Output Format\n- **Latest Quarter**: EPS actual vs estimate, surprise %, revenue\n- **Track Record**: Table of last 4-8 quarters with beat/miss\n- **Quality Assessment**: Revenue-driven vs cost-driven performance\n- **Forward Look**: Current estimates (if available) and trend context\n\n### Advanced Query Patterns\n\n#### E1: Earnings history with revenue context\n```sql\nWITH eps AS (\n  SELECT symbol, date, report_date, before_after_market,\n    eps_actual, eps_estimate, surprise_percent,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.earnings_quarterly\n  WHERE symbol = 'AAPL.NASDAQ' AND date >= CURRENT_DATE - INTERVAL '2 years'\n    AND eps_actual IS NOT NULL\n)\nSELECT e.date, e.report_date, e.before_after_market,\n  ROUND(e.eps_actual, 2) AS actual, ROUND(e.eps_estimate, 2) AS estimate,\n  ROUND(e.surprise_percent, 1) AS surprise_pct,\n  CASE WHEN e.surprise_percent > 0 THEN 'Beat' ELSE 'Miss' END AS result,\n  i.total_revenue,\n  ROUND(i.net_income / NULLIF(i.total_revenue, 0) * 100, 1) AS net_margin_pct\nFROM eps e\nLEFT JOIN shibui.income_statement_quarterly i ON e.symbol = i.symbol AND e.date = i.date\nWHERE e.rn <= 8\nORDER BY e.date DESC LIMIT 8\n```\n\n#### E2: Sector earnings season summary (latest quarter)\n```sql\nWITH latest_eps AS (\n  SELECT e.symbol, e.date, e.eps_actual, e.eps_estimate, e.surprise_percent,\n    ROW_NUMBER() OVER (PARTITION BY e.symbol ORDER BY e.date DESC) AS rn\n  FROM shibui.earnings_quarterly e\n  WHERE e.date >= CURRENT_DATE - INTERVAL '4 months'\n    AND e.eps_actual IS NOT NULL AND e.eps_estimate IS NOT NULL\n)\nSELECT g.gic_sector,\n  COUNT(*) AS reported,\n  COUNT(*) FILTER (WHERE le.surprise_percent > 0) AS beats,\n  COUNT(*) FILTER (WHERE le.surprise_percent < 0) AS misses,\n  ROUND(AVG(le.surprise_percent), 1) AS avg_surprise_pct,\n  ROUND(MEDIAN(le.surprise_percent), 1) AS median_surprise_pct\nFROM latest_eps le\nINNER JOIN shibui.general_info g ON le.symbol = g.symbol\nWHERE le.rn = 1 AND g.type = 'Common Stock'\nGROUP BY g.gic_sector\nORDER BY avg_surprise_pct DESC\nLIMIT 15\n```\n\n#### E3: Consecutive beat streak leaders\n```sql\nWITH recent_eps AS (\n  SELECT symbol, date, surprise_percent,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.earnings_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '2 years'\n    AND eps_actual IS NOT NULL AND eps_estimate IS NOT NULL\n),\nstreaks AS (\n  SELECT symbol,\n    MIN(CASE WHEN surprise_percent <= 0 THEN rn END) AS first_miss_rn\n  FROM recent_eps\n  WHERE rn <= 8\n  GROUP BY symbol\n)\nSELECT g.symbol, g.name, g.gic_sector,\n  COALESCE(s.first_miss_rn - 1, 8) AS consecutive_beats,\n  h.market_capitalization_mln\nFROM streaks s\nINNER JOIN shibui.general_info g ON s.symbol = g.symbol\nINNER JOIN shibui.highlights h ON g.symbol = h.symbol\nWHERE COALESCE(s.first_miss_rn - 1, 8) >= 4\n  AND g.type = 'Common Stock'\n  AND h.market_capitalization_mln > 1000\nORDER BY consecutive_beats DESC, h.market_capitalization_mln DESC\nLIMIT 20\n```\n"New value: +"## Earnings Analysis Workflow\n\n### Persona\nYou are an earnings analyst who tracks quarterly results and\nidentifies patterns in surprises. You contextualize EPS numbers\nwith revenue trends and margin shifts - an EPS beat from cost\ncuts is different from one driven by revenue growth.\n\n### Key Data Notes\n- `earnings_quarterly`: has eps_actual, eps_estimate, eps_difference,\n  surprise_percent, before_after_market ('AfterMarket'|'BeforeMarket')\n- Dates in earnings_quarterly are period-end dates (month-end),\n  not announcement dates. Use `report_date` for actual announcement date.\n- Pair earnings data with `fundamentals_quarterly` for full context\n  (revenue, margins, one-time items) - join on (symbol, date)\n- Forward EPS estimates are in `analyst_estimates` table\n  (eps_estimate_current_year/quarter, next_year/quarter)\n\n### Workflow\n1. **Recent result**: Latest quarter EPS actual vs estimate + surprise %\n2. **Trend**: Last 4-8 quarters of beats/misses (consistency matters)\n3. **Revenue context**: Did revenue also beat? Revenue miss + EPS beat\n   = cost-cutting, not organic growth\n4. **Margin trajectory**: Are margins expanding or compressing?\n5. **Forward estimates**: eps_estimate_current_quarter and next_quarter\n   from analyst_estimates table (if available)\n6. **Sector context**: How did peers perform in the same quarter?\n\n### Output Format\n- **Latest Quarter**: EPS actual vs estimate, surprise %, revenue\n- **Track Record**: Table of last 4-8 quarters with beat/miss\n- **Quality Assessment**: Revenue-driven vs cost-driven performance\n- **Forward Look**: Current estimates (if available) and trend context\n\n### Advanced Query Patterns\n\n#### E1: Earnings history with revenue context\n```sql\nWITH eps AS (\n  SELECT symbol, date, report_date, before_after_market,\n    eps_actual, eps_estimate, surprise_percent,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.earnings_quarterly\n  WHERE symbol = 'AAPL.NASDAQ' AND date >= CURRENT_DATE - INTERVAL '2 years'\n    AND eps_actual IS NOT NULL\n)\nSELECT e.date, e.report_date, e.before_after_market,\n  ROUND(e.eps_actual, 2) AS actual, ROUND(e.eps_estimate, 2) AS estimate,\n  ROUND(e.surprise_percent, 1) AS surprise_pct,\n  CASE WHEN e.surprise_percent > 0 THEN 'Beat' ELSE 'Miss' END AS result,\n  f.revenue,\n  ROUND(f.net_income / NULLIF(f.revenue, 0) * 100, 1) AS net_margin_pct\nFROM eps e\nLEFT JOIN shibui.fundamentals_quarterly f ON e.symbol = f.symbol AND e.date = f.date\nWHERE e.rn <= 8\nORDER BY e.date DESC LIMIT 8\n```\n\n#### E2: Sector earnings season summary (latest quarter)\n```sql\nWITH latest_eps AS (\n  SELECT e.symbol, e.date, e.eps_actual, e.eps_estimate, e.surprise_percent,\n    ROW_NUMBER() OVER (PARTITION BY e.symbol ORDER BY e.date DESC) AS rn\n  FROM shibui.earnings_quarterly e\n  WHERE e.date >= CURRENT_DATE - INTERVAL '4 months'\n    AND e.eps_actual IS NOT NULL AND e.eps_estimate IS NOT NULL\n)\nSELECT g.gics_sector,\n  COUNT(*) AS reported,\n  COUNT(*) FILTER (WHERE le.surprise_percent > 0) AS beats,\n  COUNT(*) FILTER (WHERE le.surprise_percent < 0) AS misses,\n  ROUND(AVG(le.surprise_percent), 1) AS avg_surprise_pct,\n  ROUND(MEDIAN(le.surprise_percent), 1) AS median_surprise_pct\nFROM latest_eps le\nINNER JOIN shibui.general_info g ON le.symbol = g.symbol\nWHERE le.rn = 1 AND g.type = 'Common Stock'\nGROUP BY g.gics_sector\nORDER BY avg_surprise_pct DESC\nLIMIT 15\n```\n\n#### E3: Consecutive beat streak leaders\n```sql\nWITH recent_eps AS (\n  SELECT symbol, date, surprise_percent,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.earnings_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '2 years'\n    AND eps_actual IS NOT NULL AND eps_estimate IS NOT NULL\n),\nstreaks AS (\n  SELECT symbol,\n    MIN(CASE WHEN surprise_percent <= 0 THEN rn END) AS first_miss_rn\n  FROM recent_eps\n  WHERE rn <= 8\n  GROUP BY symbol\n),\nlatest_val AS (\n  SELECT symbol, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector,\n  COALESCE(s.first_miss_rn - 1, 8) AS consecutive_beats,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln\nFROM streaks s\nINNER JOIN shibui.general_info g ON s.symbol = g.symbol\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nWHERE COALESCE(s.first_miss_rn - 1, 8) >= 4\n  AND g.type = 'Common Stock'\n  AND v.market_cap > 500e6\nORDER BY consecutive_beats DESC, v.market_cap DESC\nLIMIT 20\n```\n"
  5. Changed1 schema field changed
    • addedOutput schema / description
      Added value: +"Generic wrapper for non-object return types."
  6. Added

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, non-destructive, and closed-world, so the safety profile is covered. The description adds the genuinely non-obvious behavioral requirement that two specific tools must precede it in a fixed order, which an agent could not infer from the schema. It stops short of explaining what the loaded workflow actually returns or how large it is.

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

Conciseness4/5

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

Front-loads the action and the hard prerequisite before the trigger list, so the critical ordering constraint is not buried. The trigger enumeration is long but each phrase is a distinct retrieval cue, and there is no filler.

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

Completeness4/5

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

For a zero-parameter workflow loader with no output schema, the description covers prerequisites, timing, domain, and combinability well. The one gap is that it never says what calling it produces (instructions? a template?), leaving the agent to infer the return shape.

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

Parameters4/5

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

Zero parameters and 100% schema coverage, so the baseline is 4. There is nothing parameter-related for the description to compensate for.

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

Purpose5/5

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

States a specific verb+resource ('Load earnings workflow') and immediately scopes it to a domain (EPS surprises, beat/miss, estimates, revenue), which cleanly separates it from the many sibling workflow loaders like load_filing_workflow and load_fundamental_workflow.

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

Usage Guidelines5/5

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

Explicitly states the ordering prerequisite ('REQUIRES get_database_schema then get_query_patterns to be called first') and gives a clear when-to-use rule ('Call BEFORE writing SQL when the user asks about...') with a long list of concrete trigger phrases. It also notes it can be combined with other workflow tools.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources