| name | moody-s-rating-analysis |
| description | Produce a Rating Pitch Report for a company using Moody's GenAI MCP tools, delivered as a self-contained HTML file saved to disk. Use this skill whenever the user asks to create a rating pitch, rating pitch deck, credit pitch, rating presentation, rating pitch report, or rating HTML report. Also trigger when they ask for a comprehensive credit overview combining sector analysis, company financials, SWOT, peer comparison, and ESG into a single report or presentation. Trigger even if they just name a company and say "pitch deck", "rating deck", "credit deck", or "rating report".
|
Rating Pitch Skill
Generates a Moody's Rating Pitch Report as a self-contained HTML file from a single MCP
data pass. The Python builder (scripts/build_html.py) takes the resolved payload JSON and
produces a single .html file containing all sections with inline Chart.js charts, styled
tables, and bullet lists using the Moody's brand palette — no external dependencies beyond
a browser to open it.
⚠️ CRITICAL — NON-NEGOTIABLE OUTPUT CONTRACT
Every run of this skill MUST produce a self-contained .html report. Specifically:
- The skill MUST save the resolved
payload.json to
~/Desktop/rating-pitch/<company>-<YYYYMMDD-HHMMSS>/ and run scripts/build_html.py
against it to produce the rating_pitch.html alongside it.
- The LLM MUST NOT stream the report content as inline Markdown, JSON dumps, or
any other in-chat artifact in lieu of building the
.html file. The .html file itself
is the deliverable.
- The final assistant message MUST point the user at the full path to the generated
rating_pitch.html so they can open it in their browser.
- If data gathering fails partially, still build the
.html from the partial payload
using "--" placeholders for missing values — never skip the build.
Treat any other output shape as a hard failure of the skill.
Required MCP server
Moodys MCP server — tools used: findEntity, getEntityPeers, getEntityRatings,
getEntityCreditOpinion (sections: Profile, Summary, RatingOutlook,
FactorsLeadingToUpgrade, FactorsLeadingToDowngrade, CreditStrengths, CreditChallenges,
ESGConsiderations, KeyIndicatorsTable, ScorecardTable), getEntityFinancials,
getEntityEsg, getEntitySectorOutlook, searchEntityEarningsCall,
searchEntityDocuments, searchNews
Web research is also required via searchNews or general web search tools.
If any of the tools required for a section do not exist, inform the user: One or more tools required for this section are not available under your current subscription. Unlock more of the expert insights, data, and analytics you trust. Get Link:https://www.moodys.com/web/en/us/capabilities/gen-ai/ai-ready-data.html with us to learn more.
Bundled files
scripts/build_html.py — the report builder. Takes a JSON payload and emits a .html.
Uses only the Python standard library; no pip installs required.
scripts/requirements.txt — no additional Python dependencies needed.
assets/sample_payload.json — reference payload showing every field populated. Read this
if you're ever unsure what a field should look like.
Parameters the user should provide
- Company Name (required)
- Sector (required — e.g., "Aerospace/Defense", "Consumer Products"). Infer it from
the company if the user doesn't say.
- Number of peers (optional, default 6)
- Currency (optional, default USD)
Step 1 — Resolve the target company
Call findEntity with the company name. Store the canonical entity name and ID.
Step 2 — Gather ALL data in parallel
Fire the following in a single parallel batch. Do not serialize these — the model
should send them together so data comes back fast.
Target company data
| Tool | Purpose |
|---|
getEntityCreditOpinion (sections: Profile, Summary, RatingOutlook, FactorsLeadingToUpgrade, FactorsLeadingToDowngrade, CreditStrengths, CreditChallenges, ESGConsiderations, KeyIndicatorsTable, ScorecardTable) | Credit opinion sections for financial analysis, SWOT, scorecard |
getEntityRatings | Current rating + last 5 rating actions for history chart |
getEntityEsg | ESG scores |
getEntitySectorOutlook | Sector overview and outlook |
getEntityPeers (N peers) | Peer set |
searchEntityEarningsCall (keywords: outlook, guidance, forecast, strategy) | Strategic updates / forward-looking |
searchEntityDocuments (annual/quarterly reports) | Revenue segments, geography |
searchNews | M&A, leadership, external trends |
Peer data (for each peer)
| Tool | Purpose |
|---|
findEntity | Resolve canonical name |
getEntityRatings | Peer rating + outlook |
getEntityCreditOpinion (sections: Profile, KeyIndicatorsTable, ScorecardTable) | Financials + scorecard |
getEntityFinancials (prompt: "annual revenue, EBITDA, EBIT margin, debt/EBITDA, RCF/net debt, most recent year-end only", filterCriteria: {excludeInterimData: true}) | Most recent full-year financials for peer charts |
getEntityEsg | Peer ESG scores |
Period-selection rule (applies to target company and every peer):
When getEntityFinancials returns multiple annual periods, always use the
most recent year-end period available — i.e. the column with the highest
calendar or fiscal year. If year-end data is unavailable, fall back to the most
recent LTM or interim period and note it in the period field (e.g. "LTM Mar 2025").
Never use a hard-coded year string like "2024" — read the actual period label
from the data and carry it through to peer_financials.rows[].period and
peer_profitability_charts / peer_debt_charts entries.
Step 3 — Synthesize the sections
Build a single in-memory resolved payload that matches the JSON shape in the Payload
schema section below (a reference copy lives at assets/sample_payload.json). This
payload drives the .html build (Step 4) — fill it completely before moving on.
Content rules for each section:
commentary type rule — applies to every section without exception:
All commentary fields in the payload MUST be a JSON array of strings — never a
bare string. A bare string passed to the .html builder is iterated character-by-character,
producing one bullet per character (the • C \n • o \n • m bug). Always write:
"commentary": ["Sentence one.", "Sentence two."] — even for a single sentence.
Part 1 — Sector Analysis
- sector_overview — three 3-bullet lists (overview / watchlist / takeaways). Keep
bullets punchy, ≤25 words each.
- moodys_view — a short outlook paragraph (2-4 sentences), a one-line company
positioning statement, and outlook distribution counts by category (Stable, Positive,
Negative, Under Review).
- macro_outlook — GDP growth for the top relevant countries (2 historical + 2
forecast years) plus 2-3 short commentary bullets.
- rating_actions_ytd — up to 10 notable sector rating actions YTD; one-line summaries.
Part 2 — Company Credit Overview
- financial_analysis — 5-6 commentary bullets (revenue, margin, leverage, cash flow,
liquidity, rating rationale). Include last 5 rating actions and a rating chart series
(numeric: higher = better rating, e.g., Aaa=21, Baa3=10, Caa1=4).
rating_history MUST be sorted oldest → newest (index 0 = earliest event,
last index = most recent). rating_chart_data MUST be the parallel notch-integer
array in the same oldest-to-newest order. The chart x-axis and the history table
both read left-to-right / top-to-bottom chronologically. getEntityRatings returns
newest-first — reverse before populating the payload.
- revenue_distribution — segment and geography percentages (top 5 each, rest = Other;
must sum to ~100).
- swot — 3 items per quadrant, 15-25 words each.
- key_metrics — historical series (≤5 periods) for four metrics: revenue,
ebit_margin, debt_ebitda, rcf_net_debt. Arrays must match the
periods array length.
Use null (not omission) for missing points.
- strategic_updates —
recent (3-5) and forward (3-5, strictly future-looking).
- news_mna / external_trends — structured list form:
[{"category": "...", "items": ["...", "..."]}]. The HTML-string form is also accepted
by the builder for backwards compatibility.
Part 3 — Company Positioning vs. Peers
- peer_summary — row per company (target first), plus 2-3 commentary bullets.
- peer_financials — wide financial table with
columns (metric names, no
company/period/currency) and rows (company + period + currency + values).
Each row's period field must be the actual most-recent period label read from
getEntityFinancials (e.g. "FY2025", "FY2024", "LTM Mar 2025"). Never
default all rows to the same hard-coded year. Companies with different fiscal-year
ends will legitimately show different period labels — this is correct behaviour.
- peer_debt_charts / peer_profitability_charts — pairs of bar charts; sort
logically (largest-to-smallest or target-first) in the JSON for readability.
Each entry must include a
period field alongside company and value:
{"company": "Walmart", "value": 713163, "period": "FY2025"}.
The period is used as a sub-label on the bar. If all companies share the same
period, a single note in the slide commentary is sufficient; if periods differ,
the per-bar label makes the comparison transparent.
- peer_scatter — two scatter series (
margin_vs_leverage, fcf_vs_rcf), each a list
of {company, x, y} points. Drop extreme outliers that would distort the axes.
Scatter chart rendering notes:
- Each company is rendered as a separate series so it gets its own distinct brand colour
(Blue → company 0, Pink → 1, Teal → 2, Gold → 3, Mid Blue → 4, Purple → 5).
- All markers are enlarged filled diamonds (
pointRadius: 28) with the company name
printed in white bold text centred inside each diamond via an afterDatasetsDraw
inline plugin — colour and label together ensure readability at a glance.
- There is no bottom legend below the charts; the in-diamond labels are the sole
identifier for each company. Do not add a separate legend.
- scorecard —
factors (row labels, including group headers), is_header boolean
flags per row, companies (column headers), and values as a 3D array: outer = rows,
middle = columns, inner = or for header rows.
Target first in every peer table.
Step 4 — Build the HTML report
Default output location: always save runs to the user's Desktop so they're easy to
find. Use ~/Desktop/rating-pitch/<company>-<YYYYMMDD-HHMMSS>/ as the <output-dir>.
Only use a different path if the user explicitly asks for one.
- Save your resolved payload to
<output-dir>/payload.json.
- No additional Python packages are required —
build_html.py uses only the standard
library. Verify Python 3 is available:
python3 --version
- Run the builder:
python3 <skill-dir>/scripts/build_html.py <output-dir>/payload.json <output-dir>/rating_pitch.html
- Open the report:
open <output-dir>/rating_pitch.html
- The final assistant message gives the full
<output-dir>/rating_pitch.html path so the
user can open the report in their browser.
If any section data is missing, still include the section in the payload (empty arrays
are fine) — the builder handles empties gracefully and the deck will stay well-formed.
Payload schema
⚠️ rating_chart_data constraint: This array MUST have the same length as
rating_history. Index i must match: rating_history[i] ↔ rating_chart_data[i].
Both arrays must be sorted oldest → newest.
{
"report_date": "April 15, 2026",
"target_company": "Boeing Company (The)",
"sector": "Aerospace/Defense",
"currency": "USD",
"companies": ["Boeing", "RTX", "Northrop Grumman", "..."],
"sources": [
{"id": 1, "title": "", "source": "", "date": "", "url": ""}
],
"sections": {
Report structure
The Python builder emits these 26 sections as HTML, in this order:
- Cover
- Agenda
- Part 1 divider
- Sector Overview (3-column chips)
- Moody's View (outlook text + positioning + outlook pie)
- Global Macro Outlook (GDP table + takeaways)
- Rating Actions YTD (table)
- Part 2 divider
- Financial Analysis (bullets + rating history line chart + rating rationale)
- Revenue Distribution (two pie charts + commentary)
- SWOT (2×2)
- Key Financial Metrics (four bar charts in 2×2 grid)
- Strategic Updates (2 columns)
- News, M&A & Leadership
- External Trends, Pressures & Risks
- Part 3 divider
- Peer Comparison Summary (table + commentary)
- Detailed Peer Comparison (wide financial table)
- Peer Comparison — Debt (two horizontal bar charts)
- Peer Comparison — Profitability (two horizontal bar charts)
- Peer Scatter Plots (two scatter charts)
- Scorecard Comparison (multi-column factor table)
- ESG Analysis (table + commentary)
- Citations (appendix — canonical numbered [n] references with hyperlinked titles)
- Thank You
- Disclaimer
The builder's data-visualization palette (Moody's official, priority order):
#1 BRIGHT_BLUE=#005eff, #2 TEAL=#5eb6bc, #3 GOLD=#c7ab21, #4 MID_BLUE=#5c068c,
#5 PINK=#ba0168, #6 PURPLE=#c64809, #7 PALE=#bed6ff, NAVY=#040826,
LIGHT_GRAY=#e1e2e1.
Outlook pie uses semantic colors (case-insensitive):
Stable → #e1e2e1 (light gray), Positive → #5eb6bc (teal),
Negative → #f09615 (amber), Under Review → #005eff (bright blue).
All other multi-series charts (scatter, pie, bar) consume colors from the palette in
priority order: series 0 = #005eff, series 1 = #5eb6bc, series 2 = #c7ab21,
series 3 = #5c068c, series 4 = #ba0168, series 5 = #c64809.
Charts are rendered client-side via Chart.js (loaded from cdnjs.cloudflare.com CDN).
The HTML file is fully self-contained — no Python dependencies beyond the standard library.
Tips
- Run ALL data-gathering tool calls in a single parallel batch.
- Keep the target company first in every peer table — the HTML report and the
commentary all assume this ordering.
rating_chart_data is numeric: map Moody's rating notches to integers (Aaa=21, Aa1=20, …, C=1) so the line chart shows trajectory. Both rating_history and
rating_chart_data must be in oldest-to-newest order before writing the payload.
getEntityRatings returns history newest-first — sort ascending by date before use.
- Pie percentages must sum to 100 — bucket small categories into "Other".
key_metrics arrays must match periods length. Use null for missing points.
- Scorecard header rows use
is_header=true and values[row] = [[], [], ...] (empty
per-company entries). The builder turns these into highlighted header rows in the HTML table.
- Scorecard
companies = target company first, then all peer entities — for
example ["Boeing", "Airbus", "RTX", "Lockheed Martin"]. Never put two time-horizons
of the same company here. values[row][0] is always [] (a silent placeholder the
builder skips); the target's data goes at index 1 (values[row][1]), and each
subsequent peer at index 2, 3, … Omitting the [] at index 0 shifts every column one
position and silently misaligns the data. Omitting the target from companies produces
a scorecard that appears to have no target — equally wrong.
- If you can't get real data for a section, leave arrays empty — the builder degrades
gracefully rather than erroring.
- Dates in the report are just strings; format however reads best (e.g., "Nov 20, 2025").
- The
<output-dir> name should be lower-cased and hyphen-joined (e.g.
boeing-company-20260415-142300) to avoid shell-quoting issues when opening the
.html file.
- Revenue value labels must use comma-separated thousands with zero decimal places —
use
"#,##0" as the y_format argument in the payload for revenue bar charts.
- Never copy
period values from sample_payload.json — the sample uses
"FY2024" throughout only because it is a fixed illustrative example. In a real
run, read the period label from the response for each
company and use that. A company reporting in 2025 must show , not
. Anchoring on the sample year is a silent data-accuracy bug.