base_quote
Retrieve current exchange rates from a base currency to multiple target currencies using the Frankfurter Forex MCP server.
Instructions
Get latest exchange rates from one base currency to multiple target currencies.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| base | Yes | ||
| targets | Yes |
Implementation Reference
- Core handler function that executes the base_quote tool. Calls Frankfurter API's latest endpoint, validates response payload, extracts rates for target currencies, and returns structured output.
async def execute(input_data: BaseQuoteInput, client: FrankfurterClient) -> BaseQuoteOutput: payload = await client.latest(base=input_data.base, targets=input_data.targets) payload_rates = payload.get("rates", {}) payload_date = payload.get("date") if not isinstance(payload_rates, dict): raise FrankfurterClientError("Frankfurter API returned malformed rates payload") if not isinstance(payload_date, str): raise FrankfurterClientError("Frankfurter API returned malformed date payload") rates: dict[str, float] = {} for currency in input_data.targets: value = payload_rates.get(currency) if value is not None: try: rates[currency] = float(value) except (TypeError, ValueError) as exc: raise FrankfurterClientError("Frankfurter API returned non-numeric rate") from exc if not rates: raise FrankfurterClientError("Frankfurter API returned no rates for requested targets") return BaseQuoteOutput( base=str(payload.get("base", input_data.base)), date=date.fromisoformat(payload_date), rates=rates, ) - src/frankfurter_forex_mcp/server.py:28-45 (registration)MCP tool registration with @mcp.tool decorator. Wraps the execute function, creates FrankfurterClient, handles validation errors and upstream errors, returns JSON response.
@mcp.tool(name="base_quote") async def base_quote_tool(base: str, targets: list[str]) -> dict: """Get latest exchange rates from one base currency to multiple target currencies.""" try: input_data = BaseQuoteInput(base=base, targets=targets) async with FrankfurterClient.from_env() as client: output = await base_quote.execute(input_data, client) return output.model_dump(mode="json") except ValidationError as exc: return _to_error(str(exc), tool="base_quote", error_code="validation_error") except FrankfurterClientError as exc: return _to_error(str(exc), tool="base_quote", error_code="upstream_error") except Exception: return _to_error( "Unexpected internal error", tool="base_quote", error_code="internal_error", ) - Input validation schema (BaseQuoteInput) with field validators for normalizing currency codes, validating 3-letter ISO 4217 format, deduplicating targets, and ensuring base currency is not in targets.
class BaseQuoteInput(BaseModel): model_config = ConfigDict(str_strip_whitespace=True) base: str = Field(min_length=3, max_length=3, default="EUR") targets: list[str] = Field(min_length=1) @field_validator("base") @classmethod def normalize_base(cls, value: str) -> str: value = _normalize_currency(value) if len(value) != 3 or not value.isalpha(): raise ValueError("base must be a 3-letter ISO 4217 currency code") return value @field_validator("targets") @classmethod def normalize_targets(cls, values: list[str]) -> list[str]: normalized: list[str] = [] for value in values: currency = _normalize_currency(value) if len(currency) != 3 or not currency.isalpha(): raise ValueError("targets must contain valid 3-letter ISO 4217 codes") normalized.append(currency) deduplicated = list(dict.fromkeys(normalized)) if not deduplicated: raise ValueError("targets cannot be empty") return deduplicated @model_validator(mode="after") def validate_base_not_in_targets(self) -> BaseQuoteInput: if self.base in self.targets: raise ValueError("base currency must not be included in targets") return self - Output schema (BaseQuoteOutput) defining the structure of the response with base currency, date, and dictionary of exchange rates.
class BaseQuoteOutput(BaseModel): base: str date: date rates: dict[str, float]