关于 exchange-rates-mcp-server
exchange-rates-mcp-server 提供 Frankfurter v1 每日汇率数据,可用于货币换算、汇率查询和历史序列分析。DataCanvas 可暂存较长序列,供只读 SQL 查询。
主要功能
- 01发现支持的货币
- 02获取当前和历史汇率
- 03换算货币金额
- 04查询每日汇率时间序列
- 05暂存序列供 SQL 分析
适用场景
- 01按最新或历史汇率换算金额
- 02查询特定日期的货币对汇率
- 03查看一段时间内的汇率变化
- 04使用 SQL 汇总暂存的汇率数据
MCP 相关
通过 STDIO 或 Streamable HTTP 向 MCP 客户端提供货币与时间序列工具及资源。DataCanvas 可提供可选暂存和 SQL 工具,也提供公共托管端点。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://exchange-rates.caseyjhand.com/mcpclaude mcp add --transport http cyanheads-exchange-rates https://exchange-rates.caseyjhand.com/mcp{"mcpServers":{"cyanheads-exchange-rates":{"type":"http","url":"https://exchange-rates.caseyjhand.com/mcp"}}}在 Cursor 中打开{"servers":{"cyanheads-exchange-rates":{"type":"http","url":"https://exchange-rates.caseyjhand.com/mcp"}}}在 VS Code 中打开{"mcpServers":{"cyanheads-exchange-rates":{"type":"http","url":"https://exchange-rates.caseyjhand.com/mcp"}}}{"mcpServers":{"cyanheads-exchange-rates":{"serverUrl":"https://exchange-rates.caseyjhand.com/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
7此工具无需参数。
详细介绍
@cyanheads/exchange-rates-mcp-server
通过 MCP 转换货币、获取外汇汇率,并查询 Frankfurter v1 历史汇率数据。支持 STDIO 或 Streamable HTTP。
概述
获取 Frankfurter v1 每日汇率、当前货币信息,以及从 1999-01-04 起的历史数据。转换金额、查询特定时点或历史汇率、汇总完整日期范围的数据,并对暂存的时间序列运行 SQL。历史数据涵盖已停用货币,并会根据请求的数据进行检查。响应中包含 source: "Frankfurter v1" 和 rate_type: "Daily rate (Frankfurter v1)";v1 返回的数据不会标明具体的数据提供方。无需 API 密钥。
工具
| 工具 | 描述 |
|---|---|
fx_list_currencies | 发现当前支持的货币,提供完整名称和最新提供汇率的日期 |
fx_get_rates | 指定基础货币在最新日期或历史日期的所有汇率快照 |
fx_get_rate | 指定货币对在最新日期或历史日期的汇率 |
fx_convert_currency | 按最新汇率或历史汇率转换两种货币之间的金额 |
fx_get_timeseries | 指定货币对在日期范围内的历史每日汇率 |
fx_dataframe_describe | 列出先前调用 fx_get_timeseries 暂存的 DataCanvas 表和列 |
fx_dataframe_query | 对暂存的 DataCanvas 表运行只读 SQL SELECT 查询 |
fx_dataframe_drop | 移除一个暂存的 DataCanvas 表或视图(需主动启用,且具有破坏性) |
DataCanvas(DuckDB)默认开启。设置 CANVAS_PROVIDER_TYPE=none 可将其关闭:此时,三个 fx_dataframe_* 工具会从 tools/list 中移除;若省略 canvas_id,fx_get_timeseries 会直接返回范围内的数据;显式暂存则会失败并返回 canvas_unavailable。Claude Desktop bundle(.mcpb)不包含 DuckDB 的原生绑定,因此运行时会关闭 DataCanvas。fx_dataframe_drop 还需要设置 FX_ENABLE_CANVAS_DROP=true。
四个汇率和货币转换工具都接受 base 作为 base_currency 的别名;货币对工具还接受 quote 作为 quote_currency 的别名。同一字段只能发送其中一种写法,不能同时发送。公布的架构使用规范名称。
货币值(包括 symbols)会先去除首尾空格,再转换为大写。指定日期接受 YYYY-MM-DD 或字面值 latest(也是默认值);范围日期必须使用 YYYY-MM-DD。公布的日期模式会保留处理程序级别的 invalid_date_format 错误,用于处理格式错误、空白或日历上不存在的日期。对于未来日期范围错误,建议将 end_date 设为今天或更早的日期;end_date 仍为必填项。
资源
| 资源 | 描述 |
|---|---|
fx://currencies | 所有受支持货币的稳定参考文档 |
fx://rates/latest/{base} | 基础货币最新汇率快照的稳定 URI |
所有资源数据也可通过工具访问——程序化访问时可使用 fx_list_currencies 或 fx_get_rates。
取消工具调用或资源读取会停止其上游获取、响应正文读取和重试退避。未取消的请求仍会重试两次,且每次尝试的超时时间为 10 秒。
功能参考
fx_list_currencies 工具
- 无输入参数
- 返回
currencies: [{ code, name }]、count、source和as_of(最新提供每日汇率的日期),并按代码字母顺序排序 - 此工具用于发现当前支持的货币。历史请求会根据未经筛选的 EUR 快照或原始提供范围进行验证,因此已停用的代码在有观测数据的日期仍可使用。未知代码会返回
unsupported_currency,并提供已接受和已拒绝代码的排序列表以及输入字段;当前货币若没有历史观测数据,则会返回upstream_no_data。
fx_get_rates 工具
base_currency为必填项;date为可选项(默认为最新日期,历史数据始于 1999-01-04,不接受未来日期);可选的symbols数组可缩小响应范围,且至少须包含一个代码- 返回
rates映射(报价货币代码 → 汇率)、实际的rate_date,以及当周末或节假日的请求日期回退到前一个工作日时返回的date_snapped: true - 在
symbols中指定基础货币是有效的——工具会在本地以汇率 1 作答,而不会将请求发送到上游 - 类型化错误:
invalid_date_format、unsupported_currency、date_out_of_range、upstream_no_data
fx_get_rate 工具
- 必须提供
base_currency、quote_currency;date可选(默认最新日期,历史数据自 1999-01-04 起,不支持未来日期) - 返回
rate、rate_date;如果请求的日期是周末或节假日并回退到前一个工作日,则还会返回date_snapped: true - 每个货币对都使用同一份未经筛选的 EUR 快照中的报价货币 EUR 腿 / 基础货币 EUR 腿计算,不进行中间舍入;EUR 的腿为 1。同种货币的货币对仅在实际观测日期返回 1。
- 类型化错误:
invalid_date_format、unsupported_currency、date_out_of_range、upstream_no_data
fx_convert_currency 工具
- 必须提供
base_currency、quote_currency、amount(必须大于 0);date可选(默认最新日期,历史数据自 1999-01-04 起,不支持未来日期) - 通过一次上游调用处理 EUR↔任意货币、任意货币↔EUR 以及交叉汇率货币对(例如 USD→JPY)
- 返回
quote_amount(舍入至小数点后 6 位)、rate、rate_date、date_snapped,以及用于溯源的rate_type和source - 类型化错误:
invalid_date_format、unsupported_currency、date_out_of_range、upstream_no_data
fx_get_timeseries 工具
- 必须提供
base_currency、quote_currency、start_date、end_date(历史数据自 1999-01-04 起,不支持未来日期,且 start ≤ end);canvas_id为可选项,可追加到现有画布 - 内联结果每页最多包含 500 个发布日;
rate_count始终表示整个范围的总数;若返回truncated: true,可使用next_start_date继续获取 - 超过
FX_TIMESERIES_CANVAS_THRESHOLD_DAYS(默认 90 天)的范围会被考虑用于 DataCanvas 暂存。超过 40,000 个字符预览预算的时间序列会返回spilled: true、canvas_id和table_name;较小的时间序列以及设置了CANVAS_PROVIDER_TYPE=none的部署会以内联方式分页 - 同种货币的货币对在实际观测日期返回 1。每条非恒等关系记录都必须在同一日期具有两条 EUR 腿;缺少的腿绝不会用于伪造观测值。成员资格使用所有提供的原始日期,再进行包含边界的截取,从而保留仅包含周末的空窗口。
- 空范围(仅包含周末或节假日)会返回
rate_count: 0和说明性notice,可与错误区分 - 能放入内联预览的长范围会释放其新获取的画布;暂存失败或取消时也会释放。提供的画布会保持可用。
- 明确指定
canvas_id时,始终会暂存完整结果,包括较短范围和空范围,并使用rate DOUBLE。每个表名都会附加一个新的标识符后缀;重复和并发调用都会保留所有先前的表。未知 ID 会报错canvas_not_found;DataCanvas 已禁用时会报错canvas_unavailable。预览包含所有观测值时,truncated为 false。 - 非空结果会包含
summary: { mean, min: { rate, date }, max: { rate, date }, first: { rate, date }, last: { rate, date }, change_pct }。该摘要涵盖请求区间内分页或暂存前的所有观测值;续页调用则汇总其较短的请求区间。极值相同时选择最早日期;单个观测值和恒等关系的变动均为 0。空序列不包含 summary。Mean 是每日观测值的非加权算术平均值,并非税务机关或会计政策的规定。
fx_dataframe_describe 工具
- 必须提供
canvas_id(来自先前的fx_get_timeseries调用) - 返回每个已暂存表的
kind、row_count和列架构(name、type、nullable),以及expires_at - 在调用
fx_dataframe_query前必须先执行;当CANVAS_PROVIDER_TYPE=none时不注册 - ID 不存在或已过期时返回
canvas_not_found
fx_dataframe_query 工具
- 必须提供
canvas_id和只读 SQLquery;row_limit必须是正整数(默认值为 150)。超过 10,000 的值会被限制为 10,000,并在通知中列出请求值和生效值;限制和截断通知会同时出现,分页指引使用生效值。 - 支持聚合、GROUP BY、窗口函数,以及跨多个
fx_get_timeseries调用所创建表的 JOIN - 返回的行数不超过
row_limit;truncated: true和notice会提供下一页的ORDER BY <column> LIMIT <n> OFFSET <m>格式;稳定分页必须使用 ORDER BY - Markdown 表格单元格中的管道符、尖括号和换行符会经过转义,以确保它们留在各自的单元格内;
structuredContent保留原始值 - 当
CANVAS_PROVIDER_TYPE=none时不注册;失败时返回类型化错误:canvas_not_found、missing_table、invalid_query
fx_dataframe_drop 工具
- 必须提供
canvas_id和准确的table_name(来自fx_dataframe_describe) - 移除一个暂存表或视图;上游汇率数据不受影响,可通过
fx_get_timeseries重新暂存该序列 - 根据表是否存在,返回
dropped: true/false - 除非设置
FX_ENABLE_CANVAS_DROP=true,否则此工具处于禁用状态——此时它不会出现在tools/list中,仅在启动日志和 HTTP 入口页中以名称和启用提示的形式显示;当CANVAS_PROVIDER_TYPE=none时也不注册
fx://currencies 资源
- 无参数;以
application/json格式返回当前发现结果,其中包含currencies、count、source、as_of——与fx_list_currencies返回的负载相同 - 作为单个静态资源列出
fx://rates/latest/{base} 资源
base是 URI 中的 ISO 4217 货币代码- 返回最新已提供的每日快照中的
base_currency、rate_date、rates映射、rate_type和source - 列出四个示例 URI(EUR、USD、GBP、JPY)作为发现提示
功能
基于 @cyanheads/mcp-ts-core 构建:支持 stdio 和 Streamable HTTP 传输、可插拔身份验证(none / jwt / oauth)、可切换存储(in-memory、filesystem、Supabase、Cloudflare KV/R2/D1),并提供结构化日志记录和可选的 OpenTelemetry 跟踪。
汇率数据:
- 通过 Frankfurter v1 获取无需密钥的每日数据
- 每个快照或日期范围仅请求一次未筛选的 EUR 数据,并在本地计算交叉汇率;同一响应也会提供历史成员信息
- 周末/节假日日期语义:当 API 返回的日期与请求日期不同时,会显示
date_snapped - 在货币有观测到的发布数据的日期,本地计算该货币的身份汇率为 1
- 超出内联预览预算的长时间序列会暂存到 DataCanvas(默认启用 DuckDB),以便对完整日期范围进行 SQL 聚合
便于 Agent 使用的输出:
- 每个响应都包含汇率来源信息——
rate_type、source、rate_date和date_snapped,便于 Agent 判断可信度和时效性 - 结构化错误契约——类型化的
reason字段(unsupported_currency、date_out_of_range、invalid_query等)让调用方能够根据失败类型分支处理,无需解析字符串 - 有界响应——内联时间序列分页通过
next_start_date继续,SQL 结果限制为row_limit,因此没有任何调用会返回无界负载 - 成功路径的
notice补充信息——说明序列为空、分页序列的后续位置,或哪些工具可以读取已暂存的序列,避免将合法的零结果误认为失败
开始使用
公共托管实例
公共实例位于 https://exchange-rates.caseyjhand.com/mcp,无需安装。通过 Streamable HTTP 将任意 MCP 客户端连接到该实例:
{
"mcpServers": {
"exchange-rates-mcp-server": {
"type": "streamable-http",
"url": "https://exchange-rates.caseyjhand.com/mcp"
}
}
}
自托管 / 本地
无需 API 密钥——Frankfurter 无需密钥。在 MCP 客户端配置文件中添加以下内容:
{
"mcpServers": {
"exchange-rates-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/exchange-rates-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
或使用 npx(无需 Bun):
{
"mcpServers": {
"exchange-rates-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/exchange-rates-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
或使用 Docker:
{
"mcpServers": {
"exchange-rates-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/exchange-rates-mcp-server:latest"
]
}
}
}
DataCanvas(DuckDB)默认启用,适用于长时间序列的 SQL 分析。若要关闭,请添加 CANVAS_PROVIDER_TYPE=none:数据框工具会从 tools/list 中移除;省略 canvas_id 时,fx_get_timeseries 会以内联方式分页。显式请求暂存时会失败,并返回 canvas_unavailable。
{
"mcpServers": {
"exchange-rates-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/exchange-rates-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"CANVAS_PROVIDER_TYPE": "none"
}
}
}
}
若要使用 Streamable HTTP,请设置传输方式并启动服务:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
先决条件
- Bun v1.4.0 或更高版本(或 Node.js v24+)。
- 无需 API 密钥——Frankfurter 免费且无需密钥。
安装
- 克隆仓库:
git clone https://github.com/cyanheads/exchange-rates-mcp-server.git
- 进入目录:
cd exchange-rates-mcp-server
- 安装依赖:
bun install
- 配置环境:
cp .env.example .env
# edit .env as needed (all vars are optional — no keys required)
配置
所有配置都会在启动时通过 Zod 架构验证。环境变量:
| 变量 | 描述 | 默认值 |
|---|---|---|
FRANKFURTER_BASE_URL | Frankfurter API 基础 URL。可覆盖此值以进行本地测试或使用自托管实例。 | https://api.frankfurter.dev/v1 |
FX_TIMESERIES_CANVAS_THRESHOLD_DAYS | 超过此天数范围时,fx_get_timeseries 会考虑自动暂存到 DataCanvas;若未提供 canvas_id,适合预览的序列仍以内联方式返回。显式提供 canvas_id 始终会请求暂存。 | 90 |
FX_ENABLE_CANVAS_DROP | 启用具有破坏性的 fx_dataframe_drop 工具。默认关闭:不会注册该工具,也不会将其列入 tools/list。 | false |
CANVAS_PROVIDER_TYPE | Canvas 引擎。duckdb 会为 fx_get_timeseries 启用长范围数据溢出所用的 DataCanvas,并注册三个 fx_dataframe_* 工具;none 会禁用它们,并将其从 tools/list 中移除。空值会采用默认值。.mcpb 包设置为 none。 | duckdb |
MCP_TRANSPORT_TYPE | 传输方式:stdio 或 http。 | stdio |
MCP_HTTP_PORT | HTTP 服务端口。 | 3010 |
MCP_SESSION_MODE | HTTP 会话模式:auto、stateful 或 stateless。代码中将服务声明为 stateless——这里没有处理器会在调用过程中向客户端请求输入,因此无需会话即可恢复;设置此变量会覆盖该声明。 | stateless |
MCP_AUTH_MODE | 认证模式:none、jwt 或 oauth。 | none |
MCP_REQUEST_STATE_KEY | 可选密钥(≥ 32 字节,所有实例必须相同),用于在多轮交互间封装处理器返回的 requestState。这里没有任何工具会返回此值。 | — |
MCP_LOG_LEVEL | 日志级别(RFC 5424:debug、info、notice、warning、error)。同时也是镜像到客户端的日志最低级别。 | info |
LOG_TOOL_FAILURE_PAYLOADS | 记录每次失败的工具调用参数和结果(仅按键名进行脱敏)。 | false |
LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES | LOG_TOOL_FAILURE_PAYLOADS 的单个负载上限,单位为 UTF-8 字节。 | 16384 |
OTEL_ENABLED | 启用 OpenTelemetry 插桩。 | false |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP 基础 URL;追踪数据发送到 /v1/traces,指标发送到 /v1/metrics。 | — |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | 可选的 OTLP 日志导出端点(例如 http://localhost:4318/v1/logs);基础端点不会启用日志导出。 | — |
完整的可选覆盖项(包括存储、会话和遥测变量)请参阅 .env.example。
运行服务
本地开发
-
构建并运行:
bun run rebuild bun run start:stdio # or bun run start:http -
运行检查和测试:
bun run devcheck # Lint, format, typecheck, security, changelog sync bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t exchange-rates-mcp-server .
docker run --rm -p 3010:3010 exchange-rates-mcp-server
Dockerfile 默认使用 HTTP 传输、无状态会话模式,并将日志写入 /var/log/exchange-rates-mcp-server。默认会安装 OpenTelemetry 对等依赖;如需省略这些依赖,请使用 --build-arg OTEL_ENABLED=false 构建。生产依赖(包括 DuckDB 的原生绑定)会在单独的 deps 阶段针对目标平台交叉安装,因此多架构构建无需在仿真环境下运行 Bun,运行时映像仅包含 node_modules 和 dist/。
项目结构
| 目录 | 用途 |
|---|---|
src/index.ts | createApp() 入口点 — 注册工具、资源和画布访问器。 |
src/config/ | 使用 Zod 解析并验证服务器专属环境变量。 |
src/mcp-server/tools/ | 工具定义(*.tool.ts)— fx_* 工具。 |
src/mcp-server/resources/ | 资源定义 — fx://currencies 和 fx://rates/latest/{base}。 |
src/services/frankfurter/ | Frankfurter HTTP 客户端、重试逻辑和领域类型。 |
src/services/canvas/ | 模块级 DataCanvas 访问器,用于 fx_get_timeseries 溢出处理。 |
src/utils/ | 输出辅助工具 — 为 fx_dataframe_query 转义 Markdown 表格单元格。 |
tests/ | 与 src/ 结构对应的单元测试和集成测试。 |
docs/ | 设计文档和想法笔记。 |
开发指南
开发指南和架构规则请参阅 CLAUDE.md。简要来说:
- 处理程序抛出异常,框架负责捕获 — 工具逻辑中不要使用
try/catch - 使用
ctx.log进行请求级日志记录,使用ctx.state进行租户级存储 - 通过
src/mcp-server/*/definitions/index.ts中的聚合导出注册新工具和资源 - 封装外部 API 调用:验证原始数据 → 规范化为领域类型 → 返回输出架构;绝不伪造缺失字段
- 在每个响应中保留
rate_type: "Daily rate (Frankfurter v1)"和source: "Frankfurter v1"来源信息
贡献
欢迎提交问题。提交前请运行检查和测试:
bun run devcheck
bun run test
许可证
Apache-2.0 — 详情请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。