Skip to main content
Glama

emma_quick_search

Search municipal bond issuers and issues by obligor name, keyword, or CUSIP. Returns matching issuers across states and specific issues for further analysis.

Instructions

START HERE when the user names a specific obligor, issuer, or keyword. Uses EMMA's global quick search (the same box in EMMA's header) which indexes BOTH long-form and obligor-in-parens names — so 'Childrens Hospital' finds 'CALIFORNIA HEALTH FACILITIES FINANCING AUTHORITY (LUCILE SALTER PACKARD CHILDRENS HOSPITAL AT STANFORD)' without needing the conduit-issuer chase. Returns matching issuers (across all states) and matching specific issues. Each issuer row has a URL you can feed into get_issuer_outstanding_bonds.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesNatural-language search: obligor name, issuer name, CUSIP, state, or keyword
limitNo

Implementation Reference

  • The `emma_quick_search` handler function in the _dispatch method. It accepts a 'query' parameter, navigates to EMMA's QuickSearch/Results page using Playwright (headless Chromium), scrapes both issuer and issue result tables, and returns structured issuer_matches and issue_matches with URLs.
    if name == "emma_quick_search":
        query = args["query"].strip()
        if not query:
            return {"error": "query is required"}
        url = f"{EMMA_BASE}/QuickSearch/Results?quickSearchText={query.replace(' ', '%20')}"
        async with new_page() as page:
            await page.goto(url, wait_until=NAV_WAIT, timeout=NAV_TIMEOUT_MS)
            try:
                await page.wait_for_selector("#lvIssuers tbody tr, #lvIssues tbody tr, .no-data", timeout=15000)
            except Exception:
                pass
            await page.wait_for_timeout(500)
            data = await page.evaluate(
                """() => {
                    const pull = id => {
                        const t = document.getElementById(id);
                        if (!t) return {head: [], rows: []};
                        const head = Array.from(t.querySelectorAll('thead th')).map(th => th.innerText.trim());
                        const rows = Array.from(t.querySelectorAll('tbody tr')).map(tr => {
                            const cells = Array.from(tr.cells).map(td => td.innerText.trim());
                            const a = tr.querySelector('a[href*="QuickSearch/Navigate"], a[href*="IssuerHomePage/Issuer"], a[href*="IssueView/Details"], a[href*="IssuerView/IssuerDetails"], a[href*="Security/Details"]');
                            return {cells, link: a ? a.href : null};
                        });
                        return {head, rows};
                    };
                    return {issuers: pull('lvIssuers'), issues: pull('lvIssues')};
                }"""
            )
        limit = int(args.get("limit", 25))
    
        def _rowdicts(block: dict, link_key: str) -> list[dict]:
            out = []
            for r in block.get("rows", []):
                item = structured(block.get("head", []), [r.get("cells", [])])
                if not item:
                    continue
                row = item[0]
                if r.get("link"):
                    row[link_key] = r["link"]
                out.append(row)
            return out
    
        return {
            "query": query,
            "issuer_matches": _rowdicts(data["issuers"], "issuer_url")[:limit],
            "issue_matches": _rowdicts(data["issues"], "issue_url")[:limit],
        }
  • server.py:590-615 (registration)
    Tool registration for 'emma_quick_search' including its name, description (starting point for obligor/issuer searches), and inputSchema requiring 'query' (natural-language search string) with optional 'limit' parameter.
    Tool(
        name="emma_quick_search",
        description=(
            "**START HERE** when the user names a specific obligor, issuer, "
            "or keyword. Uses EMMA's global quick search (the same box in "
            "EMMA's header) which indexes BOTH long-form and obligor-in-"
            "parens names — so 'Childrens Hospital' finds "
            "'CALIFORNIA HEALTH FACILITIES FINANCING AUTHORITY (LUCILE "
            "SALTER PACKARD CHILDRENS HOSPITAL AT STANFORD)' without "
            "needing the conduit-issuer chase. Returns matching issuers "
            "(across all states) and matching specific issues. Each "
            "issuer row has a URL you can feed into "
            "get_issuer_outstanding_bonds."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Natural-language search: obligor name, issuer name, CUSIP, state, or keyword",
                },
                "limit": {"type": "integer", "default": 25},
            },
            "required": ["query"],
        },
    ),
  • The call_tool wrapper that dispatches to _dispatch for 'emma_quick_search', applies caching based on name+arguments, and wraps errors into JSON envelopes.
    @server.call_tool()
    async def call_tool(name: str, arguments: dict) -> list[TextContent]:
        cache_key = f"{name}:{json.dumps(arguments, sort_keys=True)}"
        if (cached := cache_get(cache_key)) is not None:
            return [TextContent(type="text", text=cached)]
        try:
            result = await _dispatch(name, arguments)
        except Exception as e:
            log(f"tool '{name}' failed: {e}\n{traceback.format_exc()}")
            return [TextContent(type="text", text=json.dumps({"error": str(e), "tool": name}))]
        text = json.dumps(result, indent=2, default=str)
        cache_put(cache_key, text)
        return [TextContent(type="text", text=text)]
Behavior4/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. It describes the search behavior (indexes both name types, returns matching issuers and issues, includes URLs) and implies a read-only operation. It lacks details on failure modes or rate limits but is transparent for a search tool.

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 concise, front-loaded with the usage hint, and every sentence adds value. No wasted words.

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?

Given the tool's simplicity and lack of output schema, the description adequately explains what is returned (matching issuers, issues, URLs) and how to use the results with sibling tools. It is complete for its purpose.

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?

The description adds meaning to the 'query' parameter with examples and context, going beyond the schema description. The 'limit' parameter is not elaborated, but the schema coverage is 50%, and the description adds value for the main parameter.

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?

The description explicitly states 'START HERE when the user names a specific obligor, issuer, or keyword' and explains that it uses EMMA's global quick search to find both issuers and specific issues. This clearly distinguishes it from sibling tools like get_issuer_outstanding_bonds or search_issuers_by_state.

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

Usage Guidelines4/5

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

The description provides clear context with 'START HERE' and examples of queries, indicating this is the first tool to try. It does not explicitly state when not to use it, but the context is sufficient.

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/ark9164-create/blaylock-emma-mcp'

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