Skip to main content
Glama
debtstack-ai

DebtStack MCP Server

search_bonds

Search corporate bonds by ticker, seniority, yield, and maturity to identify high-yield opportunities or analyze maturity walls using real-time credit data.

Instructions

Search bonds by ticker, seniority, yield, spread, and maturity. Use for yield hunting, finding high-yield opportunities, or analyzing maturity walls. Example: 'Find senior unsecured bonds yielding above 8%'

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
tickerNoCompany ticker(s)
seniorityNoBond seniority level
min_ytmNoMinimum yield to maturity (%)
has_pricingNoOnly bonds with pricing data
maturity_beforeNoMaturity before date (YYYY-MM-DD)
limitNoMaximum results (default 10)

Implementation Reference

  • MCP tool registration for 'search_bonds' - defines the tool name, description, and input schema with parameters like ticker, seniority, min_ytm, has_pricing, maturity_before, and limit
    Tool(
        name="search_bonds",
        description=(
            "Search bonds by ticker, seniority, yield, spread, and maturity. "
            "Use for yield hunting, finding high-yield opportunities, or analyzing maturity walls. "
            "Example: 'Find senior unsecured bonds yielding above 8%'"
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "ticker": {
                    "type": "string",
                    "description": "Company ticker(s)"
                },
                "seniority": {
                    "type": "string",
                    "enum": ["senior_secured", "senior_unsecured", "subordinated"],
                    "description": "Bond seniority level"
                },
                "min_ytm": {
                    "type": "number",
                    "description": "Minimum yield to maturity (%)"
                },
                "has_pricing": {
                    "type": "boolean",
                    "description": "Only bonds with pricing data"
                },
                "maturity_before": {
                    "type": "string",
                    "description": "Maturity before date (YYYY-MM-DD)"
                },
                "limit": {
                    "type": "integer",
                    "description": "Maximum results (default 10)"
                }
            },
            "required": []
        }
    ),
  • MCP tool handler that executes search_bonds - processes arguments, makes API call to /bonds endpoint, formats results using format_bond helper, and returns formatted text
    elif name == "search_bonds":
        params = {k: v for k, v in arguments.items() if v is not None}
        params.setdefault("limit", 10)
        result = api_get("/bonds", params)
    
        bonds = result.get("data", [])
        if not bonds:
            return [TextContent(type="text", text="No bonds found matching criteria.")]
    
        text = f"Found {len(bonds)} bonds:\n\n"
        text += "\n\n---\n\n".join(format_bond(b) for b in bonds)
        return [TextContent(type="text", text=text)]
  • Core async implementation of search_bonds - builds parameters from filters (ticker, seniority, min_ytm, max_ytm, spread, maturity, etc.), makes HTTP GET request to /bonds endpoint, returns JSON response with bond data
    async def search_bonds(
        self,
        ticker: Optional[str] = None,
        cusip: Optional[str] = None,
        sector: Optional[str] = None,
        seniority: Optional[str] = None,
        security_type: Optional[str] = None,
        instrument_type: Optional[str] = None,
        issuer_type: Optional[str] = None,
        rate_type: Optional[str] = None,
        min_coupon: Optional[float] = None,
        max_coupon: Optional[float] = None,
        min_ytm: Optional[float] = None,
        max_ytm: Optional[float] = None,
        min_spread: Optional[int] = None,
        max_spread: Optional[int] = None,
        maturity_before: Optional[Union[str, date]] = None,
        maturity_after: Optional[Union[str, date]] = None,
        min_outstanding: Optional[int] = None,
        has_pricing: Optional[bool] = None,
        has_guarantors: Optional[bool] = None,
        has_cusip: Optional[bool] = None,
        currency: Optional[str] = None,
        fields: Optional[str] = None,
        sort: str = "name",
        limit: int = 50,
        offset: int = 0,
    ) -> Dict[str, Any]:
        """
        Search bonds with powerful filtering for yield hunting and screening.
    
        Args:
            ticker: Filter by company ticker(s)
            cusip: Filter by CUSIP(s)
            sector: Filter by company sector
            seniority: senior_secured, senior_unsecured, subordinated
            security_type: first_lien, second_lien, unsecured
            instrument_type: term_loan_b, senior_notes, revolver, etc.
            issuer_type: holdco, opco, subsidiary
            rate_type: fixed, floating
            min_coupon: Minimum coupon rate (%)
            max_coupon: Maximum coupon rate (%)
            min_ytm: Minimum yield to maturity (%)
            max_ytm: Maximum yield to maturity (%)
            min_spread: Minimum spread to treasury (bps)
            max_spread: Maximum spread to treasury (bps)
            maturity_before: Maturity before date
            maturity_after: Maturity after date
            min_outstanding: Minimum outstanding (cents)
            has_pricing: Has pricing data
            has_guarantors: Has guarantor entities
            has_cusip: Has CUSIP (tradeable)
            currency: Currency code (e.g., "USD")
            fields: Comma-separated fields to return
            sort: Sort field, prefix with - for descending
            limit: Results per page (max 100)
            offset: Pagination offset
    
        Returns:
            Dictionary with "data" (list of bonds) and "meta" (pagination info)
    
        Example:
            # Find high-yield bonds yielding >8%
            result = await client.search_bonds(
                seniority="senior_unsecured",
                min_ytm=8.0,
                has_pricing=True,
                fields="name,cusip,company_ticker,coupon_rate,maturity_date,pricing",
                sort="-pricing.ytm"
            )
        """
        params = {
            "sort": sort,
            "limit": limit,
            "offset": offset,
        }
    
        if ticker:
            params["ticker"] = ticker
        if cusip:
            params["cusip"] = cusip
        if sector:
            params["sector"] = sector
        if seniority:
            params["seniority"] = seniority
        if security_type:
            params["security_type"] = security_type
        if instrument_type:
            params["instrument_type"] = instrument_type
        if issuer_type:
            params["issuer_type"] = issuer_type
        if rate_type:
            params["rate_type"] = rate_type
        if min_coupon is not None:
            params["min_coupon"] = min_coupon
        if max_coupon is not None:
            params["max_coupon"] = max_coupon
        if min_ytm is not None:
            params["min_ytm"] = min_ytm
        if max_ytm is not None:
            params["max_ytm"] = max_ytm
        if min_spread is not None:
            params["min_spread"] = min_spread
        if max_spread is not None:
            params["max_spread"] = max_spread
        if maturity_before:
            params["maturity_before"] = str(maturity_before)
        if maturity_after:
            params["maturity_after"] = str(maturity_after)
        if min_outstanding is not None:
            params["min_outstanding"] = min_outstanding
        if has_pricing is not None:
            params["has_pricing"] = has_pricing
        if has_guarantors is not None:
            params["has_guarantors"] = has_guarantors
        if has_cusip is not None:
            params["has_cusip"] = has_cusip
        if currency:
            params["currency"] = currency
        if fields:
            params["fields"] = fields
    
        client = await self._get_client()
        response = await client.get("/bonds", params=params)
        response.raise_for_status()
        return response.json()
  • Synchronous wrapper for search_bonds in DebtStack class - wraps async implementation in synchronous interface using _run helper
    def search_bonds(self, **kwargs) -> Dict[str, Any]:
        async def _call():
            async with DebtStackClient(self.api_key, **self.kwargs) as client:
                return await client.search_bonds(**kwargs)
        return self._run(_call())
  • LangChain input schema (SearchBondsInput) - defines Pydantic BaseModel with fields for ticker, seniority, min_ytm, max_ytm, min_spread, has_pricing, and maturity parameters
    class SearchBondsInput(BaseModel):
        """Input for bond search tool."""
        ticker: Optional[str] = Field(
            None,
            description="Company ticker(s) to filter by"
        )
        seniority: Optional[str] = Field(
            None,
            description="Seniority level: 'senior_secured', 'senior_unsecured', 'subordinated'"
        )
        min_ytm: Optional[float] = Field(
            None,
            description="Minimum yield to maturity (%)"
        )
        max_ytm: Optional[float] = Field(
            None,
            description="Maximum yield to maturity (%)"
        )
        min_spread: Optional[int] = Field(
            None,
            description="Minimum spread to treasury (basis points)"
        )
        has_pricing: Optional[bool] = Field(
            None,
            description="Filter for bonds with pricing data"
        )
        maturity_before: Optional[str] = Field(
            None,
            description="Maturity before date (YYYY-MM-DD)"
        )
        maturity_after: Optional[str] = Field(
            None,
            description="Maturity after date (YYYY-MM-DD)"
        )
        fields: Optional[str] = Field(
            None,
            description="Comma-separated fields to return"
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 search functionality but doesn't describe key behavioral traits such as whether this is a read-only operation, potential rate limits, authentication requirements, or what the output format looks like (e.g., list of bonds with details). This leaves significant gaps for an agent to understand how to handle the tool effectively.

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?

The description is appropriately sized with two sentences: the first states the purpose and parameters, and the second provides usage context and an example. It's front-loaded with key information and avoids unnecessary details, though the example could be slightly more concise.

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 complexity of a search tool with 6 parameters, no annotations, and no output schema, the description is moderately complete. It covers the purpose and usage but lacks details on behavioral aspects and output format, which are important for an agent to use the tool correctly without structured output guidance.

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

Parameters3/5

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

The description lists parameters (ticker, seniority, yield, spread, maturity) that partially map to the input schema, which has 100% coverage with detailed descriptions for all 6 parameters. Since schema coverage is high, the baseline is 3, and the description adds minimal value by naming some parameters but not providing additional semantics beyond what the schema already documents.

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 as searching bonds by specific criteria (ticker, seniority, yield, spread, maturity), which is a specific verb+resource combination. However, it doesn't explicitly differentiate this tool from sibling tools like 'search_companies' or 'search_pricing', which might also involve bond-related searches, so it doesn't reach the highest score.

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 for when to use this tool ('for yield hunting, finding high-yield opportunities, or analyzing maturity walls') and includes an example query. However, it doesn't explicitly state when not to use it or name alternatives among the sibling tools, such as when to use 'search_companies' instead for broader company searches.

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/debtstack-ai/debtstack-python'

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