DEV Community

viraj geeth
viraj geeth

Posted on

Building a Simple Exchange Rate MCP Server using FastMCP and AllRatesToday

MCP, or Model Context Protocol, is a protocol created by Anthropic (the creators of Claude) to streamline and standardise LLM (large language model) access to external tools and resources.

FastMCP is a Python framework that allows you to implement MCP in your Python applications.

In this article, I'll walk you through creating an Exchange Rate MCP server that you can use in your coding agent, Claude Desktop, or any other MCP-compatible environment to get current exchange rates.


What really is the point of MCP?

LLMs, such as ChatGPT, Gemini, and Claude models, are trained on vast datasets up to a specific date, known as the knowledge cut-off date.

This is fine when you ask them about anything historical, but the moment you ask for something current — "what's the USD to EUR rate right now?" — they'll either guess based on stale training data or, worse, hallucinate a number that looks plausible but is completely wrong. For an LLM, "0.92" and "1.07" are equally valid-looking strings; the model has no built-in mechanism to know which one is true today.

Also, by default, LLMs cannot inherently take action in the real world. They are just next-token predictors and cannot perform tasks such as searching, fetching live data, or making API calls on the internet independently.

MCP solves these problems by enabling access to up-to-date information while allowing them to carry out external tasks that they would otherwise be unable to.

So let's build one and serve our agents some sweet, real-time exchange rates.


Let's build our Exchange Rate MCP Server!

To get current exchange rates, we'll be using the AllRatesToday API. At the time of writing, AllRatesToday supports more than 150 currencies, gives you live mid-market rates, and has a generous free tier (300 requests per month) that's more than enough for development.

The plan: create an AllRatesToday account for an API key, set up a Python virtual environment, write the MCP server code, and run it on Claude Desktop.

Step 1 — Get your API key

Head over to allratestoday.com/register, sign up (it takes about thirty seconds), and copy the API key from your dashboard. Keep it somewhere safe — we'll add it to a .env file in a moment.

Step 2 — Create the project folder and virtual environment

We'll use uv — it's faster than pip and handles environments cleanly.

mkdir /path/to/your/allrates-mcp
cd allrates-mcp
Enter fullscreen mode Exit fullscreen mode
uv init
# This command creates the following:
# ├── .git/
# ├── .gitignore
# ├── .python-version
# ├── README.md
# ├── main.py
# └── pyproject.toml

uv venv  # This creates the virtual environment

# To activate the virtual environment in UNIX-based OSes:
source .venv/bin/activate

# For Windows:
.\.venv\Scripts\activate.bat
Enter fullscreen mode Exit fullscreen mode
code .  # This opens our project folder in VSCode

# If you use Cursor:
cursor .

# For those of you who live on the edge:
vim .
Enter fullscreen mode Exit fullscreen mode
uv add fastmcp dotenv httpx
Enter fullscreen mode Exit fullscreen mode

That's fastmcp for the MCP framework, dotenv for reading the API key from a .env file, and httpx for the async HTTP client.

Step 3 — Write the MCP server

Open main.py and paste the following:

import httpx
import os
from dotenv import load_dotenv
from fastmcp import FastMCP

load_dotenv()

mcp = FastMCP(name="AllRatesToday Exchange Rate MCP Server")

ALLRATES_API_KEY = os.getenv("ALLRATES_API_KEY")
if not ALLRATES_API_KEY:
    raise EnvironmentError("ALLRATES_API_KEY is not set in environment variables.")

API_BASE = "https://allratestoday.com/api"


@mcp.tool
async def convert_currency(amount: float, base_curr: str, target_curr: str) -> str:
    """Convert an amount from one currency to another using current exchange rates."""

    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{API_BASE}/rate",
                params={
                    "source": base_curr.upper(),
                    "target": target_curr.upper(),
                },
                headers={"Authorization": f"Bearer {ALLRATES_API_KEY}"},
            )
            response.raise_for_status()
            data = response.json()

            if "rate" not in data:
                raise Exception(f"Unexpected response from AllRatesToday: {data}")

            rate = data["rate"]
            converted = round(amount * rate, 2)

            return (
                f"{amount} {base_curr.upper()} = {converted} {target_curr.upper()} "
                f"(Rate: {rate})"
            )

    except httpx.HTTPStatusError as e:
        if e.response.status_code == 401:
            raise Exception("Invalid AllRatesToday API key")
        elif e.response.status_code == 429:
            raise Exception("AllRatesToday API quota exceeded")
        elif e.response.status_code == 400:
            raise Exception(
                f"Bad request — possibly an unknown currency code: "
                f"{base_curr} or {target_curr}"
            )
        else:
            raise Exception(
                f"HTTP error occurred: {e.response.status_code} - {e.response.text}"
            )
    except httpx.RequestError as e:
        raise Exception(f"Request error occurred: {str(e)}")
    except Exception as e:
        raise Exception(f"Unexpected error occurred: {str(e)}")


if __name__ == "__main__":
    mcp.run()
Enter fullscreen mode Exit fullscreen mode

A quick walkthrough of what the code does:

We load the API key from .env at startup. If it's missing, we crash early — better than failing silently in the middle of a tool call.

The @mcp.tool decorator turns convert_currency into a tool that the LLM can invoke. FastMCP introspects the type hints (float, str, str) and the docstring to build the tool schema automatically — that's what the LLM uses to decide when to call this function.

The function calls the AllRatesToday /api/rate endpoint with the base and target currencies as query parameters, and the API key as a Bearer token in the Authorization header. AllRatesToday returns a JSON object containing a rate field. We multiply that rate by the amount, round to two decimal places, and return a clean human-readable string.

Error handling is the part most tutorials skip and you shouldn't. We map the common failure modes — invalid key (401), quota exceeded (429), unknown currency code (400) — to readable messages so Claude can surface them sensibly when something goes wrong.

Step 4 — Set up your environment variable

Create a .env file in your project folder with your API key:

echo "ALLRATES_API_KEY=your_api_key" > /path/to/your/allrates-mcp/.env
# Make sure to replace 'your_api_key' with your actual key from allratestoday.com
Enter fullscreen mode Exit fullscreen mode

The dotenv package picks this up automatically when the server starts. Don't commit this file to git — uv init already added it to .gitignore, but it's worth double-checking before your first push.

Step 5 — Wire it into Claude Desktop

Open the Claude Desktop config file:

# On MacOS:
code ~/Library/Application\ Support/Claude/claude_desktop_config.json

# On Windows:
code %APPDATA%\Claude\claude_desktop_config.json
Enter fullscreen mode Exit fullscreen mode

Add the server under mcpServers:

{
  "mcpServers": {
    "allrates-mcp": {
      "command": "uv",
      "args": [
        "--directory",
        "/path/to/your/allrates-mcp/",
        "run",
        "main.py"
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Replace /path/to/your/allrates-mcp/ with the absolute path to your project folder. Relative paths look like they should work but don't, because Claude Desktop launches the subprocess from a different working directory than your shell.

Save the file, then fully quit and reopen Claude Desktop (Cmd+Q on Mac, not just close the window — closing the window leaves it running in the background and the new config won't load).

Step 6 — Try it out

Open a new conversation in Claude Desktop and ask something like:

"What's 250 USD in JPY today?"

Claude will recognise that convert_currency is the right tool, fill in the arguments, and call your MCP server. The tool call appears inline in the conversation, and the answer comes back grounded in real-time data — no hallucinated numbers.

Try a more open-ended one:

"If I had 1000 EUR and converted half to GBP and half to JPY, what would I have in total in USD?"

Claude will chain the tool calls itself — no orchestration logic on your side. That's the magic of MCP. You wrote one function; the agent figured out how to compose it.


Conclusion

You now have a working Exchange Rate MCP server in roughly sixty lines of Python. The same pattern — @mcp.tool plus type hints plus a docstring plus an HTTP call — generalises to any API in the world. Replace the AllRatesToday URL with a weather API and you have a weather server. Replace it with your internal CRM and your AI agent suddenly knows about your customers.

The bigger point is this: not very long ago, hooking an LLM up to a live data source was a research-grade undertaking with custom plumbing on every side. With FastMCP it's a thirty-minute coding session. The friction is gone, and that changes what's worth building.

If you want to extend this further, here are some natural next steps:

  • Add a get_historical_rate(base, target, date) tool using the AllRatesToday /v1/rates?time= endpoint
  • Add a get_rates_multi(base, [targets]) tool that fetches several rates in one call
  • Cache responses in memory to avoid burning your free-tier quota when the agent re-checks the same rate twice in a conversation
  • Wrap it with a FastAPI layer and deploy it as an HTTP MCP server so multiple agents can share one instance

The full AllRatesToday API documentation is at allratestoday.com/docs.


If this was useful, leave some claps 👏 — it helps the article reach more developers building agent applications.

Connect with me on LinkedIn or X, and let me know what you build with it.

Until next time, happy hacking. 🚀


Tags: MCP Server, FastMCP, Python, AI, LLM, Claude, Currency API, Forex

Top comments (0)