currency-exchange
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@currency-exchangeConvert 250 GBP to JPY, INR and AUD"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
currency-mcp
An MCP (Model Context Protocol) server that gives AI assistants and agents live foreign-exchange rates. It wraps the public Frankfurter API, which serves the European Central Bank (ECB) reference rates, and exposes it as MCP tools.
It uses the Streamable HTTP transport in stateless mode, so you can run several replicas behind a load balancer or Kubernetes Service with no sticky sessions.
Use case
An LLM doesn't know today's exchange rates, and asking it to guess produces made-up numbers. This server lets an MCP client (Claude Desktop, Claude Code, MCP Inspector, an agent framework, or an API gateway that proxies MCP) answer questions such as:
"What is 1 USD in EUR today?"
"Convert 250 GBP to JPY, INR and AUD."
"Which currencies can you convert between?"
The model calls a tool, the server fetches the current ECB rate from Frankfurter, and the model answers with a real figure and its date.
It also works as a small reference implementation of turning an existing REST API into an MCP server: two thin tools over two HTTP endpoints, a health probe, a container image and a Kubernetes manifest.
Related MCP server: Currency Exchange MCP Server
How it works
┌──────────────┐ MCP (JSON-RPC over HTTP) ┌─────────────────┐ HTTPS GET ┌────────────────────┐
│ MCP client │ ───────────────────────────▶ │ currency-mcp │ ───────────────▶ │ Frankfurter API │
│ (LLM / agent)│ ◀─────────────────────────── │ POST /mcp │ ◀─────────────── │ (ECB ref. rates) │
└──────────────┘ tool result (JSON) └─────────────────┘ JSON └────────────────────┘Connect: the client sends
initializetoPOST /mcpand gets back the server name (currency-exchange) and its instructions.Discover: the client calls
tools/listand receives the two tools with their argument schemas. The LLM uses these to decide when to call a tool.Call: when a user asks about rates, the LLM issues
tools/call, for exampleget_latest_exchange_rate(from_currency="USD", to_currency="EUR", amount=100).Fetch: the server normalizes the arguments (trims whitespace, uppercases codes) and calls Frankfurter:
GET /latest?from=USD&to=EUR&amount=100. Redirects are followed, becauseapi.frankfurter.appredirects toapi.frankfurter.dev/v1.Respond: the Frankfurter JSON is returned as the tool result. If Frankfurter returns an error, such as an unknown currency code, or can't be reached, the server returns an MCP tool error (
isError: true) with the reason, so the LLM can recover instead of failing.
Because the server is stateless (no MCP session is stored between requests), any replica can handle any request.
ECB reference rates are published once per working day, around 16:00 CET. "Latest" means the most recent published rate, not a real-time market quote, and the response's
datefield shows which day it is from.
Tools
get_latest_exchange_rate
Get the latest exchange rate from one currency to one or more others, optionally converting an amount.
Argument | Type | Default | Description |
| string |
| ISO 4217 base currency code, e.g. |
| string |
| Target currency code, or several comma-separated: |
| number |
| Amount of the base currency to convert |
Example result for amount=100, from_currency=USD, to_currency=EUR,GBP,JPY:
{
"amount": 100.0,
"base": "USD",
"date": "2026-09-30",
"rates": { "EUR": 88.07, "GBP": 75.262, "JPY": 15700 }
}With an amount, each value in rates is the converted amount, not the unit rate.
list_currencies
List every currency code the API supports, with its name. No arguments.
{ "AUD": "Australian Dollar", "BRL": "Brazilian Real", "CAD": "Canadian Dollar", "...": "..." }HTTP endpoints
Method & path | Purpose |
| MCP endpoint (Streamable HTTP, JSON responses) |
| Liveness/readiness probe, returns |
Configuration
All settings come from environment variables.
Variable | Default | Description |
|
| Upstream API base URL; point it at a self-hosted Frankfurter if you have one |
|
| Upstream request timeout, in seconds |
|
| Bind address |
|
| Listen port |
|
|
|
Run it
Prerequisites
Python 3.10 or later (the Docker image uses 3.12)
Outbound HTTPS access to
api.frankfurter.app
Locally
git clone https://github.com/harsha1979/currency-mcp.git
cd currency-mcp
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python server.pyThe server listens on http://localhost:8000/mcp. To check that it's up:
curl -s localhost:8000/health
# {"status":"ok"}With Docker
docker build -t currency-mcp:0.1.0 .
docker run --rm -p 8000:8000 currency-mcp:0.1.0Test it
Smoke-test script
scripts/test_client.py uses the official MCP Python client to connect, list the tools and call each one:
.venv/bin/python scripts/test_client.py # defaults to http://localhost:8000/mcp
.venv/bin/python scripts/test_client.py http://host:port/mcpExpected output (the rates will differ):
Connecting to http://localhost:8000/mcp
Server: currency-exchange, protocol 2025-11-25
Tools: get_latest_exchange_rate, list_currencies
get_latest_exchange_rate(USD->EUR) isError=False
{ "amount": 1.0, "base": "USD", "date": "2026-09-30", "rates": { "EUR": 0.88067 } }
...MCP Inspector
npx @modelcontextprotocol/inspectorIn the Inspector UI, set the transport to Streamable HTTP, set the URL to http://localhost:8000/mcp, click Connect, then open Tools to run them.
curl
The MCP protocol is plain JSON-RPC over HTTP, so curl works too. Because the server is stateless, you can call a tool directly without an initialize handshake first:
# List tools
curl -s -X POST localhost:8000/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2025-06-18' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Convert 100 USD to EUR and GBP
curl -s -X POST localhost:8000/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2025-06-18' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_latest_exchange_rate","arguments":{"from_currency":"USD","to_currency":"EUR,GBP","amount":100}}}'Use it from an MCP client
Claude Code
claude mcp add --transport http currency http://localhost:8000/mcpThen ask, for example: "Using the currency tool, convert 500 EUR to USD and JPY."
Claude Desktop, or other clients that only support stdio
Use the mcp-remote bridge in the client's config file (for Claude Desktop, claude_desktop_config.json):
{
"mcpServers": {
"currency": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:8000/mcp"]
}
}
}Any client that supports Streamable HTTP
Point it at http://<host>:<port>/mcp with the transport set to Streamable HTTP. No authentication is built in (see Security notes).
Deploy to Kubernetes (EKS)
k8s/deployment.yaml creates:
the
internal-mcpnamespacea
currency-mcpDeployment that runs as a non-root user with a read-only root filesystem, has/healthprobes and small resource requestsa
currency-mcpClusterIP Service on port 80
# 1. Build and push the image to ECR
AWS_ACCOUNT=<account-id>; AWS_REGION=<region>
REPO=$AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com/currency-mcp
aws ecr create-repository --repository-name currency-mcp --region $AWS_REGION
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com
docker buildx build --platform linux/amd64 -t $REPO:0.1.0 --push . # match your node architecture
# 2. Set `image:` in k8s/deployment.yaml to $REPO:0.1.0, then apply it
kubectl apply -f k8s/deployment.yaml
kubectl -n internal-mcp rollout status deploy/currency-mcp
# 3. Quick test through a port-forward
kubectl -n internal-mcp port-forward svc/currency-mcp 8000:80
.venv/bin/python scripts/test_client.py http://localhost:8000/mcpTo scale out, raise replicas. Stateless mode means no sticky sessions are needed.
The Service is ClusterIP, so it's only reachable inside the cluster. To expose it, add an Ingress (for example with the AWS Load Balancer Controller) or change the Service type to LoadBalancer. Pods need outbound internet access (for example through a NAT gateway) to reach api.frankfurter.app.
Security notes
The server has no authentication. Keep it on an internal network, or put it behind a gateway that handles auth and rate limiting before exposing it.
It is read-only: it only makes outbound
GETrequests to the configured Frankfurter URL.
Project layout
.
├── server.py # MCP server: tools, /health route, Streamable HTTP startup
├── requirements.txt # mcp, httpx, uvicorn
├── Dockerfile # python:3.12-slim, runs as UID 10001
├── k8s/deployment.yaml # Namespace, Deployment and Service
└── scripts/test_client.py # MCP client smoke testLicense
This server cannot be deployed
Maintenance
Related MCP Connectors
Live & historical FX rates and currency conversion for AI agents. No API keys.
Live & historical FX rates and currency conversion for AI agents. No API keys.
Convert currencies, get FX rates, and query historical ECB exchange rate data.
Live and historical FX rates (ECB via Frankfurter) — paid per call (x402/credits), 2 tools
Related MCP Servers
- AlicenseAqualityBmaintenanceProvides real-time foreign-exchange rates, historical data, and multi-currency lookups to MCP-compatible AI coding assistants like Claude Code and Cursor.492 npmMIT
- FlicenseCqualityDmaintenanceEnables real-time currency exchange rate queries between any two currencies using ExchangeRate API, with automatic triggering by AI models through the MCP protocol.11-
- AlicenseNot gradedqualityAmaintenanceConvert currencies, get FX rates, and query historical ECB exchange rate data via MCP.278 npm1Apache 2.0
- AlicenseAqualityCmaintenanceEnables currency conversion and exchange rate lookups using real ECB reference rates, free and without an API key. Supports natural language queries like 'convert 100 USD to EUR' or 'what's the exchange rate between GBP and JPY?'.2MIT