Skip to main content
Glama
harishmore05

fx-mcp-server

by harishmore05
README.md
# fx-mcp-server

A small [MCP](https://modelcontextprotocol.io) (Model Context Protocol) server that gives an MCP client — Claude Desktop, Claude Code, the FastMCP inspector, etc. — two finance-related tools:

| Tool | What it does |
|---|---|
| `convert` | Converts an amount from one currency to another using live daily reference rates published by the European Central Bank (ECB). |
| `validate_iban` | Validates the structure and checksum of an IBAN (International Bank Account Number) and returns `true`/`false`. |

Built with [FastMCP](https://gofastmcp.com), the server speaks MCP over stdio, so any MCP-compatible client can spawn it as a subprocess and call its tools directly from a chat conversation.

## How it works

```
src/
├── server.py             # MCP entrypoint — registers the `convert` and `validate_iban` tools
├── fx_rate_converter.py  # Currency math: looks up rates and does the conversion
├── fx_rate_fetcher.py    # Fetches the ECB daily reference-rate XML feed and caches it as JSON
└── iban_validator.py     # Wraps the `schwifty` library to validate IBAN structure/checksum

data/
└── fx_rates.json         # Local cache of the last-fetched ECB rates (auto-generated on first `convert` call, gitignored)
```

- **`convert(source, destination, amount)`** — calls `fx_rate_fetcher.get_fx_rates()`, which downloads the ECB's [`eurofxref-daily.xml`](https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml) feed, converts it to JSON, writes it to `data/fx_rates.json`, and returns it. Rates are EUR-based, so a conversion is computed as `(amount / rate[source]) * rate[destination]`. Every call re-fetches from ECB, so it requires network access and always reflects the latest published rates (ECB updates once per business day, around 16:00 CET).
- **`validate_iban(iban)`** — delegates to the [`schwifty`](https://pypi.org/project/schwifty/) library, which checks the IBAN's country-specific format and the mod-97 checksum. It only validates structure, not whether the account actually exists at a bank.

## Prerequisites

- Python 3.13
- [`uv`](https://github.com/astral-sh/uv) — used by FastMCP to install and run the server in an isolated environment
- Claude Desktop (if you want to use the tools from a chat conversation)

Install `uv` if you don't already have it:

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

## Project setup

1. Clone/open this repo and create a virtual environment however you prefer (`uv venv`, `python -m venv .venv`, etc.), then install the dependencies:

   ```bash
   pip install -r requirements.txt
   ```

   `requirements.txt` is a pinned snapshot of the working environment, generated with:

   ```bash
   pip freeze > requirements.txt
   ```

   Regenerate it the same way any time you add or upgrade a dependency, so the pinned versions stay in sync with what's actually installed.

2. Sanity-check the server definition before running anything — this loads `server.py` and reports the tools it exposes, or any import/registration errors:

   ```bash
   fastmcp inspect /absolute/path/to/your/fx-mcp-server/src/server.py
   ```

3. (Optional) Run it against the interactive FastMCP dev inspector to call the tools by hand and watch requests/responses:

   ```bash
   fastmcp dev inspector /absolute/path/to/your/fx-mcp-server/src/server.py
   ```

## Installing into Claude Desktop

FastMCP can register the server with Claude Desktop directly. Replace the path below with the absolute path to `server.py` on your machine (`pwd` inside `src/` will give you it):

```bash
fastmcp install claude-desktop /absolute/path/to/your/fx-mcp-server/src/server.py \
  --python 3.13 \
  --with-requirements requirements.txt
```

This writes an entry into Claude Desktop's MCP config pointing at this server, using Python 3.13 and the dependencies listed in `requirements.txt`.

Then, in Claude Desktop:

1. Restart Claude Desktop if it was already open.
2. Click the **`+`** icon in the connectors panel.
3. You should see **fx-mcp-server** listed — enable it.

That's it — `convert` and `validate_iban` are now available as tools Claude can call during a conversation. Try asking things like:

> Convert 100 USD to INR
>
> Is DE89370400440532013000 a valid IBAN?

## Troubleshooting

- **`print()` statements break the server.** MCP over stdio uses stdout as the message channel between the client and server. Any stray `print()` in the server or its imports corrupts that stream and Claude Desktop will fail to talk to the tool (often silently, or with a generic connection error). Run `fastmcp inspect` first — it will surface most of these — and replace any `print()` calls with Python's `logging` module instead, which writes to stderr and won't interfere with the protocol.
- **Check the Claude Desktop MCP logs** when a tool fails to install, connect, or crashes at runtime:

  ```bash
  cat ~/Library/Logs/Claude/mcp.log
  ```

  This shows the stdout/stderr of the server subprocess and is usually the fastest way to see the real exception.
- **`convert` fails with a currency error** — the source/destination currency code isn't in the ECB feed (it only covers the ~30 currencies the ECB publishes against EUR). Check `data/fx_rates.json` for the supported list.
- **`convert` requires network access** — it fetches ECB rates live on every call; if you're offline it will raise an error rather than silently falling back to the cached `data/fx_rates.json`.