Skip to main content
Glama
anirbanbasu

FrankfurterMCP

convert_currency_latest

Read-only

Convert currency amounts between ISO codes using current exchange rates from the Frankfurter API.

Instructions

Converts an amount from one currency to another using the latest exchange rates.

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.

Implementation Reference

  • The main handler function that implements the convert_currency_latest tool. It accepts amount, from_currency, and to_currency parameters, fetches the latest exchange rates using the helper function, performs the conversion calculation, and returns a CurrencyConversionResponse.
    async def convert_currency_latest(
        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.")],
    ):
        """Converts an amount from one currency to another using the latest exchange rates."""
        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 latest exchange rates for {from_currency} to {to_currency} from Frankfurter API at {self.frankfurter_api_url}"
        )
        cache_key = hashkey(
            self,
            base_currency=from_currency,
            symbols=tuple([to_currency]),
        )
        cache_hit = cache_key in ttl_cache
        latest_rates, http_response = self._get_latest_exchange_rates(
            base_currency=from_currency,
            symbols=tuple([to_currency]),
        )
        if cache_hit:
            await ctx.info("Latest exchange rates fetched from TTL cache.")
        await ctx.info(f"Converting {amount} of {from_currency} to {to_currency}")
        if not latest_rates or "rates" not in latest_rates:  # pragma: no cover
            raise ValueError(f"Could not retrieve exchange rates for {from_currency} to {to_currency}.")
        rate = latest_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=latest_rates["date"],
        )
        return self.get_response_content(response=result, http_response=http_response, cached_response=cache_hit)
  • The CurrencyConversionResponse Pydantic model that defines the output schema for currency conversion results, including fields for 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 metadata for convert_currency_latest, defining tags (currency-rates, currency-conversion) and annotations (readOnlyHint, openWorldHint) that configure how the tool is exposed via MCP.
    {
        "fn": "convert_currency_latest",
        "tags": ["currency-rates", "currency-conversion"],
        "annotations": {
            "readOnlyHint": True,
            "openWorldHint": True,
        },
    },
  • The _get_latest_exchange_rates helper function used by convert_currency_latest to fetch exchange rates from the Frankfurter API. It is cached with TTL caching and supports optional base_currency and symbols filtering.
    def _get_latest_exchange_rates(
        self,
        base_currency: str | None = None,
        symbols: tuple[str, ...] | None = None,
    ):
        """Internal function to get the latest 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)
            with self.get_httpx_client() as client:
                http_response = client.get(
                    f"{self.frankfurter_api_url}/latest",
                    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 latest exchange rates from {self.frankfurter_api_url}. {e}")
  • The register_features method in MCPMixin that iterates through the tools list and registers each tool with the FastMCP instance using the mcp.tool() decorator, connecting the tool metadata to the actual handler function.
    def register_features(self, mcp: FastMCP) -> FastMCP:
        """Register tools, resources, and prompts with the given FastMCP instance.
    
        Args:
            mcp (FastMCP): The FastMCP instance to register features with.
    
        Returns:
            FastMCP: The FastMCP instance with registered features.
        """
        # Register tools
        for tool in self.tools:
            assert "fn" in tool, "Tool metadata must include the 'fn' key."
            tool_copy = copy.deepcopy(tool)
            fn_name = tool_copy.pop("fn")
            fn = getattr(self, fn_name)
            mcp.tool(**tool_copy)(fn)
            logger.debug(f"Registered MCP tool: {fn_name}")
Behavior3/5

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

Annotations provide readOnlyHint=true and openWorldHint=true, indicating a safe, read-only operation with potentially dynamic data. The description adds context about using 'latest exchange rates,' which clarifies the temporal aspect and data freshness, but doesn't disclose rate limits, accuracy, or source details beyond what annotations imply.

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 a single, efficient sentence that front-loads the core functionality ('converts an amount') and includes key qualifiers ('latest exchange rates'). There is no wasted verbiage, and it directly addresses the tool's purpose without redundancy.

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 tool's moderate complexity (currency conversion with three parameters), annotations cover safety and dynamism, but there's no output schema to describe return values. The description lacks details on output format (e.g., numeric result, timestamp), error handling, or rate limits, leaving gaps for an agent to infer behavior.

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?

Schema description coverage is 100%, with clear parameter descriptions and enums for currencies. The description adds minimal value beyond the schema, mentioning 'amount' and 'currencies' generically without explaining ISO4217 codes or validation rules. Baseline 3 is appropriate as the schema carries most of the semantic burden.

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 with specific verbs ('converts') and resources ('amount from one currency to another'), and specifies the rate source ('latest exchange rates'). It distinguishes from sibling 'convert_currency_specific_date' by implying temporal scope, though not explicitly naming the alternative.

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

Usage Guidelines3/5

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

The description implies usage context through 'latest exchange rates,' suggesting this is for current conversions rather than historical ones. However, it doesn't explicitly state when to use this tool versus alternatives like 'convert_currency_specific_date' or 'get_historical_exchange_rates,' nor does it mention prerequisites or exclusions.

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