Skip to main content
Glama

EMMA MCP — Custom Muni-Finance Agent for Blaylock Van

NYU Stern Consulting Capstone · Spring 2026 · Marco Figueroa, Justin Ganjian, Andrew Kay, Isaac Mizrahi, Roshan Raja · Professor Marciano


What this is

A custom MCP (Model Context Protocol) server that lets Claude read the muni-bond new-issue market directly from the MSRB's EMMA website. This is the Tier-4 example on our capstone tier ladder — a firm-owned connector to a data source that no vendor covers.

EMMA — the Electronic Municipal Market Access site operated by the Municipal Securities Rulemaking Board — is the official public book of record for every muni bond sold in the United States. New-issue calendars, Official Statements, CUSIP-level details, recent trade prices: all on EMMA. It has no public API.

This MCP exposes 18 tools and 2 resources (a CLAUDE.md rules file and an EMMA_ABBREVIATIONS.md glossary) that Claude auto-loads on connect. Once installed, a banker can ask Claude natural-language questions like "pull every outstanding Rady Children's Hospital bond and download the most recent OS" and Claude executes the full chain — search → issuer lookup → issue details → PDF download → text extraction — in about 20 seconds.

For the full pitch and why it matters to Blaylock, see USAGE.md → Why this exists.


Related MCP server: IsoFinancial-MCP

Prerequisites

You need four things on your laptop. If any are missing, the install instructions below cover it.

Requirement

What it is

Install check

Claude Desktop

Anthropic's native macOS/Windows client (the MCP host)

claude.ai/download

Python 3.10+

Runtime for the server

Mac: python3 --version · Win: python --version

uv

Python package manager (handles the venv automatically)

uv --version

Playwright Chromium

Headless browser the server drives EMMA with

Installed by one command below


Install — Mac

# 1. Unzip this folder somewhere sensible
cd ~/Downloads
unzip emma-mcp.zip -d ~/emma-mcp
cd ~/emma-mcp

# 2. If you don't have uv:
brew install uv

# 3. Create the venv + install dependencies (one command)
uv sync

# 4. Install the headless browser Playwright drives
uv run playwright install chromium

# 5. Smoke-test the server (optional — confirms it boots)
uv run python server.py
# You should see the server start and wait on stdin.
# Press Ctrl+C to stop.

Wire it into Claude Desktop (Mac)

  1. Open Claude Desktop → Settings → Developer → Edit Config. That opens claude_desktop_config.json.

  2. Add an emma entry inside the mcpServers object. Use the absolute path to the .venv/bin/python that uv sync just created and the absolute path to server.py:

{
  "mcpServers": {
    "emma": {
      "command": "/Users/YOURNAME/emma-mcp/.venv/bin/python",
      "args": ["/Users/YOURNAME/emma-mcp/server.py"]
    }
  }
}
  1. Save, fully quit Claude Desktop (Cmd+Q, not just close the window), and reopen it.

  2. In a new chat, the hammer icon in the composer should now list EMMA tools. That's the tell.


Install — Windows

# 1. Unzip this folder, e.g. to C:\emma-mcp
cd C:\emma-mcp

# 2. If you don't have Python: install from python.org (3.10 or later).
#    Check "Add Python to PATH" during install.

# 3. Install uv
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

# 4. Create the venv + install deps
uv sync

# 5. Install Playwright's Chromium
uv run playwright install chromium

# 6. Smoke-test
uv run python server.py
# Ctrl+C to stop.

Wire it into Claude Desktop (Windows)

  1. Open %APPDATA%\Claude\claude_desktop_config.json in a text editor. (Paste that path into the Explorer address bar — Windows resolves %APPDATA% to C:\Users\YOU\AppData\Roaming.)

  2. Use forward slashes or double backslashes in the JSON paths — Windows single backslashes break JSON:

{
  "mcpServers": {
    "emma": {
      "command": "C:/emma-mcp/.venv/Scripts/python.exe",
      "args": ["C:/emma-mcp/server.py"]
    }
  }
}
  1. Save, fully quit Claude Desktop (right-click tray icon → Quit), reopen.

  2. Check the hammer icon in the composer for EMMA tools.


First prompt — the 60-second verification

Open a new Claude Desktop chat and paste:

Use the EMMA MCP to pull the upcoming new-issue calendar for the next week. Filter to NY state only. Return a clean table with issuer, par, dated, and sector.

You should see Claude call get_new_issue_calendar with state="NY", get ~10-25 rows back in a few seconds, and format them as a table with EMMA URLs. If that works, every other tool works the same way.

For a more impressive demo, try:

Find Rady Children's Hospital on EMMA, pull their outstanding bonds, get the most recent issue's details, download the Official Statement, and summarize the rating language and use of proceeds from the first 10 pages.

That runs the full five-tool chain end-to-end in about 20 seconds.

More demo prompts in USAGE.md.


Troubleshooting

Claude Desktop doesn't show EMMA tools after restart.

  • Confirm the JSON is valid (python3 -m json.tool < claude_desktop_config.json — no output = good).

  • Confirm both paths in the config are absolute and exist. Relative paths and ~ do not work.

  • Fully quit Claude Desktop (Cmd+Q / tray Quit) — closing the window keeps it running.

  • Check Claude Desktop's MCP logs at ~/Library/Logs/Claude/mcp*.log (Mac) or %APPDATA%\Claude\logs\mcp*.log (Windows) for a specific error.

playwright install chromium hangs or fails.

  • First-time install downloads ~150 MB and takes 1-2 min on a home connection. Be patient.

  • Behind a corporate proxy, set HTTPS_PROXY and HTTP_PROXY before running.

Claude calls a tool and it returns "EMMA disclaimer loop" or times out.

  • EMMA sets a CUSIP disclaimer cookie on first visit. The server pre-seeds it (Disclaimer6=msrborg) but very rarely EMMA changes the cookie name. Restart Claude Desktop to reset the browser context. If it persists, EMMA may be down — check https://emma.msrb.org in a regular browser.

A returned table shows cusip_hash: ABC123… instead of a 9-digit CUSIP.

  • This is intentional, not a bug. EMMA renders CUSIPs as image tags (CGS licensing rule), so the server cannot read them off HTML. The Official Statement PDF is the authoritative CUSIP source. Use download_official_statement then extract_pdf_text — the cover and inside-cover carry the real CUSIPs.

"Rady Children's" search finds Franklin OH and Pittsburgh PA children's hospitals but not Rady.

  • This is a known EMMA quirk, not a server bug. Rady's bonds file under a California conduit (CHFFA / CPFA) with "RADY CHILDRENS" only in the obligor-in-parens portion, which EMMA's QuickSearch does index but weakly. Try emma_quick_search {query: "rady"} or search the conduit directly: search_issuers_by_state {state: "CA", contains: "health care facilities"}. Full obligor lookup playbook in USAGE.md → The conduit cheat sheet.

Server starts but Claude says "no tools available."

  • You likely edited server.py and there's a Python syntax error. Run uv run python server.py in a terminal — any error prints to stderr. Fix and restart Claude Desktop.


What's in this folder

File / folder

What it is

server.py

The MCP server itself — ~1,500 lines, Python, Playwright. 18 tools, 2 resources.

CLAUDE.md

Blaylock compliance rules. Loaded into every EMMA session as MCP resource emma://rules/CLAUDE.md.

EMMA_ABBREVIATIONS.md

EMMA's telegraphic issuer-name abbreviations (HOSP, AUTH, SR, CMNTY, etc.) + conduit cheat sheet. Loaded as resource emma://rules/EMMA_ABBREVIATIONS.md.

README.md

This file.

USAGE.md

Extensive reference: the 18 tools, the canonical workflow chain, sample banker prompts, caveats.

pyproject.toml, uv.lock

Python dependency manifest.

_demo_pulse.py

One-shot driver used during slide preparation — calls the MCP directly so we could screenshot outputs. Not needed to run the server.

_fetch_os_pdfs.py

Companion script that batch-downloaded OS PDFs for the capstone's Tier 2 (Cowork) demo. Not needed to run the server.

os_pdfs/

Sample Official Statements downloaded via the MCP during testing. Keep or delete — the folder auto-recreates on first download_official_statement call.


The one-line version

We built the EMMA MCP that no vendor ships, wired it into Claude via the same protocol as every marketplace connector, and governed it with a plain-markdown rules file a banker can read and edit. That is what Tier 4 looks like when a firm actually owns its own tool.

Available Tools

18 tools
classify_sectorA

Classify a muni bond issue description into a sector using the same keyword rules the calendar tool uses. Useful to show Marciano the classifier is deterministic, not a black box.

Inputs: description (required).

download_official_statementA

Download an Official Statement PDF from EMMA to local disk. Pass either a direct PDF URL or an IssueView URL (the tool will resolve it to the PDF link). Returns file path + size. Use when the banker wants the OS to attach to a deck or read offline.

Inputs: url (required), filename.

extract_pdf_textA

Extract text from a previously-downloaded OS PDF so you can pull financials, debt service tables, obligor financials, rating language, bond purpose, sources-and-uses, etc. into an Excel model. Works best on OS PDFs saved via download_official_statement.

Inputs: path (required), pages, max_chars.

get_calendar_summaryA

Aggregate the new-issue calendar by a grouping dimension. Returns count and total par per group, sorted by par descending. Perfect for slides: 'NY leads this week at $1.2B across 8 deals; hospitals and schools dominate the sector mix.'

Inputs: group_by, top_n.

get_issue_detailsA

Full IssueView page for a given IssueView URL — per-CUSIP detail (par at issuance, coupon, maturity, initial offering price, current LT rating, price, yield, and ratings from Fitch, KBRA, Moody's, S&P), the Official Statement PDF link, and recent trade activity. This is the single most information-dense page on EMMA for modeling a specific deal.

Inputs: issue_url (required).

get_issuer_outstanding_bondsB

List an issuer's outstanding bond issues from their EMMA profile — issue description, dated date, maturity range. The most reliable drill-down on any issuer.

Inputs: issuer_id, issuer_url, limit.

get_issuer_profileA

Full issuer profile: name, outstanding issues, attempted fetch of Official Statements, Pre-Sale docs, Continuing Disclosures, and Recent Trades. EMMA loads the latter via tab interaction; empty sections are reported honestly rather than guessed at.

Inputs: issuer_id, issuer_url.

get_market_pulseA

Snapshot of this week's muni primary market: total par, deal count, top 5 states, top 5 sectors, top 5 lead managers if available, competitive-vs-negotiated split. Built entirely from the live new-issue calendar — one call, boardroom-ready.

Inputs: no named parameters.

get_new_issue_calendarA

Upcoming municipal bond issues from MSRB EMMA — the primary competitive-intel tool for any muni banker. Each row is enriched with an inferred sector classification. Multi-axis filters: state, sector, tax_status, bank_qualified, sale_method, description_contains, min_principal, max_principal. Use this to answer questions like 'what hospital deals over $100M are pricing this week?'

Inputs: state, sector, tax_status, bank_qualified, +6 more.

get_official_statement_pdfA

Given an EMMA IssueView/Details/P##### URL, return the direct Official Statement PDF URL (and issue title). This is the canonical primary-source OS — use it before quoting coupons, maturities, or call features.

Inputs: issue_url (required).

get_recent_annual_reportsA

Recent issuer annual financial reports filed as continuing disclosures — the source for post-issuance updated obligor financials (revenue, operating income, debt-service coverage).

Inputs: limit, issuer_contains.

get_recent_continuing_disclosuresA

Recent continuing-disclosure filings (audited financials, material events, rating changes, quarterly operating data). Each row has a direct link to the filed PDF. This is where post-issuance credit work lives — ratings today, not at issuance; updated obligor financials for modeling.

Inputs: limit, disclosure_contains, issuer_contains.

get_recent_official_statementsA

List the EMMA Recent Official Statements grid — the 10 most recently filed OS documents across the muni market, with issuer, series, dated date, and a link back to the issuer. Feed a row's issue_url into get_official_statement_pdf to grab the PDF URL.

Inputs: limit.

get_recent_preliminary_statementsA

Recent Preliminary Official Statements (POS) — pre-pricing documents filed right before a deal comes to market. This is the leading edge of the new-issue pipeline. Each row links to the filing PDF and the issuer page.

Inputs: limit, issuer_contains.

get_recent_salesA

Most actively traded muni CUSIPs right now from EMMA's /TradeData grid — description, coupon, maturity, high/low price & yield, trade count, total trade amount, and a link to each security's EMMA detail page. Use this to gauge live-market color for comparable deals.

Inputs: limit.

get_security_detailsA

Pull security-level fields for a 9-digit CUSIP: issuer, coupon, maturity, dated date, par. CUSIPs that are redeemed / matured / pre-refunded return a clear 'no records' response — try a currently-outstanding CUSIP from get_issuer_outstanding_bonds.

Inputs: cusip (required).

search_issuers_by_stateA

List municipal issuers for a state (all 9k+ rows, not just page 1) and filter by name. Feed the issuer_id into get_issuer_outstanding_bonds or get_issuer_profile to drill down. IMPORTANT: the contains filter auto-expands your query into every EMMA abbreviation variant (hospital→HOSP, authority→AUTH, children→CHLDN, senior→SR, community→CMNTY, etc.) — always search in natural English, never in the telegraphic form. For hospital/senior-living/charter-school obligors, search the CONDUIT, not the obligor: e.g. CHLA bonds are filed under CALIFORNIA PUB FIN AUTH HEALTH CARE FACS REV — found by searching contains="health care facilities". See the emma://rules/EMMA_ABBREVIATIONS.md resource for the full variant map and conduit cheat sheet.

Inputs: state (required), contains, limit.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 18 tools

    Disambiguation5/5

    Each tool has a distinct purpose with detailed descriptions. For example, 'download_official_statement' vs 'get_official_statement_pdf' vs 'extract_pdf_text' are clearly separated. No two tools overlap in functionality; agents can unambiguously choose the right tool.

    Naming Consistency4/5

    Names follow a consistent verb_noun pattern (e.g., 'classify_sector', 'download_official_statement', 'get_calendar_summary'). While not all use the same verb (mix of 'get_', 'search_', 'extract_'), the structure is uniform and clear, earning a 4 for minor deviation from a single prefix.

    Tool Count5/5

    With 18 tools, the server covers a wide range of municipal bond data operations without being excessive. Each tool addresses a specific need, from searching issuers to extracting PDF text, making the count well-scoped for the domain.

    Completeness4/5

    The tool surface is comprehensive, covering search, issuer details, calendar, documents, and market data. Minor gaps exist, such as lack of historical trade analytics or advanced filtering, but core workflows are fully supported.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      An enhanced MCP server providing comprehensive financial market data endpoints for quantitative trading analysis, including SEC filings, FINRA short volume, earnings, news, and Google Trends. It features meta-tools that consolidate multiple data sources into single efficient calls, optimizing for AI agents with iteration budgets.
      8
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      An MCP server that gives Claude deep access to US public data -- demographics, economics, crime, employment, weather, housing, transit, schools, budgets, and more across 30+ cities for government intelligence workflows.
      28
      MIT
    • F
      license
      Not graded
      quality
      D
      maintenance
      A production-grade MCP server enabling Claude to perform comprehensive NJ real estate workflows including property search, valuation, neighborhood intelligence, investment analysis, and agent tools via 20 tools and 15+ data sources.
      -