Skip to main content
Glama
anirbanbasu

FrankfurterMCP

convert_currency_specific_date

Read-only

Convert currency amounts using historical exchange rates for a specific date. If no rate exists for that date, uses the closest available prior rate.

Instructions

Convert an amount from one currency to another using the exchange rates for a specific date.

If there is no exchange rate available for the specific date, the rate for the closest available date before the specified date will be used.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
amountYesThe amount in the source currency to convert.
from_currencyYesThe source currency ISO4217 code.
to_currencyYesThe target currency ISO4217 code.
specific_dateYesThe specific date for which the conversion is requested in the YYYY-MM-DD format.

Implementation Reference

  • The main handler function that converts an amount from one currency to another using exchange rates for a specific date. It validates currencies, fetches historical exchange rates via the _get_historical_exchange_rates helper, calculates the converted amount, and returns a CurrencyConversionResponse.
    async def convert_currency_specific_date(
        self,
        ctx: Context,
        amount: Annotated[
            PositiveFloat,
            Field(description="The amount in the source currency to convert."),
        ],
        from_currency: Annotated[ISO4217, Field(description="The source currency ISO4217 code.")],
        to_currency: Annotated[ISO4217, Field(description="The target currency ISO4217 code.")],
        specific_date: Annotated[
            date,
            Field(description="The specific date for which the conversion is requested in the YYYY-MM-DD format."),
        ],
    ):
        """Convert an amount from one currency to another using the exchange rates for a specific date.
    
        If there is no exchange rate available for the specific date, the rate for the closest available date before
        the specified date will be used.
        """
        if from_currency.lower() == to_currency.lower():
            # If the source and target currencies are the same, no conversion is needed
            raise ValueError(
                f"Source currency '{from_currency}' and target currency '{to_currency}' are the same. No conversion needed."
            )
        await ctx.info(
            f"Obtaining historical exchange rates for {from_currency} to {to_currency} on {specific_date} from Frankfurter API at {self.frankfurter_api_url}"
        )
        cache_key = hashkey(
            self,
            specific_date=specific_date.isoformat(),
            base_currency=from_currency,
            symbols=tuple([to_currency]),
        )
        cache_hit = cache_key in lru_cache
        date_specific_rates, http_response = self._get_historical_exchange_rates(
            specific_date=specific_date.isoformat(),
            base_currency=from_currency,
            symbols=tuple([to_currency]),
        )
        if cache_hit:
            await ctx.info(
                f"Retrieved historical exchange rates for {specific_date} from least-recently used (LRU) cache."
            )
        await ctx.info(f"Converting {amount} of {from_currency} to {to_currency} on {specific_date}")
        if not date_specific_rates or "rates" not in date_specific_rates:
            raise ValueError(  # pragma: no cover
                f"Could not retrieve exchange rates for {from_currency} to {to_currency} for {specific_date}."
            )
        rate = date_specific_rates["rates"].get(to_currency)
        if rate is None:  # pragma: no cover
            raise ValueError(f"Exchange rate for {from_currency} to {to_currency} not found.")
        converted_amount = amount * float(rate)
        result = CurrencyConversionResponse(
            from_currency=from_currency,
            to_currency=to_currency,
            amount=amount,
            converted_amount=converted_amount,
            exchange_rate=rate,
            rate_date=date_specific_rates["date"],
        )
        return self.get_response_content(response=result, http_response=http_response, cached_response=cache_hit)
  • Pydantic BaseModel defining the response schema for currency conversion, including fields: from_currency, to_currency, amount, converted_amount, exchange_rate, and rate_date.
    class CurrencyConversionResponse(BaseModel):
        """Response model for currency conversion."""
    
        from_currency: ISO4217 = Field(description="The ISO 4217 code of the currency to convert from.")
        to_currency: ISO4217 = Field(description="The ISO 4217 code of the currency to convert to.")
        amount: PositiveFloat = Field(description="The amount (of the source currency) to convert.")
        converted_amount: PositiveFloat = Field(description="The converted amount (of the target currency).")
        exchange_rate: PositiveFloat = Field(description="The exchange rate used for the conversion.")
        rate_date: date = Field(description="The date, in ISO format, of the exchange rate used for the conversion.")
  • Tool registration entry in the FrankfurterMCP.tools class variable, defining the tool name 'convert_currency_specific_date' with tags for currency-rates, currency-conversion, and historical-exchange-rates, plus readOnlyHint and openWorldHint annotations.
    {
        "fn": "convert_currency_specific_date",
        "tags": [
            "currency-rates",
            "currency-conversion",
            "historical-exchange-rates",
        ],
        "annotations": {
            "readOnlyHint": True,
            "openWorldHint": True,
        },
    },
  • Internal helper method _get_historical_exchange_rates that fetches historical exchange rates from the Frankfurter API. Uses LRU cache for performance and supports specific_date, start_date, end_date parameters.
    @cached(cache=lru_cache, lock=threading.Lock(), key=hashkey)
    def _get_historical_exchange_rates(
        self,
        specific_date: str | None = None,
        start_date: str | None = None,
        end_date: str | None = None,
        base_currency: str | None = None,
        symbols: tuple[str, ...] | None = None,
    ):
        """Internal function to get historical exchange rates. This is a helper function for the main tool."""
        try:
            params = {}
            if base_currency:
                params["base"] = base_currency
            if symbols:
                params["symbols"] = ",".join(symbols)
    
            frankfurter_url = self.frankfurter_api_url
            if start_date and end_date:
                frankfurter_url += f"/{start_date}..{end_date}"
            elif start_date:
                # If only start_date is provided, we assume the end date is the latest available date
                frankfurter_url += f"/{start_date}.."
            elif specific_date:
                # If only specific_date is provided, we assume it is the date for which we want the rates
                frankfurter_url += f"/{specific_date}"
            else:
                raise ValueError("You must provide either a specific date, a start date, or a date range.")
    
            with self.get_httpx_client() as client:
                http_response = client.get(
                    frankfurter_url,
                    params=params,
                )
                http_response.raise_for_status()
                result = http_response.json()
                return result, http_response
        except httpx.RequestError as e:
            raise ValueError(f"Failed to fetch historical exchange rates from {self.frankfurter_api_url}. {e}")
Behavior4/5

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

The description adds valuable behavioral context beyond annotations by explaining the fallback behavior when no exchange rate is available for the specified date ('the rate for the closest available date before the specified date will be used'). This is crucial information not covered by the readOnlyHint and openWorldHint annotations. No contradiction with annotations exists.

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 perfectly concise with two sentences that each earn their place. The first sentence states the core purpose, and the second provides critical behavioral context about date fallback. No wasted words or redundancy.

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 read-only conversion tool with excellent schema coverage and annotations, the description provides good contextual completeness by explaining the date fallback behavior. However, without an output schema, it doesn't describe the return format (e.g., whether it returns just the converted amount or includes rate details), leaving a minor gap.

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?

With 100% schema description coverage, the input schema already documents all parameters thoroughly. The description doesn't add significant parameter semantics beyond what's in the schema, though it reinforces the 'specific date' concept. This meets the baseline for high schema coverage.

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 clearly states the tool's purpose with specific verbs ('convert an amount') and resources ('from one currency to another'), and distinguishes it from siblings by specifying 'using the exchange rates for a specific date'. This differentiates it from tools like convert_currency_latest that likely use current rates.

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 about when to use this tool (for historical currency conversion with a specific date) and implicitly distinguishes it from alternatives like convert_currency_latest. However, it doesn't explicitly state when NOT to use it or name specific alternatives, preventing a perfect score.

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/anirbanbasu/frankfurtermcp'

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